简介
要用SysTick计时器写延时函数,首先要了解SysTick是怎么工作的。
SysTick是位于单片机内核上的外设,只要是Cortex M内核的单片机,都拥有SysTick计时器,并且SysTick计时器是一个递减计时器。
SysTick定时器介绍
在不同的微处理器或系统中,SysTick定时器的时钟频率可能会有不同的设置,但是一般情况下STM32(除H7外)SysTick的时钟频率默认是系统时钟SystemCoreClock的1/8(开始我也试着去找,但是没有找到。查阅资料了解到,HAL库中似乎并没有明确的提到SysTick时钟频率为SystemCoreClock的1/8,SysTick定时器的时钟频率的设置是在内部自动完成的,也不需要用户手动配置。)
SysTick定时器寄存器介绍
以下时STM32固件库中提到SysTick的几个寄存器,
CTRL -> 控制和状态寄存器,它用于配置和控制SysTick定时器的行为,CTRL寄存器是一个32位的寄存器,寄存器位介绍如下:
- Bit 0(ENABLE):启用/禁用SysTick定时器。设置为1时启用定时器,设置为0时禁用定时器。
- Bit 1(TICKINT):定时器中断使能位。设置为1时,当SysTick定时器到达零时,会触发SysTick中断;设置为0时,不触发中断。
- Bit 2(CLKSOURCE):定时器时钟源选择位。设置为1时,选择外部时钟源作为SysTick定时器的时钟;设置为0时,选择内部时钟作为SysTick定时器的时钟。
- Bit 16(COUNTFLAG):定时器溢出标志位。当SysTick定时器计数器溢出时,该位被置为1。在软件读取该位后,它将被自动清除。
通过配置CTRL寄存器的不同位来控制SysTick定时器的启用与禁用、中断使能、时钟源选择以及
读取定时器溢出标志。
LOAD -> 重装载寄存器,它用于设置SysTick定时器的初始计数值。LOAD寄存器是一个32位的
寄存器,当VAL递减到0时,它存储的值会重新加载到计数器VAL中。从而实现循环计数的效果。
所以,我们就可以通过设置LOAD寄存器的值,达到控制SysTick定时器的周期定时效果。
具体计算方法如下:
定时周期T = (LOAD寄存器的值 + 1) × 时钟周期;
而SysTick的时钟频率 f = SystemCoreClock/8;
所以,SysTick时钟周期 = 1/(SystemCoreClock/8);
定时周期T = (LOAD寄存器的值 + 1)/(SystemCoreClock/8);
而我们需要1us的延时,就相当于是LOAD里的值给到VAL之后,VAL递减到0的时间刚好是1us,
所以LOAD寄存器的值 = 1us * (SystemCoreClock/8)-1;
这样就算到了延时1us,我们需要赋予SysTick重载寄存器的值了。
VAL -> SysTick定时器的当前值寄存器,SysTick递减就是指VAL寄存器里的值递减;
CALIB -> SysTick定时器的校准寄存器,用于提供SysTick定时器的校准值和特征信息。
程序
知道这些,我么就可以开始编写延时程序了:
寄存器版us延时程序
void delay_us(uint16_t us)
{
SysTick->LOAD = (us * (SystemCoreClock/8000000))-1; //设置1us所需要的重装载值
SysTick->CTRL = 0; //关闭定时器
SysTick->VAL = 0; //清空计数值
SysTick->CTRL |= 1<<0; //开启定时器
while(!(SysTick->CTRL & (1<<16))); //等待VAL递减到0
SysTick->CTRL &= ~(1<<0); //关闭定时器
SysTick->VAL = 0;
}
HAL库版us延时程序:
HAL库的话,有专门的掩码控制对应寄存器,跟寄存器类似,只是用一大串英文字母替换了几个数字
void delay_us(uint16_t us)
{
SysTick->LOAD = (us * (SystemCoreClock/8000000))-1;
SysTick->VAL = 0;
SysTick->CTRL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
根据HAL库core_cm3.h第700行;
SysTick_CTRL_ENABLE_Msk:CTRL寄存器使能掩码
SysTick_CTRL_COUNTFLAG_Msk:CTRL溢出标志位掩码
至于ms延时,我们都已经知道us延时了,ms嘛 /手动滑稽:
void delay_ms(uint16_t ms)
{for(int i = ms;i>0;i--)delay_us(1000);}
不好意思,之前寄存器那个while循环里面少加了一个否定符,导致延时卡死,现在改正过来了。