Skip to content

重启原因分析

本文主要分析模块的重启原因, 可以通过yopen_get_powerup_reason接口获取开机原因值,来判断重启的原因。

测试代码

测试项目:demo_restart

主要针对软件重启,异常重启,assert重启,看门狗重启进行测试验证。 通过test_opt变量进行配置不同的测试模块。代码如下所示

#include "yopen_debug.h"
#include "yopen_os.h"
#include "yopen_power.h"

enum  {
    YOPEN_REASON_POWERKEY_TEST,
    YOPEN_REASON_SWRESET_TEST,
    YOPEN_REASON_PADRESET_TEST,
    YOPEN_REASON_HARDFAULT_TEST,
    YOPEN_REASON_ASSERT_TEST,
    YOPEN_REASON_WDT_TEST,
} yopen_reset_test_opt_e;

static int test_opt = YOPEN_REASON_POWERKEY_TEST;

const char *yopen_reset_reason_str(yopen_reset_reason_e reason)
{
    switch (reason)
    {
        case YOPEN_REASON_POWERKEY:  return "POWERKEY";
        case YOPEN_REASON_SWRESET:   return "SWRESET";
        case YOPEN_REASON_PADRESET:  return "PADRESET";
        case YOPEN_REASON_HARDFAULT: return "HARDFAULT";
        case YOPEN_REASON_ASSERT:    return "ASSERT";
        case YOPEN_REASON_WDT:       return "WDT";
        case YOPEN_REASON_UNKNOWN: 
        default:                     return "UNKNOWN";  // 防止新增枚举未处理
    }
}

void restart_demo_thread(void* argv)
{
    uint16 i = 0;
    yopen_reset_reason_e reason;
    // 获取上电原因
    yopen_get_powerup_reason(&reason);

    while (1)
    {
        yopen_rtos_task_sleep_ms(1000);
        yopen_trace("restart_demo_thread cnt-%d, %s", i++, yopen_reset_reason_str(reason));

        if (i == 10) {
            switch(test_opt)
            {
                case YOPEN_REASON_SWRESET_TEST:
                    yopen_trace("test swreset");
                    yopen_power_reset(RESET_NORMAL);
                    break;
                case YOPEN_REASON_HARDFAULT_TEST:
                    yopen_trace("test hardfault");
                    *(volatile uint32*)0x00000000 = 0x12345678; // 触发硬错误
                    break;
                case YOPEN_REASON_ASSERT_TEST:
                    yopen_trace("test assert");
                    YOPEN_ASSERT(0); // 触发断言
                    break;
                case YOPEN_REASON_WDT_TEST:
                    yopen_trace("test wdt");
                    while(1);
                case YOPEN_REASON_PADRESET_TEST:
                case YOPEN_REASON_POWERKEY_TEST:
                default:
                    break;
            }
        }
    }
}

测试结果

按键开机

配置 test_opt = YOPEN_REASON_SWRESET_TEST

模块完全断电后,通过powerkey键开机,然后查看开机原因值

测试log:

restart_demo_thread cnt-0, POWERKEY 
restart_demo_thread cnt-1, POWERKEY 
restart_demo_thread cnt-2, POWERKEY 
restart_demo_thread cnt-3, POWERKEY 
restart_demo_thread cnt-4, POWERKEY 
restart_demo_thread cnt-5, POWERKEY 
restart_demo_thread cnt-6, POWERKEY 
restart_demo_thread cnt-7, POWERKEY 
restart_demo_thread cnt-8, POWERKEY 

软件重启

配置 test_opt = YOPEN_REASON_SWRESET_TEST

开机10s后,调用yopen_power_reset(RESET_NORMAL);接口触发软件重启,然后查看开机原因值

测试log:

restart_demo_thread cnt-0, SWRESET 
restart_demo_thread cnt-1, SWRESET 
restart_demo_thread cnt-2, SWRESET 
restart_demo_thread cnt-3, SWRESET 
restart_demo_thread cnt-4, SWRESET 
restart_demo_thread cnt-5, SWRESET 
restart_demo_thread cnt-6, SWRESET 
restart_demo_thread cnt-7, SWRESET 
restart_demo_thread cnt-8, SWRESET 

硬件重启

配置 test_opt = YOPEN_REASON_PADRESET_TEST

开机后, 按下reset按键重启,然后查询开机原因值

测试log:

restart_demo_thread cnt-0, PADRESET 
restart_demo_thread cnt-1, PADRESET 
restart_demo_thread cnt-2, PADRESET 
restart_demo_thread cnt-3, PADRESET 
restart_demo_thread cnt-4, PADRESET 
restart_demo_thread cnt-5, PADRESET 
restart_demo_thread cnt-6, PADRESET 
restart_demo_thread cnt-7, PADRESET 
restart_demo_thread cnt-8, PADRESET 

软件异常

配置 test_opt = YOPEN_REASON_HARDFAULT_TEST;

开机10秒后, 程序访问非法指针,触发异常重启,然后查询开机原因值

测试log:

restart_demo_thread cnt-1, HARDFAULT 
restart_demo_thread cnt-2, HARDFAULT 
restart_demo_thread cnt-3, HARDFAULT 
restart_demo_thread cnt-4, HARDFAULT 
restart_demo_thread cnt-5, HARDFAULT 
restart_demo_thread cnt-6, HARDFAULT 
restart_demo_thread cnt-7, HARDFAULT 
restart_demo_thread cnt-8, HARDFAULT 

ASSERT重启

配置 test_opt = YOPEN_REASON_ASSERT_TEST;

开机10秒后, 触发ASSERT重启,然后查询开机原因值

测试log:

restart_demo_thread cnt-0, ASSERT 
restart_demo_thread cnt-1, ASSERT 
restart_demo_thread cnt-2, ASSERT 
restart_demo_thread cnt-3, ASSERT 
restart_demo_thread cnt-4, ASSERT 
restart_demo_thread cnt-5, ASSERT 
restart_demo_thread cnt-6, ASSERT 
restart_demo_thread cnt-7, ASSERT 
restart_demo_thread cnt-8, ASSERT 

看门狗重启

配置 test_opt = YOPEN_REASON_WDT_TEST;

开机10秒后, 进入while(1)死循环,等待看门狗超时重启,然后查询开机原因值

测试log:

restart_demo_thread cnt-0, WDT 
restart_demo_thread cnt-1, WDT 
restart_demo_thread cnt-2, WDT 
restart_demo_thread cnt-3, WDT 
restart_demo_thread cnt-4, WDT 
restart_demo_thread cnt-5, WDT 
restart_demo_thread cnt-6, WDT 
restart_demo_thread cnt-7, WDT 
restart_demo_thread cnt-8, WDT 

注: 看门狗喂狗原理:除了应用层调用yopen_debug_feed_wdt接口喂狗外,底层也有喂狗逻辑:

1.当系统接入idle task时,即所有task都被挂起或者阻塞,idle task会喂狗

2.当有网络数据交互时,处理的task优先级比较高,这个地方存在喂狗,不然一直有数据交互可能会导致低优先级的task无法执行喂狗。例如rndis通信的时候就会进行喂狗。哪怕上层业务逻辑存在while(1)死等也不会立刻触发看门狗超时。