劉 肖 王宜懷 汪 恒 劉長勇,2
1(蘇州大學計算機科學與技術學院 江蘇 蘇州 215006)
2(武夷學院認知計算與智能信息處理福建省高校重點實驗室 福建 武夷山 354300)
在面向以微控制器(Microcontroller Unit,MCU)的嵌入式系統的開發中,常常需要使用實時操作系統(Real Time Operation System,RTOS)。為降低嵌入式系統的編程難度,節省程序的編譯時間,將實時操作系統駐留在MCU中,即固化在MCU內的非易失存儲器中,并提供對外的應用程序接口。
目前針對RTOS移植文獻較為豐富,如文獻[1]實現了將muTKernel RTOS移植到H8S/2377 MCU上;文獻[2]實現了uC/OS-III的移植;文獻[3]實現了將MicroC/OS-II RTOS移植到PowerPC 7410處理器上等,而對于RTOS的駐留較少;文獻[4]以VxWorks 653分區操作系統為研究實例,采用統一建模語言分析了分區配置和啟動機制,為理解分區和駐留提供了參考;文獻[5-6]雖實現操作系統的駐留,但均針對的是PC機。
實時操作系統駐留的關鍵技術在于駐留后實時操作系統的函數如何被調用、駐留后的程序框架如何設計合理和RAM和Flash空間的劃分設置。本文在通用嵌入式計算機(General Embedded Computer,GEC)架構的基礎上,針對以上問題設計出合理方案,利用直接調用應用程序編程接口(Application Programming Interface,API)實現開發應用程序,使得用戶不需要進行寄存器級編程,無須關心RTOS的調用細節,提高編程顆粒度和可移植性,達到降低編程難度、快速編譯之目的,同時為RTOS的駐留提供了一種方案。
MCU性能的不斷提高、軟件工程概念的普及和為提高編程顆粒度和可移植性,借鑒通用計算機概念與做法,提出通用嵌入式計算機的概念。在軟件上,將嵌入式軟件分為基本輸入輸出系統程序(Basic Input and Output System,BIOS)與用戶程序(User)兩個部分[7]。RTOS隨BIOS程序駐留于MCU內的Flash中,在User程序中啟動RTOS。
BIOS的功能主要是進行系統的初始化;實現RTOS的駐留;為用戶程序提供相關構件接口,包括底層驅動構件接口、應用構件接口以及操作系統構件接口。
以“分門別類、各有歸處”為主要原則,遵循硬件層次與軟件層次的遞進包含關系以及合理命名文件夾名稱設計了如表1所示帶前綴編號01-09的文件夾,分別存放文檔、芯片內核、微控制器、GEC、用戶板、軟件構件、主程序、RTOS組件和RTOS啟動相關文件。這里將文檔放入工程結構中,其目的是讓文檔與源程序密切聯系在一起。接著是由內到外的四個文件夾:芯片內核、微控制器、GEC、用戶板。然后是軟件構件文件夾,存放與硬件無關的構件,如操作系統構件。隨后是主程序文件夾,存放相應的頭文件、中斷例程文件、主程序文件。最后是RTOS文件夾,存放RTOS封裝的組件和啟動相關文件。

表1 BIOS程序工程框架
User程序是真正的用戶二次編程模板,在User程序中以正常函數名及傳參的方式調用BIOS程序提供的API接口,實現對底層驅動構件、應用構件、操作系統構件的調用等,而無須再次進行底層驅動的開發。User程序工程框架類似于BIOS程序工程框架,區別在于User程序08文件夾和09文件夾,BIOS程序的08文件夾為RTOS封裝的組件,User程序08文件夾為08_OSThread,用于存放主線程和任務線程,User程序無09文件夾。
基于GEC架構,將RTOS隨BIOS程序駐留于MCU中有以下好處:
(1) 降低編程難度。由于RTOS隨BIOS程序駐留于MCU中,提供RTOS相關接口函數給User程序使用,使得用戶只需關心User程序的編程,無須關心RTOS的調用細節,從而降低了編程難度。
(2) 節省編譯時間。由于RTOS隨BIOS程序駐留在MCU中,編譯成功燒入非易失存儲器Flash后就一直存在于MCU中,而User程序中無RTOS,只需編譯User程序,從而縮短了程序的編譯時間。
(3) 提高用戶程序的可移植性。在不同的內核、不同MCU已駐留RTOS的前提下,由于BIOS程序提供統一的接口函數給User程序使用,故User程序只需修改少量配置信息,便可在不同的內核之間移植,提高了用戶程序的可移植性。
在GEC架構下,整個工程被分為BIOS程序和User程序,要想實現mbedOS的駐留,首先需要對MCU的RAM和Flash空間進行合理的劃分,使得代碼不重疊,變量使用不越界,其次應設計出符合軟件工程可復用、可移植、可拓展的接口函數。
(1) Flash空間的劃分。在嵌入式系統軟件開發中,中斷向量、程序代碼和常數通常存放于Flash中。在GEC架構下,Flash空間的劃分采用互不干涉的原則,即BIOS程序占據Flash空間的前a個扇區,User程序占據Flash空間的后b個扇區,Flash總空間為a+b個扇區,如圖1所示。因為Flash的擦除是以扇區為單位的,故也應該以扇區為單位進行Flash空間的劃分。

