,,
(1.昆山鑫盛盟創科技有限公司,昆山 215300;2.蘇州大學;3.武夷學院)
嵌入式系統終端構件是具有通用功能的嵌入式外圍設備的軟硬件實體總稱[1]。它們負責實現嵌入式系統的信息采集、顯示、交互、控制等具體應用功能。這類應用對象具有一定的通用性,因此終端構件可復用可移植性顯得相當重要。當前終端構件的復用和移植效率還處于較低的水平。這其中有嵌入式系統本身特性的因素,也有設計規范化的問題。目前市場上終端構件銷售商通常是將工程師提供的一系列零碎的源碼、文檔、參考手冊、工具等十幾甚至幾十個文件打包和硬件一起提供給用戶,用戶面對一堆雜亂無章的文件,通常需要花費大量的時間來尋找、梳理與自己需求相關的線索。從這側面可以看出大多數嵌入式工程師的目的只是開發出產品實現功能,構件設計過程往往是依經驗行事,對于底層驅動程序設計沒有徹底貫徹軟件工程思想,開發過程缺少規范化的文檔管理等。這些因素都直接導致了終端構件可復用性和可移植性較差。
近些年來人們參考通用計算機軟件工程體系,將基于構件化的軟件開發(Component-Based Software Development,CBSD)思想引入到嵌入式系統設計當中。Kopetz等人提出了嵌入式軟件組合構建理論,明確了構件化設計、組合構造的思想和原則,并建立構件可組合設計的理論基礎[2-3]。參考文獻[4]提出了一種嵌入式系統代碼合成技術,通過增加中間層,屏蔽硬件和平臺特性。本文針對嵌入式系統終端構件設計可移植性差等問題,從面向應用對象的角度,厘清終端構件共性和個性知識要素,建立終端構件概念模型,從硬件原理圖繪制到驅動程序設計一系列過程,提出一套符合構件化思想、層次清晰、封裝合理且與MCU無關的終端構件一般性設計方法,并通過一個液晶屏終端構件設計實例來闡述并驗證其合理性和有效性。

圖1 嵌入式系統構件層次模型
構件是系統中模塊化、可部署和可替換的部件,構件封裝對外提供一組接口[5]。嵌入式終端構件也稱底層外設構件或應用構件,在嵌入式系統中屬于最外層次的構件,按照生產者和消費者關系,終端構件只有需求接口而沒有供給接口。需求接口接受其中間構件或者核心構件提供的服務,如鍵盤模塊、LED或LCD顯示模塊等。終端構件可以調用底層內部構件,如LCD可調用GPIO、SPI等。終端構件通常對應著一種或多種具體應用功能,如嵌入式系統的信息采集、顯示、交互、控制等功能。嵌入式系統構件層次模型圖如圖1所示。
嵌入式硬件構件(Hardware Component,HwC)是指將一個或者多個硬件功能模塊、支撐電路及其功能描述封裝在一起,提供一系列規范的輸入/輸出接口的可重用的硬件實體[6]。一個完整硬件構件通常包含三大部分:構件描述、構件實體、構件接口[7]。構件描述是一個硬件構件的說明文檔,主要內容包括硬件名稱、主要功能、引腳說明及注意事項等。一個終端硬件構件可表述為如下模型[8]:
HwC={構件實體,構件描述,接口描述};
構件實體={封裝的硬件實現};
構件描述={構件的功能描述,構件注意事項};
接口描述={接口標識,接口網標,接口含義,調用說明}。
硬件構件的概念模型可通過硬件原理圖進行表述。為了清晰表達硬件構件概念模型,在繪制原理圖時,需遵循以下基本規則:①同類型元器件命名時以相同字母為前綴并進行編號,如電阻名稱為R1、R2,電容名稱C1、C2等;②應給出詳細的文字描述,包括構件中英文名稱、功能描述、接口描述以及注意事項等,以提升硬件構件的可讀性;③用虛框封裝硬件構件的電路及文字描述,虛框之內為硬件構件的實體;④標明硬件構件的對外輸入/輸出接口,接口標識分為接口注釋和接口網標兩種,接口注釋用來描述接口功能,標于虛框之內,接口網標則標于虛框之外,用來表示電路連接特性。
對于終端構件而言,因其只有需求接口,硬件設計時首要考慮的問題就是該構件需要什么樣的信號才能工作。因此繪制終端構件原理圖時,沒有對外接口網標而只有接口注釋。圖2給出一個典型LCD終端構件的原理圖。一個遵循規范繪制的原理圖,可讀性強,與之相對應的驅動構件修改方便,可有效提高移植效率。

