深入理解 ARMv7-M | SysTick
1、The system timer, SysTick
Armv7-M 实现必须包含一个系统定时器 SysTick。它是一个简单的24 位递减计数器,采用 “写清除、到零回绕(wrap)” 机制,并提供灵活的控制方式。系统可以通过多种方式使用该计数器,例如:
- 作为 RTOS 的系统 tick 定时器,以可编程频率触发中断,例如 100 Hz,并在每次触发时执行 SysTick 处理例程;
- 作为基于主处理器时钟(main processor clock)的高速告警定时器;
- 作为基于主处理器时钟的高速告警定时器,SysTick 的输入时钟可选自主处理器时钟或外部参考时钟
- 作为可变频率的告警或信号定时器,其可用定时范围取决于参考时钟和计数器的动态范围;
- 作为可变频率的告警或信号定时器,其可用定时范围取决于参考时钟频率和计数器的动态范围,且由于 LOAD 可动态修改,中断触发频率也可随之调整
- 作为简单计数器,软件可借此测量任务完成耗时或已用时间;
- 作为内部时钟源控制机制,用于判断某段操作是否在规定时间内完成或未按时完成,从而参与动态时钟管理控制环路;此时可通过控制和状态寄存器中的
COUNTFLAG位进行判断。
2、SysTick 工作原理
SysTick 包含四个寄存器:
- 控制和状态寄存器(SYST_CSR):用于配置 SysTick 时钟源、使能计数器、使能 SysTick 中断,并提供计数器状态指示;
- 重载值寄存器(SYST_RVR):提供计数器回绕时加载的目标值;
- 当前值寄存器(SYST_CVR):保存计数器当前值;
- 校准值寄存器(SYST_CALIB):指示为获得 10 ms(对应 100 Hz 触发频率)系统时钟所需的重载值。
使能后,定时器从SYST_CVR中的值开始递减计数。当计数器减到零时,会在下一个时钟边沿从SYST_RVR重新加载计数值,随后继续递减(Periodic / Auto-reload 模式。与之对应的是 One-shot 模式)。计数器在到达零时重新加载的行为称为回绕。
当计数器转换到零时,状态位COUNTFLAG会被置 1;读取COUNTFLAG会将其自动清零。
SysTick 会在计数器转换到 0 时,产生中断。可以参考
SYST_CSR.TICKINT
向SYST_CVR写入任意值都会将该寄存器和COUNTFLAG状态位清零,并促使 SysTick 逻辑在下一个定时器时钟从SYST_RVR重新加载计数值。但对SYST_CVR的写操作不会直接触发 SysTick 异常。
读取SYST_CVR时,返回的是访问该寄存器时刻的计数器值。
向SYST_RVR写入零会在下一次回绕时禁用计数器。回绕发生后,SysTick 计数器逻辑将保持计数值为零。
当 计数器 被禁用、计数器逻辑保持为 0,实际上也意味着 SysTick 中断不会再产生。因为不会再产生由 1 -> 0 的跳变(转换)
注意:
- 将
SYST_RVR设为零会独立于计数器使能位,直接导致 SysTick 计数器被禁用。 - 复位后
SYST_CVR的值不确定。因此在使能 SysTick 计数器之前,软件必须先向SYST_RVR写入所需重载值,再向SYST_CVR执行一次写操作,以将SYST_CVR清零。计数器使能后,会从SYST_RVR重新加载,并从该确定值开始递减,而不是从一个不确定的初始值开始计数。
软件可以利用校准值TENMS将计数器缩放到其他期望的时钟频率,前提是该频率处于计数器的动态范围内。
当处理器进入调试暂停状态(Debug state)时,计数器不会继续递减。
定时器的时钟来自参考时钟。该参考时钟可以是处理器时钟,也可以是外部时钟源,具体取决于实现。如果实现采用外部时钟,则必须文档化处理器时钟与外部参考时钟之间的关系。进行系统时间校准时,需要充分考虑亚稳态、时钟偏移和抖动等因素。
3、寄存器详解
3.1 SysTick Control and Status Register, SYST_CSR
3.2 SysTick Reload Value Register, SYST_RVR
3.3 SysTick Current Value Register, SYST_CVR
3.4 SysTick Calibration value Register, SYST_CALIB
SYST_CALIB是 SysTick 的 校准值寄存器,只读,主要用来告诉软件:“在当前硬件配置下,如果要得到大约 10 ms 的定时,重载值应该填多少。”
理想情况下,如果你看到 TENMS = 168000,说明当前时钟下大约数 168000 个周期就是 10 ms。那你想做 1 ms tick,就可以按比例算:
SysTick->LOAD=(TENMS/10)-1;所以它的设计初衷是让软件不用显式知道 SysTick 的参考时钟频率,也能快速得到一个 10 ms 量级的基准定时。
但实际工程里基本不用它。工程上基本还是靠软件自己按时钟频率计算LOAD。