In this tutorial I will show how to configure clocks on the STM32F4xx. Clocks drive the connected peripherals such as Timers, GPIOs, USB, etc. The System Timer is an independent timer that has a crucial role in synchronising tasks and delays within an embedded application. I will show how to operate an LED on the discovery board using HAL (Hardware Abstraction Layer) libraries from STM. They provide the implementation of register-level operations and simplify writing embedded software. Of course, different microcontrollers’ manufacturers provide their own libraries but in general they are quiet similar. So once you set them up once, you will find it easy to do the same for any other microcontroller you wish to use! Finally, I will show how you can verify that your clocks are configured correctly. The code is provided here. You can compile by typing make from tut2_clocks directory.
Clock distribution
All STM32F407x microcontrollers have two of each buses – AHB (Advanced High-Performance Bus) and APB (Advanced Peripheral Bus) that drive the whole system. For example, if you want to use GPIO port A, you have to enable AHB1 bus. But if you also want to use Timer 2, you have to enable APB2. You can check the whole diagram on page 19 of the STM32F407xx datasheet. All of those are clocked by either of the two clock sources:
- HSE OSC (High-speed external oscillator)
- HSI OSC (High-speed internal)
You can pass the clocking signal from both of them through PLL (Phase-locked loop) circuit. This will multiply the clock frequency. To illustrate, the diagram below shows how clock distribution looks like. The full clock tree diagram of an STM32F407xx is provided in the reference manual on page 216. In general, HSE requires adding an external oscillator to your board but it gives more accurate timing comparing to the HSI. Some peripherals such as USB require HSE for high speed operations (USB on-the-go full-speed).
Clocks configuration with HAL libraries
HAL libraries are really useful because they provide functions and structures to operate peripherals. Therefore, you will not need to develop register-level drivers to take advantage of your peripherals such as SPI, USB, I2C, etc. This boosts up your productivity and makes your code more scalable and portable. The copied version of HAL drivers is in the root directory of the tutorial’s repository. You can check the source code and understand how peripheral functions are implemented. In this part of the tutorial, we will mainly use GPIOs and RCC (Reset and clock control).
So how do we set the clock correctly? The system clock that is driving almost all other clocks (APBx, HCLK, Cortex System timer) is referred to as SYSCLK. It is generated from either HSI, HSE or PLL, as shown in the above diagram. Also, it cannot exceed the value of 168 MHz on some of STM32F4xx microcontrollers which is our case. The frequency of SYSCLK depends on the application. If you are doing heavy computations, controlling a motor and enabling multiple sensors in a time-critical manner then you will want to make it fast. But heavy workload makes you sweat so expect the temperature to increase.
Let’s configure our SYSCLK to run at 100 MHz and use the external 8 MHz oscillator on the discovery board. We will need to use PLL as the clock source for SYSCLK. Otherwise, SYSCLK would be either equal to HSE frequency of 8 MHz or HSI frequency of 16 MHz. Basic calculations for PLL module tell us:
fVCO = fOSC*PLLN/PLLM
fPLL = fVCO/PLLP
fPLL/fOSC = 6.25 = PLLN/(PLLM*PLLP)
Let’s set up clocks
You can set all these values individually by writing to the RCC_PLLCFGR register. Those values cannot be any values that would fit the equation – there are some recommendations for setting them for STM32F407xx (page 226). For example, to eliminate the PLL jitter, the input clock for PLL should be equal to 2 MHz. Also, VCO output should be between 100 and 432 MHz. We can then come up with the values, e.g. PLLN= 100, PLLM= 8, PLLP= 2.
The source code that initialises clocks is implemented in clocks.c. We are using RCC API from HAL libraries and initialising structures to setup RCC. Here is a part of clocks.c showing clocks initialisation
static RCC_ClkInitTypeDef rccClkInstance =
{
.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2,
.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK,
.AHBCLKDivider = RCC_SYSCLK_DIV1,
.APB1CLKDivider = RCC_HCLK_DIV1,
.APB2CLKDivider = RCC_HCLK_DIV1,
};
static RCC_OscInitTypeDef clock_setup = {
.OscillatorType = RCC_OSCILLATORTYPE_HSE,
.HSEState = RCC_CR_HSEON,
.PLL = {
.PLLState = RCC_PLL_ON,
.PLLSource = RCC_PLLSOURCE_HSE,
.PLLM = 8,
.PLLN = 100,
.PLLP = RCC_PLLP_DIV2,
}
};
void clocks_initialise(void) {
HAL_RCC_OscConfig(&clock_setup);
HAL_RCC_ClockConfig(&rccClkInstance, 5);
...
}
You can see that by using HAL library we do not have to write any values directly to registers. All we need to do is to call HAL_RCC_OscConfig and HAL_RCC_ClockConfig functions. And if you decide to change your microcontroller? This code will likely stay as it is with only few values to be changed.
What is SysTick?
So now we have the clocks set up and we want to blink an LED at 2 Hz. We have two choices – polling or interrupting. By polling we would continuously check a timer’s value until 500 ms had passed. This would be wasting a lot of our precious time by doing nothing. Interrupting, however, would notify us that a timing event had occured asynchronously. And this is how we will do it – using SysTick interrupt.
SysTick is basically the Cortex System Timer. You can use it with HAL libraries to create delays. It can also be used as the main driver in scheduling tasks for embedded OSs such as RTOS. We have to configure it to work with either AHB clock divided by 8 (as shown in Figure 1) or with no division. In our case, it will run on the same frequency as our SYSCLK – 100 MHz. By setting its calibration value we can adjust the time period, which will count down and trigger an interrupt once it reaches zero. This is also implemented in clocks.c
void clocks_initialise(void) {
...
/// configure SysTick
HAL_NVIC_DisableIRQ(SysTick_IRQn);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_NVIC_EnableIRQ(SysTick_IRQn);
...
}
The first line in above code disables the interrupt for SysTick. SysTick_IRQn is a defined value referring to SysTick. After that, we configure SysTick source clock to be SYSTICK_CLKSOURCE_HCLK. This is equal to AHB clock value NOT divided by 8, as I mentioned earlier. Finally, we configure the calibration value (or period value) to be equal to the frequency of HCLK divided by 1000 (equivalent to 1kHz). By doing so, the SysTick will always trigger after 1ms.
Handling SysTick
So what happens next? Once SysTick interrupt occurs, the processor saves the context of currently executing task and the stack. Then it searches for the address corresponding to the active interrupt in the Interrupt Vector Table. The content of this table is defined inside the startup script – startup_stm32f407xx.s. Let’s take a look!
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* External Interrupts */
...
For every defined interrupt, the vector table has an address of the handling function. You can see that SysTick_Handler is defined among interrupt handlers. But how can you define SysTick_Handler and why you don’t need to implement every handler on the list? The answer lies also in the startup script, a bit below in the code as shown here
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
SysTick_Handler is defined with the weak symbol and assigned to a Default_Handler function when startup script is compiled. Therefore, you can create another SysTick_Handler function with a strong symbol (no __weak keyword in function declaration) and compile an object file. During linking, a function declared with a strong symbol will link to SysTick_Handler. We can therefore implement SysTick_Handler in the main.c
void SysTick_Handler(void) {
if (++time_counter == 500) {
HAL_GPIO_TogglePin(GPIOD, led_gpio.Pin);
time_counter = 0;
}
}
When an interrupt triggers we increase the timer and toggle the LED every 500 counts. Since the handler is called every 1ms, we will toggle the LED at 2Hz.
Measuring execution time
How can you be sure that the interrupt is triggered at the desired frequency? How do you verify that the clocks are configured correctly and there is no issue with the connected oscillator? The simplest answer is: measure it on the oscilloscope. This way you can not only verify clocking sources but also precisely measure the execution of your system. I will demonstrate it easily by measuring how often SysTick interrupts By toggling any spare GPIO, which you have physical access to, you can measure the toggling frequency on the scope. The discovery kit provides access to many GPIOs – I will toggle C1 pin as implemented below. The result from the scope is shown in Figure 2.
void SysTick_Handler(void) {
/// toggle C1 to measure SysTick frequency on the scope
HAL_GPIO_TogglePin(GPIOC, measure_gpio.Pin);
if (++time_counter == 500) {
HAL_GPIO_TogglePin(GPIOD, led_gpio.Pin);
time_counter = 0;
}
}
No updates? This is very high quality documentation and I would love to read “Debugging with JATG” and further.
feel free to check it out now