劉長(zhǎng)勇,王宜懷,孫亞軍
1(武夷學(xué)院 數(shù)學(xué)與計(jì)算機(jī)學(xué)院,武夷山 354300)
2(蘇州大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,蘇州 215006)
3(認(rèn)知計(jì)算與智能信息處理福建省高校重點(diǎn)實(shí)驗(yàn)室,武夷山 354300)
為了提升編程顆粒度、提高可移植性,借鑒通用計(jì)算機(jī)的概念與做法,把基本輸入輸出系統(tǒng)(Basic Input and Output System,BIOS)與用戶程序分離開(kāi)來(lái),實(shí)現(xiàn)徹底的工作分工,形成了通用嵌入式計(jì)算機(jī)(General Embedded Computer,GEC)[1].GEC 架構(gòu)將嵌入式軟件系統(tǒng)分為BIOS 工程程序(簡(jiǎn)稱BIOS)和USER 工程程序(簡(jiǎn)稱USER)兩部分,BIOS 先于USER 固化于微控制器(Microcontroller Unit,MCU)內(nèi)的非易失存儲(chǔ)器(如Flash)中,為實(shí)時(shí)操作系統(tǒng)(Real-Time Operating System,RTOS)的駐留提供了空間.實(shí)時(shí)操作系統(tǒng)能提供精確的實(shí)時(shí)控制和任務(wù)管理功能,保證系統(tǒng)的實(shí)時(shí)性需求[2].通過(guò)將RTOS 駐留在BIOS 中,降低用戶的編程難度、簡(jiǎn)化程序的串口寫(xiě)入以及方便用戶調(diào)用,同時(shí)也可以很好地幫助用戶解決因RTOS 在不同開(kāi)發(fā)環(huán)境的編譯困難而造成的煩惱.
同時(shí),為了充分發(fā)揮RTOS的功能,方便用戶使用,提高用戶程序的可移植性,通過(guò)提供對(duì)外函數(shù)接口的形式是一種比較好的做法.對(duì)外函數(shù)接口(也稱為應(yīng)用程序編程接口,API)是軟件庫(kù)提供的一組可訪問(wèn)的接口,軟件庫(kù)通過(guò)API 向外提供服務(wù),開(kāi)發(fā)人員通過(guò)使用API 實(shí)現(xiàn)代碼復(fù)用,提高生產(chǎn)效率[3].因此,在對(duì)外函數(shù)接口設(shè)計(jì)上,要充分考慮其可用性[4,5]、穩(wěn)定性[6]和安全性[7,8].目前,對(duì)外函數(shù)接口的研究已取了一定的研究成果,主要集中在API 使用規(guī)約[9–11]、API 推薦研究[12,13]、API 文檔研究[14,15]、API 組合模式的應(yīng)用[16]等方面,但有關(guān)RTOS的駐留及其對(duì)外函數(shù)接口研究方面文獻(xiàn)較少.為此,本文首先給出通用嵌入式計(jì)算機(jī)架構(gòu)下RTOS的BIOS 駐留方法,剖析了RTOS 對(duì)外函數(shù)接口設(shè)計(jì)方法,提出了接口函數(shù)重映射機(jī)制,最后在mbedOS 下進(jìn)行應(yīng)用實(shí)踐.實(shí)踐表明,將RTOS 駐留在BIOS 中,能有效地節(jié)省用戶程序的編譯時(shí)間,同時(shí)通過(guò)對(duì)外接口函數(shù)的重映射,能有效地提升了應(yīng)用程序的可靠性性和開(kāi)發(fā)效率,易于用戶調(diào)用,也使應(yīng)用程序易于復(fù)用,為嵌入式人工智能與物聯(lián)網(wǎng)終端程序的開(kāi)發(fā)提供了技術(shù)基礎(chǔ).
在GEC 架構(gòu)中,雖然嵌入式軟件系統(tǒng)分為BIOS和USER 兩部分,但最終程序代碼和各種變量數(shù)據(jù)都是放在同一個(gè)MCU的Flash和RAM 中,要將RTOS 駐留在BIOS 中,實(shí)現(xiàn)RTOS與應(yīng)用程序的物理隔離,就必須對(duì)MCU的Flash和RAM 空間進(jìn)行合理的劃分,這樣才能確保代碼不重疊,變量使用不越界,從而保證RTOS 能得到正常運(yùn)行,而又不影響USER的執(zhí)行.因此,要考慮RTOS 駐留的硬件載體,即MCU的Flash和RAM 空間大小的因素.對(duì)Flash 空間的劃分可采用分割獨(dú)享方式,一部分給BIOS 程序使用,另一部分給USER 程序使用,兩者使用的空間不重疊.對(duì)RAM 空間的劃分則需要考慮MCU的RAM 空間大小,當(dāng)RAM空間足夠大時(shí),可采用分割獨(dú)享方式,BIOS和USER 各單獨(dú)使用不重疊的空間;當(dāng)RAM 空間較小時(shí),可采用重疊共享方式,即BIOS 使用的空間和USER 使用的空間部分重疊共享,這樣可以提高RAM 空間的利用率.在實(shí)現(xiàn)RTOS 駐留的過(guò)程中,還需要考慮如何合理劃分Flash 空間,使USER 程序占用的空間盡量大;重疊共享方式分配RAM 空間時(shí),如何避免出現(xiàn)數(shù)據(jù)越界與沖突;RTOS的調(diào)度會(huì)依賴于系統(tǒng)服務(wù)調(diào)用,何時(shí)將這些調(diào)用權(quán)移交給RTOS 等問(wèn)題.
雖然RTOS 已駐留在BIOS 中,但要發(fā)揮其作用,還需將RTOS的功能函數(shù)設(shè)計(jì)成對(duì)外函數(shù)接口表,然后通過(guò)映射形成對(duì)外函數(shù)映射表,這樣才能向USER 提供服務(wù).也就是說(shuō),USER 可以通過(guò)對(duì)外函數(shù)映射表實(shí)現(xiàn)對(duì)RTOS 提供的接口函數(shù)的調(diào)用.
在GEC 架構(gòu)下,將RTOS 提供的函數(shù)進(jìn)行對(duì)外接口的設(shè)計(jì),不僅能夠發(fā)揮RTOS的功能,而且還能方便用戶使用,提高USER 程序的可復(fù)用性.因此,RTOS 向用戶提供接口函數(shù)是非常必要的.
(1)提高應(yīng)用程序的可復(fù)用性.由于USER是通過(guò)對(duì)外函數(shù)映射表實(shí)現(xiàn)對(duì)RTOS 提供的對(duì)外接口函數(shù)調(diào)用的.因此,當(dāng)在BIOS 中駐留不同的RTOS 時(shí),只要向USER 提供相同功能的接口就可以,即使這些接口的名稱發(fā)生的改變,也不會(huì)影響USER的調(diào)用,USER 程序不需要修改,提高了USER 程序的可復(fù)用性.
(2)提升應(yīng)用程序的穩(wěn)定性和可靠性.由于RTOS已經(jīng)駐留在BIOS 中,它所提供的對(duì)外函數(shù)功能已經(jīng)在BIOS 中通過(guò)編譯、測(cè)試和驗(yàn)證,變成了一段可靠的、穩(wěn)定的機(jī)器碼.因此,在USER 程序中可以放心地調(diào)用RTOS 提供的對(duì)外函數(shù),不用擔(dān)心會(huì)出現(xiàn)的代碼錯(cuò)誤,從而提升了應(yīng)用程序的穩(wěn)定性和可靠性.
(3)縮短應(yīng)用程序的開(kāi)發(fā)時(shí)間.由于RTOS 提供了原型級(jí)的對(duì)外函數(shù)調(diào)用接口,用戶可以直接使用,無(wú)需花大量精力深入理解RTOS的工作原理和調(diào)度機(jī)制,不需要知道具體的實(shí)現(xiàn)細(xì)節(jié),只需關(guān)注用戶程序的編寫(xiě),大大地提高了開(kāi)發(fā)效率.
(4)方便應(yīng)用程序調(diào)用.由于RTOS 提供的對(duì)外函數(shù)已經(jīng)變成了機(jī)器碼駐留于BIOS 內(nèi),用戶難以調(diào)用,通過(guò)對(duì)這些函數(shù)進(jìn)行重映射,最終向USER 提供函數(shù)調(diào)用原型接口,用戶就可以像調(diào)用普通函數(shù)一樣方便使用這些函數(shù).
要實(shí)現(xiàn)RTOS 對(duì)外函數(shù)接口,首先需要在BIOS中對(duì)函數(shù)進(jìn)行重定義、聲明、注冊(cè),形成對(duì)外函數(shù)接口表;其次要在USER 中通過(guò)映射獲取對(duì)外函數(shù)接口表的入口地址,形成對(duì)外函數(shù)映射表,并重定向函數(shù)名稱,最后在USER 中實(shí)現(xiàn)對(duì)函數(shù)的調(diào)用,其過(guò)程如圖1所示.

