一、滴答定时器(SysTick)概述
SysTick 是Cortex-M 内核(如 STM32F103 用的 Cortex-M3)自带的24位递减定时器,属于内核级外设,而非 STM32 片上外设。核心用途:
- 裸机开发:实现微秒 / 毫秒级精准延时;
- RTOS 开发:作为系统心跳(Tick),支撑任务调度、时间片轮转;
- 通用场景:统计函数执行时间、实现简单定时任务。
二、相关寄存器
1、关键寄存器
| 寄存器 | 作用 |
| LOAD | 重装值寄存器,设置计数最大值 |
| VAL | 当前计数值寄存器,可手动清零 |
| CTRL | 控制 + 状态寄存器(时钟源选择、使能、中断使能、计数完成标志) |
| CALIB | 校准值寄存器(一般用于 RTOS 节拍校准,裸机开发较少用) |
2、CTRL 控制与状态寄存器
- ENABLE:Systick的使能位。
- TICKINT:在使用时选择Systick是否要产生中断。若把位2设置为1,则Systick在从初值倒计数到0时就会产生中断。
- CLKSOURCE:设置为0,则Systick的时钟源就是外部时钟;设置为1,则Systick的时钟源就是内部时钟。选择如下图
3、LOAD寄存器
当计数器从 LOAD 的值递减到 0 时,会自动重载 LOAD 的值,重新开始计数(ENABLE = 1)。 寄存器的低24位有效。
4、VAL 当前计数值寄存器
读取时返回当前计数器的递减值
5、CALIB 校准值寄存器
主要用于 RTOS 中校准系统节拍,裸机开发几乎不用。
三、裸机 - 精确延时
基于STM32f103 - 标准库
1、寄存器配置
1> 选择配置寄存器延时1us,后面的函数调用其就可以实现
- CTRL:0x00000005 -> 使能滴答定时器 + 选择 HCLK(72MHz)作为 SysTick 时钟源
- LOAD:HCLK=72MHz ->1 us需要计数 72 次
- VAL:让定时器从
LOAD的数值重新开始计数,避免残留的计数值导致延时误差。
/** * @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; //关闭定时器 }2> 代码部分:
- while(!(SysTick->CTRL & 0x00010000)); CTRL的 BIT16 是COUNTFLAG(计数完成标志位):当 SysTick 从 LOAD 值递减到 0 时,该位会自动置 1。这行代码的作用是 “循环等待,直到计数完成”,也就是等待延时时间到。
- SysTick->CTRL = 0x00000004;关闭 SysTick 定时器(BIT0=0),避免定时器继续运行影响后续操作,BIT2 保持 1(不影响,仅关闭使能)。
2、毫秒/秒级延时
有了上面的微秒延时,实现毫秒/秒就简单了,只需要不停的调用微秒函数就可以了
1>ms延时
/** * @brief 毫秒级延时 * @param xms 延时时长,范围:0~4294967295 * @retval 无 */ void Delay_ms(uint32_t xms) { while(xms--) { Delay_us(1000); } }2>s延时
/** * @brief 秒级延时 * @param xs 延时时长,范围:0~4294967295 * @retval 无 */ void Delay_s(uint32_t xs) { while(xs--) { Delay_ms(1000); } }四、使用注意
滴答时钟实现的精准延时属于阻塞型延时,在延时过程中,CPU 会持续等待延时完成,无法执行其他任务。在使用的时候要格外注意,以避免延时过多导致系统故障。
参考:@Barkley X