0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

淺談嵌入式中的C語(yǔ)言編程技術(shù)分析

FPGA那點(diǎn)事兒 ? 來(lái)源:STM32嵌入式開(kāi)發(fā) ? 作者:STM32嵌入式開(kāi)發(fā) ? 2021-08-19 14:50 ? 次閱讀

嵌入式產(chǎn)品的可靠性自然與硬件密不可分,但在硬件確定、并且沒(méi)有第三方測(cè)試的前提下,使用防御性編程思想寫(xiě)出的代碼,往往具有更高的穩(wěn)定性。

防御性編程首先需要認(rèn)清C語(yǔ)言的種種缺陷和陷阱,C語(yǔ)言對(duì)于運(yùn)行時(shí)的檢查十分弱小,需要程序員謹(jǐn)慎的考慮代碼,在必要的時(shí)候增加判斷;防御性編程的另一個(gè)核心思想是假設(shè)代碼運(yùn)行在并不可靠的硬件上,外接干擾有可能會(huì)打亂程序執(zhí)行順序、更改RAM存儲(chǔ)數(shù)據(jù)等等。

1具有形參的函數(shù),需判斷傳遞來(lái)的實(shí)參是否合法

程序員可能無(wú)意識(shí)的傳遞了錯(cuò)誤參數(shù);外界的強(qiáng)干擾可能將傳遞的參數(shù)修改掉,或者使用隨機(jī)參數(shù)意外的調(diào)用函數(shù),因此在執(zhí)行函數(shù)主體前,需要先確定實(shí)參是否合法。

4c99c0fa-fd3c-11eb-9bcf-12bb97331649.png

2仔細(xì)檢查函數(shù)的返回值

對(duì)函數(shù)返回的錯(cuò)誤碼,要進(jìn)行全面仔細(xì)處理,必要時(shí)做錯(cuò)誤記錄。

4ca27a06-fd3c-11eb-9bcf-12bb97331649.png

3防止指針越界

如果動(dòng)態(tài)計(jì)算一個(gè)地址時(shí),要保證被計(jì)算的地址是合理的并指向某個(gè)有意義的地方。特別對(duì)于指向一個(gè)結(jié)構(gòu)或數(shù)組的內(nèi)部的指針,當(dāng)指針增加或者改變后仍然指向同一個(gè)結(jié)構(gòu)或數(shù)組。

4防止數(shù)組越界

數(shù)組越界的問(wèn)題前文已經(jīng)講述的很多了,由于C不會(huì)對(duì)數(shù)組進(jìn)行有效的檢測(cè),因此必須在應(yīng)用中顯式的檢測(cè)數(shù)組越界問(wèn)題。下面的例子可用于中斷接收通訊數(shù)據(jù)。

4cbb786c-fd3c-11eb-9bcf-12bb97331649.png

在使用一些庫(kù)函數(shù)時(shí),同樣需要對(duì)邊界進(jìn)行檢查,比如下面的memset(RecBuf,0,len)函數(shù)把RecBuf指指向的內(nèi)存區(qū)的前l(fā)en個(gè)字節(jié)用0填充,如果不注意len的長(zhǎng)度,就會(huì)將數(shù)組RecBuf之外的內(nèi)存區(qū)清零:

4ce8cf2e-fd3c-11eb-9bcf-12bb97331649.png

5數(shù)學(xué)數(shù)運(yùn)算

5.1除法運(yùn)算,只檢測(cè)除數(shù)為零就可靠嗎?

除法運(yùn)算前,檢查除數(shù)是否為零幾乎已經(jīng)成為共識(shí),但是僅檢查除數(shù)是否為零就夠了嗎?

