Systick是ARM内核自带的系统滴答定时器,利用查询Systick的方法实现延时,可以节省定时器资源,同时不会影响到Systick的运行。
注意:由于不同单片机制造的差异,存在误差概念中的“恒值系统误差”,需要自己利用示波器,更改ticks的系数值。
例如在main.c中利用GPIO延时1us翻转制造方波,不断修正系数,作者手上的MCU修正系数为7/20,系数一般是在1/2以下。
另外,手边没有示波器的友友,可以直接用keil测试代码运行时间,这里就不展开说了。
如何修改修正系数?
如果实际延时时间长,就增大这个系数,反之减少。直到能正确延时1us,此时1ms的延时也会随之正常。
ticks=_nus*fac_us-fac_us*系数
以下为毫秒、微秒级延时的实现代码,注释十分清晰,不再赘述。
delay.c
#include "delay.h"
/**
* @brief 提供us级的延时
* @param _nus 延时的时间,单位us
* @return 无
*/
void delay_us(uint32_t _nus)
{
uint8_t fac_us=0;
uint32_t ticks;
uint32_t t_old,t_now,t_cnt=0;
uint32_t reload=SysTick->LOAD;//获取重装载值(只查询,不影响)
fac_us=SystemCoreClock/1000000;
ticks=_nus*fac_us-fac_us*7/20;
t_old=SysTick->VAL;//获取当前值作为旧值(只查询,不影响)
while(1)
{
/*systick为向下递减,未减到0导致重装载的情况下,新值小于旧值*/
t_now=SysTick->VAL;//获取当前值作为新值(只查询,不影响)
if(t_now<t_old)
{
t_cnt+=t_old-t_now;//未发生重装载,cnt记录减少的值
}
else
{
t_cnt+=t_old+(reload-t_now);//重装载后,旧值已递减完毕,cnt需在再加上重装载值与新值的差值
}
if(t_cnt>=ticks)
{
break;//当计数cnt大于等于用户需要的ticks,即认为延时完毕,跳出循环
}
t_old=t_now;//获取当前值作为旧值,以供下一次循环做判断(只查询,不影响)
};
}
/**
* @brief 提供ms级的延时
* @param _nms 延时的时间,单位ms
* @return 无
*/
void delay_ms(uint16_t nms)
{
uint32_t i;
for(i=0;i<nms;i++)
{
delay_us(1000);
}
}