圖1 Flash空間的劃分
(2) RAM空間的劃分。RAM通常用來存放可讀可寫的數據段(.data段)、可讀可寫且沒有初始化的.bss段、heap段、stack段。因為stack段是用來存放臨時變量的,故RAM空間的劃分可分為棧獨享和棧共享兩種方式。棧共享方式如圖2所示,BIOS程序占用整個RAM空間,User程序則從BIOS程序堆段之后開始使用,且BIOS程序和User程序有共同的棧底(RAM地址的最大值+1)。棧共享方式能夠使得RAM空間得到充分使用,但需注意內存沖突,一般當RAM空間小于16 KB時使用。棧獨享方式如圖3所示,類似于Flash空間的劃分,BIOS程序占據RAM空間的前mKB,User程序占據RAM空間的后nKB,RAM總空間為m+nKB,BIOS程序和User程序有各自的棧底。棧獨享方式可以使得BIOS程序和User程序內存不沖突,但RAM空間利用率低,一般當RAM空間大于60 KB時使用。

圖2 RAM空間棧共享方式

圖3 RAM空間棧獨享方式
在Flash和RAM空間分配合理的基礎上,User程序若要成功調用BIOS程序提供的接口函數,還需要獲取被調用函數的地址。為此,在GEC架構下,BIOS程序將接口函數封裝后固化于Flash中,并將接口函數的地址有序地存放于API表中,User程序就可以通過訪問這個API表實現對接口函數的調用。API表的設計主要包括接口函數的定義、聲明、登記,其過程如圖4所示。

圖4 API表設計
2.2.1接口函數的定義與聲明
接口函數的定義與聲明需滿足嵌入式軟件構件(Embedded Software Component)的基本原則。規范的軟件構件由頭文件(.h)及源程序文件(.c或.cpp)文件構成[8]。接口函數在源程序文件中定義,在頭文件中聲明。API表中主要包含底層驅動構件、應用構件和操作系統構件(軟件構件)這三類構件的函數。
(1) 底層驅動構件。底層驅動構件是根據MCU內部功能模塊的基本知識要素,針對MCU引腳功能或MCU內部功能,利用MCU內部寄存器所制作的直接干預硬件的構件[7]。常用的底層驅動構件主要有GPIO、UART構件等。
(2) 應用構件。應用構件是調用芯片底層驅動構件而制作完成的,符合軟件工程封裝規范的,面向實際應用硬件模塊的驅動構件,例如printf構件。
(3) 操作系統構件。RTOS提供各種類,如線程類、線程信號類等。為了方便用戶使用,將RTOS常用函數如操作系統啟動函數、創建線程函數、延時函數等封裝成構件,表2列出了操作系統部分接口函數。

表2 操作系統部分接口函數
2.2.2接口函數的登記
所謂接口函數登記,就是將接口函數的入口地址按順序放置在一個統一的區域,該區域即為API表。API表可用一個一維數組(如ComponentFun)來表示,接口函數的編號與數組的下標一一對應。在API表中,預留了一些缺省的接口函數名,方便應用程序接口表的更新與擴充。
User程序獲取BIOS程序的API表地址后還需要對接口函數地址進行重定義,最后才能實現對接口函數的調用,其過程如圖5所示。

