概述
系统定时器(SysTick)也叫做叫做系统滴答时钟,属于Cortex-M4内核中的一个外设(系统外设),内嵌在NVIC中,并且是24bit向下递减的计数器,当计数器的值为0时就申请中断请求,告诉CPU紧急处理该事件。当计数器为0时,可设置自动重装计数器的值便可以实现周期性的调用中断函数,可以利用这种特性实现一个调度器,基于时间片轮转会根据时间片切换任务,用于任务管理和上下文切换,这使得处理器能够在不同的时隙中处理不同的任务。
上图是从《stm32f4xx中文参考手册》时钟树中截取的部分截图,可以看出RCC向Cortex系统定时器 (SysTick) 提供了8分频的AHB时钟(HCLK),SysTick可使用此时钟作为时钟源,也可使用HCLK作为时钟源,具体可在SysTick控制和状态寄存器中配置。
库函数
/**该函数初始化系统计时器及其中断,并启动系统计时计时器,其时钟源被设置为了AHB时钟,
计数器处于自由运行模式,以产生周期性中断。
*/
uint32_t SysTick_Config(uint32_t ticks)
参数ticks就是计数值,为了方便计算直接使用全局变量SystemCoreClock,AHB时钟的值与其相等,将SysTick_Config的参数设置为SystemCoreClock时,也就是一秒钟的时间到达,每过1秒就会产生一个中断。当需要设置一秒钟到达1000次时,只需要将SystemCoreClock/1000,也就是可以将分母看成程序systick中断产生的频率,如下所示:
SysTick_Config(SystemCoreClock/1000);//假设SystemCoreClock为168MHz,系统定时器进行168000次计数,就是1ms时间的到达。
延时函数
在许多情况下,可能不需要使用SysTick_Config函数,因为可能使用别的参考时钟,或者不希望启用SysTick中断。在这些情况下,需要直接对SysTick寄存器进行编程,推荐如下顺序:
1.按SysTick->CTRL写入0,关闭SysTick定时器;
2.将新的重载值写入SysTick->LOAD;
3.向SysTick当前值寄存器SysTick->VAL写入0;
4.写入SysTick控制和状态寄存器SysTick->CTRL,选择时钟源,启动SysTick定时器;
5.循环检测计数是否完毕,计数完毕便退出;
6.关闭系统定时器。
可通过上述方法便可以实现延迟函数,创建sys_tick.c,sys_tick.h文件,保存到Hardware文件夹下,在添加到目录树的Hardware组合Inc组中。
int32_t delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->CTRL = 0;
SysTick->LOAD = (nus*21)-1;
SysTick->VAL = 0;
SysTick->CTRL = 1;//使能定时器,并使用AHB的八分频时钟作为时钟源,不使能systick中断
while(1)
{
temp=SysTick->CTRL;
//检测count flag
if(temp & 0x00010000)
break;
//检测系统定时器是否意外关闭
if((temp & 0x1)==0)
return -1;
}
SysTick->CTRL = 0;
return 0;
}
int32_t delay_ms(uint32_t nms)
{
uint32_t t = nms;
uint32_t temp;
while(t--)
{
SysTick->CTRL = 0;
SysTick->LOAD = 21000-1;
SysTick->VAL = 0;
SysTick->CTRL = 1;
while(1)
{
temp=SysTick->CTRL;
//检测count flag
if(temp & 0x00010000)
break;
//检测系统定时器是否意外关闭
if((temp & 0x1)==0)
return -1;
}
}
SysTick->CTRL = 0;
return 0;
}
与官方的代码不同,上述代码考虑到了系统定时器意外关闭的情况,当使用官方代码的延时函数进行反复调用的时候(如轮询检测矩阵键盘需要大量调用延时函数),程序终将会跳转到延时函数中无法退出。
总结
1.系统定时器不够精准,需要高精度计时时可以使用TIM(外设定时器)实现;
2.不过就我编写过的程序来看,与外设通信时使用上面的延迟函数时,没有出现什么大问题;
3.系统定时器也可以实现高精度计时,不过目前还没有去研究,后续有机会再补充。