考慮兩個(gè)整數(shù)相除,對(duì)于一個(gè)signed long類(lèi)型變量,它能表示的數(shù)值范圍為:-2147483648 ~+2147483647,如果讓-2147483648/ -1,那么結(jié)果應(yīng)該是+2147483648,但是這個(gè)結(jié)果已經(jīng)超出了signedlong所能表示的范圍了。所以,在這種情況下,除了要檢測(cè)除數(shù)是否為零外,還要檢測(cè)除法是否溢出。

 #include <limits.h>  
   signed long sl1,sl2,result; /*初始化sl1和sl2*/   
  if((sl2==0)||(sl1==LONG_MIN && sl2==-1)) {  
   //處理錯(cuò)誤     } else  
  {     result = sl1 / sl2; }

5.2檢測(cè)運(yùn)算溢出

整數(shù)的加減乘運(yùn)算都有可能發(fā)生溢出,在討論未定義行為時(shí),給出過(guò)一個(gè)有符號(hào)整形加法溢出判斷代碼,這里再給出一個(gè)無(wú)符號(hào)整形加法溢出判斷代碼段:

 #include      unsigned int a,b,result; /*初始化a,b*/     if(UINT_MAX-a {     //處理溢出     } else    {     result=a+b; }

嵌入式硬件一般沒(méi)有浮點(diǎn)處理器,浮點(diǎn)數(shù)運(yùn)算在嵌入式也比較少見(jiàn)并且溢出判斷嚴(yán)重依賴(lài)C庫(kù)支持,這里不討論。

5.3檢測(cè)移

在討論未定義行為時(shí),提到有符號(hào)數(shù)右移、移位的數(shù)量是負(fù)值或者大于操作數(shù)的位數(shù)都是未定義行為,也提到不對(duì)有符號(hào)數(shù)進(jìn)行位操作,但要檢測(cè)移位的數(shù)量是否大于操作數(shù)的位數(shù)。下面給出一個(gè)無(wú)符號(hào)整數(shù)左移檢測(cè)代碼段:

 unsigned int ui1; unsigned int ui2; unsigned int uresult; /*初始化ui1,ui2*/   if(ui2>=sizeof(unsigned int)*CHAR_BIT) {     //處理錯(cuò)誤   } else   {     uresult=ui1< }

6如果有硬件看門(mén)狗,則使用它

在其它一切措施都失效的情況下,看門(mén)狗可能是最后的防線。它的原理特別簡(jiǎn)單,但卻能大大提高設(shè)備的可靠性。如果設(shè)備有硬件看門(mén)狗,一定要為它編寫(xiě)驅(qū)動(dòng)程序。

  • 要盡可能早的開(kāi)啟看門(mén)狗

這是因?yàn)閺纳想姀?fù)位結(jié)束到開(kāi)啟看門(mén)狗的這段時(shí)間內(nèi),設(shè)備有可能被干擾而跳過(guò)看門(mén)狗初始化程序,導(dǎo)致看門(mén)狗失效。盡可能早的開(kāi)啟看門(mén)狗,可以降低這種概率;

  • 不要在中斷中喂狗,除非有其他聯(lián)動(dòng)措施

在中斷程序喂狗,由于干擾的存在,程序可能一直處于中斷之中,這樣會(huì)導(dǎo)致看門(mén)狗失效。如果在主程序中設(shè)置標(biāo)志位,中斷程序喂狗時(shí)與這個(gè)標(biāo)志位聯(lián)合判斷,也是允許的;

  • 喂狗間隔跟產(chǎn)品需求有關(guān),并非特定的時(shí)間

產(chǎn)品的特性決定了喂狗間隔。對(duì)于不涉及安全性、實(shí)時(shí)性的設(shè)備,喂狗間隔比較寬松,但間隔時(shí)間不宜過(guò)長(zhǎng),否則被用戶(hù)感知到,是影響用戶(hù)體驗(yàn)的。對(duì)于設(shè)計(jì)安全性、有實(shí)時(shí)控制類(lèi)的設(shè)備,原則是盡可能快的復(fù)位,否則會(huì)造成事故。

克萊門(mén)汀號(hào)在進(jìn)行第二階段的任務(wù)時(shí),原本預(yù)訂要從月球飛行到太空深處的Geographos小行星進(jìn)行探勘,然而這艘太空探測(cè)器在飛向小行星時(shí)卻由于一個(gè)軟件缺陷而使其中斷運(yùn)作20分鐘,不但未能到達(dá)小行星,也因?yàn)榭刂茋娮烊紵?1分鐘使電力供應(yīng)降低,無(wú)法再透過(guò)遠(yuǎn)端控制探測(cè)器,最終結(jié)束這項(xiàng)任務(wù),但也導(dǎo)致了資源與資金的浪費(fèi)。

“克萊門(mén)汀太空任務(wù)失敗這件事讓我感到十分震驚,它其實(shí)可以透過(guò)硬件中一款簡(jiǎn)單的看門(mén)狗計(jì)時(shí)器避免掉這項(xiàng)意外,但由于當(dāng)時(shí)的開(kāi)發(fā)時(shí)間相當(dāng)緊縮,程序設(shè)計(jì)人員沒(méi)時(shí)間編寫(xiě)程序來(lái)啟動(dòng)它,”Ganssle說(shuō)。

遺憾的是,1998年發(fā)射的近地號(hào)太空船(NEAR)也遇到了相同的問(wèn)題。由于編程人員并未采納建議,因此,當(dāng)推進(jìn)器減速器系統(tǒng)故障時(shí),29公斤的儲(chǔ)備燃料也隨之報(bào)銷(xiāo)──這同樣是一個(gè)本來(lái)可經(jīng)由看門(mén)狗定時(shí)器編程而避免的問(wèn)題,同時(shí)也證明要從其他程序設(shè)計(jì)人員的錯(cuò)誤中學(xué)習(xí)并不容易。

