STM32 system ticking and the delay techniques you need to know

Publisher:骄阳少年Latest update time:2016-05-25 Source: eefocusKeywords:STM32 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
I think every microcontroller enthusiast and engineering development designer has had the experience of lighting a lamp. Flowing lights are good things, especially in an environment with limited debugging resources, which can sometimes be of great help.

 

However, when we first get started, how can we make these small lights run happily according to our ideas? Most children's approach is to add a delay program in a while loop, let the small lights stay in each state for a period of time, and then enter the next state. In this way, the small lights will switch between different states and flash according to the program we designed.

 

This involves the issue of writing a delay program, and the general approach is to subtract a large number in a for loop until it reaches 0, then the delay is completed. The value of that number is estimated based on the clock frequency and the instruction running cycle. I still remember reading a post a long time ago that introduced several methods of precise delay for 51 microcontrollers. One method is to set the clock frequency in keil, and then calculate the delay time through software simulation to achieve more precise timing.

However, these methods are generally not convenient enough, and the delay is not accurate enough. A more advanced method is to start a timer and count in the timer interrupt to achieve the purpose of accurate delay.

 

In the application of STM32, you can consider using the SysTick system tick timer to implement it. However, there is little introduction to it in the STM32 development manual, almost to the point of non-existence. Because it is part of the Cortex core, CM3 has specially opened an exception type for it, and it has a place in the interrupt vector table (exception number 15), so that it can be easily ported to chips with CM3 cores from different manufacturers, and for software with real-time operating systems, it is generally used as the time base of the entire system, which is very important for the operating system. For a detailed introduction to SysTick, please refer to Chapter 8 on page 133 and Chapter 13 on page 179 of the "Cortex-M3 Authoritative Guide".

 

SysTick has a total of four registers:

1,

STM32 system ticking and the delay techniques you need to know

Corresponds to SysTick->CTRL in the software;

 

2,

STM32 system ticking and the delay techniques you need to know

Corresponds to SysTick-> LOAD in the software;

 

3.

STM32 system ticking and the delay techniques you need to know

Corresponds to SysTick->VAL in the software;

 

4.

STM32 system ticking and the delay techniques you need to know
 

Corresponding to SysTick-> CALIB () in the software, it has not been used and is not often used, so it will not be introduced for now.

 

The offsets of these registers are shown in the following figure:

STM32 system ticking and the delay techniques you need to know

The definition of the register structure is in CMSISCM3CoreSupport core_cm3.h, as follows

/** @addtogroup CMSIS_CM3_SysTick CMSIS CM3 SysTick memory mapped structure for SysTick @{ */ typedef struct { __IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */ __IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */ __IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */ __I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */ } SysTick_Type;

SysTick is a 24-bit timer, which means it can count up to 224 clock pulses at a time. This pulse count value is saved in the SysTick->VAL current count value register. It can only count down. Each time a clock pulse is received, the value of SysTick->VAL is reduced by 1 until it reaches 0. Then the hardware automatically reloads the value in the reload register SysTick->LOAD to SysTick->VAL and recounts. When the SysTick->VAL value counts to 0, an exception is triggered and the void SysTick_Handler(void) function is called. The timer interrupt event can be processed in this interrupt service function, which is generally a count-down operation for the set value. As long as the 0th enable bit in the SysTick control and status register SysTick->CTRL is not cleared, it will never stop.

 

The SysTick interrupt priority issue needs to be emphasized here.

 It is a system exception, a kernel-level interrupt, and the priority can be set. The specific settings are also in core_cm3.h

/** * @brief Initialize and start the SysTick counter and its interrupt. * * @param ticks number of ticks between two interrupts * @return 1 = failed, 0 = successful * * Initialize the system tick timer and its interrupt and start the * system tick timer / counter in free running mode to generate * periodical interrupts. */ static __INLINE uint32_t SysTick_Config(uint32_t ticks) { if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; return (0); /* Function successful */ }

 


The following sentence is the function for setting the priority. This function sets both the kernel interrupt priority and the external interrupt priority. It is quite powerful, but it is not convenient to calculate the preemption and slave priorities manually. When jumping into this function, we can calculate that Systick has the lowest default priority (the effect is equivalent to SCB->SHP[11] = 0xF0;)

NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);

At this time, if other external interrupt priorities are set higher than it, it can be deprived and turned to external interrupt.

 

The following experiments can be performed to verify:

First set an event interrupt and set the priority higher.

void Exti_Config(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line1; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(& EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }

 

Note: In my experiment, the interrupt grouping was initially initialized as follows:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

 

Set as the second group.

 

exist

void SysTick_Handler(void) { EXTI_GenerateSWInterrupt(EXTI_SWIER_SWIER1); LED_1 = ON; Delay(); }

The system tick interrupt triggers an external interrupt event and lights up LED1.

 

The external interrupt processing function is as follows

void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) != RESET) { EXTI_ClearITPendingBit(EXTI_Line1); LED_0 = ON; Delay(); } }

 

This delay function is a blocking delay as follows:

void Delay(void) { u32 i; for(i=0 ; i < 0xFFFFF; i++){} }

The delay is added to see which light turns on first.

When the external interrupt priority is higher, it can preempt the Systick interrupt and execute first. The experimental result of the above code is that LED0 lights up first, and then returns to LED1 to light up.

When the external interrupt is set to the same priority as the systick, the systick priority will be relatively high. For example, change the above priority to

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;

Then LED1 will light up first, and EXTI1_IRQHandler will execute only after the SysTick_Handle function is executed.

Personally, I think that if you want to achieve accurate systick delay, it is best to set the systick priority higher, for example

NVIC_SetPriority (SysTick_IRQn, 0);

That is, by setting SCB->SHP[11] = 0x00; the systick priority can be higher than any external interrupt, and the delay will be more accurate.

 

In addition, for the selection of SysTick clock source, it should be noted that its clock source can be selected as internal clock (FCLK, free running clock on CM3, corresponding to AHB in STM32), or external clock (STCLK signal on CM3 processor, corresponding to AHB/8 in STM32)

Please refer to the following figure

STM32 system ticking and the delay techniques you need to know

It is set in the SysTick->CTRL second bit CLKSOURCE clock source selection.

 

For the writing of systick delay function, please refer to Wildfire's "Zero Dead Angle Play STM32-Beginner's Edition".

At this point we can simply implement a first-class water lamp program

while(1) { LED_0 =OFF; LED_1 = ON; Delay_ms(500); LED_0 =OFF; LED_1 = ON; Delay_ms(500); }

 

However, is this really a good idea? Blocking delay is used here, and a large part of the CPU efficiency is consumed in idling, which is a waste of resources.

Assuming that the system clock frequency is 72MHZ or tens or hundreds of MHZ, when it only takes tens or tens of nanoseconds or even shorter to complete a cycle, and there is a blocking delay of tens to hundreds of milliseconds in this cycle, it is like a bumpy muddy road suddenly appearing across the highway. You can imagine that the entire road will slow down, and even catastrophic consequences may occur. Personally, I think that during the system initialization process, the timing of each chip has time requirements, and blocking delay can be used. It only needs to be run when the system starts. After the system is running, it is best not to do this stupidly.

 

At this time, the main method is to count in the timer, query the variable in the external loop, and execute a certain action when a certain value is reached to achieve the delay effect. Before the time is up, the system can keep running in circles and do other things.

 gticks counts once every millisecond in the timer interrupt

 

while(1) { if(500 == gticks) { LED_0 =OFF; LED_1 = ON; } if(1000 == gticks) { LED_0 =OFF; LED_1 = ON; gticks = 0 } Do_others(); }

 

The above requires processing gticks during event processing, which increases the coupling of the code and is more prone to errors. If gticks are cleared in one event processing and need to be queried in the next event, this may cause processing timing disorder and mutual interference.

Is it possible to only provide query function in event processing, and leave the scheduling work to the scheduler itself?

In the next section, an expert will appear and introduce to you an ingenious design of non-blocking delay that I learned in a project.

Keywords:STM32 Reference address:STM32 system ticking and the delay techniques you need to know

Previous article:STM32 study notes 1 clock and timer
Next article:SysTick system clock tick experiment (introduction to stm32 interrupts)

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号