電子產(chǎn)業(yè)一站式賦能平臺

PCB聯(lián)盟網(wǎng)

搜索
查看: 10|回復: 0
收起左側(cè)

嵌入式代碼常見的容錯設計

[復制鏈接]

459

主題

459

帖子

2319

積分

三級會員

Rank: 3Rank: 3

積分
2319
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-9-13 11:38:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
關(guān)注+星標公眾,不錯過精彩內(nèi)容

作者 | strongerHuang
微信公眾號 | strongerHuang
如果一個大型嵌入式項目,代碼沒有做容錯設計,你能想象后果是什么嗎?
有經(jīng)驗的朋友肯定能想到,這樣的項目會有無數(shù)bug,而且有些bug很難查找。
今天就來聊聊嵌入式代碼常見的一些容錯設計方法。
使用斷言(Assert)
什么是Assert斷言?這里舉一個栗子來說明吧。

有這么一個數(shù)組和函數(shù):
  • int Array[5] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5};
    int Fun(char i){    return Array;}
    假如按下下面方式調(diào)用Fun函數(shù),你覺得會出錯嗎?
  • int a;
    a = Fun(8);
    有經(jīng)驗的朋友肯定都猜到了,在Fun函數(shù)中增加斷言(Assert)機制,就可以避免出錯。

    斷言(Assert)是代碼中最常見的一種容錯設計,很多源碼庫都能看到斷言的身影,比如STM32外設庫:
  • void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct){  /* Check the parameters */  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  /* ... */}
    明確返回值和錯誤碼
    大家常用的協(xié)議棧、外設庫、操作系統(tǒng)等,它們的API大多設計的很完美,為函數(shù)設計合理的返回值,用于反饋操作的成功或失敗。例如,使用0表示成功,非0值表示特定的錯誤代碼。

    比如RTOS創(chuàng)建任務函數(shù):
  • INT8U  OSTaskCreate (void   (*task)(void *p_arg),                     void    *p_arg,                     OS_STK  *ptos,                     INT8U    prio){    OS_STK     *psp;    INT8U       err;#if OS_CRITICAL_METHOD == 3u                 /* Allocate storage for CPU status register               */    OS_CPU_SR   cpu_sr = 0u;#endif
    #ifdef OS_SAFETY_CRITICAL_IEC61508    if (OSSafetyCriticalStartFlag == OS_TRUE) {        OS_SAFETY_CRITICAL_EXCEPTION();        return (OS_ERR_ILLEGAL_CREATE_RUN_TIME);    }#endif
    #if OS_ARG_CHK_EN > 0u    if (prio > OS_LOWEST_PRIO) {             /* Make sure priority is within allowable range           */        return (OS_ERR_PRIO_INVALID);    }#endif    OS_ENTER_CRITICAL();    if (OSIntNesting > 0u) {                 /* Make sure we don't create the task from within an ISR  */        OS_EXIT_CRITICAL();        return (OS_ERR_TASK_CREATE_ISR);    }    /* ... */}為函數(shù)設計合理的返回值和錯誤碼,也會讓你的代碼更健壯,特別是找bug時更容易。
    日志記錄
    我們?yōu)槭裁匆涗浫罩?記錄詳細的日志信息,包括錯誤發(fā)生的時間、位置、原因等,以便在有bug出現(xiàn)時進行追蹤和分析。
    我們學嵌入式之初,基本都會學習 printf 這種打印輸出的功能,這種打印對應的另一種功能就是日志記錄。

    除了存儲在本地的日志之外,也可以使用 printf 打印輸出至另外終端(比如上位機)進行存儲日志。

    致命Bug重啟策略
    我們軟件遇到一些致命的bug時,比如硬件故障(HardFault)、內(nèi)存溢出(MemManage)等,這個時候可以選擇重啟策略。

    當然,重啟也要根據(jù)項目實際情況,選擇什么方式重啟,比如:內(nèi)核復位、系統(tǒng)復位。

    1. 內(nèi)核復位
    只復位Cortex-M內(nèi)核,不會復位UART這些片內(nèi)外設。

    在Cortex-M內(nèi)核文檔中大概有這樣的描述:通過設置 NVIC 中應用程序中斷與復位控制寄存器(AIRCR)的VECTRESET 位,可只復位處理器內(nèi)核而不復位其它片上設施。
    內(nèi)核復位函數(shù)(參考內(nèi)核代碼修改而來):
  • void NVIC_CoreReset(void){  __DSB();  SCB->AIRCR  = ((0x5FA                  (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |                 SCB_AIRCR_VECTRESET_Msk);       //置位 VECTRESET  __DSB();  while(1) { __NOP(); }}
    2. 系統(tǒng)復位
    軟件復位中的系統(tǒng)復位操作的寄存器位(SYSRESETREQ)不同,復位的對象為整個芯片(除后備區(qū)域)。

    系統(tǒng)復位函數(shù):
  • void NVIC_SysReset(void){  __DSB();  SCB->AIRCR  = ((0x5FA                  (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |                  SCB_AIRCR_SYSRESETREQ_Msk);     //置位 SYSRESETREQ  __DSB();  while(1) { __NOP(); }}
    靜態(tài)分析工具
    使用靜態(tài)分析工具檢查代碼中的潛在問題,如未初始化的變量、內(nèi)存泄漏、緩沖區(qū)溢出等。這些工具可以在編譯前發(fā)現(xiàn)許多問題,從而提高代碼質(zhì)量。

    雖然這算不上容錯設計,但這也是開發(fā)過程中重要的一個環(huán)節(jié),其作用在一定程度上超過常規(guī)的容錯設計。

    這里推薦閱讀:嵌入式開發(fā)常用的代碼靜態(tài)分析工具
    最后,代碼bug千千萬,除了常規(guī)的容錯設計,代碼規(guī)范其實也很重要。
    最最后,你們平時寫代碼,有考慮哪些容錯設計,歡迎留言評論。
    猜你喜歡:
    WiFi6+藍牙+星閃,三合一開發(fā)板,真香!
    Github上熱門 C 語言項目匯總!
    嵌入式,可測試性軟件設計!
    一些低功耗軟件設計的要點!
    嵌入式 C 保護結(jié)構(gòu)體的方式
    實用 | 10分鐘教你通過網(wǎng)頁點燈
    談談嵌入式軟件的兼容性!
  • 回復

    使用道具 舉報

    發(fā)表回復

    您需要登錄后才可以回帖 登錄 | 立即注冊

    本版積分規(guī)則


    聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表