王志超,王宜懷
(1.宿遷學院計算機科學系,宿遷223800;2.蘇州大學)
構件本質上是把對象對外的接口聲明與對象內部的接口實現相隔離,一個構件在修改自己的接口時,只影響與修改接口相交互的構件,與被修改構件相交互的其他構件不受影響,因此它的重用性和可擴展性更高[1]。
嵌入式底層構件(Embedded Underlying Component,EUC)是在硬件構件的基礎上,根據硬件構件的實際功能和接口,實現與之相對應的硬件驅動模塊的分解,并將硬件驅動底層程序的實現、頭文件定義及其文件描述封裝成一個可重用的構件實體,并提供一系列規范的輸入/輸出接口,供其他嵌入式應用程序調用。底層構件同硬件構件一樣,也具有被獨立部署和被第三方組裝的特性[2]。而嵌入式底層軟件則與嵌入式系統硬件模塊相關聯,具體實現硬件構件功能的程序代碼。
嵌入式軟件構件(Embedded Software Componen t,ESC)是實現一定嵌入式系統功能的一組封裝的、規范的、可移植的、具有嵌入特性的軟件單元,是組織嵌入式系統的功能單位。
嵌入式軟件構件分為高層軟件構件和底層軟件構件(以下簡稱“高層構件”和“底層構件”)。高層構件與硬件無關,而底層構件與硬件密不可分,是硬件驅動程序的封裝。在硬件構件中,核心構件為MCU的最小系統。通常,MCU內部包含有GPIO(即通用I/O)口和一些內置功能模塊,開發者可以將通用I/O口的驅動程序封裝為GPIO構件。各內置功能模塊的驅動程序封裝為功能構件,如Freescale公司的MCF52233的功能構件有UART構件、Flash構件、USB構件、I2C構件等。
在硬件構件中,相對于核心構件而言,中間構件和終端構件是核心構件的“外設”。由這些“外設”的驅動程序封裝而成的軟件構件稱為底層外設構件。不過,值得注意的是,并不是所有的中間構件和終端構件都可以作為編程對象[3-4]。例如:鍵盤、LED、LCD等硬件構件與編程有關,而電平轉換硬件構件就與編程無關,因而不存在相應的底層驅動程序,當然也就沒有相應的軟件構件。
嵌入式硬件構件與軟件構件的層次模型如圖1所示。底層外設構件可以調用底層內部構件,例如LCD構件可以調用GPIO構件,PCF8563構件(時鐘構件)可以調用I2C構件等。而高層構件可以調用底層外設構件和底層內部構件中的功能構件,而不能直接調用GPIO構件。另外,考慮到幾乎所有的底層內部構件都涉及到MCU各種寄存器的使用,因此將MCU的所有寄存器定義組織在一起,形成MCU頭文件,以便其他構件頭文件中包含該頭文件[5-6]。

圖1 嵌入式硬件構件與軟件構件的層次模型
底層構件是與硬件直接打交道的軟件,由頭文件和源程序文件兩部分組成。
頭文件中的內容主要有:包含下層構件頭文件的#include語句、用于描述構件屬性的宏定義語句以及對外接口函數原型說明。在頭文件中使用函數原型,對于建立代碼模塊和外部接口的規范、便于他人使用,都是很有幫助的。使用這些函數的用戶,不需要查找源代碼去了解參數的具體類型,直接查看函數原型即可。
源程序文件中存放構件的內部函數和外部函數的定義,即函數的實現代碼,以實現函數的功能。
在對底層構件進行設計時,最關鍵的工作是要對構件的共性和個性進行分析,抽取出構件的屬性和對外接口函數。盡可能做到:當一個底層構件應用到不同系統中時,僅需修改構件的頭文件,對于構件的源程序文件則不必修改或改動很小。
例如,串行通信模塊SCI是大多數MCU都具有的內部模塊。仔細分析各種MCU串行通信程序發現:在查詢方式下,各種MCU都是根據狀態寄存器中的兩個標志位來判斷是否接收到數據和數據是否發送完畢,這就是SCI模塊的共性。對于不同的MCU,該狀態寄存器的名稱可能不同,這兩個標志位的位號也有可能不同。此外,用以設置波特率、通信格式、是否校驗、是否允許中斷等參數的寄存器也不同,這就是SCI模塊的個性。分析出了共性和個性之后,就可以抽取出SCI構件的屬性和操作,編制構件頭文件和程序文件了。
以Freescale公司的MC68HC908GP32的SCI模塊為例,數據寄存器名稱為SCDR,空標志位(第7位)和滿標志位(第5位)均位于狀態寄存器SCS1中。SCI構件的頭文件(SCI.h)設計如下:

