写点什么

单片机异常复位后如何保存变量数据

发布于: 2021 年 04 月 02 日
单片机异常复位后如何保存变量数据

1、理论

众所周知,单片机复位后变量数值会自动初始化,以华大半导体 HC32L136 为例,具有 7 个复位信号来源,每个复位信号都可以让 CPU 重新运行,绝大多数寄存器会被复位到复位值,程序会从复位向量处开始执行。

  • 数字区域上电掉电复位 POR

  • 外部 Reset PAD,低电平为复位信号

  • WDT 复位

  • PCA 复位

  • LVD 低电压复位

  • Cortex-M0+ SYSRESETREQ 软件复位

  • Cortex-M0+ LOCKUP 硬件复位

每个复位源由相应的复位标志进行指示,复位标志均由硬件置位,需要用户软件清零。

华大半导体各区域的复位来源如下图所示:

本篇博客主要讲授华大半导(STM32、C51 等单片机均可适用)复位(以看门狗复位为例)后变量数据保存的方法。

这里将用到__not_init 属性,其用于变量声明,可禁止系统启动时变量的初始化,有了__not_init 属性,编译器只给指定变量分配空间,不会再初始化。

__not_init 的两种定义方式如下所示:

    方式1:不指定存储位置,由编译器分配    __no_init 类型 变量名;        ///< 例如:__no_init uint8_t cou_num;    方式2:指定存储位置    __no_init 类型 变量名 @地址;  ///< 例如:__no_init uint8_t cou_num @0x20000000;
复制代码

2、实践

实践描述:使用__no_init 属性创建一个变量 cou_num,其将数据存储在 SRAM 中,每隔 300 毫秒自加 1 并通过串口打印输出数值,当检测到上电复位和按键复位后,变量 cou_num 数值置为 0,在看门狗复位下变量 cou_num 数值不变。

第 1 步:配置串口引脚、串口使能和串口中断,代码如下所示:

///< 串口引脚配置static void App_PortInit(void){    stc_gpio_cfg_t stcGpioCfg;     DDL_ZERO_STRUCT(stcGpioCfg);    ///< 使能GPIO模块时钟    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);      ///< 配置PA02端口为URART1_TX    stcGpioCfg.enDir = GpioDirOut;    Gpio_Init(GpioPortA, GpioPin2, &stcGpioCfg);    Gpio_SetAfMode(GpioPortA, GpioPin2, GpioAf1);            } ///< 串口配置static void App_UartCfg(void){    stc_uart_cfg_t    stcCfg;     DDL_ZERO_STRUCT(stcCfg);     ///< 开启UART1外设时钟    Sysctrl_SetPeripheralGate(SysctrlPeripheralUart1,TRUE);     ///< UART1初始化    stcCfg.enRunMode        = UartMskMode3;           ///< 模式3    stcCfg.enStopBit        = UartMsk1bit;            ///< 1bit停止位    stcCfg.enMmdorCk        = UartMskEven;            ///< 偶检验    stcCfg.stcBaud.u32Baud  = 9600;                   ///< 波特率9600  注意误差    stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;        ///< 通道采样分频配置    stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq();  ///< 获得外设时钟(PCLK)频率值    Uart_Init(M0P_UART1, &stcCfg);                    ///< 串口初始化     ///< UART1中断使能    Uart_ClrStatus(M0P_UART1,UartTC);                 ///< 清发送请求    Uart_EnableIrq(M0P_UART1,UartTxIrq);              ///< 使能串口发送中断    EnableNvic(UART1_IRQn, IrqLevel3, TRUE);          ///< 系统中断使能} ///< UART1中断函数void Uart1_IRQHandler(void){    ///< UART1数据发送    if(Uart_GetStatus(M0P_UART1, UartTC))             {        ///< 清中断状态位        Uart_ClrStatus(M0P_UART1, UartTC);      }}
复制代码

第 2 步:配置看门狗复位,每隔 820 毫秒若没有喂狗,则复位,代码如下所示:

///< WDT初始化配置static void App_WdtInit(void){    ///< 开启WDT外设时钟    Sysctrl_SetPeripheralGate(SysctrlPeripheralWdt,TRUE);    ///< WDT 初始化,喂狗时间:820ms    Wdt_Init(WdtResetEn, WdtT820ms);}
复制代码

第 3 步:使用__no_init 属性定义 cou_num 变量,将数组存储在 SRAM 寄存器 0x20001000 中,代码如下所示:

__no_init uint8_t cou_num @ 0x20001000;
复制代码

第 4 步:添加上电复位源和 RESET 脚复位源检测,当检测到其中之一个复位的时候,cou_num 置为 0,代码如下所示:

int32_t main(void){    char * data_buf = (char *)malloc(sizeof(char) * 19);        ///< 串口引脚配置    App_PortInit();     ///< 串口配置    App_UartCfg();        ///< WDT初始化    App_WdtInit();       ///< 启动 WDT    Wdt_Start();        ///< 当上电复位或者RESET脚复位后cou_num为0,看门狗复位数值不变    if((Reset_GetFlag(ResetFlagMskPor5V) == 1) || (Reset_GetFlag(ResetFlagMskRstb) == 1))    {      cou_num = 0;       Reset_ClearFlag(ResetFlagMskPor5V);      Reset_ClearFlag(ResetFlagMskRstb);    }       while (1)    {        cou_num = cou_num + 1;                delay1ms(300);                ///< 开启喂狗后,将不会产生复位        //Wdt_Feed();                 sprintf(data_buf,"numerical value:%d\n",cou_num);                for(int8_t i = 0;i < 19;i++)        {          Uart_SendDataIt(M0P_UART1,data_buf[i]);           delay1ms(5);        }    }}
复制代码

运行效果如下所示:

可见虽然看门狗每隔 820 毫秒复位一次,但是 cou_num 数值不收影响,但是也可以看出 cou_num 数值中间存在丢失,例如没有打印输出数值 3,主要原因是运行到此数时,恰巧看门狗复位,所以串口未来得及打印,但是不影响 cou_num 计数。

发布于: 2021 年 04 月 02 日阅读数: 10
用户头像

【研究方向】物联网、嵌入式、AI、Python 2018.02.09 加入

【公众号】美男子玩编程

评论

发布
暂无评论
单片机异常复位后如何保存变量数据