Library 03- STM32F4 system clock and delay functions
In first tutorial about discovery board we were blinking led. But I said nothing about system clock speed. In while loop we just use
1 2 |
// Waste some tome for (i = 0; i < 500000; i++); |
for some delay, to actually see how led was blinking. We didn’t know at which clock speed our processors work and for first time, I think you didn’t even ask yourself.
May 26, 2015
A new version, 2.4 was just released. It features custom timers in library.
Custom timers are a way of having some tasks which have to be called periodically in “strange” timer intervals, like one task each 100ms, another each 107ms, third each 123ms and so on.
The way this works is that each time delay timer makes 1ms interrupt, interrupt handler checks for all created software “timers”. If their value, which is decreased in this systick handler reaches zero, callback function is called for user.
Timers allows custom features like custom reload value when callbacks happen and auto reload value is possible too. Auto reload feature means that when timer reaches zero, then timer will begin counting at start value if auto reload is enabled.
If auto reload is not enabled, when timer reaches zero, callback is called and timer become disabled. To start it back, you have to manually enable it.
Look for all functions in library or API documentation for STM32F4xx libraries provided from me.
November 28, 2014
From this moment, there is new delay system. First system used Systick timer to make an interrupts every 1us. This allows us great accuracy in microseconds but not so nice for processor and interrupts. Also, Systick can be used for RTOS and then it become incompatible for my delay.
For that purpose, lib has been changes. now you have 2 options for delay:
- Systick timer
- Custom (peripheral) timer
With Systick timer, there is 1ms interrupt (not 1us anymore) for counting time and for 1us delay there is just simple variable based counter (not so accurate). If you need Systick timer for RTOS (or anything else) then you can enable any timer on your MCU for delay. To activate custom timer, open defines.h project file and add lines below:
1 2 3 4 5 |
//Select custom timer for delay, here is TIM2 selected. //If you want custom TIMx, just replace number "2" for your TIM's number. #define TM_DELAY_TIM TIM2 #define TM_DELAY_TIM_IRQ TIM2_IRQn #define TM_DELAY_TIM_IRQ_HANDLER TIM2_IRQHandler |
In case above, TIM2 is used, but if you want your custom timer (TIM6 or TIM7, or anything) you can just replace number “2” with any custom number for your timer available in F4xx MCU.
This timer will also make an interrupts every 1ms but for microseconds delay, there will be better accuracy because delay just count timer’s ticks for proper value. For that purpose, you also need some additional libs:
- CMSIS
- STM32F4xx TIM
- MISC
- TM
- TM TIMER PROPERTIES
- TM TIMER PROPERTIES
GCC sucks!
It’s nice that is is free, but code compiled is totally different from ARM compiler (in Keil). So I suggest you, that you enable your timer for delay (Instead of Systick timer), otherwise your delay will not be accurate and you will have problems.
How to enable delay with custom timer is described above!
PS: If you are using ARM Compiler (Keil uVision) then you don’t need to use peripheral timer if you don’t want to.
Libraries where you might (not needed to) have problems if you are using ARM-GCC (Coocox, etc) compiler and variable-based delay for microseconds:
Up to 180MHz core clock speed
STM32F429 can go up to 180MHz. It has PLL (Phase-Locked Loop) inside to increase frequency from 8MHz external crystal which is on board. I will describe how you do this, step by step.
PS: On Clock speeds is a great tutorial how to do it with ST’s excel clock configuration tool, but this tool does not support frequencies larger than 168MHz, so we will do it manually.
STEP 1: Open your project file stm32f4xx.h and find code below (it should start at line 91):
1 2 3 |
#if !defined (HSE_VALUE) #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */ #endif /* HSE_VALUE */ |
Change it to:
1 2 3 4 |
#if !defined (HSE_VALUE) //We use 8MHz clock as external clock #define HSE_VALUE ((uint32_t)8000000) #endif /* HSE_VALUE */ |
STEP 2: Open your project file system_stm32f4xx.c and find (you can have other numbers, code should start at line 154):
1 2 3 4 5 6 |
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ #define PLL_M 25 #define PLL_N 336 /* SYSCLK = PLL_VCO / PLL_P */ #define PLL_P 2 |
Formula for system frequency is:
1 |
Core Clock = ((HSE_VALUE / PLL_M) * PLL_N) / PLL_P |
In our case (we want 180MHz) we will set these defines to:
1 2 3 4 5 6 |
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */ #define PLL_M 8 #define PLL_N 360 //Set this to 336 for 168MHz clock /* SYSCLK = PLL_VCO / PLL_P */ #define PLL_P 2 |
These will give us exactly 180MHz.
STEP 3: In the same file, find (line 174)
1 |
uint32_t SystemCoreClock = 168000000; |
and change it to
1 |
uint32_t SystemCoreClock = 180000000; |
STEP 4: In file startup_stm32f4xx.c uncomment line below (line 143):
1 |
//extern void SystemInit(void); /*!< Setup the microcontroller system(CMSIS) */ |
to
1 |
extern void SystemInit(void); /*!< Setup the microcontroller system(CMSIS) */ |
STEP 5: On top of main() function add this line:
1 2 3 4 |
//Enable HSE clock RCC_HSEConfig(RCC_HSE_ON); //Wait for clock to stabilize while (!RCC_WaitForHSEStartUp()); |
That’s it, your system now works at 180MHz.
Precise delay
Microcontroller has Systick timer, which can be configured as interrupt generator every X ticks of core clock. In my delay library I set timer to interrupt every 1us. This is every 180 clock ticks.
Library dependencies
- CMSIS
- STM32F4xx
- STM32F4xx RCC
- TM
- defines.hdefines.h configuration example 0.00 KB
- defines.h
How this works
When you use delay function, you set number of microseconds to delay. Systick timer decrease variable everytime interrupt is called. When timing variable is zero, delay has finished.
First, you have to initialize Systick timer (inside is code for set 180MHz core clock).
1 2 |
//Initialize Systick timer TM_DELAY_Init(); |
There are 2 functions to use with delay:
1 2 3 4 |
//Delay for specific amount of microseconds Delay(nTime); //Delay for specific amount of milliseconds Delayms(nTime); |
Functions and enumerations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/** * Delay for specific amount of microseconds * * Parameters: * - uint32_t micros: * Time in microseconds for delay */ void Delay(uint32_t micros); /** * Delay for specific amount of milliseconds * * Parameters: * - uint32_t millis: * Time in milliseconds for delay */ void Delayms(uint32_t millis); /** * Initialize timer settings for delay * This function will initialize Systick or user timer, according to settings * */ void TM_DELAY_Init(void); /** * Get the TM_Time variable value * * Current time in milliseconds is returned */ #define TM_DELAY_Time() (TM_Time) /** * Set value for TM_Time variable * * Parameters: * - time: * Time in milliseconds */ #define TM_DELAY_SetTime(time) (TM_Time = (time)) /** * Re-enable delay timer. It has to be configured before with TM_DELAY_Init(); * * This function enables delay timer. It can be systick or user selectable timer. * */ void TM_DELAY_EnableDelayTimer(void); /** * Disable delay timer. * * This function disables delay timer. It can be systick or user selectable timer. */ void TM_DELAY_DisableDelayTimer(void); |
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** * Pretty precise delay functions with SysTick timer * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net */ #include "stm32f4xx.h" #include "tm_stm32f4_delay.h" #include "tm_stm32f4_disco.h" int main(void) { //Initialize system for 180MHz core clock SystemInit(); //Initialize delay Systick timer TM_DELAY_Init(); //Initialize onboard leds TM_DISCO_LedInit(); while (1) { //Toggle leds GPIO_ToggleBits(GPIOG, LED_GREEN); //Delay 500ms Delayms(500); } } |
But sometimes you don’t want to waste some time for doing nothing, but you want toggle led every 500ms and send data to usart terminal together. In this case, you can do something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/** * Keil project for delay time * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 * @packs STM32F4xx Keil packs version 2.2.0 or greater required * @stdperiph STM32F4xx Standard peripheral drivers version 1.4.0 or greater required */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_delay.h" #include "tm_stm32f4_disco.h" int main(void) { /* Initialize system */ SystemInit(); /* Initialize delay */ TM_DELAY_Init(); /* Initialize onboard leds */ TM_DISCO_LedInit(); /* Reset counter to 0 */ TM_DELAY_SetTime(0); while (1) { /* If time is more than 500ms */ if (TM_DELAY_Time() >= 500) { /* Reset time */ TM_DELAY_SetTime(0); /* Toggle leds here */ TM_DISCO_LedToggle(LED_RED | LED_GREEN); } /* Place your code here */ /* Code here will be checked without any delay */ /* Constantly */ } } |
I connected my logic analyzer to green led’s pin and result is on left.
Project is available on Github, download library below.
Recent comments