|
關(guān)注+星標(biāo)公眾號,不錯過精彩內(nèi)容
mffwx2jnc2464014812800.gif (519.05 KB, 下載次數(shù): 4)
下載附件
保存到相冊
mffwx2jnc2464014812800.gif
2024-9-18 11:54 上傳
作者 | strongerHuang
微信公眾號 | strongerHuang
如果一個大型嵌入式項目,代碼沒有做容錯設(shè)計,你能想象后果是什么嗎?
有經(jīng)驗的朋友肯定能想到,這樣的項目會有無數(shù)bug,而且有些bug很難查找。
今天就來聊聊嵌入式代碼常見的一些容錯設(shè)計方法。
使用斷言(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)機(jī)制,就可以避免出錯。
斷言(Assert)是代碼中最常見的一種容錯設(shè)計,很多源碼庫都能看到斷言的身影,比如STM32外設(shè)庫:
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é)議棧、外設(shè)庫、操作系統(tǒng)等,它們的API大多設(shè)計的很完美,為函數(shù)設(shè)計合理的返回值,用于反饋操作的成功或失敗。例如,使用0表示成功,非0值表示特定的錯誤代碼。
比如RTOS創(chuàng)建任務(wù)函數(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ù)設(shè)計合理的返回值和錯誤碼,也會讓你的代碼更健壯,特別是找bug時更容易。
日志記錄
我們?yōu)槭裁匆涗浫罩?記錄詳?xì)的日志信息,包括錯誤發(fā)生的時間、位置、原因等,以便在有bug出現(xiàn)時進(jìn)行追蹤和分析。
我們學(xué)嵌入式之初,基本都會學(xué)習(xí) printf 這種打印輸出的功能,這種打印對應(yīng)的另一種功能就是日志記錄。
除了存儲在本地的日志之外,也可以使用 printf 打印輸出至另外終端(比如上位機(jī))進(jìn)行存儲日志。
致命Bug重啟策略
我們軟件遇到一些致命的bug時,比如硬件故障(HardFault)、內(nèi)存溢出(MemManage)等,這個時候可以選擇重啟策略。
當(dāng)然,重啟也要根據(jù)項目實際情況,選擇什么方式重啟,比如:內(nèi)核復(fù)位、系統(tǒng)復(fù)位。
1. 內(nèi)核復(fù)位
只復(fù)位Cortex-M內(nèi)核,不會復(fù)位UART這些片內(nèi)外設(shè)。
在Cortex-M內(nèi)核文檔中大概有這樣的描述:通過設(shè)置 NVIC 中應(yīng)用程序中斷與復(fù)位控制寄存器(AIRCR)的VECTRESET 位,可只復(fù)位處理器內(nèi)核而不復(fù)位其它片上設(shè)施。
內(nèi)核復(fù)位函數(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)復(fù)位
軟件復(fù)位中的系統(tǒng)復(fù)位操作的寄存器位(SYSRESETREQ)不同,復(fù)位的對象為整個芯片(除后備區(qū)域)。
系統(tǒng)復(fù)位函數(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ì)量。
雖然這算不上容錯設(shè)計,但這也是開發(fā)過程中重要的一個環(huán)節(jié),其作用在一定程度上超過常規(guī)的容錯設(shè)計。
這里推薦閱讀:嵌入式開發(fā)常用的代碼靜態(tài)分析工具
最后,代碼bug千千萬,除了常規(guī)的容錯設(shè)計,代碼規(guī)范其實也很重要。
最最后,你們平時寫代碼,有考慮哪些容錯設(shè)計,歡迎留言評論。
猜你喜歡:
WiFi6+藍(lán)牙+星閃,三合一開發(fā)板,真香!
Github上熱門 C 語言項目匯總!
嵌入式,可測試性軟件設(shè)計!
一些低功耗軟件設(shè)計的要點!
嵌入式 C 保護(hù)結(jié)構(gòu)體的方式
實用 | 10分鐘教你通過網(wǎng)頁點燈
談?wù)勄度胧杰浖募嫒菪裕?/strong> |
|