7 關(guān)鍵數(shù)據(jù)儲(chǔ)存多個(gè)備份,取數(shù)據(jù)采用“表決法”

RAM中的數(shù)據(jù)在受到干擾情況下有可能被改變,對(duì)于系統(tǒng)關(guān)鍵數(shù)據(jù)應(yīng)該進(jìn)行保護(hù)。關(guān)鍵數(shù)據(jù)包括全局變量、靜態(tài)變量以及需要保護(hù)的數(shù)據(jù)區(qū)域。備份數(shù)據(jù)與原數(shù)據(jù)不應(yīng)該處于相鄰位置,因此不應(yīng)由編譯器默認(rèn)分配備份數(shù)據(jù)位置,而應(yīng)該由程序員指定區(qū)域存儲(chǔ)。

可以將RAM分為3個(gè)區(qū)域,第一個(gè)區(qū)域保存原碼,第二個(gè)區(qū)域保存反碼,第三個(gè)區(qū)域保存異或碼,區(qū)域之間預(yù)留一定量的“空白”RAM作為隔離。可以使用編譯器的“分散加載”機(jī)制將變量分別存儲(chǔ)在這些區(qū)域。需要進(jìn)行讀取時(shí),同時(shí)讀出3份數(shù)據(jù)并進(jìn)行表決,取至少有兩個(gè)相同的那個(gè)值。

假如設(shè)備的RAM從0x1000_0000開(kāi)始,我需要在RAM的0x1000_0000~0x10007FFF內(nèi)存儲(chǔ)原碼,在0x1000_9000~0x10009FFF內(nèi)存儲(chǔ)反碼,在0x1000_B000~0x1000BFFF內(nèi)存儲(chǔ)0xAA的異或碼,編譯器的分散加載可以設(shè)置為:

 LR_IROM1 0x00000000 0x00080000  {   
 ; load region size_region
ER_IROM10x000000000x00080000
{;loadaddress=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)  
 } }

如果一個(gè)關(guān)鍵變量需要多處備份,可以按照下面方式定義變量,將三個(gè)變量分別指定到三個(gè)不連續(xù)的RAM區(qū)中,并在定義時(shí)按照原碼、反碼、0xAA的異或碼進(jìn)行初始化。

 uint32  plc_pc=0;                                                       //原碼   __attribute__((section("MY_BK1"))) uint32 plc_pc_not=~0x0;              //反碼   __attribute__((section("MY_BK2"))) uint32 plc_pc_xor=0x0^0xAAAAAAAA;    //異或碼

當(dāng)需要寫(xiě)這個(gè)變量時(shí),這三個(gè)位置都要更新;讀取變量時(shí),讀取三個(gè)值做判斷,取至少有兩個(gè)相同的那個(gè)值。

為什么選取異或碼而不是補(bǔ)碼?這是因?yàn)镸DK的整數(shù)是按照補(bǔ)碼存儲(chǔ)的,正數(shù)的補(bǔ)碼與原碼相同,在這種情況下,原碼和補(bǔ)碼是一致的,不但起不到冗余作用,反而對(duì)可靠性有害。比如存儲(chǔ)的一個(gè)非零整數(shù)區(qū)因?yàn)楦蓴_,RAM都被清零,由于原碼和補(bǔ)碼一致,按照3取2的“表決法”,會(huì)將干擾值0當(dāng)做正確的數(shù)據(jù)。

8 對(duì)非易失性存儲(chǔ)器進(jìn)行備份存儲(chǔ)

非易失性存儲(chǔ)器包括但不限于Flash、EEPROM、鐵電。僅僅將寫(xiě)入非易失性存儲(chǔ)器中的數(shù)據(jù)再讀出校驗(yàn)是不夠的。強(qiáng)干擾情況下可能導(dǎo)致非易失性存儲(chǔ)器內(nèi)的數(shù)據(jù)錯(cuò)誤,在寫(xiě)非易失性存儲(chǔ)器的期間系統(tǒng)掉電將導(dǎo)致數(shù)據(jù)丟失,因干擾導(dǎo)致程序跑飛到寫(xiě)非易失性存儲(chǔ)器函數(shù)中,將導(dǎo)致數(shù)據(jù)存儲(chǔ)紊亂。

一種可靠的辦法是將非易失性存儲(chǔ)器分成多個(gè)區(qū),每個(gè)數(shù)據(jù)都將按照不同的形式寫(xiě)入到這些分區(qū)中,需要進(jìn)行讀取時(shí),同時(shí)讀出多份數(shù)據(jù)并進(jìn)行表決,取相同數(shù)目較多的那個(gè)值。

