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