天天看點

單片機異常複位後如何儲存變量資料

目錄

1、理論

2、實踐

衆所周知,單片機複位後變量數值會自動初始化,以華大半導體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;      

實踐描述:使用__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計數。

繼續閱讀