9 軟件

對(duì)于初始化序列或者有一定先后順序的函數(shù)調(diào)用,為了保證調(diào)用順序或者確保每個(gè)函數(shù)都被調(diào)用,我們可以使用環(huán)環(huán)相扣,實(shí)質(zhì)上這也是一種軟件鎖。此外對(duì)于一些安全關(guān)鍵代碼語(yǔ)句(是語(yǔ)句,而不是函數(shù)),可以給它們?cè)O(shè)置軟件鎖,只有持有特定鑰匙的,才可以訪問(wèn)這些關(guān)鍵代碼。也可以通俗的理解為,關(guān)鍵安全代碼不能按照單一條件執(zhí)行,要額外的多設(shè)置一個(gè)標(biāo)志。

比如,向Flash寫(xiě)一個(gè)數(shù)據(jù),我們會(huì)判斷數(shù)據(jù)是否合法、寫(xiě)入的地址是否合法,計(jì)算要寫(xiě)入的扇區(qū)。之后調(diào)用寫(xiě)Flash子程序,在這個(gè)子程序中,判斷扇區(qū)地址是否合法、數(shù)據(jù)長(zhǎng)度是否合法,之后就要將數(shù)據(jù)寫(xiě)入Flash。

由于寫(xiě)Flash語(yǔ)句是安全關(guān)鍵代碼,所以程序給這些語(yǔ)句上鎖:必須具有正確的鑰匙才可以寫(xiě)Flash。這樣即使是程序跑飛到寫(xiě)Flash子程序,也能大大降低誤寫(xiě)的風(fēng)險(xiǎn)。

  • * 名稱(chēng):RamToFlash() * 功能:復(fù)制RAM的數(shù)據(jù)到FLASH,命令代碼51。
  • * 入口參數(shù):dst 目標(biāo)地址,即FLASH起始地址。以512字節(jié)為分界
  • * src 源地址,即RAM地址。地址必須字對(duì)齊 * no 復(fù)制字節(jié)個(gè)數(shù),為512/1024/4096/8192 * ProgStart 軟件鎖標(biāo)志 * 出口參數(shù):IAP返回值(paramout緩沖區(qū)) CMD_SUCCESS,SRC_ADDR_ERROR,DST_ADDR_ERROR, SRC_ADDR_NOT_MAPPED,DST_ADDR_NOT_MAPPED,COUNT_ERROR,BUSY,未選擇扇區(qū) ****************************************************************/
  • void RamToFlash(uint32 dst, uint32 src, uint32 no,uint8 ProgStart)
  • { PLC_ASSERT("Sector number",(dst>=0x00040000)&&(dst<=0x0007FFFF)); PLC_ASSERT("Copy bytes number is 512",(no==512)); PLC_ASSERT("ProgStart==0xA5",(ProgStart==0xA5)); paramin[0] = IAP_RAMTOFLASH; // 設(shè)置命令字 paramin[1] = dst; // 設(shè)置參數(shù) paramin[2] = src; paramin[3] = no; paramin[4] = Fcclk/1000;
  • if(ProgStart==0xA5)
  • //只有軟件鎖標(biāo)志正確時(shí),才執(zhí)行關(guān)鍵代碼
  • { iap_entry(paramin, paramout); // 調(diào)用IAP服務(wù)程序 ProgStart=0; } else { paramout[0]=PROG_UNSTART; } }

該程序段是編程lpc1778內(nèi)部Flash,其中調(diào)用IAP程序的函數(shù)iap_entry(paramin, paramout)是關(guān)鍵安全代碼,所以在執(zhí)行該代碼前,先判斷一個(gè)特定設(shè)置的安全鎖標(biāo)志ProgStart,只有這個(gè)標(biāo)志符合設(shè)定值,才會(huì)執(zhí)行編程Flash操作。如果因?yàn)橐馔獬绦蚺茱w到該函數(shù),由于ProgStart標(biāo)志不正確,是不會(huì)對(duì)Flash進(jìn)行編程的。

10 通信

通訊線上的數(shù)據(jù)誤碼相對(duì)嚴(yán)重,通訊線越長(zhǎng),所處的環(huán)境越惡劣,誤碼會(huì)越嚴(yán)重。拋開(kāi)硬件和環(huán)境的作用,我們的軟件應(yīng)能識(shí)別錯(cuò)誤的通訊數(shù)據(jù)。對(duì)此有一些應(yīng)用措施:

  • 制定協(xié)議時(shí),限制每幀的字節(jié)數(shù);

每幀字節(jié)數(shù)越多,發(fā)生誤碼的可能性就越大,無(wú)效的數(shù)據(jù)也會(huì)越多。對(duì)此以太網(wǎng)規(guī)定每幀數(shù)據(jù)不大于1500字節(jié),高可靠性的CAN收發(fā)器規(guī)定每幀數(shù)據(jù)不得多于8字節(jié),對(duì)于RS485,基于RS485鏈路應(yīng)用最廣泛的Modbus協(xié)議一幀數(shù)據(jù)規(guī)定不超過(guò)256字節(jié)。因此,建議制定內(nèi)部通訊協(xié)議時(shí),使用RS485時(shí)規(guī)定每幀數(shù)據(jù)不超過(guò)256字節(jié);

  • 使用多種校驗(yàn)

編寫(xiě)程序時(shí)應(yīng)使能奇偶校驗(yàn),每幀超過(guò)16字節(jié)的應(yīng)用,建議至少編寫(xiě)CRC16校驗(yàn)程序。

  • 增加額外判斷

1)增加緩沖區(qū)溢出判斷。這是因?yàn)閿?shù)據(jù)接收多是在中斷中完成,編譯器檢測(cè)不出緩沖區(qū)是否溢出,需要手動(dòng)檢查,在上文介紹數(shù)據(jù)溢出一節(jié)中已經(jīng)詳細(xì)說(shuō)明。