圖1 對(duì)外函數(shù)接口的設(shè)計(jì)與實(shí)現(xiàn)過(guò)程示意圖
(1)對(duì)外函數(shù)的二次封裝
對(duì)外函數(shù)不僅可以包含RTOS的功能函數(shù),而且還可以包括各類構(gòu)件函數(shù),本文主要介紹如何封裝RTOS提供的對(duì)外接口函數(shù).RTOS 一般都具備線程管理、同步與通信、中斷管理等基本功能,相應(yīng)的提供了線程類、事件類及操作系統(tǒng)啟動(dòng)函數(shù)等,在這些類中提供了大量的成員函數(shù),都是采用C++實(shí)現(xiàn)的.由于這些成員函數(shù)的執(zhí)行依賴于類對(duì)象的創(chuàng)建及其成員變量,而不是使用絕對(duì)地址的方式來(lái)實(shí)現(xiàn)對(duì)類中的某個(gè)成員函數(shù)的調(diào)用.因此,必須在BIOS 程序中對(duì)這些函數(shù)進(jìn)行重定義,二次封裝成C 語(yǔ)言可以調(diào)用的函數(shù)形式,這樣才能實(shí)現(xiàn)在USER 中調(diào)用它們.對(duì)外函數(shù)的二次封裝(或稱重定義)主要包括函數(shù)名、函數(shù)的返回值類型、函數(shù)的參數(shù)、函數(shù)體以及為了便于理解程序而加入的功能說(shuō)明和代碼注釋等.其格式如下:
格式:void 重定義函數(shù)名(參數(shù)表列)

