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

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

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


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

圖2 匯編函數SVC_HandlerS 實現流程

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

圖4 RAM 空間劃分示意圖

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

圖5 測試工程執行流程

圖6 功能性測試結果

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