2)增加超時(shí)判斷。當(dāng)一幀數(shù)據(jù)接收到一半,長(zhǎng)時(shí)間接收不到剩余數(shù)據(jù),則認(rèn)為這幀數(shù)據(jù)無(wú)效,重新開(kāi)始接收??蛇x,跟不同的協(xié)議有關(guān),但緩沖區(qū)溢出判斷必須實(shí)現(xiàn)。這是因?yàn)閷?duì)于需要幀頭判斷的協(xié)議,上位機(jī)可能發(fā)送完幀頭后突然斷電,重啟后上位機(jī)是從新的幀開(kāi)始發(fā)送的,但是下位機(jī)已經(jīng)接收到了上次未發(fā)送完的幀頭,所以上位機(jī)的這次幀頭會(huì)被下位機(jī)當(dāng)成正常數(shù)據(jù)接收。這有可能造成數(shù)據(jù)長(zhǎng)度字段為一個(gè)很大的值,填滿(mǎn)該長(zhǎng)度的緩沖區(qū)需要相當(dāng)多的數(shù)據(jù)(比如一幀可能1000字節(jié)),影響響應(yīng)時(shí)間;另一方面,如果程序沒(méi)有緩沖區(qū)溢出判斷,那么緩沖區(qū)很可能溢出,后果是災(zāi)難性的。

  • 重傳機(jī)制

如果檢測(cè)到通訊數(shù)據(jù)發(fā)生了錯(cuò)誤,則要有重傳機(jī)制重新發(fā)送出錯(cuò)的幀。

11 開(kāi)關(guān)量輸入的檢測(cè)、確認(rèn)

開(kāi)關(guān)量容易受到尖脈沖干擾,如果不進(jìn)行濾除,可能會(huì)造成誤動(dòng)作。一般情況下,需要對(duì)開(kāi)關(guān)量輸入信號(hào)進(jìn)行多次采樣,并進(jìn)行邏輯判斷直到確認(rèn)信號(hào)無(wú)誤為止。

12 開(kāi)關(guān)量輸出

開(kāi)關(guān)信號(hào)簡(jiǎn)單的一次輸出是不安全的,干擾信號(hào)可能會(huì)翻轉(zhuǎn)開(kāi)關(guān)量輸出的狀態(tài)。采取重復(fù)刷新輸出可以有效防止電平的翻轉(zhuǎn)。

13 初始化信息的保存和恢復(fù)

微處理器寄存器值也可能會(huì)因外界干擾而改變,外設(shè)初始化值需要在寄存器中長(zhǎng)期保存,最容易被破壞。由于Flash中的數(shù)據(jù)相對(duì)不易被破壞,可以將初始化信息預(yù)先寫(xiě)入Flash,待程序空閑時(shí)比較與初始化相關(guān)的寄存器值是否被更改,如果發(fā)現(xiàn)非法更改則使用Flash中的值進(jìn)行恢復(fù)。

公司目前使用的4.3寸LCD顯示屏抗干擾能力一般。如果顯示屏與控制器之間的排線距離過(guò)長(zhǎng)或者對(duì)使用該顯示屏的設(shè)備打靜電或者脈沖群,顯示屏有可能會(huì)花屏或者白屏。

