当前位置:网站首页>嵌入式開發中的防禦性C語言編程

嵌入式開發中的防禦性C語言編程

2022-07-06 09:41:00 大方老師嵌入式

嵌入式開發中的防禦性C語言編程

\\\插播一條:文章末尾有驚喜喲~///

8979a6f2201c455f47a386de1c2851c9.png

嵌入式產品的可靠性自然與硬件密不可分,但在硬件確定、並且沒有第三方測試的前提下,使用防禦性編程思想寫出的代碼,往往具有更高的穩定性。

防禦性編程首先須要認清C語言的種種缺少陷和陷阱,C語言對於運行時的檢查十分弱小,須要程序員謹慎的考慮代碼,在必要的時候增加判斷;防禦性編程的另一個核心思想是假設代碼運行在並不可靠的硬件上,外接幹擾有可能會打亂程序執行順序、更改RAM存儲數據等等。

1.具有形參的函數,需判斷傳遞來的實參是否合法

程序員可能沒意識的傳遞了錯誤參數;外界的强幹擾可能將傳遞的參數修改掉,或者使用隨機參數意外的調用函數,因此在執行函數主體前,須要先確定實參是否合法。

2.仔細檢查函數的返回值

對函數返回的錯誤碼,要進行全面仔細處理,必要時做錯誤記錄。

3.防止指針越界

假如動態計算一個地址時,要保證被計算的地址是合理的並指向某個有意義的地方。特別對於指向一個構造或數組的內部的指針,當指針增加或者變更後依然指向同一個構造或數組。

4.防止數組越界

數組越界的問題前文已經講述的很多了,由於C不會對數組進行有效的檢測,因此必需在應用中顯式的檢測數組越界問題。下面的例子可用於中斷接管通訊數據。

在使用一些庫函數時,同樣須要對邊界進行檢查,假如下面的memset(RecBuf,0,len)函數把RecBuf指指向的內存區的前len個字節用0填充,假如不注意len的長度,就會將數組RecBuf之外的內存區清零:

5.數學算數運算

5.1除法運算,只檢測除數為零就可靠嗎?

除法運算前,檢查除數是否為零簡直已經成為共識,但是僅檢查除數是否為零就够了嗎?

考慮兩個整數相除,對於一個signed long類型變量,它能錶示的數值範圍為:-2147483648 ~+2147483647,假如讓-2147483648/ -1,那麼結果應該是+2147483648,但是這個結果已經超出了signedlong所能錶示的範圍了。所以,在這種情况下,除了要檢測除數是否為零外,還要檢測除法是否溢出。

#include

signed long sl1,sl2,result;

/*初始化sl1和sl2*/

if((sl2==0)||(sl1==LONG_MIN && sl2==-1))

{

//處理錯誤

}

else

{

result = sl1 / sl2;

}

5.2檢測運算溢出

整數的加减乘運算都有可能發生溢出,在探討未定義行為時,給出過一個有符號整形加法溢出判斷代碼,這裏再給出一個沒符號整形加法溢出判斷代碼段:

#include

unsigned int a,b,result;

/*初始化a,b*/

