SysTick定时器详解
关于SysTick定时器,如果想从STM32官方手册去寻找该定时器,会发现并没有该定时器的相关介绍,仅有库函数介绍。由此可以看出SysTick并不是意法半导体公司设定的。从Cortex-M3权威指南可以找到该定时器的详细介绍,由此可以知道SysTick定时器是在ARM芯片当中设定的。
这里有一个疑问,为什么M3有这么多定时器还要弄一个SysTick定时器呢?
这要从它的概念入手,系统滴答定时器是一个非常基本的倒计时定时器,用于在每隔一定时间产生一个中断,即使是系统在睡眠模式下也能工作。它使得OS在各CM3器件之间的移植中不必修改系统定时器的代码,移植工作一下子容易多了。SysTick定时器也是作为NVIC的一部分实现的。
如果有外设就一定会存在一堆用来配置该外设的寄存器,SysTick定时器也不例外。用来控制该定时器的寄存器有四个,下面详细介绍这四个寄存器。
SysTick控制及状态寄存器(地址:0xE000_E010)
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
16 | COUNTFLAG | R | 0 | 如果在上次读取本寄存器后, SysTick 已经数到了 0,则该位为 1。如果读取该位,该位将自动清零 |
2 | CLKSOURCE | R/W | 0 | 0=外部时钟源(STCLK) 1=内核时钟(FCLK) |
1 | TICKINT | R/W | 0 | 1=SysTick 倒数到 0 时产生 SysTick 异常请求 0=数到 0 时无动作 |
0 | ENABLE | R/W | 0 | SysTick 定时器的使能位 |
四个位都挺简单,对两个稍难的解释一下:
TICKINT 该位置为1时,SysTick定时器倒数到零会产生中断
COUNTFLAG 已经有了TICKINT为什么还要该位呢,原因是当TICKINT位置为0时,如果没有该位,就无法得知定时器是否已经倒数到0,因为倒数到0不会有任何动作。
SysTick重装载数值寄存器(地址:0xE000_E014)
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23:0 | RELOAD | R/W | 0 | 当倒数至零时,将被重装载的值 |
该寄存器24位,最大可装载值为FF FFFF
SysTick当前数值寄存器(地址:0xE000_E018)
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23:0 | CURRENT | R/Wc | 0 | 读取时返回当前倒计数的值,写它则使之清零, 同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志 |
该寄存器被读取后,会清零,重装载寄存器就会将它里面的重装载值传递到该寄存器。
SysTick校准数值寄存器(地址:0xE000_E01C)
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
31 | NOREF | R | ‐ | 1=没有外部参考时钟(STCLK 不可用) 0=外部参考时钟可用 |
30 | SKEW | R | ‐ | 1=校准值不是准确的 10ms 0=校准值是准确的 10ms |
23:0 | TENMS | R/W | 0 | 10ms 的时间内倒计数的格数。芯片设计者应该通 过 Cortex‐M3 的输入信号提供该数值。若该值读 回零,则表示无法使用校准功能 |
介绍完了寄存器,那就去STM32官方手册看看相关库函数吧,有一点可以肯定的是,库函数就是用来设置上面四个寄存器中某些位的。
在core_cm3.h文件中有这么一个函数可以用来配置SysTick定时器:
/**
* @brief Initialize and start the SysTick counter and its interrupt.
*
* @param ticks number of ticks between two interrupts
* @return 1 = failed, 0 = successful
*
* Initialise the system tick timer and its interrupt and start the
* system tick timer / counter in free running mode to generate
* periodical interrupts.
* 初始化并启动系统滴答定时器和它的中断/计数器自由运行模式用来产生周期性的中断
*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register * SysTick_LOAD_RELOAD_Msk为0xFFFFFF */
SysTick->VAL = 0; /* 把当前计数值设置为0,这* 样会将重装载寄存器中的值自动加载进来 Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* 将SysTick控制及状态寄存器低三位全部置为1。0、SysTick 定时器被使能 2、SysTick 倒数到 0 时产生 SysTick 异常请求 3、选择内核时钟(FCLK)为时钟源,也就是72MHZ*/
return (0); /* Function successful */
}
#endif
用中断的方式实现delay:
void SysTick_Configuration(void){
while(SysTick_Config(72)==1); //1us产生一次SysTick异常
SysTick->CTRL &= ~(1<<0); //SysTick暂不开启,等使用时再开启
}
Void delay_us(unsigned long n){
SysTick->CTRL |= (1<<0);
while(n); //每1us会产生一次中断,n--
SysTick->CTRL &= ~(1<<0);
}
void SysTick_Handler(void){
n--;
}