圖5 API表的調用
在BIOS程序跳轉到User程序之后,執行main函數之前,調用函數BIOS_API_Init,該函數獲取BIOS程序提供的API表首地址并將其保存在一個全局數組(如component_fun)中。由于數組component_fun中的元素只是接口函數的入口地址,并沒有接口函數實現的具體形式,因此,還需要對接口函數的地址進行重定義,以便用戶調用。重定義后的函數名可與之前的不一致,但接口函數重定義順序必須和登記順序一致。其一般格式如下:
#define 函數名((接口函數聲明指針表達形式)(全局數組[接口函數序號])),例如操作系統啟動函數可以定義為#define OS_start((void(*)(void(*func)(void)))(component_fun[63]))。
(1) 合理分配Flash空間。在實際的Flash空間劃分中,應避免BIOS程序的Flash空間劃分過大或過小。過大會使得Flash空間的浪費,甚至以犧牲User程序功能來滿足程序的需要;過小則會使得BIOS程序無法運行。當Flash空間較小時,只需保留最基本的各類構件和mbedOS的最基本功能函數以滿足實際工程需要即可,確保BIOS的Flash空間不浪費。
(2) RAM內存沖突問題。當RAM空間的劃分采用棧共享的方式時,由于堆的使用方向是由小地址向大地址方向進行的,因此當BIOS程序中使用new或malloc函數申請空間時,可能會使得堆溢出,從而和User程序的.data和.bss段重疊,導致User程序無法正常運行。因此,在BIOS程序中主動在堆區申請空間,從而避開User程序的全局變量區,如圖6所示。

圖6 RAM空間分配示意圖
(3) 系統服務調用問題。mbedOS的調度依賴于SVC、可掛起系統調用(Pendable Supervisor,PendSV)、系統時間嘀嗒SysTick[9],故在啟動mbedOS前,應及時將SVC、PendSV、SysTick的中斷向量寫入User程序的中斷向量表中。
選取mbedOS實時操作系統進駐留測試,駐留測試工程在STM32CubeIDE 1.3.0開發環境和STM32L431RC微控制器上完成。其中,RAM空間劃分方式采用棧共享方式。
mbedOS是ARM公司在2014年推出的,它是一個專門為物聯網(IoT)中的“物體”而設計開源嵌入式實時操作系統[10]。mbedOS提供統一的應用程序編程接口[11],具有嵌入式系統的軟、硬件資源的分配、線程(任務)調度、同步機制、中斷處理等基本功能,在IP網絡組件[12]、物聯網[13]等方面得到廣泛應用。
STM32L431RC微控制器的片內Flash大小為256 KB,Flash區的地址范圍為:0x0800_0000-0x0804_0000。Flash區中扇區大小2 KB,扇區總共有128個。片內RAM為靜態隨機存儲SRAM,大小為64 KB,地址范圍為0x2000_0000-0x2001_0000。
(1) 功能設計。User程序的主要功能是依次創建紅燈、綠燈、藍燈線程,實現三個線程申請同一個互斥量并在申請到互斥量后,分別延時3 s、1 s、2 s。
(2) 測試結果。User程序中,在調用操作系統啟動、線程創建、線程啟動、互斥量鎖定、互斥量釋放等函數前加上相應printf函數輸出提示信息,來驗證mbedOS是否駐留成功。駐留測試實驗結果如圖7所示,可以看出mbedOS駐留成功,能準確調用BIOS程序提供的接口函數,線程運行正常,該方法具有可行性。

圖7 測試結果
在駐留測試工程中,BIOS程序占Flash的前26個扇區(52 KB)、RAM的全部空間;User程序占Flash的后102個扇區(204 KB)、RAM的后48 KB。mbedOS駐留后的Flash和RAM空間劃分和實際使用如表3所示,BIOS和User程序實際使用空間大小均小于實際分配空間大小,且User程序剩余空間較大,能滿足后續程序開發需要,故Flash和RAM空間分配較為合理。

表3 STM32L431RC中BIOS和User空間劃分表
本文在GEC架構的基礎上,深入剖析RTOS駐留的關鍵技術,給出駐留后程序的合理工程框架,提出采用互不干涉的原則劃分Flash空間和棧共享、棧獨享兩種方式劃分RAM空間,詳細給出API表的設計與實現,最后以mbedOS實時操作系統為例,在STM32L431RC上進行了駐留測試,測試結果表明mbedOS駐留成功,Flash和RAM空間劃分合理,并且降低編程難度,縮短程序的編譯時間,提高用戶程序的可移植性,具有一定的參考價值。