對(duì)此,我們可以將初始化顯示屏的數(shù)據(jù)保存在Flash中,程序運(yùn)行后,每隔一段時(shí)間從顯示屏的寄存器讀出當(dāng)前值和Flash存儲(chǔ)的值相比較,如果發(fā)現(xiàn)兩者不同,則重新初始化顯示屏。下面給出校驗(yàn)源碼,僅供參考。

定義數(shù)據(jù)結(jié)構(gòu):

4d0349b2-fd3c-11eb-9bcf-12bb97331649.png

定義const修飾的結(jié)構(gòu)體變量,存儲(chǔ)LCD部分寄存器的初始值,這個(gè)初始值跟具體的應(yīng)用初始化有關(guān),不一定是表中的數(shù)據(jù),通常情況下,這個(gè)結(jié)構(gòu)體變量被存儲(chǔ)到Flash中。

 /*LCD部分寄存器設(shè)置值列表*/  
 lcd_redu_list_struct const lcd_redu_list_str[]= { 

  {SSD1963_Get_Address_Mode,{0x20}                                  
 ,1}, /*1*/    {SSD1963_Get_Pll_Mn      
,{0x3b,0x02,0x04}                      
   ,3}, /*2*/   
 {SSD1963_Get_Pll_Status 
 ,{0x04}                              
     ,1}, /*3*/   
 {SSD1963_Get_Lcd_Mode  
  ,{0x24,0x20,0x01,0xdf,0x01,0x0f,0x00}    
 ,7}, /*4*/   
 {SSD1963_Get_Hori_Period ,{0x02,0x0c,0x00,0x2a,0x07,0x00,0x00,0x00},8}, /*5*/  
  {SSD1963_Get_Vert_Period ,{0x01,0x1d,0x00,0x0b,0x09,0x00,0x00}    
 ,7}, /*6*/    {SSD1963_Get_Power_Mode  ,{0x1c}                                  
 ,1}, /*7*/    {SSD1963_Get_Display_Mode,{0x03}                                 
  ,1}, /*8*/    {SSD1963_Get_Gpio_Conf   ,{0x0F,0x01}                       
       ,2}, /*9*/   
 {SSD1963_Get_Lshift_Freq ,{0x00,0xb8}                         
     ,2}, /*10*/ };

實(shí)現(xiàn)函數(shù)如下所示,函數(shù)會(huì)遍歷結(jié)構(gòu)體變量中的每一個(gè)命令,以及每一個(gè)命令下的初始值,如果有一個(gè)不正確,則跳出循環(huán),執(zhí)行重新初始化和恢復(fù)措施。這個(gè)函數(shù)中的MY_DEBUGF宏是我自己的調(diào)試函數(shù),使用串口打印調(diào)試信息,在接下來(lái)的第五部分將詳細(xì)敘述。

通過(guò)這個(gè)函數(shù),我可以長(zhǎng)時(shí)間監(jiān)控顯示屏的哪些命令、哪些位容易被干擾。程序里使用了一個(gè)被妖魔化的關(guān)鍵字:goto。大多數(shù)C語(yǔ)言書(shū)籍對(duì)goto關(guān)鍵字談之色變,但你應(yīng)該有自己的判斷。在函數(shù)內(nèi)部跳出多重循環(huán),除了goto關(guān)鍵字,又有哪種方法能如此簡(jiǎn)潔高效!

 /**  * lcd 顯示冗余  * 每隔一段時(shí)間調(diào)用該程序一次  */  
 void lcd_redu(void)   {  
   uint8_t  tmp[8];   
  uint32_t i,j;   
  uint32_t lcd_init_flag;   
  lcd_init_flag =0;
for(i=0;i<sizeof(lcd_redu_list_str)/sizeof(lcd_redu_list_str[0]);i+)     
{         LCD_SendCommand(lcd_redu_list_str[i].lcd_command);         
uyDelay(10);     
    for(j=0;j<lcd_redu_list_str[i].lcd_value_num;j++)     
    {             tmp[j]=LCD_ReadData();       
      if(tmp[j]!=lcd_redu_list_str[i].lcd_get_value[j])       
      {                 lcd_init_flag=0x55;                
 MY_DEBUGF(MENU_DEBUG,("讀lcd寄存器值與預(yù)期不符,命令為:0x%x,第%d個(gè)參數(shù),          
   該參數(shù)正確值為:0x%x,實(shí)際讀出值為:0x%xn",lcd_redu_list_str[i].lcd_command,j+1,       
      lcd_redu_list_str[i].lcd_get_value[j],tmp[j]));                
 goto handle_lcd_init;             }         }     } 
    handle_lcd_init:  
   if(lcd_init_flag==0x55)   
  {         //重新初始化LCD        
   //一些必要的恢復(fù)措施       } }

14 陷阱


對(duì)于8051內(nèi)核單片機(jī),由于沒(méi)有相應(yīng)的硬件支持,可以用純軟件設(shè)置軟件陷阱,用來(lái)攔截一些程序跑飛。對(duì)于ARM7或者Cortex-M系列單片機(jī),硬件已經(jīng)內(nèi)建了多種異常,軟件需要根據(jù)硬件異常來(lái)編寫(xiě)陷阱程序,用來(lái)快速定位甚至恢復(fù)錯(cuò)誤。

15 阻塞處理

有時(shí)候程序員會(huì)使用while(!flag);語(yǔ)句阻塞在此等待標(biāo)志flag改變,比如串口發(fā)送時(shí)用來(lái)等待一字節(jié)數(shù)據(jù)發(fā)送完成。這樣的代碼時(shí)存在風(fēng)險(xiǎn)的,如果因?yàn)槟承┰驑?biāo)志位一直不改變則會(huì)造成系統(tǒng)死機(jī)。

