前言
在我之前做基于FreeRTOS的小车时,要用到微秒级的延时,最方便的就是用sysTick延时,发现了用sysTick系统就会卡死系统,没办法只能用软件延迟代替了。
原因是系统要用sysTick生成时间片,产生sysTick来切换任务。你用了,系统就没得用了,就会停止执行,解决方案两个,一是用sysTick是暂停系统,等sysTick用完后在归还给系统。二是在系统时间片的基础上截取要延时的时间段。
一、用完sysTick后归还
优点:延时较精准
缺点:系统被迫暂停
void Delay_us(uint32_t us)
{
/*保存sysTick的状态*/
uint32_t val_temp=SysTick->VAL;
uint32_t load_temp=SysTick->LOAD;
uint16_t ctrl_temp=SysTick->CTRL;
/*SysTick状态重新设置,用于延时(系统暂停)*/
SysTick->LOAD = 9 * us; // 设置定时器重装值,72MHz 8分频后9HMz,即一微秒记9个数
SysTick->VAL = 0x00; // 清空当前计数值,清空后会自动装入重载值
SysTick->CTRL = 0x00000001; // 位2设置为0,8分频,启动定时器
while (!(SysTick->CTRL & 0x00010000));// 等待计数到0
/*延时结束后恢复之前的状态,系统继续运行*/
SysTick->LOAD = load_temp;
SysTick->VAL = val_temp;
SysTick->CTRL = ctrl_temp;
//SysTick->CTRL = 0x00000000; // 关闭定时器
}
二、截取sysTick片段
优点:系统不用暂停,可以正常运行
缺点:两个时间片之间,系统运行需要时间,任务轮转执行也要时间,故不能做到精准延时
(对于这个缺点可以提高当前任务的优先级或者挂起其他的任务,保证下次轮询百分百论到该任务,如果轮到别的任务,至少一个时间片(1ms)后才能轮到该任务,对于微秒级的延时来说,这是致命的,延时完毕后恢复优先级或其他任务)
void Delay_us(uint32_t us)
{
uint32_t val_temp=SysTick->VAL;//读取SysTick->VAL的值
/*9MHz的时钟,所以us要乘9,如果当前时间片剩余的时间大于延时的时间*/
if( val_temp > ( us * 9 ))
{
while( SysTick -> VAL != ( val_temp - us * 9));//等待延时结束
}else//如果当前时间片剩余的时间小于延时的时间
{
/*等待当前时间片计时完毕,不足的时间在下一个时间片延时*/
while (!(SysTick->CTRL & 0x00010000));
/*等待新的时间片把剩下的时间延迟完毕*/
while( SysTick -> VAL != ( SysTick->LOAD - (us * 9-val_temp)));
}
}
总结
两种方法各有优劣,第一种要暂停系统,优点是延时准确,第二种不会暂停系统,但是延时相对于第一种来说不够准确,
对于FreeRTOS中实现微秒级延时,最好的方法还是不要用和系统一样的时钟,比如说用定时器就很好。