MCU在低温环境运行时偶发系统重启的问题查找与分析

1、问题现象

      项目使用恩智浦的 i.MX RT1050MCU,跑的rt_thread操作系统, 在高低温测试中,高温没问题, -40℃低温环境运行时,偶发系统重启现象(几个小时出现一次),通过日志得知是看门狗重启。(MCU复位原因可通过寄存器获取,下图截取自i.MX RT1050手册)

2、问题原因

2.1、看门狗的时钟异常

        将问题反馈给MCU的FAE,得到答复是——看门狗的时钟是内部rc,受温度影响很大,试试把复位时间拉长。

        程序目前看门狗复位时间是14S,喂狗任务的执行周期是0.5S,温度影响再大,也不太可能有这么离谱,但还是要测下看门狗时钟受温度影响的程度究竟有多大。

        只需在喂狗前打印看门狗计数值,对比常温和低温环境时的计数值即可。代码如下。

void feed_dog_task(void *param)
{
    ......    //看门狗初始化
    
    while(1)
    {
        RTWDOG_Refresh(RTWDOG);
        rt_thread_mdelay(500);
        rt_kprintf("watchdog CNT:%d", RTWDOG->CNT);//喂狗前打印计数值
    }
}

        可以看到,温度虽有影响,但问题不大,计数值只快了2%。

        此项目是基于之前项目更换了flash,其他硬件没变,但程序启动时会将程序copy到sdram运行,所以排除flash,硬件这边暂时没得找了,那就找找软件。

2.2、有线程卡住,导致无法喂狗

        这种情况可能是软件在死等某个外设状态就绪,由于低温环境等不来想要的状态,导致线程卡住,无法喂狗,逻辑如下。

do{
    state = read_state();
}while(state != OK)

        如果真是这么回事,要怎么定位到这段代码呢?首先是不能让软件复位,得把看门狗关闭,自己模拟个无复位的看门狗,用于检测任务卡住情况(倒是有一个看门狗中断,复位前会进入中断,尝试过在中断里关闭看门狗,没搞定);其次检测到任务卡住时,打印所有就绪任务的栈,从每个栈里查看任务运行的位置,找到在死等某个状态的任务。

2.2.1、模拟看门狗

        在 SysTick_Handler(1ms执行一次) 里累加一个变量,在喂狗任务里清0此变量,如果变量累加时间超过14S,则说明有任务卡住了,则输出一个提示信息,代码逻辑如下。

int _1ms_cnt_test = 0;

void SysTick_Handler(void)
{
    static int flag = 0;
    ...
    _1ms_cnt_test++;
    if((_1ms_cnt_test > 14000)&&(flag == 0))
    {
        flag = 1;    //只提示一次
        rt_kprintf("task blocked \r\n");
    }
    ...
}

void feed_dog_task(void *param)
{
    ......    //看门狗初始化
    
    while(1)
    {
        //RTWDOG_Refresh(RTWDOG);
        _1ms_cnt_test = 0;
        rt_thread_mdelay(500);
    }
}

2.2.2、添加打印所有就绪任务栈数据的命令

        任务卡住就不会主动让出CPU,因此只会处于就绪或者运行状态,不可能是挂起状态,在使用命令行打印时,Shell任务处于运行状态,那卡住任务则处于就绪状态,先打印所有就绪任务栈数据,再分析和定位。

        rt_thread可以通过MSH_CMD_EXPORT宏添加命令,但执行命令行的Shell任务优先级默认比较低,先将其设为最高,确保有任务卡住时仍然能执行。打印命令代码如下。


void print_thread_stack_frame(struct rt_thread *thread)
{
    rt_kprintf("Thread:%s\r\n",thread->name);
    
    //打印任务栈中的前50个数据
    for(int i = 0; i < 50; i++)
    {
        rt_kprintf("0x");
        rt_kprintf("%08x "((int *)(thread->sp))[i]);
        rt_kprintf("\r\n");
    }
    rt_kprintf("***********************************\r\n\n"):
}

void print_all_ready_threads_stack_frame(void)
{
    struct rt_thread *thread;
    struct rt_list_node *node;
    int priority;
    rt_enter_critical();

    //遍历所有优先级
    for (priority = 0; priority < RT_THREAD_PRIORITY_MAX; priority++)
    {
        //遍历当前优先级的线程链表
        for (node = rt_thread_priority_table[priority].next;
             node != &rt_thread_priority_table[priority];
             node = node->next)
        {
            thread = rt_list_entry(node, struct rt_thread, tlist);
            print_thread_stack_frame(thread);
        }
    }
    rt_exit_critical();
}
MSH_CMD_EXPORT(print_all_ready_threads_stack_frame, Print all ready threads register info);

2.2.3、复现问题

        运行几个小时后,打印了"task blocked",说明问题复现,此时通过命令行先看看线程状态,发现AAA_task任务的优先级由原来的20提高到了5,再看互斥量,发现此任务占用了smi_mutex互斥量导致有两个任务阻塞从而触发了优先级翻转机制。说明极有可能卡住的任务就是AAA_task,通过上述添加的打印栈信息命令获取到AAA_task的栈帧,再确认AAA_task任务是否在死等某个状态。

msh />list_thread
thread           pri  status      sp     stack size max used left tick  error
---------------- ---  ------- ---------- ----------  ------  ---------- ---
AAA_task           5  ready   0x000000e8 0x00001000    10%   0x00000027 000
...
...
...

msh />list_mutex
mutex              owner  hold suspend thread
---------------- -------- ---- --------------
smi_mutex        AAA_task 0001 2
...
...
...

msh />print_all_threads_stack_frame
Thread: AAA_task
0xdeadbeef
0xdeadbeef
0xdeadbeef
0xdeadbeef
0xdeadbeef
0xdeadbeef
0xdeadbeef
0xdeadbeef
0x0000034e
0x00000a08
0x00000000
0x00000000
0x00000005
0x80054dab
0x80091140
0x21000200
...
...
...

2.2.4、确认卡住任务

        

        RTOS切换任务会保存现场,这个现场就是内核中的寄存器,栈中前8个0xdeadbeef是r4-r11,后8个对应栈帧,返回地址(0x80091140)是一个延时函数,LR(0x80054dab)是一个do while的死等(汇编里是0x80054daa是因为bit0表示要选择什么指令集,我们cortex-m内核的都是thumb-2,所以bit0==1),此处代码与想象中情况基本一致,将此任务执行周期由原来的1s提升至100ms,复现时间由原来的几个小时提升至十多分钟,至此,已定位到问题代码。

2.2.5、分析原因

       这段代码逻辑就有问题, value的最高位是一个状态位,为1时,表示对应动作还没执行完,为0时才应该退出循环,而代码恰恰相反。在低温环境中,可能会导致对应动作加快,在while里判断时状态已经为0了,因此卡在了这里。

3、解决方案

        代码改为对应动作执行完才退出循环,重新低温测试,问题不再复现,至此问题解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值