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