(北京信息職業(yè)技術學院,北京 100015)
C語言是嵌入式軟件開發(fā)使用最多的語言[1],主要是由于C語言兼具高低級語言的特性,簡潔高效、靈活方便,支持對硬件的直接操作,但其靈活性也往往會帶來復雜的代碼管理和維護問題。不同于一般形式的軟件編程,嵌入式系統(tǒng)編程建立在特定的硬件平臺上,面向的是一種專用的計算機系統(tǒng)[2],既有對硬件操作的復雜性,也有應用層次上的通用性。因此,在軟件開發(fā)過程中,采用良好的軟件框架和設計方法,對項目進行工程化管理,能夠更好地指導軟件開發(fā)的層次劃分和功能模塊設計。既能提高軟件系統(tǒng)的開發(fā)、執(zhí)行和維護效率,又有利于提高程序代碼的重用性、拓展性和可靠性。本文在當前流行的軟件工程思想基礎上,將面向對象設計技術、分層技術應用到C語言嵌入式系統(tǒng)編程中,探討在C語言嵌入式系統(tǒng)開發(fā)中的系統(tǒng)設計思路、分層實際、程序架構以及模塊重用等問題。
無論是面向過程思想,還是面向對象思想,都是為了更好地將開發(fā)需求轉變成軟件模塊劃分,進而轉變成能夠用代碼實現的程序功能[3]。在實際系統(tǒng)開發(fā)中,不是說一定要用C++或Java等面向對象語言才能進行面向對象程序設計,用C語言也一樣可以實現程序模塊的封裝、繼承等特性,關鍵是如何體現模塊劃分的“高內聚、低耦合”特點,提高代碼的重用性和拓展性。隨著嵌入式軟件系統(tǒng)的規(guī)模和復雜度日益增長,如何更好地進行模塊劃分,開發(fā)出可正確工作的復雜軟件,成為系統(tǒng)設計面臨的主要問題。
在進行模塊化的過程中,通常采用分層技術對應用需求問題進行梳理,抽象出不同層次的模塊結構,界定各層次之間的依賴關系,最終將應用需求轉變?yōu)檐浖O計。一個方向是自頂向下,從抽象到具體,從最頂層的程序或者邏輯整體描述規(guī)范出發(fā)向下到具體的操作模塊,這是目前嵌入式系統(tǒng)應用層開發(fā)常用的方法。比如,液晶屏幕顯示控制,可以細化到對點陣的一些操作,如“點亮一個點”、“點滅一個點”等。另一個方向是自底向上,從具體到抽象,從某個應用對象的操作出發(fā)分析常用的操作方法,這是在硬件驅動開發(fā)中常常采用的方法。比如,設計液晶屏幕的驅動,可以分析設計出一些操作原語,如“置一個點位亮”、“置一個點位滅”等,供上層開發(fā)調用。
在嵌入式系統(tǒng)設計過程中,可以將兩種方法結合使用,針對硬件的操作采用自底向上,盡可能抽象出所有的元操作,應對不同上層應用的重用要求;在邏輯應用上,則采用自頂向下,對應用邏輯表達進行抽象規(guī)范,盡量使得模塊劃分便于開發(fā)實現、重用和維護。
功能模塊是獨立實現某一特定功能的最小代碼集。軟件模塊實現的功能應該簡單明了,方便理解和應用,而且對外依賴關系越少越好,能夠更好地組織程序開發(fā)、集成和重用。在操作模塊的設計過程中,應該遵循兩個原則:一是緊湊性,封裝良好的模塊決不互相暴露內部信息,也不去調用其他模塊的操作實現,而是通過函數接口來相互通信;二是正交性,任何模塊的功能點應當是唯一的、無歧義的,在系統(tǒng)中以確定無疑的方式存在。在純正交的模塊設計中,每一個操作行動只限于該項功能,系統(tǒng)的每一屬性只有一條途徑改變,不影響其他功能,這有助于將復雜的設計緊湊化。比如,顯示器功能設計的正交性,在調節(jié)明暗時不會影響到飽和度,色彩平衡的控制也彼此獨立,否則將會對顯示方式的調整帶來很大的麻煩。對于有些太復雜的問題域,可能無法實現模塊完全的緊湊設計,但要盡可能地保持模塊封裝的安全可靠。
分層技術是應付軟件日益復雜、功能不斷拓展的重要手段。通過采用分層技術,很多復雜的問題得以分割、簡化,轉化成具體的應用功能實現,衍生出多層結構以及中間件技術等,在軟件開發(fā)活動中的作用日益凸顯。隨著嵌入式系統(tǒng)應用復雜程度不斷提高,采用分層技術對嵌入式系統(tǒng)進行合理設計,成為提高軟件開發(fā)效率、執(zhí)行效率和維護效率的關鍵。
分層的目的是更好地對開發(fā)需求進行分解,合理區(qū)分軟件功能層次,將軟件劃分為不同概念層次、不同功能的軟件模塊,確定不同模塊之間的關系,從而實現復雜的軟件系統(tǒng)功能。
在軟件邏輯架構的分層設計上,一般遵循以下三個方面的原則:一是層次劃分兼顧功能顆粒度和重用可能性,每層解決不同的問題,下層要能夠為上層應用提供支撐,比如環(huán)境溫度監(jiān)測功能,可以從概念上劃分為數據采集層、處理層、顯示層等,層層遞進實現;二是層與層之間的相關性盡量小,確保某一層的軟件設計出現問題,只會影響到該層次的上下結構,不會影響到軟件系統(tǒng)的整體(比如,顯示層不應對溫度數據進行處理或修改,避免影響整個處理層的邏輯實現);三是每層內部按照任務分解、功能優(yōu)化、重用程度進行模塊劃分,盡量實現軟件功能的高內聚、低耦合。理論上,功能分解得越簡單,實現起來越容易,重復使用頻次就會越高,但目標過度細化會使設計管理、功能調度的復雜度迅速上升,所以一般劃分到概念上能夠獨立完成一項功能、與其他功能相關性合適的程度[5]。
按照自頂向下、自底向上和最優(yōu)模塊化的系統(tǒng)設計思路,針對嵌入式應用與硬件結合緊密、屬于專用系統(tǒng)、軟硬層次比較明顯等特點,對系統(tǒng)邏輯架構進行詳細設計,梳理明確軟件功能模塊劃分。
首先,采取自頂向下的方法對嵌入式系統(tǒng)應用需求進行梳理,抽象出不同的邏輯功能要求,明確概念層次,再轉化成軟件層次。這是一個逐步理解需求、轉化成開發(fā)需求的過程。比如,開發(fā)電子羅盤,需要采集傳感器的x、y、z軸數據,轉換成方位數據,在液晶屏上顯示輸出,就分別涉及到界面顯示、數據處理、硬件訪問、硬件驅動等邏輯層次。
其次是采取自底向上的方法對涉及到的硬件功能進行抽象,應盡可能細化出應用開發(fā)需要的硬件操作原語。對于嵌入式系統(tǒng)而言,大量的開發(fā)工作是通過軟件驅動底層硬件實現相應的專用功能,對硬件功能的封裝既有利于降低當前系統(tǒng)開發(fā)的復雜度,又便于實現硬件的無關性,提高程序代碼的復用性。比如傳感器數據的采集,可以區(qū)分為硬件驅動層和功能拓展層,分別用來實現硬件的無關性和器件的無關性。
再次,采用自頂向下和自底向上相結合的方法,逐層檢驗相鄰層次間的信息交互和調用關系,確保每一個上層的調用都能得到滿足。
最后,對每一層的功能進行合并整合,優(yōu)化功能模塊設計,努力實現最優(yōu)模塊化。在實際系統(tǒng)開發(fā)中,最優(yōu)模塊化的過程也是對現有程序代碼重用的優(yōu)化選擇過程。
通過對嵌入式系統(tǒng)進行分層設計,有利于理清層次結構、優(yōu)化功能模塊組織,使得系統(tǒng)設計過程敏捷靈活、產品功能可擴展性強。常見的功能模塊劃分是圍繞中心處理器/控制器來設計系統(tǒng)邏輯架構,采用面向過程的設計思路,區(qū)分為輸入/輸出、應用調度、設備驅動、網絡通信等功能模塊。這樣的劃分方式能夠充分利用系統(tǒng)的處理能力,進行精細化的存儲空間管理,但也帶來應用邏輯交叉重復、與硬件依賴關系強等缺點,很難進行功能拓展,代碼重用性也較差。采用本文描述的設計思路和分層設計方法,對嵌入式系統(tǒng)進行面向對象、去中心化設計,可以將系統(tǒng)邏輯架構區(qū)分為以下4個層次[4]:
① 應用管理層。主要實現界面交互、業(yè)務邏輯調度等功能。
② 算法協(xié)議層。主要實現模型算法、協(xié)議解析、文件管理、數據庫管理等功能,如位置轉換計算、羅盤指針方位計算等。
③ 功能拓展層。主要實現器件的無關性,提供各種器件的通用性處理、接口訪問等功能,如LCD的線、圓、矩形處理,傳感器數據轉換等功能。
④ 硬件驅動層。主要實現硬件的無關性,提供硬件的操作原語功能,如LCD的定位、寫點、寫字節(jié)、傳感器數據采集等功能。
上述分層設計方案,將同類或相似技術實現的功能進行聚合,減少業(yè)務應用、模型算法和硬件操作之間的耦合性,避免功能在分析設計中的交叉混淆,整個應用程序的結構變得更加清晰和靈活,使得一個成熟的模型算法能夠支持多個應用邏輯,一個成熟的軟件功能模塊能夠適應不同的硬件環(huán)境,提高了軟件功能模塊的開發(fā)效率和可重用性。
軟件編程實現與采用的編程語言緊密相關,基于C語言的嵌入式系統(tǒng)開發(fā)必須遵循C語言的編程原則。靈活運用C語言的編程模式,能夠提高項目開發(fā)效率和代碼編寫質量,也便于對代碼進行維護。
C語言的靈活性往往會導致文件組織混亂、代碼可閱讀性下降等問題。雖然標準的C語言開發(fā)工具并不提供軟件框架管理,但根據本文提供的系統(tǒng)邏輯架構設計,可以建立自己的工程文件管理原則,提高代碼文件的組織管理和協(xié)同開發(fā)能力。
一是文件目錄管理。按照分層原則組織文件目錄,主程序文件、全局變量頭文件放在根目錄,其他文件按照應用管理層、算法協(xié)議層、功能拓展層、硬件驅動層分別存放在AppFunc、ModelFunc、HardExt、HardOpt文件夾,所有文件命名遵循統(tǒng)一的規(guī)范。如果有第三方的通用函數庫,可以建立ComFunc文件夾來存放。這樣在開發(fā)過程中,可以充分利用分層模型的優(yōu)勢,各層功能的開發(fā)人員可以在不同的文件夾內進行并行工作,實現工程化管理。
二是功能模塊管理。為了實現模塊化設計的高內聚性,應少用或不用全局變量,盡量通過函數參數來傳遞數據。同一類的業(yè)務應用功能、同一硬件的操作功能盡量放在同一文件內實現。上層功能模塊的開發(fā)可以調用下層功能模塊,下層功能模塊盡量避免交叉調用或越級調用。
在實際編程過程中,可以通過靈活運用C語言的結構類型和函數指針,實現類似面向對象的繼承、封裝、多態(tài)等重要特性,從而提高編程的效率和代碼復用。
(1)繼承
通過結構嵌套可以實現對象屬性的繼承。下面為羅盤對象參數繼承的簡化示例:
typedef struct_compassbase{ //羅盤基類
int radius; //羅盤半徑
int centerx,centery; //羅盤中心
}CompassBase;
typedef struct_compass{
struct_compassbase;
int handle; //指針位置
}Compass;
(2)封裝
利用函數指針將數據和函數進行綁定,可以實現對象屬性和對象實現的封裝。下面為羅盤基類封裝的簡化示例:
struct _compassbase;
typedef void (*drawcompass)(struct_compassbase*pComBase);
typedef struct_compassbase{ //羅盤基類
int radius; //羅盤半徑
int centerx,centery; //羅盤中心
drawcompass pDrawcompass;
}CompassBase;
(3)多態(tài)
上述的示例中已經隱含了多態(tài),在調用showgrade的實現時并不用考慮該函數的具體數據處理方式,可以有多種實現方法。
C語言代碼重用一般通過函數模塊來實現,包括頭文件和函數實現文件,也就是.h和對應的.c文件。函數定義可以通過兩種方式實現:一是宏定義,如#define maxi(a,b) (a>;b?a:b),而且宏是與類型無關的,不會帶來額外的開銷,但有些任務是無法通過宏來實現的;二是函數,函數是一段可以重復使用的代碼,用來獨立地完成某個功能,可以接收用戶傳遞的數據,也可以將計算結果通過函數值返回或通過地址參數返回。下面是分層設計邏輯框架下的函數調用示例,也可采用相同的調用實現不同項目代碼的復用,對于羅盤中心位置、顯示區(qū)域等變量則采用了面向對象設計方法進行封裝,在此僅簡單描述函數的調用關系。
本應用案例是利用角速度傳感器制作一個電子羅盤,在LCD顯示屏上實時顯示當前方位,可以復用已有算法協(xié)議層、功能拓展層、硬件驅動層的功能模塊。其中LCD顯示功能在各層的示例代碼如下:
① 應用管理層:uint Draw_Compass(uint angle,uint pcolor);顯示當前angle角度的電子羅盤,pcolor為當前顯示顏色,對angle的計算通過調用算法模型層中羅盤角度函數獲取,畫指針函數則調用功能拓展層的畫線函數。
② 算法模型層:uint Cac_Compass(uint x,uint y,uint z);計算羅盤指針方位,x、y、z為傳感器獲取的數值,轉換成指針的角度。
③ 功能拓展層:uint Lcd_Line(uint x1,uint y1,uint x2,uint y2,uint pcolor);這是畫線、調用畫點函數。
④ 硬件驅動層:uint Lcd_Pixel(uint x,uint y,uint pcolor);驅動LCD進行畫點。
[1] Barr M.Real men program in C[J].Embedded Systems Design,2009(7).
[2] 田澤.嵌入式系統(tǒng)開發(fā)與應用[M].北京:北京航空航天大學出版社,2005.
[3] 林越,王翠珍.淺談面向對象開發(fā)思想與軟件設計架構分析[J].信息通信,2016(3):152-154.
[4] 張智慧.多層模型在嵌入式軟件開發(fā)中的應用研究[J].計算機時代,2017(4):17-20.
[5] 郭瀟濛,王崑聲.面向對象系統(tǒng)工程方法改進探索[J].科學決策,2016(6):73-94.