例如,mbedOS的延時(shí)函數(shù)名為T(mén)hread::wait,通過(guò)二次封裝重定義為thread_wait.
(2)對(duì)外函數(shù)的聲明
對(duì)外函數(shù)重定義好之后,一般應(yīng)在與之同名的.h頭文件中進(jìn)行聲明,函數(shù)的聲明要給出函數(shù)名、函數(shù)的返回值類型、函數(shù)的參數(shù)以及函數(shù)的功能說(shuō)明,即使用者通過(guò)函數(shù)的聲明就能了解函數(shù)的功能和使用方法,而不需要查看函數(shù)的具體實(shí)現(xiàn).
格式:void 重定義函數(shù)名(參數(shù)表列);
(3)對(duì)外函數(shù)的注冊(cè)
當(dāng)對(duì)外函數(shù)定義和聲明之后,還要對(duì)函數(shù)進(jìn)行注冊(cè)才能形成對(duì)外函數(shù)接口表.借鑒中斷向量表的定義做法,可以給所有的或部分的函數(shù)編號(hào),并將函數(shù)名(即函數(shù)的入口地址)集中在一起按編號(hào)有序地放在一個(gè)統(tǒng)一的區(qū)域中,形成對(duì)外函數(shù)接口表.在對(duì)外函數(shù)接口表中,對(duì)外接口函數(shù)的入口地址用32 位的二進(jìn)制表示,可以看作和定義成long 類型的數(shù)據(jù),一般采用匯編語(yǔ)言編寫(xiě)一個(gè)SVC 中斷來(lái)注冊(cè).對(duì)外函數(shù)接口表采用數(shù)組(如BIOS_API)存儲(chǔ),其入口地址就是數(shù)組名或數(shù)組的首地址,函數(shù)的編號(hào)與數(shù)組的下標(biāo)元素的序號(hào)一一對(duì)應(yīng),其中,0 號(hào)表示對(duì)外函數(shù)的數(shù)量,1 號(hào)函數(shù)對(duì)應(yīng)BIOS_API[1],2 號(hào)函數(shù)對(duì)應(yīng)BIOS_API[2],依此類推,換句話說(shuō),可以通過(guò)數(shù)組元素來(lái)訪問(wèn)這些對(duì)外函數(shù).同時(shí),為了便于擴(kuò)充或更新對(duì)外函數(shù)的個(gè)數(shù),還預(yù)留了一些缺省的函數(shù)名(如DefaultFUN).