圖2 LCD構件原理圖
構件是軟硬件綜合體,硬件引腳連接只是最基本前提,要實現構件的正常功能,必須有構件驅動程序進行配合。引腳是硬件構件對外的唯一接口,與之對應驅動構件要有相應的代碼表達出原理圖的含義。從終端驅動構件可移植角度出發,驅動構件應面向應用對象也就是構件本身進行編程,實現這一目的必須在驅動程序的頭文件中添加引腳映射功能。通過引腳宏定義映射,可以屏蔽不同MCU引腳的差異性,驅動程序編程只需針對終端構件的引腳展開即可,無需關心不同型號MUC引腳的區別。構件復用到相同型號的MCU不需要更改任何內容即可直接復用,如果移植到不同型號MCU為核心的應用系統當中,只需改動頭文件引腳的映射關系,對于驅動程序來說,則可做到最小的改動即可實現高效移植。例如圖2中原理圖中基于恩智浦KL25微控制器的引腳映射,定義如下:
#define LCD_CLK (PTA_NUM|15) //LCD時鐘
#define LCD_SDI (PTA_NUM|16) //LCD主出從入
#define LCD_RS (PTA_NUM|4) //LCD復位
#define LCD_DC (PTA_NUM|12) //LCD數據/命令
#define LCD_SDO (PTA_NUM|17) //LCD主入從出
終端構件是包含硬件構件和驅動構件的完整軟硬件實體。硬件構件生產通常都會遵循一定的標準規范。從某種意義上說,驅動構件設計更大程度地決定了該終端構件的可復用和可移植性的高低。驅動構件是直接面向硬件操作的軟件程序及相應的說明文檔。驅動構件直接和硬件打交道,從軟件工程角度來看,驅動程序必須與核心構件具體型號的MCU無關,而是面向終端構件本身,這樣才具有復用和移植價值。終端驅動構件設計的關鍵問題是如何對這類構件的共性和具體應用系統的特性進行分析,抽象出該構件對象的屬性和對外接口[9]。
終端驅動構件(Terminal Drive Component,TDC)包括頭文件(.h)、源程序文件(.c)文件、文件描述、接口描述等,其概念模型可表述為:
TDC={頭文件,源程序文件,文件描述,接口描述}
頭文件={具體硬件引腳連接映射關系,功能函數聲明,全局常量定義}
源程序文件={函數功能實現}
文件描述={頭文件標注,源程序文件標注,函數頭注}
函數頭注={函數名稱,函數功能描述,返回值類型,參數描述,注意事項}
參數描述={參數列表,參數方向,參數含義}
用戶在使用構件時,可以通過頭文件描述的構件完整信息進行接口調用,而不必關心封裝在構件源程序中的服務實現細節。一個設計良好的構件被復用或者移植到不同嵌入式系統中,通常只需修改少量頭文件,對于源程序盡量做到少改甚至不改。
為了便于復用和移植,驅動構件設計應遵循以下基本原則和規范:
① 命名一致性原則。驅動構件的頭文件和源程序文件的主文件名必須與驅動構件名一致,構件的屬性和內外部函數命名統一以構件名+屬性名/操作名形式,命名應有見名知義的效果。
② 通信內聚性和分層內聚性原則。內聚性指的是構件內部函數功能清晰單一明確,只負責一組相關的操作。通信內聚指的要有專門負責構件的底層通信函數;分層內聚指的是函數設計應有層次化,高層函數能調用底層函數,底層函數不能調用高層函數[10]。
③ 內外有別原則。構件函數分為內部函數和外部函數,內部函數僅限構件內部調用,外部接口函數是外界操作構件的接口,外部程序訪問構件屬性必須借助外部接口函數進行,不可直接訪問構件屬性。
④ 松耦合原則。構件應嚴格限制全局變量的使用,且所有數據傳遞都要通過函數的形參進行,這樣編碼既簡潔高效又安全可靠。
TFTLCD即薄膜晶體管液晶顯示器,是一種常用液晶顯示器件。下面我們利用上文所述的設計思想和基本原則從原理圖繪制、公共要素分析及頭文件設計、驅動要素分析及函數設計等方面給出一個面向應用對象的TFTLCD構件分析和設計完整過程。
LCD的硬件對外唯一接口是其連接引腳。因此原理圖的繪制用虛線封裝其內部結構,虛線內部給出引腳的基本含義,虛線外部給出對應的接口網標,本例LCD引腳連接的網標為恩智浦公司的Kinetis 系列KL25微控制器[11]。同時在虛擬框內部給出詳細的構件說明,包括構件中英文名稱、各引腳功能和使用說明以及注意事項等,如圖3所示。這樣在系統移植過程中就可快速進行引腳的連接。

