商用空調產品功能復雜,運行環境惡劣,尤其對于商用多聯機來說,其控制軟件可是說是電器類產品中最復雜的軟件之一。 商用多聯機控制軟件不僅包含整個空調各個運行系統的控制邏輯而且還包含復雜的多機通訊,同時相同功能的代碼需要在不同處理器之間移植。 因此,怎樣提升多聯機軟件的可靠性、可移植性一直是多聯機軟件工程師研究的問題。
1 MISRA C 編程規范
MISRA C 是有汽車產業軟件可靠性協會 (Motor IndustrySoftware Reliability Association)(MISRA) 提 出的 C 語 言開發標準。 其目的是在增進嵌入式系統的安全性以及可移植性 .MISRA C 一開始主要是針對汽車產業, 不過隨著對 MISRA C認可度的提高,其他行業也開始逐漸使用 MISRA C. 目前大家熟悉的版本是 MISRA C : 2004 版。
MISRA C:2004 包含 141 條規則,其中 121 條強制規則,20條建議規則。141 條規則分為 21 個類別,分別為:編程環境相關類規則;語言擴展類規則;文檔類規則;字符集規則;標識符規則;類型規則;常量規則;聲明與定義類規則;初始化規則;數值類型轉換規則;指針類型轉換規則;表達式規則;控制語句表達式規則;控制流規則;Switch 語句規則;函數類規則;指針和數組類規則;結構與聯合體規則;預處理指令規則;標準庫規則;運行時錯誤規則。
2 多聯機軟件開發中常見錯誤以及 MISRA C 的應用
多聯機軟件開發中常用的錯誤舉例:
2.1 條件遺漏
開發過程中,程序員經常只對符合條件的處理,而遺漏了一些潛在的可能性,即在 if …else if…語句中經常忘記增加一個 else 語句,來處理剩余條件。 MISRA C 的第 14.10 規則要求所有的 if…else if…結構都必須以 else 句子結束,保證所有可能的條件都得到處理。
2.2 表達式的計算順序
如 A = b+++c;語句, 程序員可能會依據標準 C 編譯器的表達式計算順序來得到預期的值,但因處理器的 C 編譯器和標準 C 編譯器并不一定相同,且不同編譯器所編譯的結果也不盡相同。 因此這類的表達式可能有不確定的結果,同時也增加了代碼移植的難度, MISRA C 的 12.2 規則, 要求表達式在任何情況下求值順序必須保持一致。 編程過程中盡可能的通過括號將表達式的計算順序定好,保證唯一的計算順序。
2.3 數組越界
多聯機外機程序中經常定義一個數組用于存儲所有內機的狀態信息,通訊接收程序根據接收到的內機地址來定位對應內機在數組中的位置,如 if (InterDoorData[Address])語句中,Ad-dress 這個地址變量在特殊情況下可能超出數組定義的最大個數,因此數組訪問就有可能越界。 MISRA C 的 21.1 規則,要求采用靜態或動態分析工具,對數組越界的錯誤做出排查,可以及時發現數組訪問越界的情況。
2.4 代碼移植過程中易出錯
相同功能的代碼比如通訊功能的代碼、內機自動尋址功能的代碼,在不同品牌的處理器之間移植,經常出現難以發現的錯誤。 如變量類型定義、局部變量和全局變量重名導致的一些問題。 MISRA C 的一些規則能夠有效的保證代碼的可移植性,如局部變量不能和全局變量重名;作用于文件范圍內的變量應當定義為 Static 型, 這樣避免與其他文件中同名的全局變量沖突;局部變量在使用之前一定要賦值;移位操作符不應該在有符號變量上使用。
2.5 由于聯合體的使用產生的錯誤
由于多聯機控制芯片多使用 8 位的單片機,RAM 資源有效,為了有效利用資源,程序中一般會使用聯合體來定義位變量。 多聯機軟件通訊部分聯合體的應用常出現如下問題,如:用聯合體定義了兩個位變量 fRxOk 和 FlashLedOn.typedef union{unsigned char Byte;struct{unsigned RxOk : 1;unsigned FlashLedOn: 1;……}Bits;} FLAG3;extern FLAG3 mFlag3;#define fRxOk mFlag3.Bits.RxOk /* 正 確接收到一幀數據 */#define fFlashLedOn mFlag3.Bits.FlashLedOn……fFlashLedOn 在主循環中被清 0, fFlashLedOn = 0;以 Code-Warrior 編譯器為例,其編譯后的匯編代碼為:
LDA mFlag3 ……(1)AND #0FDH ……(2)STA mFlag3 ……(3)fRxOk 在通訊中斷函數中被賦值。 fRxOk = 1;其編譯后的匯編代碼為:
LDA mFlag3……(4)ORA #01H ……(5)STA mFlag3……(6)假設執行指令(1)之前,mFlag3= 0x02,當主循環(2)指令執行完畢瞬間,通訊接收中斷產生,A =0x00 入棧,再執行(4)(5)(6)條指令后,mFlag3 = 0x03, 退出中斷函數后, 堆棧中的 A =0x00 出棧,此時 A = 0x00;再執行指令(3),mFlag3=0x00. 而指令(4)(5)(6)原本的目的是將 mFlag3 的 第 0 位 置上 ,結果原本應該是 mFlag3 等于 0x01 而不是 0x00. 采用聯合體定義位變量就有可能導致這樣的問題。 鑒于單片機 RAM 有限, 可以對 MISRAC 的 18.4 規則進行部分遵循,即在所有中斷函數中不允許用到聯合體定義的數據,包括位變量和字符串定義。 只允許在主循環程序中使用聯合體定義的數據。 如上例中,fRxOk 可以用一個單獨定義的 unsigned char 型變量替代。
3 結語
軟件開發過程中遵循 MISRA C 規范能夠有效提高軟件的安全性和可移植性,但在一定程度上也會增加編程難度。 由于MISRA C 規則比較多,難以人工逐一檢查 ,許多 C 語言靜態分析工具都支持 MISRA C 規則檢查, 如 PCLINT 和 IAR 開發環境自帶的規則檢測工具等,開發過程中可以采用這類分析工具來提高工作效率。
參考文獻:
[1]MISRA C :2004 版[2]CodeWarrior 集成開發環境使用手冊[M].