當(dāng)RTOS 提供的對(duì)外函數(shù)經(jīng)過(guò)重定義、聲明、注冊(cè),形成對(duì)外函數(shù)接口表后,USER 程序還需進(jìn)一步通過(guò)重映射機(jī)制形成對(duì)外函數(shù)映射表,才能最后在USER中實(shí)現(xiàn)對(duì)函數(shù)的調(diào)用.
當(dāng)RTOS的對(duì)外函數(shù)接口表形成之后,此時(shí)RTOS提供的函數(shù)已經(jīng)變成了一段機(jī)器碼,必須先將BIOS的對(duì)外函數(shù)接口表映射成USER的對(duì)外函數(shù)映射表,獲得存放對(duì)外函數(shù)接口表的數(shù)組首地址,這樣USER 程序才能使用它.為了與BIOS_API 數(shù)組的元素一一對(duì)應(yīng),USER 程序的對(duì)外函數(shù)映射表也采用數(shù)組(如USER_API),用它來(lái)存放對(duì)外函數(shù)接口表的地址.這樣,當(dāng)USER 使用USER_API 時(shí)就相當(dāng)于使用BIOS_API,也就是說(shuō),USER 通過(guò)USER_API 就可以訪問(wèn)BIOS 提供的對(duì)外函數(shù).
在USER 中,采用SVC 中斷的方式來(lái)實(shí)現(xiàn)對(duì)外函數(shù)接口表的映射.因此,在BIOS 中先要將SVC 中斷重定向?yàn)橛脩舻腟VC_IRQ,接著在USER 中調(diào)用svc1_init函數(shù)觸發(fā)BIOS 中的SVC_IRQ 中斷,然后由SVC_IRQ中斷觸發(fā)實(shí)際的SVC 封裝函數(shù)SVC_HandlerS,最后在這個(gè)匯編程序SVC_HandlerS 中實(shí)現(xiàn)將BIOS 提供的對(duì)外函數(shù)接口表的入口地址映射到對(duì)外函數(shù)映射表中,匯編函數(shù)SVC_HandlerS 實(shí)現(xiàn)流程如圖2所示.
當(dāng)對(duì)外函數(shù)映射表形成之后,此時(shí)USER 就可以通過(guò)USER_API 數(shù)組訪問(wèn)函數(shù),如USER_API[1]訪問(wèn)的是1 號(hào)對(duì)外函數(shù).但采用USER_API[i]這種形式對(duì)具體要訪問(wèn)的對(duì)外函數(shù)的類型、函數(shù)名、參數(shù)以及功能等不夠清晰明了.因此,類似中斷向量重定向的做法,也可以重定向?qū)ν夂瘮?shù),重新給USER_API[i]取另外一個(gè)用戶熟悉的函數(shù)名,這個(gè)函數(shù)名可以與對(duì)外函數(shù)名同名,也可以是不同名的,這樣就可以為用戶提供函數(shù)原型級(jí)接口,易于用戶記住和使用,圖3描述了從RTOS的原型函數(shù)到最終實(shí)現(xiàn)用戶實(shí)際調(diào)用函數(shù)的映射關(guān)系.函數(shù)重定向宏定義的一般形式如下:

圖2 匯編函數(shù)SVC_HandlerS 實(shí)現(xiàn)流程

圖3 對(duì)外函數(shù)映射關(guān)系示意圖
#define 函數(shù)重定向名 ((對(duì)外函數(shù)聲明指針表達(dá)形式)(全局?jǐn)?shù)組[對(duì)外函數(shù)編號(hào)]))
例如:
#define delay ((void (*)(uint_32 millisec))(USER_API[4]))
當(dāng)對(duì)外函數(shù)重定向之后,就可以利用重定向后的名字來(lái)調(diào)用函數(shù).例如,當(dāng)USER 調(diào)用delay 函數(shù)時(shí),實(shí)際上是指向了USER_API[4],而USER_API[4]對(duì)應(yīng)BIOS_API[4],BIOS_API[4]存放的是thread_wait 函數(shù)的入口地址,而thread_wait 函數(shù)實(shí)際上就是Thread::wait 函數(shù).因此,可以認(rèn)為USER 通過(guò)調(diào)用delay 函數(shù)達(dá)到調(diào)用Thread::wait 函數(shù)的目的.
2014 年ARM 公司推出了mbedOS,它是一種專為物聯(lián)網(wǎng) (IoT)中的“物體”設(shè)計(jì)的開(kāi)源嵌入式實(shí)時(shí)操作系統(tǒng)[17].本文選用mbedOS 進(jìn)行應(yīng)用實(shí)踐,測(cè)試工程在Kinetis Design Studio 3.0.0 IDE 集成開(kāi)發(fā)環(huán)境和金葫蘆AHL-A 系列Cortex-M0+內(nèi)核的KL36 微控制器[18](即AHL-AN100VL 型號(hào)開(kāi)發(fā)板)上進(jìn)行.KL36 片內(nèi)Flash大小為64 KB,一般用來(lái)存放中斷向量、程序代碼、常數(shù)等;片內(nèi)RAM為靜態(tài)隨機(jī)存儲(chǔ)器SRAM,大小為8 KB,一般用來(lái)存儲(chǔ)全局變量、靜態(tài)變量、臨時(shí)變量(堆棧空間)等.
使用KL36 微控制器作為mbedOS 駐留的硬件載體,考慮其Flash和RAM 空間大小的因素,在mbedOS駐留于BIOS 時(shí),對(duì)Flash 空間采用分割獨(dú)享方式劃分.對(duì)RAM 空間可采用分割獨(dú)享和重疊共享方式劃分,如圖4所示,在分割獨(dú)享方式中BIOS和USER 各占一半的RAM 空間;本文采用重疊共享方式,BIOS 占全部RAM 空間,USER 占一半以上的RAM 空間,這樣可以最大程度地提高RAM 空間的利用率.根據(jù)前面介紹的RTOS 對(duì)外函數(shù)接口設(shè)計(jì)方法和重映射機(jī)制,可以將mbedOS 提供的啟動(dòng)、線程、延時(shí)、事件、消息隊(duì)列、信號(hào)量和互斥量等函數(shù)進(jìn)行重定義、聲明、注冊(cè)、映射、重定向,最后提供給用戶程序調(diào)用.mbedOS提供的對(duì)外函數(shù)原型名稱、對(duì)外函數(shù)二次封裝名稱以及重定向函數(shù)名稱之間的映射關(guān)系如表1所示,僅列舉了本測(cè)試工程中使用到的對(duì)外接口函數(shù).

圖4 RAM 空間劃分示意圖

