System timer of STM32F407

Article directory

  • System timer SysTick
    • Tick timer register
    • STK_CTRL control register
    • STK_LOAD reload register
    • STK_VAL current value register
    • STK_CALRB calibration value register
  • Non-system initialization Systick timer
    • SysTick_Init
      • SysTick_CLKSourceConfig
    • delay_us register
    • delay_us library function
    • delay_xms short time
    • delay_ms long
    • SysTick_Config
  • System RTOS delay

System timer SysTick

The processing of the CM4 kernel is the same as that of CM3, and it contains a SysTick timer internally. There is no difference between the two. For its detailed introduction, please refer to
Page 230 of “STM32F3 and F4 Series Cortex M4 Core Programming Manual”.

  • The system timer is a peripheral in the CM3 core and is embedded in the NVIC. Because all CM3 chips have this timer, software migration between different CM3 devices is simplified.

  • The system timer is a 24-bit downward-decreasing counter. The time for each count of the counter is 1/SYSCLK. Generally, we set the system clock SYSCLK equal to 72M.

    • The clock source of this timer can be an internal clock or an external clock. However, the specific source of STCLK is determined by the chip designer.
  • When the value of the reload value register decrements to 0, the system timer generates an interrupt, and the cycle continues.

    • After SysTick is set to the initial value and enabled, the count value is decremented by 1 every system clock cycle. When counting to 0, the SysTick counter automatically reloads the initial value and continues counting. At the same time, the internal COUNT FLAG flag will be set to trigger an interrupt (if the interrupt is enabled).
  • As long as its enable bit in the SysTick control and status register is not cleared, it will never stop.
    It is said in the M4 kernel programming manual: SysTick is a 24-bit system timer that will reload The value decreases to zero. The value is reloaded into the STK_LOAD register on the next clock edge and then decremented based on subsequent clocks.

Tick timer register

Part of the Systick content belongs to the NVIC control part. There are a total of 4 registers. The names and addresses are:

STK_CTRL 0xE000E010 -- Control register
//It seems that this is the control register
STK_LOAD 0xE000E014 -- Reload register
STK_VAL 0xE000E018 -- Current value register
STK_CALIB 0xE000E01C -- Calibration value register

STK_CTRL control register


There are 4 bits in the register that have meaning

  • Bit 0: ENABLE, Systick enable bit (0: turn off the Systick function; 1: turn on the Systick function)
  • Bit 1: TICKINT, Systick interrupt enable bit (0: turn off Systick interrupt; 1: turn on Systick interrupt)
  • Bit 2: CLK SOURCE, Systick clock source selection (0: Use HCLK/8 as the Systick clock; 1: Use HCLK as the Systick clock)
  • Bit 16: COUNT FLAG, Systick count comparison flag. When the count reaches 0, it is set to 1; when the timer starts to count again (CLK_LOAD rewrites the value), it is automatically cleared.

STK_LOAD reload register

Systick is a decrementing timer. When the timer decrements to 0, the value in the reload register will be reloaded and continues to decrement. The STK_LOAD reload register is a 24-bit register with a maximum count of 0xFFFFFF.

STK_VAL current value register

A 24-bit register that returns the current countdown value when read. Writing it clears it and also clears the COUNTFLAG flag in the SysTick control and status register.

STK_CALRB calibration value register

SysTick calibration value register

  • Bit 31 NOREF: = 1 No external reference clock (STCLK not available), = 0 External reference clock available.
  • Bit 30 SKEW: = 1 the calibration value is not accurate to 1ms, = 0 the calibration value is accurate to 1ms
  • uncommonly used

Non-system initialization Systick timer

SysTick_Init

void SysTick_Init(u8 SYSCLK);
SysTick_Init(72);

//The clock of SYSTICK is fixed to 1/8 of the AHB clock
//SYSCLK: system clock frequency
void SysTick_Init(u8 SYSCLK)
{<!-- -->
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=SYSCLK/8;
fac_ms=(u16)fac_us*1000;
}
 

