重启原因分析
本文主要分析模块的重启原因, 可以通过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)死等也不会立刻触发看门狗超时。