|
|
# STM32 Clocks
|
|
|
|
|
|
:notes: _Why'd you have to go and make things so complicated?_ :notes:
|
|
|
|
|
|
One classical problem that pops up when developing a device driver is having to set the **baudrate** of a communication peripheral (e.g. SPI) or the **period** of a Timer. In these cases, in fact, the peripheral typically provides just a `DIV` or `PRESCALER` register. The baudrate for that specific peripheral is then defined as:
|
|
|
|
|
|
$$
|
|
|
baudrate = \frac{input\_clock}{Prescaler}
|
|
|
$$
|
|
|
|
|
|
But wait, what exactly _is_ the input clock? Where does it come from? Were can I read or adjust its value? This page tries to address these questions for a typical `stm32_f4xx` micro using MIOSIX and boardcore.
|
|
|
|
|
|
## Internal Buses: AHB, APBx, WTF?
|
|
|
|
|
|
STM32 microcontrollers are equipped with multiple buses for internal communication.
|
|
|
These buses are part of the **AMBA** (Advanced Microcontroller Bus Architecture) standard provided by ARM. Here is a general diagram of the buses:
|
|
|
|
|
|
<div style="text-align:center"><img src="images/clocks/amba-bus.png" /><br/><i>Typical bus hierarchy for STM32F4xx</i></div>
|
|
|
<br>
|
|
|
|
|
|
Let's read it from right to left:
|
|
|
|
|
|
- Each peripheral (green) is connected to either **APB1** (low speed _Advanced Peripheral Bus_) or **APB2** (high speed).
|
|
|
- **Note**: This is an important piece of information to have when choosing between different peripherals of the same type, e.g. since TIM1 is on APB2 and TIM2 is on APB1 they will have different maximum periods, depending on how the Bus prescaler has been set.
|
|
|
- These peripheral buses are both connected to the main **AHB** (_Advanced High-speed Peripheral Bus_), which also interconnects the Processor with the DMA controller and the high-bandwidth memories (on-chip SRAM and Flash).
|
|
|
|
|
|
The speed of `APB1` and `APB2` can be set independently as some **fraction** of the speed of the `AHB` bus (note that all of these buses have a different maximum working frequency).
|
|
|
|
|
|
The AHB's speed itself is derived from the **System Clock** (`SYSCLK`), divided by a certain prescaler.
|
|
|
|
|
|
So, what's the speed of the System Clock then? Well well...
|
|
|
|
|
|
## Clock Tree
|
|
|
|
|
|
This is (a portion of) the **clock tree** that you can find in the [Holy Bible](https://drive.google.com/file/d/0B41AbAqKq4JnaWxRQWRaalp1Y3M/view?usp=sharing), a.k.a. the Discovery reference manual, in _Section 7.2 Clocks_.
|
|
|
|
|
|

|
|
|
|
|
|
From the same manual:
|
|
|
|
|
|
> Three different clock sources can be used to drive the system clock (SYSCLK):
|
|
|
> • HSI oscillator clock
|
|
|
> • HSE oscillator clock
|
|
|
> • Main PLL (PLL) clock
|
|
|
|
|
|
Hence, for each board the clock can be generated either by an **internal RC** or some **external source** that you can connect externally to the corresponding pins. The frequency of this source can then be **upscaled** by the PLLs and finally used as **System Clock**. This is the main clock of the processor, and will be used to derive the AHB speed and consequently the APBs speeds.
|
|
|
|
|
|
## Where is this stuff in the code?
|
|
|
|
|
|
If you want to do stuff from scratch, you can access the `RCC` registers to:
|
|
|
|
|
|
- choose which clock source to use
|
|
|
- set the PLL multiplier value
|
|
|
- set the AHB prescaler `RCC_CFGR->HPRE`
|
|
|
- set the APB prescaler `RCC_CFGR->PPREx`
|
|
|
- God knows what else
|
|
|
|
|
|
Luckily enough, this kind of stuff is handled by MIOSIX, which uses the vendor-provided libraries to initialize the clocks correctly. Basically, what you do in MIOSIX is:
|
|
|
|
|
|
- Define the input clock type and speed and the desired `SYSCLCK` in `miosix/config/Makefile.inc`
|
|
|
|
|
|
```Makefile
|
|
|
## Select clock frequency (HSE_VALUE is the xtal on board, fixed)
|
|
|
CLOCK_FREQ := -DHSE_VALUE=8000000 -DSYSCLK_FREQ_168MHz=168000000
|
|
|
```
|
|
|
|
|
|
- For stm32f4xx boards, the bus prescalers are set in `/miosix/arch/common/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c`
|
|
|
|
|
|
```c
|
|
|
/* HCLK = SYSCLK / 1*/
|
|
|
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
|
|
|
|
|
|
/* PCLK2 = HCLK / 2*/
|
|
|
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
|
|
|
|
|
|
/* PCLK1 = HCLK / 4*/
|
|
|
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
|
|
|
```
|
|
|
|
|
|
And that's pretty much it.
|
|
|
|
|
|
> :warning: **IMPORTANT:** Clocks and startup
|
|
|
> On reset the 16 MHz internal RC oscillator is selected as the default CPU clock.
|
|
|
> The 16 MHz internal RC oscillator is factory-trimmed to offer 1% accuracy over the full temperature range.
|
|
|
> The application can then select as system clock either the RC oscillator
|
|
|
> or an external 4-26 MHz clock source.
|
|
|
> This clock can be monitored for failure. **If a failure is detected, the system automatically switches back to the internal RC oscillator** and a software interrupt is generated (if enabled).
|
|
|
|
|
|
## References
|
|
|
|
|
|
- A nice introduction on architectural aspects of STM32 boards: <a href="http://courses.eees.dei.unibo.it/mphseng-old/wp-content/uploads/2018/04/09_MCU_Lecture3_Interconnects_FRANCESCO.pdf"> Unibo course on STM32 </a> |