The system clock is 72/8M, and the counting time is 1/9000000 seconds. When converted into us, it is 1/9us. Then counting 72/8 times, that is, 9 times is 1us.

SysTick_CLKSourceConfig

Set the clock source of SysTick. The options are as follows:

#define SysTick_CLKSource_HCLK_Div8 ((uint32_t)0xFFFFFFFB)
#define SysTick_CLKSource_HCLK ((uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
                                       ((SOURCE) == SysTick_CLKSource_HCLK_Div8))

Introduction to HCLK

delay_us register

void delay_us(u32 nus)
{<!-- -->
u32 temp;
SysTick->LOAD = 9*nus;
SysTick->VAL=0X00;//Clear the counter
SysTick->CTRL=0X01;//Enable, reducing to zero means no action, using external clock source
do
{<!-- -->
temp=SysTick->CTRL;//Read the current countdown value
}while((temp & amp;0x01) & amp; & amp;(!(temp & amp;(1<<16))));//Waiting time to arrive
//Enable and loop until the count reaches 0
SysTick->CTRL=0x00; //Close counter
SysTick->VAL =0X00; //Clear the counter
}

9*nus: Assume that the peripheral frequency is 9M, that is, divided by 8, then counting 9 times is 1us. The meaning of multiplying by 9 is the number of times corresponding to the parameter time, which is the reload value.

  • Let’s take a look at the judgment conditions. The first bit is enable, and the 16th bit counts to 0.
    Bit 16: COUNT FLAG, Systick count comparison flag. When the count reaches 0, it is set to 1; when the timer starts to count again (CLK_LOAD rewrites the value), it is automatically cleared.

Summarize the process

  • First convert the number of us to be delayed into the number of SysTick clocks
  • Write to LOAD register
  • Clear the contents of the current register VAL
  • Turn on the countdown function.
  • When the countdown ends, nus is delayed.
  • Finally close SysTick and clear the value of VAL

Notice:

  • The value of nus cannot be too large. It must be ensured that nus<=(2^24)/fac_us, otherwise the delay time will be inaccurate.
  • temp &0x01, this sentence is used to determine whether the systick timer is still on, which can prevent an infinite loop caused by unexpected shutdown of systick.

delay_us library function

//Delay nus
//Note: The value of nus should not be greater than 798915us (the maximum value is 2^24/fac_us@fac_us=21)
void delay_us(u32 nus)
{<!-- -->
u32 temp;
SysTick->LOAD=nus*fac_us; //Time loading
SysTick->VAL=0x00; //Clear the counter
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //Start the countdown
do
{<!-- -->
temp=SysTick->CTRL;
}while((temp & amp;0x01) & amp; & amp;!(temp & amp;(1<<16))); //Wait for time to arrive
SysTick->CTRL & amp;=~SysTick_CTRL_ENABLE_Msk; //Close the counter
SysTick->VAL =0X00; //Clear the counter
}

delay_xms short time

//Delay nms
//SysTick->LOAD is a 24-bit register, so the maximum delay is:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK unit is Hz, nms unit is ms
//Under 168M conditions, nms<=798ms
void delay_xms(u16 nms)
{<!-- -->
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //Time loading (SysTick->LOAD is 24bit)
SysTick->VAL =0x00; //Clear the counter
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //Start the countdown
do
{<!-- -->
temp=SysTick->CTRL;
}while((temp & amp;0x01) & amp; & amp;!(temp & amp;(1<<16))); //Wait for time to arrive
SysTick->CTRL & amp;=~SysTick_CTRL_ENABLE_Msk; //Close the counter
SysTick->VAL =0X00; //Clear the counter
}

delay_ms long

//Delay nms
//nms:0~65535
void delay_ms(u16 nms)
{<!-- -->
u8 repeat=nms/540; //540 is used here because some customers may use it for overclocking.
//For example, when overclocked to 248M, the maximum delay_xms can only be about 541ms.
u16 remain=nmsT0;
while(repeat)
{<!-- -->
delay_xms(540);
repeat--;
}
if(remain)delay_xms(remain);
}

