关于SysTick延时卡死问题的解决

2019-08-19 19:24发布

http://www.openedv.com/thread-75729-1-1.html
在上贴我分享了32外设的一个小程序,但是用SysTick定时器生成的延时函数在主函数和中断中同时使用时,会造成中断完成之后返回主函数时,延时函数卡死,究其原因,先看代码:
[mw_shl_code=c,true]/*****************************
* @fileelay.c
* @brief:initializing SysTick registers as base time for delay and set us&ms delay
* @author:zx
* @date: 2016/5/18
* @version: V0.1
* @attention: none
*****************************/
#include "Delay.h"

void SysTick_Init(void)
{  
    //SysTick_CLKSource_HCLK  72MHz
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  //配置SysTick时钟源为8分频 即 72MHz/8 也可选择不分频
    SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;          //计数器减到0时 不产生异常中断   
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;           //关定时器
      SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG_Msk;    //异常标志位清零
     
      SysTick->LOAD = 9; // 72MHz/8/9 = 1us 我要每1us产生一次异常则就在重装寄存器中存9
     
    SysTick->VAL = 0;  //默认计数器内容是0
}

void Delay_us(u32 us)
{
     SysTick->VAL = 0;
     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 开定时器
     while(us--)
     {
        while(!(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)); //判断标志位是否置位,没有就等待
    }
     
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关定时器
}

void Delay_ms(u32 ms)
{
while(ms--)
    {
     Delay_us(1000); // 延时1ms
    }
}[/mw_shl_code]


      上面是问题代码,在,顺序执行延时的时候一点事也不会有,但是一旦产生了延时嵌套,就造成了主函数卡死。
      我先说说原因:systick定时器是一个递减计数器,我在Delay_us代码中,先开定时器,延时之后再关定时器,所以当主函数正在延时时(此时定时器开),中断发生,并且中断内也调用了延时函数,那么此时他也会对systick进行开——关的操作,注意:问题就是关操作,当中断关掉定时器后,返回主函数的延时,延时由于一直在等待标志位而一直处于while等待,但是此时,定时器是关闭的,也就是说,主函数永远也不可能等到标志位置位的发生,所以陷入了无限循环,导致程序卡死。


     思考:从主函数延时到中断延时,再到返回中断延时,对于主函数延时的精度会有什么样的影响呢?由于跳出中断以及主函数发生中断时计数器内的计数值的不确定性,导致返回中断会有几百纳秒的延时。
     其次,程序的卡死看似解决,但是多次测试之后发现还会有那么几次会卡死,有待进一步探索。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。