一個(gè)良好冗余的程序是設(shè)置一個(gè)超時(shí)定時(shí)器,超過(guò)一定時(shí)間后,強(qiáng)制程序退出while循環(huán)。

2003年8月11日發(fā)生的W32.Blaster.Worm蠕蟲(chóng)事件導(dǎo)致全球經(jīng)濟(jì)損失高達(dá)5億美元,這個(gè)漏洞是利用了Windows分布式組件對(duì)象模型的遠(yuǎn)程過(guò)程調(diào)用接口中的一個(gè)邏輯缺陷:在調(diào)用GetMachineName()函數(shù)時(shí),循環(huán)只設(shè)置了一個(gè)不充分的結(jié)束條件。

原代碼簡(jiǎn)化如下所示:

4d20a930-fd3c-11eb-9bcf-12bb97331649.png

軟發(fā)布的安全補(bǔ)丁MS03-026解決了這個(gè)問(wèn)題,為GetMachineName()函數(shù)設(shè)置了充分終止條件。一個(gè)解決代碼簡(jiǎn)化如下所示(并非微軟補(bǔ)丁代碼):

 HRESULT GetMachineName( WCHAR *pwszPath,
   WCHARwszMachineName[MAX_COMPUTTERNAME_LENGTH_FQDN+1]) {   
     WCHAR *pwszServerName = wszMachineName;      
  WCHAR *pwszTemp = pwszPath + 2;      
  WCHAR *end_addr = pwszServerName +MAX_COMPUTTERNAME_LENGTH_FQDN;

while((*pwszTemp!=L’\’)&&(*pwszTemp!=L’’)&&(pwszServerName            
  *pwszServerName++= *pwszTemp++;       
 /*… */   }

編輯:hfy


聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5054

    文章

    18920

    瀏覽量

    301058
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7581

    瀏覽量

    135656

原文標(biāo)題:嵌入式開(kāi)發(fā)中的防御性C語(yǔ)言編程

文章出處:【微信號(hào):gh_94c30763133f,微信公眾號(hào):FPGA那點(diǎn)事兒】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    嵌入式編程語(yǔ)言

    嵌入式編程語(yǔ)言隨著社會(huì)的飛速發(fā)展,IT技術(shù)已經(jīng)進(jìn)入高速發(fā)展階段,互聯(lián)網(wǎng)正在逐步向物聯(lián)網(wǎng)科技時(shí)代。發(fā)展。物聯(lián)網(wǎng)通過(guò)智能感知、識(shí)別
    發(fā)表于 11-08 06:10

    基于ARM的嵌入式系統(tǒng)與C語(yǔ)言編程的結(jié)合

    嵌入式平臺(tái)上進(jìn)行C語(yǔ)言編寫(xiě)系統(tǒng)軟件和應(yīng)用軟件的方法,希望為軟件編程人員提供一些思考。隨著計(jì)算機(jī)技術(shù)尤其是計(jì)算機(jī)信息控制
    發(fā)表于 12-14 08:49

    SQL編程技術(shù)的特點(diǎn)有哪些

    SQL編程技術(shù)可以有效的克服SQL語(yǔ)言實(shí)現(xiàn)復(fù)雜應(yīng)用方面的不足,提高應(yīng)用系統(tǒng)和數(shù)據(jù)管理系統(tǒng)間的互操作性。SQL的特點(diǎn)之一是在交互嵌入式二種不同的使用方式下, SQL的語(yǔ)法結(jié)構(gòu)基本上是
    發(fā)表于 12-22 07:04

    基于嵌入式實(shí)時(shí)操作系統(tǒng)的編程技術(shù)之資源同步介紹

    《基于嵌入式實(shí)時(shí)操作系統(tǒng)的編程技術(shù)》筆記清單:第三章任務(wù)劃分.《基于嵌入式實(shí)時(shí)操作系統(tǒng)的編程技術(shù)》筆記清單:第四章任務(wù)設(shè)計(jì).《基于嵌入式實(shí)時(shí)
    發(fā)表于 12-22 06:30

    嵌入式系統(tǒng)高級(jí)C語(yǔ)言編程

    嵌入式系統(tǒng)高級(jí)C語(yǔ)言編程》將以實(shí)際項(xiàng)目中的代碼作實(shí)例來(lái)進(jìn)行介紹,詳細(xì)分析嵌入式系統(tǒng)開(kāi)發(fā)中程序
    發(fā)表于 10-27 16:36 ?4699次閱讀

    嵌入式c語(yǔ)言編程(由淺入深)

    本內(nèi)容詳細(xì)介紹了嵌入式c語(yǔ)言編程的各項(xiàng)知識(shí),包括嵌入式c語(yǔ)言
    發(fā)表于 11-02 14:37 ?0次下載
    <b class='flag-5'>嵌入式</b><b class='flag-5'>c</b><b class='flag-5'>語(yǔ)言</b><b class='flag-5'>編程</b>(由淺入深)

    嵌入式C編程

    嵌入式C編程,非常有用的資料,介紹嵌入式C語(yǔ)言編程
    發(fā)表于 12-29 17:29 ?0次下載

    C語(yǔ)言嵌入式系統(tǒng)編程教程

    C語(yǔ)言嵌入式系統(tǒng)編程教程
    發(fā)表于 01-16 13:54 ?36次下載

    嵌入式c編程語(yǔ)言入門(mén)與深入

    嵌入式c編程語(yǔ)言入門(mén)與深入
    發(fā)表于 10-24 08:38 ?61次下載
    <b class='flag-5'>嵌入式</b><b class='flag-5'>c</b><b class='flag-5'>編程</b><b class='flag-5'>語(yǔ)言</b>入門(mén)與深入

    關(guān)于嵌入式C語(yǔ)言它有些什么意義

    目前數(shù)控技術(shù)和智能裝備等技術(shù)都得到了廣泛的關(guān)注。無(wú)論是數(shù)控技術(shù)還是智能裝備等相關(guān)技術(shù),都離不開(kāi)嵌入式編程
    發(fā)表于 07-12 17:04 ?2235次閱讀

    嵌入式DSP系統(tǒng)C語(yǔ)言硬件編程技術(shù)簡(jiǎn)介

    嵌入式DSP系統(tǒng)C語(yǔ)言硬件編程技術(shù)簡(jiǎn)介(嵌入式開(kāi)發(fā)環(huán)境配置)-該文檔為嵌入式DSP系統(tǒng)
    發(fā)表于 07-30 09:14 ?7次下載
    <b class='flag-5'>嵌入式</b>DSP系統(tǒng)<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>硬件<b class='flag-5'>編程技術(shù)</b>簡(jiǎn)介

    嵌入式DSP系統(tǒng)C語(yǔ)言硬件編程技術(shù)總結(jié)

    嵌入式DSP系統(tǒng)C語(yǔ)言硬件編程技術(shù)總結(jié)(北京嵌入式開(kāi)發(fā)招聘)-該文檔為嵌入式DSP系統(tǒng)
    發(fā)表于 07-30 12:55 ?11次下載
    <b class='flag-5'>嵌入式</b>DSP系統(tǒng)<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>硬件<b class='flag-5'>編程技術(shù)</b>總結(jié)

    嵌入式DSP系統(tǒng)C語(yǔ)言硬件編程技術(shù)

    嵌入式DSP系統(tǒng)C語(yǔ)言硬件編程技術(shù)(計(jì)算機(jī)三級(jí)嵌入式開(kāi)發(fā)技術(shù))-該文檔為
    發(fā)表于 07-30 14:04 ?27次下載
    <b class='flag-5'>嵌入式</b>DSP系統(tǒng)<b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b>硬件<b class='flag-5'>編程技術(shù)</b>

    C語(yǔ)言嵌入式Linux高級(jí)編程

    C語(yǔ)言本質(zhì)上是編程語(yǔ)言的“通用語(yǔ)言”,在今天仍具有極大的影響力。那么,C
    發(fā)表于 11-02 11:21 ?24次下載
    <b class='flag-5'>C</b><b class='flag-5'>語(yǔ)言</b><b class='flag-5'>嵌入式</b>Linux高級(jí)<b class='flag-5'>編程</b>

    c語(yǔ)言嵌入式編程

    比較詳盡的嵌入式C語(yǔ)言解答和分析
    發(fā)表于 03-10 14:53 ?161次下載