SysTick_Config

Set reload value. The internal part of this function is: SysTick->LOAD = 9*nus;
The parameter of SysTick_Config is a clock count, which means I want to interrupt after how many 1/fosc times.

static void BSP_CoreClockInit(void)
{<!-- -->
SysTick_Config(SystemCoreClock / 100); //10ms
//SysTick_Config(720000); //10ms
}

SystemCoreClock / 100 represents the number of clocks, that is, the value placed in the reload register. Each time a number is clocked, it takes 1/72000000s. Then the time to count SystemCoreClock / 100 (SystemCoreClock is 72M) is 10ms. By interrupting Set the flag bit in the systick to achieve the function of scheduled interrupt.
Function implementation

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{<!-- -->
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */

  SysTick->LOAD = ticks - 1; /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */
  SysTick->VAL = 0; /* Load the SysTick Counter Value */
  SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk |
                   SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
  return (0); /* Function successful */
}

In the interrupt priority setting of peripherals, the hardware numbers of peripherals are greater than or equal to zero, and the numbers of core peripherals are less than zero, which is equivalent to an IP.

  • __NVIC_PRIO_BITS=4, because F4 uses four bits.
  • Interrupt handling function IP:
typedef enum IRQn
{<!-- -->
/****** Cortex-M4 Processor Exceptions Numbers ************************************* ***************************/
  NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
  MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */
  BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */
  UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */
  SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */
  DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */
  PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */
  SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */
/****** STM32 specific Interrupt Numbers *************************************** *******************************/
  WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
  PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
  TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */
  RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */
  FLASH_IRQn = 4, /*!< FLASH global Interrupt */
  RCC_IRQn = 5, /*!< RCC global Interrupt */
  EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
  EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */
  EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
  EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */
  EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */
  DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */
  DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */
  DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */
  DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */
  DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */
  DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */
  DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */
  ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts

Comparing the interrupt priorities of core peripherals and ordinary peripherals, although the two are not in the same register, when comparing, the priority register configuration of the core peripherals is also interpreted according to ordinary interpretation.

System RTOS delay

//Initialize delay function
//When using the OS, this function will initialize the clock beat of the OS
//SYSTICK's clock is fixed to 1/8 of the AHB clock
//SYSCLK: system clock frequency
void delay_init(u8 SYSCLK)
{<!-- -->
u32 reload;
 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //168M, divided by 8 21M
fac_us=SYSCLK/8; //Whether OS is used or not, fac_us needs to use 21M
//168M is 168 times/us, for ticks it is 21 times/us, so timing 21 times is 1us, and the unit is fac_us
reload=SYSCLK/8; //The number of counts per second, the unit is M
reload*=1000000/delay_ostickspersec; //Set the overflow time according to delay_ostickspersec
//reload is a 24-bit register, the maximum value is: 16777216, at 168M, it is about 0.7989s
fac_ms=1000/delay_ostickspersec; //Represents the minimum unit that the OS can delay
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //Enable SYSTICK interrupt
SysTick->LOAD=reload; //Interrupt every 1/delay_ostickspersec seconds
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //Enable SYSTICK

}

What needs to be noted here is that the clock of SysTick is derived from HCLK divided by 8. Assuming that our external crystal oscillator is 8M,
Then multiply the frequency to 168M, then the clock of SysTick is 21Mhz, that is, every time the counter VAL of SysTick decreases by 1,
Represents that the time has passed 1/21us.

fac_us=SYSCLK/8; ; This sentence is to calculate how many SysTick clock cycles are needed to delay 1us at the SystemCoreClock clock frequency. For a 21M clock, 21 times is 1us, and 1 time is 1/21us. sysClk is 168. fac_us is 168/8 times.

Systick’s clock comes from the system clock divided by 8. Because of this, if the system clock is not a multiple of 8 (not divisible by 8), it will cause the delay function to be inaccurate. This is why we recommend choosing 8M for the external clock.