STM32延时函数
STM32延时函数(查询定时器实现)
之前在STM32中使用延时函数的时候,都是直接使用,并不了解其中的原理。经过查阅资料,这里记录一下大致原理。
首先直接贴出来延时函数delay.c,通过查询的SysTick Timer的STK_CTRL寄存器的计数标志位COUNTFLAG方式实现,其中SysTick是一个结构体指针,在core_cm3.h
中声明了。
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK并启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
可以看到延时函数使用的是系统定时器Systick Timer,有关Systick Timer的介绍,参考手册《Cortex®-M3 programming manual》中写道:
The processor has a 24-bit system timer, SysTick, that counts down from the reload value to zero, reloads (wraps to) the value in the LOAD register on the next clock edge, then counts down on subsequent clocks.
控制及状态寄存器 (STK_CTRL)
Cortex-M3处理器有一个24位的系统定时器,这个定时器从计数值一直减到0,在下一个时钟边缘重新加载(包装到)LOAD寄存器中的值,然后计数
下一个时钟。Systick Timer的CTRL寄存器(控制寄存器)如下:
![](https://img2023.cnblogs.com/blog/2126010/202406/2126010-20240629235107715-2127883165.png)
- 其中第31~17位、第15~3位为保留位且必须被清除。
- 第16位COUNTFLAG,计数标志位,上一次该位被读取时,定时器倒计时计数到0,计数标志位为1。
- 第2位CLKSOURCE,时钟源选择,0:AHB的时钟8分频;1:处理器时钟(AHB时钟,即HCLK)
- 第1位TICKINT,是否开启SysTick异常请求。0:倒计时到零不断言SysTick异常请求;1:0:倒计时到零断言SysTick异常请求
- 第0位ENABLE,计数器启用。0:停止,1:启用
因此用Systick->CTRL = 0x00000005;
语句,实现HCLK作为SysTick Timer的时钟源并且启用定时器。
装载寄存器(STK_LOAD)
![img](https://img2023.cnblogs.com/blog/2126010/202406/2126010-20240629235108335-1071094981.png)
-
第31~24位为保留位
-
第23~0位共24位作为重载值,数值范围
0x0000000~0x00FFFFFF
-
根据需要重载值要进行调整:
-
原文:To deliver a single SysTick interrupt after a delay of N processor clock cycles, use a RELOAD of value N. For example, if a SysTick interrupt is required after 400 clock pulses, set RELOAD to 400.
-
在N个处理器时钟周期要产生一次SysTick中断,重载值RELOAD就设置为N。例如需要在400个时钟周期后产生一次SysTick中断,重载值RELOAD就设置为400。
-
原文:To generate a multi-shot timer with a period of N processor clock cycles, use a RELOAD value of N-1. For example, if the SysTick interrupt is required every 100 clock pulses, set RELOAD to 99
-
要生成一个周期为N个处理器时钟周期的多触发定时器,请使用RELOADN-1的值。例如,如果每100个时钟脉冲需要SysTick中断,则设置重新加载到99(这时候用作实时操作系统RTOS)
-
因此用SysTick->LOAD = 72 * xus;
语句,设定重载值,STM32F10X系列系统时钟(SYSCLK)最大频率为72MHz,它是供STM32中绝大部分部件工作的时钟源,选择HCLK作为时钟源,HCLK最大频率72MHz,即时钟周期(clock cycle)为\(\frac{1}{72000000}\)秒。当参数xus为1000时,延时了1000us=1ms。有关STM32F10X的时钟树参照:https://www.cnblogs.com/Darrick-Jan/p/11379913.html以及《STM32F10X参考手册》
当前值寄存器(STK_VAL)
- 第31位~24位作为保留位
- 余下24位作为当前值,写入任何值都会将字段清除为0,同时也会清除中的STK_CTRL寄存器中的计数标志位COUNTFLAG位为0。
语句SysTick->VAL = 0X00
,将当前值设为0,并将控制寄存器的COUNTFLAG位设为0。
总结
当计数器倒计时计数为0时,读取到控制寄存器STK_CTRL中的COUNTFLAG为1时,说明设定的时间到了,这时退出循环,将ENABLE位复位为0,于是有语句SysTick->CTRL = 0x00000004;
,用来关闭计数器。相对于定时器中断的方式实现延时,查询法上避免了频繁调用中断。