段玉波,吳光敏
(昆明理工大學(xué)理學(xué)院,昆明 650093)
移植主要解決三個(gè)問題,時(shí)鐘獲得、中斷處理和任務(wù)切換。這里僅分析這幾個(gè)問題的解決思路,后面第三部分將詳細(xì)講述其實(shí)現(xiàn)過程。
在PC 中,時(shí)鐘節(jié)拍由硬件定時(shí)器產(chǎn)生,硬件定時(shí)器會(huì)周期性中斷CPU。PC 機(jī)時(shí)鐘節(jié)拍的中斷向量為0x08,μCOS- II 將此向量截取,使之指向μCOS-II的時(shí)鐘節(jié)拍中斷服務(wù)子程序OSTickISR(),而原先的中斷向量保存在中斷向量0x81 中。然而,在基于控制臺(tái)的windows 保護(hù)模式下不能像DOS 下面那么容易,直接通過一個(gè)函數(shù)調(diào)用就能夠修改中斷。windows 下要修改中斷涉及到驅(qū)動(dòng)程序,這樣就加大了移植的困難度與復(fù)雜度。因此,考慮到本移植只是為了教學(xué)和學(xué)習(xí),并沒有應(yīng)用到對(duì)實(shí)時(shí)性要求高的產(chǎn)品,所以最終決定采用windows下的軟件定時(shí)器來模擬時(shí)鐘中斷。
時(shí)鐘中斷處理子程序。通過軟件定時(shí)器來模擬產(chǎn)生μC/OS-II的時(shí)鐘中斷,但timeSetEvent()函數(shù)調(diào)用定時(shí)回調(diào)函數(shù)是和主線程同時(shí)被windows 操作系統(tǒng)調(diào)度的,并沒有起到中斷的作用。所以在調(diào)用定時(shí)回調(diào)函數(shù)的時(shí)候必須停止主線程的運(yùn)行,退出回調(diào)函數(shù)則恢復(fù)主線程的運(yùn)行。這些事情可以都放在定時(shí)回調(diào)函數(shù),也就是μC/OS-II的時(shí)鐘中斷處理函數(shù)中完成。Windows 下要掛起一個(gè)線程的運(yùn)行,首先要得到這個(gè)線程的句柄,然后調(diào)用Suspend-Thread(hangdler)和ResumeThread(handler)就可以掛起和繼續(xù)執(zhí)行線程。
任務(wù)的切換。任務(wù)切換就是進(jìn)行任務(wù)的上下文切換,研究選擇了不帶浮點(diǎn)運(yùn)算的上下文切換進(jìn)行分析,任務(wù)的上下文和μCOS-II 在80x86 上移植的上下文很相近,不同點(diǎn)是段寄存器不用保存,因?yàn)閂C 下任務(wù)的切換是在同一個(gè)線程完成的,而保護(hù)模式下段寄存器的值在同一個(gè)線程下是不改變的。
μCOS-II的作者使用了Borland C/C++編譯器,如今為便于學(xué)習(xí)要將其移植到VC ++6.0 編譯環(huán)境下,首先要說明二者的一些區(qū)別,因?yàn)檫@直接關(guān)系到與CPU相關(guān)的那部分代碼的編寫。
采用Borland C/C ++ 編譯器編譯連接源文件后,μCOS-II 作為DOS 程序運(yùn)行于大存儲(chǔ)模式實(shí)模式下,而VC ++6.0 建立win32 工程文件編譯連接后,μCOS-II是在Windows 系統(tǒng)平臺(tái)上作為控制臺(tái)應(yīng)用程序運(yùn)行于保護(hù)模式下,二者主要區(qū)別如表1 所示。
表1 DOS 程序與控制臺(tái)程序比較表
Borland C/C++的庫(kù)文件與VC 使用的庫(kù)文件有很大差異,Borland C/C ++的一些宏也是VC ++6.0 所不支持的,因此需要做相應(yīng)的替換或屏蔽部分未使用代碼。
Borland C/C++編譯器對(duì)c 文件不支持匯編語言嵌套,而VC ++6.0 支持在c 程序中嵌套匯編代碼?;诖?,工程中沒有單獨(dú)的匯編文件,而是將相關(guān)代碼放到OS_CPU_C.C 中。
創(chuàng)建工程:建立一個(gè)空的win32 控制臺(tái)應(yīng)用程序,在此工程內(nèi)創(chuàng)建相關(guān)目錄并將μCOS-II 文件添加到其中。
配置工程:將μCOS-II相關(guān)路徑添加到工程配置選項(xiàng)中;添加與軟件定時(shí)器有關(guān)的庫(kù)winmm.lib。
在原有基礎(chǔ)上增加兩個(gè)頭文件:
#include <windows.h >
#include <mmsystem.h >//包含時(shí)鐘函數(shù)的頭文件,需要windows.h的支持。
3.3.1 OSTaskStkInit()
初始化任務(wù)堆棧。保護(hù)模式下程序是在同一個(gè)段址內(nèi)處理的,因此段址不用壓棧,為研究方便,也沒有把浮點(diǎn)寄存器壓棧。Windows 保護(hù)模式下堆棧以32 位字為單位進(jìn)行處理,因此數(shù)據(jù)類型由INT16U 改為INT32U,代碼如下:
程序清單L1 初始化任務(wù)堆棧
3.3.2 OSStartHighRdy()
從處于就緒態(tài)優(yōu)先級(jí)最高的任務(wù)的TCB 中取得堆棧地址并恢復(fù)到SP,恢復(fù)所有寄存器使優(yōu)先級(jí)最高的任務(wù)開始運(yùn)行,該函數(shù)由OSStart()函數(shù)調(diào)用。代碼如下:
程序清單L2 啟動(dòng)最高優(yōu)先級(jí)任務(wù)
3.3.3 OSCtxSw()
任務(wù)級(jí)的任務(wù)切換函數(shù),其實(shí)是完成任務(wù)的上下文切換。由于VC++6.0 下任務(wù)是在一個(gè)線程中切換的,而且保護(hù)模式下段址寄存器在同一個(gè)線程下的值不變,因此不用保存段寄存器。任務(wù)切換時(shí)的壓棧情況如圖1 所示。
圖1 任務(wù)切換壓棧后狀態(tài)
程序清單L3 手動(dòng)任務(wù)切換
3.3.4 OSIntCtxSw()
中斷級(jí)任務(wù)切換函數(shù),由于控制臺(tái)程序不能處理中斷,所以此處用庫(kù)函數(shù)模擬中斷來實(shí)現(xiàn)時(shí)鐘節(jié)拍終端服務(wù)子程序,但其中并沒有保存相應(yīng)寄存器,因此需要在這里保存CPU 寄存器,這與μCOS-II原來的程序是不同的。在Test.c 中定義了一個(gè)主線程句柄HANDLE 和一個(gè)CONTEXT 保存主線程上下文。在進(jìn)行任務(wù)切換時(shí),首先保存相應(yīng)寄存器,然后把要運(yùn)行的的任務(wù)的上下文填入CONTEXT 結(jié)構(gòu)并保存,完成切換。代碼如下:
程序清單L4 中斷級(jí)任務(wù)切換函數(shù)
3.3.5 OSTickISR()
在調(diào)用timeSetEvent 后,被定時(shí)器線程被周期調(diào)用,其代碼如程序清單L5 所示。
開關(guān)中斷通過設(shè)置一個(gè)全局變量來解決,代碼如下:
在main 函數(shù)的OSInit()前,加入了一個(gè)VCInit()函數(shù),主要初始化VC 環(huán)境,包括獲得主線程的句柄,設(shè)置上下文環(huán)境標(biāo)志位,特別要注意的是,句柄的獲得是要通過偽句柄轉(zhuǎn)換的,代碼如下所示:
random 函數(shù)不是ANSI C 標(biāo)準(zhǔn),不能在gcc,vc等編譯器下編譯通過,可改用C ++下的rand 函數(shù)來實(shí)現(xiàn),它由C+ +標(biāo)準(zhǔn)函數(shù)庫(kù)提供。
具體實(shí)現(xiàn)方法是:首先用srand()來初始化隨機(jī)種子數(shù),rand 產(chǎn)生的隨機(jī)數(shù)是從0 到rand_max的,而rand_max是一個(gè)很大的數(shù),因此產(chǎn)生從X 到Y(jié)的數(shù):從X 到Y(jié) 有Y-X +1個(gè)數(shù),所以要產(chǎn)生從X 到Y(jié)的數(shù),只需要這樣寫:k=rand()%(Y-X +1)+X。
多任務(wù)啟動(dòng)后,優(yōu)先級(jí)最高的任務(wù)TaskStart()首先運(yùn)行,該任務(wù)調(diào)用timeSetEvent()產(chǎn)生定時(shí)器線程,定時(shí)器線程就會(huì)按預(yù)定的時(shí)間周期調(diào)用μC/OS-II的時(shí)鐘節(jié)拍中斷服務(wù)子程序OSTickISR()。代碼如下:
盡量為任務(wù)分配足夠大的堆??臻g,如果分配不夠,則可能導(dǎo)致調(diào)試或運(yùn)行時(shí)發(fā)生內(nèi)存溢出錯(cuò)誤。
通過以上分析進(jìn)行試驗(yàn),移植工作成功完成,運(yùn)行正常,且經(jīng)過單步跟蹤調(diào)試也沒有出現(xiàn)錯(cuò)誤。
雖然μCOS-II 在各種處理器上的移植項(xiàng)目不勝枚舉,但單純?cè)赩C ++6.0 下進(jìn)行該例程的移植還未見到。移植的過程可以使人進(jìn)一步了解與CPU相關(guān)部分程序的編寫以及弄懂μCOS-II時(shí)鐘調(diào)用的方法,為深入研究μCOS-II 及運(yùn)用到實(shí)際項(xiàng)目中打下基礎(chǔ)。
最后,希望移植后的例程能夠?yàn)棣藽OS- II 學(xué)習(xí)者提供一個(gè)新的學(xué)習(xí)環(huán)境,也為μCOS-II 課堂教學(xué)提供參考。
[1]Jean J Labrosse.嵌入式實(shí)時(shí)操作系統(tǒng)μC/OS-II[M].北京:北京航空航天大學(xué)出版社,2003.
[2]沈美明,溫東撣.IBM-PC 匯編語言程序設(shè)計(jì)[M].北京:清華大學(xué)出版社,1999.
[3]羅清平.基于X86 體系結(jié)構(gòu)的μCOSII 移植研究[D].成都:四川大學(xué),2008.
[4]Jean Louis Gareau.Porting μC/OS-II to the x86 Protected Mode[EB/OL].2001.http://theblog.tistory.com/71.