在編寫構件時,須注意以下幾方面的內容:
①構件的頭文件和源程序文件的主文件名一致,且為構件名。
②屬性和操作的命名統一以構件名開頭。這樣做的好處是,當使用底層構件組裝軟件系統時,可以避免構件之間出現同名現象;同時,含義清晰、一目了然。
③對MCU內的模塊寄存器名和端口名進行重定義,其他的代碼都將使用宏名對模塊寄存器和端口進行操作。這樣,當底層驅動程序移植到其他MCU時,只要修改重定義語句就可以了。
④內部函數與外部函數要設計合理,函數參數個數及類型要考慮全面。內部函數僅提供給同一構件中的其他內部函數或外部函數調用,作用域僅限于定義該函數的文件。外部函數是對外接口函數,供上層應用程序調用。在定義外部函數時,應該對函數名、函數功能、入口參數、函數返回值、使用說明、函數適用范圍等進行詳細描述,以增強程序的可讀性。上層應用程序不能直接對構件的屬性進行讀取或設置,必須借助于該構件提供的接口操作函數來實現。
⑤應用程序在使用底層構件時,嚴格禁止通過全局變量來傳遞參數,所有的數據傳遞都要通過函數的形式參數來接收。這樣做不但使得接口簡潔,而且避免了全局變量可能引發的安全隱患。
當一個已設計好的底層構件移植到另外一個嵌入式系統中時,其頭文件和程序文件是否需要改動,這要視具體情況而定。例如:系統的核心構件發生改變(即MCU型號改變)時,底層內部構件頭文件和某些對外接口函數也要隨之改變,如模塊初始化函數。以SCI構件為例,由于不同MCU的SCI模塊的數據寄存器名稱、狀態寄存器名稱及位定義不同,初始化工作所涉及的波特率、通信格式、是否校驗等設置值也不同,因此當MCU改變時,構件頭文件中相關宏定義值和SCI_Init的函數體實現代碼也會有所不同。以Freescale公司的DG 128單片機為例,其有兩個串行口SCI0和SCI1。若使用串行口SCI0進行收發數據,則數據寄存器名稱為SCI0DRL,空標志位(第7位)和滿標志位(第5位)位于狀態寄存器SCI0SR1中。因此需要將MC68HC908GP32的SCI構件頭文件中前三行修改成如下內容:

底層構件的編程方法在嵌入式系統的開發中具有舉足輕重的地位。底層軟件開發的成敗直接關系著整個系統穩定性和可靠性的好壞。如果開發人員在編寫底層軟件時采用軟件工程思想的方法,就可以最大程度地使底層軟件具有良好的可移植性和復用性。不過必須說明的是,本文提出構件化設計方法的目的是,在進行底層軟件移植時,設計人員所做的改動盡量小,而不是不作任何改動。事實上,不作任何改動是不現實的。
[1]王宜懷,陳建明,蔣銀珍.基于32位ColdFire構建嵌入式系統[M].北京:電子工業出版社,2009:413-417.
[2]薦紅梅.基于硬件構件的嵌入式底層軟件開發方法研究及其應用[D].蘇州:蘇州大學,2008:28-30.
[3]沙占友,王彥朋,孟志永.嵌入式外圍電路設計[M].北京:電子工業出版社,2003:161-173.
[4]桑楠.嵌入式系統原理及應用開發設計[M].北京:北京航空航天大學出版社,2004:121-126.
[5]王宜懷,劉曉升.嵌入式應用技術基礎教程[M].北京:清華大學出版社,2007:8-15.
[6]王志超.基于MC68HC908GP32的射頻卡密碼認證系統設計與實現[J].微計算機信息,2009:79-81.