滕艷平, 賈思禹, 金 梅, 李麗麗
(齊齊哈爾大學 計算機與控制工程學院, 黑龍江 齊齊哈爾 161006)
?
基于ARM11的μC/OS-II操作系統內核移植實驗的設計
滕艷平, 賈思禹, 金梅, 李麗麗
(齊齊哈爾大學 計算機與控制工程學院, 黑龍江 齊齊哈爾161006)
在分析嵌入式實時操作系統μC/OS-II內核結構的基礎上,針對ARM11微處理器,提出了μC/OS-II移植的方案,并在所移植的操作系統上進行了多任務同步設計。通過RVDS集成開發環境的測試結果表明,移植后的操作系統運行正常,實現了多個任務之間的切換,并滿足系統對實時性、穩定性的需求。
操作系統; 內核移植; ARM11; μC/OS-II; RVDS
ARM11系列微處理器是ARM公司近年推出的新一代RISC處理器,它具有ARM新指令架構,提供高性能處理能力,使其在嵌入式系統開發中得到廣泛的應用。嵌入式實時操作系統μC/OS-II以其實時性強、內核公開、易于學習和開發等特點受到廣大技術人員和嵌入式愛好者的青睞[1-3]。本文以μC/OS-II為系統設計平臺,給出其內核代碼在ARM11系列微處理器上移植的實驗步驟,并在所移植的操作系統上進行多任務的同步設計,完成多任務之間的切換。
在μC/OS-II內核的移植時,需要考慮編譯器的選取和ARM11工作模式的選取、移植中需要修改的文件以及利用ARM11軟中斷實現系統調用等相關問題。移植框架見圖1所示。

