一、SysTick 简介
SysTick:系统定时器,24位,只能递减,存在于内核,嵌套在NVIC中,所有的Cortex-M内核的单片机都具有这个定时器。
SysTick—系统定时器有 4 个寄存器,简要介绍如下。在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。
二、SysTick功能框图
counter在时钟的驱动下,从reload初值开始往下递减计数到0,产生中断和置位COUNTFLAG标志。然后又从reload值开始重新递减计数,如此循环。
SysTick定时时间计算
t : 一个计数循环的时间,跟reload和CLK有关
CLK : 72M或者9M,由CTRL寄存器配置
RELOAD :24位,用户自己配置
t = reload * ( 1/clk )
Clk = 72M时,t = (72)*(1/72 M)= 1U
SClk = 72M时,t = (72000)*(1/72 M)= 1MS时间单位换算:
1s = 1000ms = 1000 000 us = 1000 000 000ns
三、SysTick 定时实验
利用 SysTick 产生 1s 的时基,LED 以 1s 的频率闪烁
1.硬件设计
SysTick 属于单片机内部的外设,不需要额外的硬件电路,剩下的只需一个 LED 灯即可。
2.软件设计
我们创建了两个文件:bsp_SysTick.c 和 bsp_ SysTick.h 文件用来存放 SysTick 驱动程序及相关宏定义,中断服务函数放在 stm32f10x_it.c 文件中。SysTick 属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件 core_cm3.h 中。
3.编程要点
设置重装载寄存器的值
清除当前数值寄存器的值
配置控制与状态寄存器
(1)SysTick 配置库函数
// 这个 固件库函数 在 core_cm3.h中 static __INLINE uint32_t SysTick_Config(uint32_t ticks) { // reload 寄存器为24bit,最大值为2^24 if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); // 配置 reload 寄存器的初始值 SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; // 配置中断优先级为 1<<4-1 = 15,优先级为最低 NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); // 配置 counter 计数器的值 SysTick->VAL = 0; // 配置systick 的时钟为 72M // 使能中断 // 使能systick SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; return (0); }
用固件库编程的时候我们只需要调用库函数 SysTick_Config() 即可,SysTick_Config() 库函数主要配置了 SysTick 中的三个寄存器:LOAD、VAL 和 CTRL,有关具体的部分看代码注释即可。
(2)配置 SysTick 中断优先级
在 SysTick_Config() 库函数还调用了固件库函数 NVIC_SetPriority() 来配置系统定时器的中断优先 级,该库函数也在 core_m3.h 中定义,原型如下:
__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { if ((int32_t)IRQn < 0) { SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } else { NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); } }
如果要修改内核外设的优先级,只需要修改下面三个寄存器对应的某个字段即可。
(3)SysTick 初始化函数
/** * @brief 启动系统滴答定时器 SysTick * @param 无 * @retval 无 */ void SysTick_Init(void) { /* SystemFrequency / 1000 1ms中断一次 * SystemFrequency / 100000 10us中断一次 * SystemFrequency / 1000000 1us中断一次 */ // if (SysTick_Config(SystemFrequency / 100000)) // ST3.0.0库版本 if (SysTick_Config(SystemCoreClock / 100000)) // ST3.5.0库版本 { /* Capture error */ while (1); } }
SysTick 初始化函数由用户编写,里面调用了 SysTick_Config() 这个固件库函数,通过设置该固件库函数的形参,就决定了系统定时器经过多少时间就产生一次中断。
SysTick 中断时间的计算
SysTick_Config(SystemCoreClock / 100000)
SysTick_Config()的形我们配置为 SystemCoreClock / 100000=72M/100000=720,从刚刚分析我们 知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在把 SysTick 定时器中 断一次的时间 TINT=720/72M=10us。
(4)SysTick 定时函数
/** * @brief us延时程序,10us为一个单位 * @param * @arg nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us * @retval 无 */ void Delay_us(__IO u32 nTime) { TimingDelay = nTime; // 使能滴答定时器 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; while(TimingDelay != 0); }
函数 Delay_us() 中我们等待 TimingDelay 为 0,当 TimingDelay 为 0 的时候表示延时时间到。变量 TimingDelay 在中断函数中递减,即 SysTick 每进一次中断即 10us 的时间 TimingDelay 递减一次。
(5)SysTick 中断服务函数
void SysTick_Handler(void) { TimingDelay_Decrement(); }
中断服务函数调用了另外一个函数 TimingDelay_Decrement(),原型如下:
/** * @brief 获取节拍程序 * @param 无 * @retval 无 * @attention 在 SysTick 中断函数 SysTick_Handler()调用 */ void TimingDelay_Decrement(void) { if (TimingDelay != 0x00) { TimingDelay--; } }
(6)主函数
/* * t : 定时时间 * Ticks : 多少个时钟周期产生一次中断 * f : 时钟频率 72000000 * t = Ticks * 1/f = (72000000/100000) * (1/72000000) = 10us */ /** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { /* LED 端口初始化 */ LED_GPIO_Config(); /* 配置SysTick 为10us中断一次 */ SysTick_Init(); // for(;;) // { // LED1( ON ); // Delay_us(100000); // 100000 * 10us = 1000ms // //Delay_ms(100); // LED1( OFF ); // // LED2( ON ); // Delay_us(100000); // 100000 * 10us = 1000ms // //Delay_ms(100); // LED2( OFF ); // // LED3( ON ); // Delay_us(100000); // 100000 * 10us = 1000ms // //Delay_ms(100); // LED3( OFF ); // } for(;;) { LED1( ON ); SysTick_Delay_Ms( 1000 ); LED1( OFF ); LED2( ON ); SysTick_Delay_Ms( 1000 ); LED2( OFF ); LED3( ON ); SysTick_Delay_Ms( 1000 ); LED3( OFF ); } }
主函数中初始化了 LED 和 SysTick,然后在一个 while 循环中以 1s 的频率让 LED 闪烁。
4.systick 微秒级延时
void SysTick_Delay_Us( __IO uint32_t us) { uint32_t i; SysTick_Config(SystemCoreClock/1000000); for(i=0;i<us;i++) { // 当计数器的值减小到0的时候,CRTL寄存器的位16会置1 while( !((SysTick->CTRL)&(1<<16)) ); } // 关闭SysTick定时器 SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk; }
5.systick 毫秒级延时
void SysTick_Delay_Ms( __IO uint32_t ms) { uint32_t i; SysTick_Config(SystemCoreClock/1000); for(i=0;i<ms;i++) { // 当计数器的值减小到0的时候,CRTL寄存器的位16会置1 // 当置1时,读取该位会清0 while( !((SysTick->CTRL)&(1<<16)) ); } // 关闭SysTick定时器 SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk; }