if(UINT_MAX-a<>

{

//處理溢出

}

else

{

result=a+b;

}

嵌入式硬件一般沒有浮點處理器,浮點數運算在嵌入式也比較少見並且溢出判斷嚴重依賴C庫支持,這裏不探討。

5.3檢測移比特

在探討未定義行為時,提到有符號數右移、移比特的數量是負值或者大於操作數的比特數都是未定義行為,也提到不對有符號數進行比特操作,但要檢測移比特的數量是否大於操作數的比特數。下面給出一個沒符號整數左移檢測代碼段:

unsigned int ui1;

unsigned int ui2;

unsigned int uresult;

/*初始化ui1,ui2*/

if(ui2>=sizeof(unsigned int)*CHAR_BIT)

{

//處理錯誤

}

else

{

uresult=ui1<<>

}

6.假如有硬件看門狗,則使用它

在其它一切措施都失效的情况下,看門狗可能是最後的防線。它的原理特別簡略,但卻能大大提高設備的可靠性。假如設備有硬件看門狗,一定要為它編寫驅動程序。

?要盡可能早的開啟看門狗

這是由於從上電複比特完畢到開啟看門狗的這段時長內,設備有可能被幹擾而跳過看門狗初始化程序,導致看門狗失效。盡可能早的開啟看門狗,能够降低這種概率;

?不要在中斷中喂狗,除非有其他聯動措施

在中斷程序喂狗,由於幹擾的存在,程序可能一直處於中斷之中,這樣會導致看門狗失效。假如在主程序中設置標志比特,中斷程序喂狗時與這個標志比特聯合判斷,也是允許的;

?喂狗間隔跟產品需求有關,並非特定的時長

產品的特性决定了喂狗間隔。對於不波及安全性、實時性的設備,喂狗間隔比較寬松,但間隔時長不宜過長,否則被用戶感知到,是影響用戶體驗的。對於設計安全性、有實時控制類的設備,原則是盡可能快的複比特,否則會造成事故。

克萊門汀號在進行第二階段的任務時,原本預訂要從月球飛行到太空深處的Geographos小行星進行探勘,然而這艘太空探測器在飛向小行星時卻由於一個軟件缺少陷而使其中斷運作20分鐘,不光未能到達小行星,也由於控制噴嘴焚燒了11分鐘使電力供給降低,沒法再透過遠端控制探測器,最終完畢這項任務,但也導致了資源與資金的浪費。

“克萊門汀太空任務失敗這件事讓我感到十分震驚,它其實能够透過硬件中一款簡略的看門狗計時器避免掉這項意外,但由於當時的開發時長相當緊縮,程序設計人員沒時長編寫程序來啟動它,”Ganssle說。

遺憾的是,1998年發射的近地號太空船(NEAR)也遇到了相同的問題。由於編程人員並未采納建議,因此,當推進器减速器系統故障時,29公斤的儲備燃料也隨之報銷──這同樣是一個原本可經由看門狗定時器編程而避免的問題,同時也證明要從其他程序設計人員的錯誤中進修並不容易。

7.關鍵數據儲存多個備份,取數據采用“錶决法”

RAM中的數據在受到幹擾情况下有可能被變更,對於系統關鍵數據應該進行珍愛。關鍵數據包括全局變量、靜態變量以及須要珍愛的數據區域。備份數據與原數據不應該處於相鄰比特置,因此不應由編譯器默認分配備份數據比特置,而應該由程序員指定區域存儲。

能够將RAM分為3個區域,第一個區域保存原碼,第二個區域保存反碼,第三個區域保存异或碼,區域之間預留一定量的“空白”RAM作為隔離。能够使用編譯器的“分散加載”機制將變量分別存儲在這些區域。須要進行讀取時,同時讀出3份數據並進行錶决,取至少有兩個相同的那個值。

假設設備的RAM從0x1000_0000初始,我須要在RAM的0x1000_0000~0x10007FFF內存儲原碼,在0x1000_9000~0x10009FFF內存儲反碼,在0x1000_B000~0x1000BFFF內存儲0xAA的异或碼,編譯器的分散加載能够設置為:

LR_IROM1 0x00000000 0x00080000 { ; load region size_region

ER_IROM1 0x00000000 0x00080000 { ; load address = execution address

*.o (RESET, +First)

*(InRoot$$Sections)

.ANY (+RO)

}

RW_IRAM1 0x10000000 0x00008000 { ;保存原碼

.ANY (+RW +ZI )

}

RW_IRAM3 0x10009000 0x00001000{ ;保存反碼

.ANY (MY_BK1)

}

RW_IRAM2 0x1000B000 0x00001000 { ;保存异或碼

.ANY (MY_BK2)

}

}

假如一個關鍵變量須要多處備份,能够依照下面方式定義變量,將三個變量分別指定到三個不不間斷的RAM區中,並在定義時依照原碼、反碼、0xAA的异或碼進行初始化。

uint32 plc_pc=0; //原碼

__attribute__((section("MY_BK1"))) uint32 plc_pc_not=~0x0; //反碼

__attribute__((section("MY_BK2"))) uint32 plc_pc_xor=0x0^0xAAAAAAAA; //异或碼

當須要寫這個變量時,這三個比特置都要更新;讀取變量時,讀取三個值做判斷,取至少有兩個相同的那個值。

為什麼選取异或碼而不是補碼?這是由於MDK的整數是依照補碼存儲的,正數的補碼與原碼相

寫文章

keil下的STM32程序開發部署(一)

史强

https://github.com/freeeyes

?關注他

3人贊同了該文章

買一塊&nbsp;STM32的進修版很重要,雖然STM32的老本不足5元,但是對應的接口GPIO輸出到不同的硬件連接,假如完全自己做的話還是比較大的工程,且意義有限。

首先下載keil編譯器,這個和STM配合比較好。

舉薦keil5,下載後,安裝一下&nbsp;STM的驅動包。

這裏最好吧STM的所有芯片驅動都裝上,由於自身也不大,STM小型號比較多,假如常見的103XX等等。

這裏所有的數據包,安裝好保持最新的即可。

然後配置一下keil環境。

這裏有幾個地方注意一下。

首先,設置DEBUG的參數。

這裏要選擇ST-LInk,這是一個小的硬件。能够和STM進修板連接。能够去京東搜搜,都有,這裏要注意一點,第一次ST-LInk接入板子,這裏請更新一下ST-Link的驅動,詳細在買ST-LInk的時候一般會有一個小光盤,或者直接找對應廠商要,由於ST-LInk的老驅動對keil5兼容有問題,昇級後就能够了。

還有一個注意下面的DEBUG配置

這裏須要指定連接後,直接reset板子,讓程序生效,這樣,當你燒錄程序後,馬上就能够看到結果了。

另外,建議初學者,找一個keil的樣例工程來改寫。

由於文件組織目錄是有學問的。

假如,最簡略的。

這裏的目錄構造。最好和你的實際文件目錄構造一致。

所有的驅動放在一個目錄下,系統文件放在一個目錄下。

你的主程序放在一個目錄下。組織比較清晰。

keil的所有主入口是main,和C代碼是一致的。

然後就是如何讓程序跑起來。

這裏是編譯

你能够在這裏編譯你的代碼。

這裏有一個小技巧。假如你的代碼比較複雜,你能够使用F12查找你的函數定義和實現。

編譯的結果,能够在下面的輸出看到

最後一步,等鄙汆譯都沒錯了。

把程序燒錄進STM板子即可。

以上就是最根本的keil5和stm板子調試方法。

後面,我會慢慢會補充一些實際有用的小程序代碼以及說明。

如何使用串口來給STM32下載程序

彩蛋:最近有同學跟我要單片機的資料,我特意花幾個月時間,總結了我10年產品研發經驗,資料包幾乎覆蓋了C語言、單片機、模電數電、原理圖和PCB設計、單片機高級編程等等,非常適合初學者入門和進階。除此以外,再含淚分享我壓箱底的22個熱門開源項目,包含源碼+原理圖+PCB+說明文檔,不是市面上打包賣的那種課程,我認為教程多未必是好事,10年前我自學快,除了自身執行力以外,還有就是教程少。不要害羞做伸手黨,等你一個小紅點。後期我也會組建一些純技術交流的小圈子,讓大家能認識更多的大佬,有個好的圈子,你對行業的認知一定是最前沿的。50477e660b08c3e9ccd7f0a9a44789e4.pnge76be661bf7c90a40dcd6a3f035a0084.png

原网站

版权声明
本文为[大方老師嵌入式]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060903026510.html