圖1 μC/OS-II操作系統移植框架
在移植工作中,需要對μC/OS-II內核中的OS_CPU.H、OS_CPU_A.S和OS_CPU_C.C等3個文件進行重新編寫,以下面給出具體的實現步驟。
2.1文件OS_CPU.H的實驗設計
在編寫文件OS_CPU.H時,主要包括聲明10個數據類型、聲明3個宏以及定義堆棧的增長方向。
2.1.1數據類型的定義
在定義數據類型時使用了typedef關鍵字,這樣做的目的,一方面是為了創建與平臺無關的數據類型,另一方面也為移植提供了便利。由于在移植代碼中大量使用隱含著不可移植性的數據類型,例如short、int、long等,當移植失敗后需要修改數據類型時,卻需要多處修改,浪費時間。而使用typedef關鍵字創建與平臺無關的數據類型后,當移植失敗后需要修改數據類型時,只需要修改typedef定義即可實現批量修改。在文件OS_CPU.H中,這些數據類型的定義如下:
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
typedef INT32U OS_STK;
2.1.2宏定義
在OS_CPU.H文件中,還需定義OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()兩個宏。OS_ENTER_CRITICAL()用于關中斷;而OS_EXIT_CRITICAL()用于開中斷。這兩個宏直接調用OS_CPU_A.ASM文件中的匯編語言函數OSCPUSaveSR()、OSCPURestoreSR()來實現關中斷和開中斷操作。之所以采用匯編語言來實現關中斷和開中斷操作,主要是從實時性的角度來考慮的。
C語言與匯編語言的混合編程為嵌入式操作系統的開發提供了極大的便利,例如:對ARM11的CPSR寄存器無法使用C語言直接訪問,但是只要嵌入一小段簡單的匯編代碼就可以實現這一功能。需要注意的是,在C語言與匯編語言混合編程的程序中,參數是通過通用寄存器進行傳遞的,在OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()的宏定義中,cpu_sr的值正是通過通用寄存器R0進行傳遞的。
在OS_CPU.H文件中定義OS_TASK_SW()宏,用于實現任務的切換,這個宏定義同樣是直接調用OS_CPU_A.ASM文件中的匯編語言函數OSCtxSw()來實現任務的切換。
2.1.3堆棧的生長方向的定義
存儲器堆棧可分為兩種:升棧,是向高地址方向生長,全稱為遞增堆棧;降棧,是向低地址方向生長,全稱為遞減堆棧。堆棧指針指向最后壓入的堆棧的有效數據項,稱為滿堆棧;堆棧指針指向下一個要放入的空位置,稱為空堆棧[4]。這樣就有4種類型的堆棧表示遞增和遞減的滿堆棧和空堆棧的各種組合:
(1) 滿升棧:堆棧通過增大存儲器的地址向上增長,堆棧指針指向內含有效數據項的最高地址;
(2) 空升棧:堆棧通過增大存儲器地址向上增長,堆棧指針指向堆棧上的第;
(3) 滿降棧:堆棧通過減小存儲器的地址向下增長,堆棧指針指向內含有效數據項的最低地址;
(4) 空降棧:堆棧通過減小存儲器地址向下增長,堆棧指針指向堆棧下的第一個空位置。
ARM11處理器對于4種類型的堆棧均給予支持,但考慮到本文所用的RVDS編譯器僅支持一種增長方式,即從上往下增長,并且必須是滿遞減堆棧。因此將OS_STK_GROWTH的值設為1,即使用滿降棧。其定義如下:
#define OS_STK_GROWTH1
2.2文件OS_CPU_A.S的實驗設計
在OS_CPU_A.S文件的修改中,涉及匯編函數OSStartHighRdy()、OSCtxSw()、OSTickISR()、OSIntCtxSw()、OSCPUSaveSR()以及OSCPURestoreSR()等的重新編寫。
2.2.1編寫OSStartHighRdy()函數
使就緒狀態的任務開始運行的函數是OSStart(),也就是說當程序運行到OSStart()之后就進入μC/OS-II多任務環境了;而高優先級就緒任務啟動函數OSStartHighRdy()用于運行多任務啟動前優先級最高的任務,該函數僅僅在多任務啟動時被執行一次,用來啟動第一個——也就是最高優先級的任務執行。其實現代碼如下:
OSStartHighRdy
MSRCPSR_cxsf,#SVCMODE|NOINT
BLOSTaskSwHook
LDRR0, =OSRunning
MOVR1, #1
STRBR1, [R0]
LDRR0, =OSTCBHighRdy
LDRR0, [R0]
LDRSP, [R0]
LDMFDSP!, {R0}
MSRSPSR_cxsf, R0
LDMFDSP!, {R0-R12, LR, PC}^
分析:在上述代碼的第一部分,首先對ARM11處理器的程序狀態字寄存器CPSR進行賦值,SVCMODE、NOINT是使用ARM匯編指令EQU將它們分別賦值為0x13、0xc0,對這兩個值進行按位或操作后得出的值,可以將ARM11處理器運行在SVC模式,并且屏蔽普通中斷及快速中斷。
SVC模式是ARM11處理器一種保護模式,可以將其理解為一種特權模式,在此模式下具有更高的執行權限。此后將OSRunning的值置為1,此時的LDR指令是偽指令,并不是普通的寄存器加載指令。然后將最高優先級任務的堆棧指針賦值給SP,并且將最高優先級任務的堆棧中保存的數據恢復到寄存器中,從而實現了最高優先級就緒任務的啟動。ARM11的CPSR寄存器如圖2所示。