表1 部分對(duì)外函數(shù)重定向一覽表
測(cè)試工程的功能是創(chuàng)建兩個(gè)任務(wù),實(shí)現(xiàn)每2 秒紅燈閃爍一次,藍(lán)燈任務(wù)每1 秒切換亮暗一次,綠燈任務(wù)當(dāng)收到藍(lán)燈任務(wù)的信號(hào)(17)時(shí),切換綠燈亮暗.當(dāng)芯片上電之后,首先啟動(dòng)BIOS,接著轉(zhuǎn)到USER的啟動(dòng),在USER的啟動(dòng)過(guò)程中通過(guò)映射對(duì)外函數(shù)接口表形成對(duì)外函數(shù)映射表,并重定向?qū)ν夂瘮?shù).然后啟動(dòng)mbedOS,并創(chuàng)建和啟動(dòng)藍(lán)燈任務(wù)和綠燈任務(wù),最后在一個(gè)無(wú)限循環(huán)中使紅燈每2 s 閃爍一次,同時(shí)對(duì)藍(lán)燈任務(wù)和綠燈任務(wù)進(jìn)行調(diào)度.藍(lán)燈任務(wù)主要完成每秒閃爍一次,并設(shè)置信號(hào);綠燈任務(wù)主要是等待信號(hào),當(dāng)收到信號(hào)后閃爍一次.測(cè)試工程的執(zhí)行流程如圖5所示.
在測(cè)試工程中主要調(diào)用的對(duì)外函數(shù)有:操作系統(tǒng)啟動(dòng)函數(shù)OsStart、任務(wù)創(chuàng)建函數(shù)create、任務(wù)啟動(dòng)函數(shù)start、延時(shí)函數(shù)delay、信號(hào)設(shè)置函數(shù)signal_set和信號(hào)等待函數(shù)signal_wait 等,其功能性測(cè)試結(jié)果如圖6所示,從中可以看出能精準(zhǔn)調(diào)用這些對(duì)外函數(shù),程序的功能得到準(zhǔn)確的實(shí)現(xiàn),說(shuō)明對(duì)外函數(shù)接口設(shè)計(jì)正確.
編譯時(shí)間的測(cè)試是在聯(lián)想筆記本電腦K49 上進(jìn)行,其CPU 型號(hào)為Intel Core i7-3520M,主頻2.9 GHz,內(nèi)存8 GB,采用64 位的Windows 7 操作系統(tǒng).測(cè)試工程針對(duì)KL36 (采用Kinetis Design Studio 3.0.0 IDE 編譯環(huán)境)、S32K144 (采用S32 Design Studio for ARM v1.3 編譯環(huán)境)和MSP432 (采用Code Composer Studio 6.2.0 編譯環(huán)境)微控制器分別對(duì)mbedOS是否駐留BIOS的USER 程序進(jìn)行編譯時(shí)間測(cè)試,測(cè)試結(jié)果如表2所示.從表2中可以看出,將mbedOS 駐留在BIOS 中,USER 程序可以節(jié)約2~3 倍的編譯時(shí)間,從而提高了用戶程序的開(kāi)發(fā)效率.

圖5 測(cè)試工程執(zhí)行流程

圖6 功能性測(cè)試結(jié)果

表2 駐留與非駐留時(shí)USER 程序編譯時(shí)間
為充分發(fā)揮實(shí)時(shí)操作系統(tǒng)的強(qiáng)大功能,本文給出了RTOS 在BIOS 中的駐留方法,在GEC 架構(gòu)下提出了RTOS 對(duì)外接口函數(shù)的設(shè)計(jì)方法,剖析了接口函數(shù)重映射機(jī)制,為用戶提供函數(shù)原型級(jí)的調(diào)用接口.最后以NXP的KL36 芯片為例,在mbedOS 進(jìn)行應(yīng)用實(shí)踐,測(cè)試表明對(duì)外函數(shù)接口的設(shè)計(jì)是正確的,有效地解決了函數(shù)調(diào)用的困難,提高用戶程序的可靠性,為RTOS的應(yīng)用研究提供了基礎(chǔ).后續(xù)還將進(jìn)一步探索用戶程序在不同RTOS 上的可移植性問(wèn)題,本文涉及到的測(cè)試工程可到蘇州大學(xué)嵌入式學(xué)習(xí)社區(qū)網(wǎng)站(網(wǎng)址:http://sumcu.suda.edu.cn)的“教學(xué)培訓(xùn)-教學(xué)資料-mbedOS”位置,下載“SD_mbedOS_API”查看.