圖3 LCD構件與KL25系統連接原理圖
要提高LCD構件的可移植性,就必須實現面向應用對象的設計和編程,屏蔽不同型號MCU的差異性。因此須在LCD構件的頭文件lcd.h中對給出構件的引腳連接的MCU的引腳進行定義,使用宏定義描述硬件連接線,且每個接線都單獨宏定義,更具普適性。
除此之外,構件的頭文件應給出文件頭注,包括文件名稱、功能概要、版本號、編寫者或修改者姓名、最后修改時間等信息。頭文件包含(#include)為LCD構件提供服務的核心構件或者中間構件頭文件。LCD驅動構件的函數聲明都放在lcd.h頭文件。LCD驅動構件內部函數和外部接口函數分開聲明。LCD構件使用的一些全局常量,如屏幕寬高以及顏色類型都在lcd.h頭文件中進行定義。
LCD構件源程序文件lcd.c和頭文件一樣,給出文件頭注釋,包含內容與頭文件的頭注類似,每個函數也都必須給出函數頭注,函數內部代碼關鍵語句也應有行注釋或邊注釋,提高了程序可讀性。終端驅動構件函數從屬于驅動構件,驅動函數的命名除要體現函數功能之外,還需要體現其從屬構件不同的實現方式,如LCD_Init(LCD初始化)、LCD_ShowImage(LCD顯示圖片)等,避免構件函數出現同名現象,同時函數名也要能夠“顧名思義”。
(1)驅動要素分析及通信函數設計
LCD構件能夠運行的基礎是LCD與系統核心MCU正常通信。TFTLCD采用的驅動控制器型號為ILI9341。構件驅動程序主要工作就是和ILI9341芯片打交道,MCU與ILI9341之間采用串行外設接口(Serial Peripheral Interface,SPI)方式通信。SPI通信需要考慮驅動芯片各引腳數據傳輸的時序問題,對ILI9341驅動芯片的初始化涉及對不同寄存器進行操作,是一系列的復雜過程。對于一般嵌入式開發人員,并不希望了解其復雜內部機制,而是只要少量驅動代碼修改便能移植使用。
LCD構件的中上層函數如初始化、顯示等上層功能部函數的運行均需建立在通信函數正常運行的前提之下。根據構件的通信內聚性和分層內聚性原則,給出了構件專用底層通信函數代碼如下(按照命名一致性原則,函數命名統一采用LCD_操作名):
//發送8位數據
void LCD_wr_data(uint_8 data){
GPIO_set(LCD_DC,1);
//LCD_DC高電平表示發送數據
SPI_send(0,data);
//使用0號SPI模塊發送8位數據
}
//發送8位寄存器命令
void LCD_ wr_reg (uint_8 data){
GPIO_set(LCD_DC,0);
//LCD_DC低電平表示寫寄存器
SPI_send1(0,data);
//使用0號SPI模塊發送8位寄存器命令
}
可以看出,數據通信函數調用了GPIO 構件和SPI通信構件。GPIO 構件用來實現MCU引腳的初始化和狀態設置;SPI通信的初始化必須按照LCD驅動芯片ILI9341設定的時序進行。根據ILI9341參考手冊[12],該芯片使用的是數據發送低電平空閑,上升沿取數模式,對應的時鐘極性CPOL及相位CPHA選擇均為0,因此將引腳初始化和SPI構件初始化如下:
GPIO_init(LCD_RS,1,0);
GPIO_init(LCD_DC,1,0);
SPI_init(SPI_0,1,6000,0,0);
說明:GPIO_init是GPIO構件初始化函數,分別將LCD_RS和LCD_DC設置為輸出引腳,默認值為低電平。SPI_init 為構件初始化函數,參數SPI_0為0號SPI模塊;1為MCU為通信主機;6000為通信波特率;最后兩個參數 0、0分別為時鐘極性及相位。這些通信的關鍵要素必須在文檔中交代清楚,以便移植時參考。終端構件在移植時,除了引腳連接和宏定義映射外,作為功能實現的基礎,驅動構件的通信函數是極為關鍵的,必須予以高度重視。
(2)中上層功能函數設計
有了底層通信函數作為基礎,其他中上層功能函數就可實現具體功能,例如LCD的初始化函數、LCD顯示區域設置函數等都是對ILI9341寄存器一系列的設置,過程相對比較復雜,但是每一種模式通常都遵循固定的順序步驟,需根據ILI9341參考手冊調用底層通信函數LCD_wr_reg等對寄存器進行一系列的設置,設計時將具體的步驟封裝,對外屏蔽其復雜的寫寄存器操作,必要時通過參數設置入口,開發人員只需調用即可,該類函數借助通信函數實現其操作與構件底層通信無關,易于復用和移植。下面是兩個常用功能函數樣例:
void LCD_Init(void); //LCD構件初始化
void LCD_AreaSet(uint_16 x1,uint_16 y1,uint_16 x2,uint_16 y2);
//顯示區域設置
其中,LCD_AreaSet函數中x1、y1和x2、y2是確定顯示矩形區域的對角線兩點坐標值。
LCD具體應用功能(如畫點、畫線、畫圈、顯示文字、顯示圖像等)需要綜合考慮,如何進行封裝和設計,方便用戶使用。鑒于篇幅,下面以顯示圖像函數LCD_ShowImage為例進行說明。顯示圖像通常需要知道顯示位置、圖像尺寸和圖像的點陣信息,因此LCD_ShowImage函數可設計如下:
void LCD_ShowImage (uint_16 x,uint_16 y,uint_16 width,uint_16 height,uint_8 *image);
其中,x、y是圖片顯示的起始坐標,width 和height分別為圖像的寬度和高度,image則為存儲圖像的點陣信息數組首地址。函數內部也是通過調用通信函數來實現圖像的顯示,用戶無需知道細節,只需知道參數意義即可進行調用。
在LCD驅動構件函數層次中,中上層功能函數在移植時基本無需改動,可看作驅動構件的通用層。移植的關鍵在于MCU頭文件和構件的底層通信函數,因此這部分可看作是LCD構件的適配層。LCD構件驅動程序層次及函數關系如圖4所示。

圖4 LCD構件驅動程序層次模型
一個構件的可移植性高低可以采用軟件可移植性度量進行分析。下面分別利用基于移植成本和移植工作量兩種可移植性度量模式[13]對LCD構件進行可移植度量分析。
(1)基于移植成本度量
該度量模式基于軟件的可移植性和開發成本之間的關系來衡量可移植性程度,可使用下式:
其中,DP表示可移植性大小,COSTport表示軟件的移植成本,COSTredevelop表示軟件重新開發的成本,DP越大表示軟件的可移植性越高。從復用和移植步驟可以看出,在新系統移植時,LCD構件只需修改適配層的引腳定義和通信函數,其他功能則基本不需改動,反映到成本上,COSTport遠遠小于COSTredevelop。因此,LCD構件從成本角度看可移植性較高。
(2)基于移植工作量度量
該度量模式通過與移植環境相關的靜態指標(如代碼修改量、移植耗時等)來衡量可移植性高低。如上文所述,LCD構件嚴格按照構件概念模型設計,代碼具有模塊化、層次化的特征。移植新系統時,需要修改的僅僅是頭文件的引腳定義和源文件中通信函數的重寫,除特殊情況外,其他部分基本不需改動,而且LCD構件有規范的注釋和移植說明、注意事項等文檔作為移植參考。經實驗室測試,在新購的TFTLCD構件代碼零亂、沒有相關參考文檔的情況下,移植成功花費了大約兩天時間,而經過本文封裝形成規范的TFTLCD構件,交付給另一個沒有參與設計且技術水平相當的人員,在基于不同型號的核心構件系統中進行移植花費時間僅約3小時,大大提高了效率,且運行更加穩定可靠。測試說明按照本文提出規范設計面向應用對象的終端構件具有良好的可復用可移植性。