圖2 ARM11的CPSR寄存器
2.2.2編寫OSIntCtxSw()函數
μC/OS-II在每個時間片都要進行任務的調度。調度的結果或者是返回原來的任務繼續執行,或者是因為找到了就緒的更高優先級的任務而讓該任務運行。用戶時鐘中斷服務程序OSTickISR,也就是時鐘節拍服務程序,是操作系統的核心內容。OSIntCtxSw()是一個中斷級的任務切換函數,它是在中斷程序中調用的。在μC/OS-II中,由于中斷的產生可能會引起任務切換,在中斷服務程序的最后會調用OSIntExit()函數檢查任務就緒狀態,如果需要進行任務切換,將調用OSIntCtxSw()。其中OSIntCtxSw()的實現代碼如下:
OSIntCtxSw:
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
SP =OSTCBHighRdy->OSTCBStkPtr;
RestoreNew task context
分析:OSCtxSw()和OSIntCtxSw()都是用于任務切換的函數,其區別在于:在OSIntCtxSw()中無需再保存處理器寄存器,因為在OSIntCtxSw()之前已發生中斷,所以可以保證所有的處理器寄存器都被正確地保存到了被中斷的任務的堆棧之中。
2.3文件OS_CPU_C.C的實驗設計
在編寫文件OS_CPU_C.C時,用C語言編寫get_x()、get_int_vect()、OSTaskStkInit()等函數[5-6]。以下重點給出了中斷檢測函數get_x()的代碼設計過程。
ARM1176JZF-S處理器支持2組共64種中斷源,中斷處理對于μC/OS-II的移植是必不可少的。這里2組中斷源VICO和VIC1的寄存器基地址分別為0x71200000和0x71300000,狀態寄存器的偏移地址為0x000,通過訪問這一只讀寄存器即可知道當前中斷源的屏蔽狀態。其實現關鍵代碼如下:
unsigned int get_x(unsigned long x)
{int i;
for(i=0;i<32;i++)//該循環語句用于測試x的每一位是否為1
{if(x&(1<
return i;
}
return 0;
}
unsigned int get_int_vect()
{unsigned int ret;
unsigned long status;
status = * ((unsigned long*)0x71200000); //將VIC0的地址保存到STATUS變量if(status)
{ret = get_x(status);
return ret; }//在STATUS不為0的情況下檢測其中為1的位的位號
else
{status = * ((unsigned long*)0x71300000); //將VIC1的地址保存到STATUS變量if(status)
{ret = get_x(status)+32;//一個寄存器占32位
return ret;
}
}
}
分析:根據S3C6410處理器的結構和特點來確定任務的堆棧結構,特別在OSTaskStkInit()函數代碼的編寫中,其初始化參數由R0來傳遞,而數值0x00000013L將會使處理器設置為SVC模式,即特權模式,擁有更高的權限。
3.1多任務同步的設計
在多任務同步的設計中,任務創建與任務切換的流程圖如圖3所示。

圖3 任務創建和切換流程圖
任務的設計選擇了RVDS集成開發環境,創建一個新的工程,并向工程添加一個新的文件,可為所添加文件進行分組,其實現的關鍵代碼如下:
void Task0(void *pdata)
{#if OS_CRITICAL_METHOD == 3
OS_CPU_SRcpu_sr;
#endif
OS_ENTER_CRITICAL();
ISRInit();
OS_EXIT_CRITICAL();
OSPrintfInit();
OSStatInit();
OSTaskCreate (Task1,(void *)0, &Task1Stk[Task1StkLengh -1], Task1Prio);
while(1)
{OSPrintf(″this is from task0 ″);
OSTimeDly(OS_TICKS_PER_SEC*3); } }
分析:在上述代碼中,實現了Task0、Task1、Task2等3個任務的創建,并完成任務之間的切換。首先由Task0創建了任務Task1、在窗口控制臺上打印了語句this is from task0,并延時了3個時間片;隨后Task1創建了任務Task2、在窗口控制臺上打印了語句this is from task1,并延時了2個時間片;最后Task2在窗口控制臺上打印了語句this is from task2,并延時了1個時間片。
另外,本實驗中,為了實現3個任務Task0、Task1、Task2的切換,并在串口控制臺輸出結果,還需移植printf()函數,該函數的主要功能是將要顯示的字符串信息發送到串口消息隊列中。OSPrintf函數實現代碼如下:
void OSPrintf(const char *fmt,...)//控制臺打印函數
{INT8U *pUartBuf; // 定義一個串口消息緩沖區
INT8U err; //定義錯誤信息變量
va_list ap;//定義緩沖區鏈表
pUartBuf=OSMemGet(pUartMem,&err);//將要發送的內容傳送到串口
va_start(ap,fmt);//開始遍歷鏈表
vsprintf(pUartBuf,fmt,ap);//打印控制臺內容
va_end(ap);//結束遍歷鏈表
OSQPost(pUart_Q,pUartBuf);//發送消息
}
3.2移植系統的測試
在CodeWarrior for RVDS的Edit選單中的Debug Settings項中修改測試選項,將Language Settings項中的RealView Assembler、RealView Compiler中的架構均修改為ARM1176JZF-S[7-9],還可以將Linker項中的RealView FromELF項修改生成的BIN文件名稱。代碼測試界面如圖4所示。

圖4 代碼測試界面
在CodeWarrior for RVDS的Project選項下選擇Debug,將自動啟動RVDS集成開發環境中的AXD Debugger v1.3.1,燒寫二進制文件到ARM11開發板中并運行BOOTLOADER引導程序,對部分硬件進行初始化工作。之后,內核正常啟動,并對中斷模塊進行了初始化。移植后的內核運行情況如圖5所示。3個任務同步的測試可利用JLink V8仿真器[10-12]來實現,將編寫的代碼下載到內存即可進行仿真調試。測試結果見串口控制臺的輸出,3個任務同步實現如圖6所示。

圖5 移植內核的初始化

圖6 3個任務同步實現
圖6表明3個任務在新移植的操作系統中完成了任務的切換,實現任務的調度,并達到了預期的目的。
本文在充分了解ARM11指令系統的基礎上,分析了μC/OS-II內核結構,提出了μC/OS-II在ARM11上的移植思想和方法,并實現了多個任務的同步設計,選擇了RVDS集成開發環境進行了系統測試。結果表明,新移植的操作系統運行穩定,實現了多任務的切換,完成了多任務的調度,滿足系統對實時性的要求。下一步工作將對所移植的操作系統內核進一步優化,并實現更多的應用。
References)
[1] 徐海龍,邱建,王曉娜,等.μC/OS-Ⅱ的優化移植和設備驅動框架設計[J].計算機測量與控制,2012,20(9):2501-2506.
[2] 馬濤,白瑞林,石堅.Cortex-A8平臺的μC/OS-Ⅱ及LwIP協議棧的移植與實現[J].計算機應用與軟件,2014,31(1):242-245.
[3] 孫彥景,王夢龍,王迎.基于μC/OS-Ⅱ智能公交系統終端設計與實現[J].計算機工程與設計,2012,33(12):4509-4513.
[4] 郭德源,何虎,楊旭.面向嵌入式實時操作系統的MPI實現[J].微電子學與計算機,2011,28(3):35-42.
[5] 李祁,王鳳芹,張燕紅.嵌入式實時操作系統μC/OS-Ⅱ在STM32開發板上的應用[J].計算機與數字工程,2014,42(1):164-168.
[6] 鄧昀,程小輝,王新政.微內核結構嵌入式實時操作系統的研究與設計[J].微電子學與計算機,2012,29(10):133-139.
[7] 葛強,王宜懷,曹振華.一種基于ARM核的嵌入式操作系統的設計實現[J].計算機應用與軟件,2010,27(3):268-271.
[8] 蔣建春,汪同慶.一種異構多核處理器嵌入式實時操作系統構架設計[J].計算機科學,2011,38(6):298-303.
[9] 李昌剛,黃敏江,張昕,等.μC/OS-Ⅱ在XC164CS上的移植方法[J].計算機工程,2010,36(12):242-244.
[10] 李山山,李耀鏘,劉敬晗,等.μC/OS-Ⅱ內核在基于FPGA的CPU上的移植[J].實驗技術與管理,2010,27(4):87-90.
[11] 楊云,張勇.基于ARM7的μC/OS-Ⅱ移植分析與實現[J].計算機工程與設計,2009,30(3):539-541.
[12] 孫順遠,秦會斌,崔佳冬,等.μC/OS-Ⅱ在Cortex-M3內核上的移植及優化[J].計算機系統應用,2010,19(4):208-211.
Design of kernel transplantation experiment μC/OS-II operating system based on ARM11
Teng Yanping, Jia Siyu, Jin Mei, Li Lili
(College of Computer and Control Engineering,Qiqihar University,Qiqihar 161006, China)
Based on the analysis of the kernel of embedded real-time μC/OS-II operating system and aiming at the ARM11 Micro-Processor, a migration scheme of μC/OS-II is put forward, and its multi-tasking synchronous demo is performed. The testing results by the RealView Development Suite(RVDS)Integrated Development Environment show that the migrated-operating system can run steadily and the goals of the switch between many tasks are realized, which meets the demands for real-time quality and stability.
operating system; kernet transplantation; ARM11; μC/OS-II; RVDS
10.16791/j.cnki.sjg.2016.03.036
2015- 08- 05
黑龍江省自然科學基金項目(F201218);黑龍江省教育廳科學技術研究項目(12541880)
滕艷平(1965—),女,黑龍江齊齊哈爾,碩士,副教授,主要研究方向為嵌入式操作系統.
E-mail:typ2732996@163.com
TP316.2
A
1002-4956(2016)3- 0142- 05