Qsys與uCOS學(xué)習筆記3:Hello uC/OS-II
uC/OS-II(又名Micro C/OS)是基于嵌入式系統的完整的,可移植、可固化、可裁剪的可剝奪型實(shí)時(shí)內核,其已經(jīng)廣泛應用在航空飛行器、醫療設備、工業(yè)控制等可靠性和穩定性要求較高的場(chǎng)合。該內核的代碼也是完全開(kāi)源的,如果不做商業(yè)用途,完全免費。因此對于廣大的嵌入式愛(ài)好者與工程師們而言,了解OS從uC/OS-II開(kāi)始不失為一個(gè)很好的選擇。
本文引用地址:http://dyxdggzs.com/article/276072.htm之前是使用特權同學(xué)自己的SF-NIOS2開(kāi)發(fā)套件進(jìn)行了EDS上的uC/OS-II樣板工程測試,為了當前學(xué)習筆記的持續性,這里重新就DE2-115板重新整理一個(gè)Hello uC/OS-II實(shí)例的創(chuàng )建和演示。
Qsys組件添加
在一個(gè)工程實(shí)例基礎上,添加一個(gè)Interval Timer外設,設置該Timer的定時(shí)Period為10ms,用于作為uC/OS-II的時(shí)鐘節拍(Clock tick),如圖1所示。

圖1
修改該Timer外設名稱(chēng)為ucosii_timer。將它的clk和reset信號分別連接到clk組件的clk和clk_reset信號上,將它的Avalon從機接口s1連接到nios_qsys_0的data_master上,并將它的irq連接到nios_qsys_0的d_irq上。

圖2
自動(dòng)分配地址,點(diǎn)擊SystemàAssign Base Addresses。自動(dòng)分配中斷向量號,點(diǎn)擊SystemàAssign Interrupt Numbers,接著(zhù)點(diǎn)擊Generate生成新的系統。
完成Qsys新系統的Generate,接著(zhù)重新編譯Quartus II的project。自此,硬件的修改已經(jīng)就緒。
軟件工程創(chuàng )建
如圖3所示,打開(kāi)EDS后,點(diǎn)擊FileàNewàNios II Application and BSP from Template新建模板工程。

圖3
如圖4所示,在新建工程向導中,選擇SOPC Information File name為當前工程目錄下的sopcinfo文件。Project name命名為ucosii_swprj,選擇Project template為Hello MicroC/OS II。最后點(diǎn)擊Finish創(chuàng )建工程。

圖4
新建工程出現在工程管理窗口后,右鍵單擊ucosii_swprj文件夾,選擇NIOS IIàBSP Editor,如圖5所示。

圖5
如圖6所示,確定Main頁(yè)面中Common里面的stderr/stdin/stdout均為jtag_uart,ucosii_timer為sys_clk_timer即可。點(diǎn)擊Generate更新設置。

圖6
右鍵點(diǎn)擊應用工程,選擇Build Project進(jìn)行軟件工程編譯。完成后Console窗口打印如圖7所示的信息,可見(jiàn)這個(gè)uC/OS-II內核以及軟件的HAL占用了大約94KB的存儲空間,uC/OS-II其實(shí)還是很小的,只不過(guò)NIOS II各種外設的HAL比較大,不過(guò)也都是可以裁剪的。

圖7
uC/OS-II運行調試
首先將Quartus II工程產(chǎn)生的sof硬件配置文件燒錄到FPGA中。
接著(zhù)應用工程上點(diǎn)擊右鍵彈出菜單選擇Run asàNios II Hardware,在線(xiàn)運行uC/OS-II實(shí)例工程。
這個(gè)uC/OS-II工程的實(shí)驗目的只是創(chuàng )建兩個(gè)task分別打印一串字符,正如readme所描述:
Readme - Hello MicroC/OS-II Hello Software Example
Hello_uosii is a simple hello world program running MicroC/OS-II. The
purpose of the design is to be a very simple application that just
demonstrates MicroC/OS-II running on NIOS II. The design doesn't account
for issues such as checking system call return codes. etc.
在NIOS II Console中,我們可以看到最終運行的效果,如圖8所示,兩個(gè)任務(wù)所打印的字符串”Hello from task1”和”Hello from task2”循環(huán)出現。

圖8
主要實(shí)例源碼如下:
#include
#include "includes.h"
/* Definition of Task Stacks */
#define TASK_STACKSIZE 2048
OS_STK task1_stk[TASK_STACKSIZE];
OS_STK task2_stk[TASK_STACKSIZE];
/* Definition of Task Priorities */
#define TASK1_PRIORITY 1
#define TASK2_PRIORITY 2
/* Prints "Hello World" and sleeps for three seconds */
void task1(void* pdata)
{
while (1)
{
printf("Hello from task1n");
OSTimeDlyHMSM(0, 0, 3, 0);
}
}
/* Prints "Hello World" and sleeps for three seconds */
void task2(void* pdata)
{
while (1)
{
printf("Hello from task2n");
OSTimeDlyHMSM(0, 0, 3, 0);
}
}
/* The main function creates two task and starts multi-tasking */
int main(void)
{
OSTaskCreateExt(task1,
NULL,
(void *)&task1_stk[TASK_STACKSIZE-1],
TASK1_PRIORITY,
TASK1_PRIORITY,
task1_stk,
TASK_STACKSIZE,
NULL,
0);
OSTaskCreateExt(task2,
NULL,
(void *)&task2_stk[TASK_STACKSIZE-1],
TASK2_PRIORITY,
TASK2_PRIORITY,
task2_stk,
TASK_STACKSIZE,
NULL,
0);
OSStart();
return 0;
}
源碼中,一個(gè)標準的uC/OS-II工程,如圖9所示,初始化時(shí)調用OSInit();函數;接著(zhù)調用OSTaskCreate();或OSTaskCreateExt();函數創(chuàng )建用戶(hù)任務(wù);最后調用OSStart();函數運行任務(wù)。這里的main函數里雖然沒(méi)有出現OSInit();函數,但實(shí)際上在HAL后臺外設初始化時(shí)候肯定調用了。中間是任務(wù)的創(chuàng )建,這里創(chuàng )建兩個(gè)任務(wù)task1和task2,優(yōu)先級分別為1和2,并且分配了相應的堆??臻g。在兩個(gè)任務(wù)中,分別打印字符串”Hello from task1”和”Hello from task2”,字符串打印后調用OSTimeDlyHMSM(0, 0, 3, 0);函數做了3s的延時(shí)。如果修改這個(gè)延時(shí)時(shí)間,打印效果會(huì )發(fā)生改變,根據延時(shí)的情況,Console窗口出現的打印字樣頻率和速度會(huì )不一樣。

圖9
前面提到我們的實(shí)例其實(shí)是有OSInit();函數存在的,而且是在系統的main();函數之前就調用了。這里也探個(gè)究竟,也算是給大家講講NIOS II的軟件執行順序吧。NIOS II軟件在上電后其實(shí)并非如同一幫的裸奔的嵌入式軟件一樣直接從main();函數開(kāi)始執行程序,而是先執行HAL里面的alt_main();函數,這個(gè)函數在bsp工程下的HALàsrc里面,找到名為alt_main();的函數便是。

圖10
打開(kāi)alt_main.c源代碼文件后,如圖11所示,alt_main();函數中執行了Qsys系統的幾乎所有可用外設的初始化,因為我們這個(gè)模板工程用的是uC/OS-II的例子,所以必有其初始化,圖11中的ALT_OS_INIT();便是。如果右擊該函數,選擇Open Declaration,則我們便能看到這樣的定義:
#define ALT_OS_INIT() OSInit();

圖11
再來(lái)簡(jiǎn)單的熟悉一下這個(gè)模板工程中所涉及的幾個(gè)函數,包括OSInit();函數、OSTaskCreate();函數、OSTaskCreateExt();函數、OSStart();函數。
OSInit();函數
void OSInit (void)
在使用uC/OS-II的任何功能之前,必須調用該函數。該函數建立了兩個(gè)任務(wù):空閑任務(wù)——在所有其他任務(wù)均未就緒時(shí)運行;統計任務(wù)——計算CPU的利用率。此外,該函數也對uC/OS-II所有的變量和數據結構進(jìn)行初始化。
OSTaskCreate();函數
INT8U OSTaskCreate (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT8U prio)
除了OSInit();函數執行時(shí)為系統建立的2個(gè)基本任務(wù)(優(yōu)先級最低的2個(gè)任務(wù)),uC/OS-II建議用戶(hù)另外保留2個(gè)優(yōu)先級最低的任務(wù)和4個(gè)優(yōu)先級最高的任務(wù),為了將來(lái)的內核升級只用,當然了,其實(shí)也可以不保留。所以,通常來(lái)講,用戶(hù)至少可以建立56個(gè)任務(wù)。
OSTaskCreate();函數有4個(gè)入口參數,其含義分別為:task是指向任務(wù)代碼的指針;p_arg是任務(wù)開(kāi)始執行時(shí),傳遞給任務(wù)的參數的指針;ptos是分配給任務(wù)的堆棧的棧頂指針;prio是分配給任務(wù)的優(yōu)先級。
OSTaskCreateExt();函數
INT8U OSTaskCreateExt (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT8U prio,
INT16U id,
OS_STK *pbos,
INT32U stk_size,
void *pext,
INT16U opt)
OSTaskCreateExt();函數其實(shí)是OSTaskCreate();函數的擴展,前4個(gè)參數的定義用法完全一致,后面5個(gè)入口參數的定義為:id是為要建立的任務(wù)創(chuàng )建一個(gè)特殊標志符,主要是保留為了將來(lái)內核升級使用,當前只要將此參數值設置成和任務(wù)的優(yōu)先級一樣就行;pbos指向任務(wù)堆棧棧底的指針,用于堆棧的檢驗;stk_size用于指定堆棧的容量;pext指向用戶(hù)附加的數據域的指針,用來(lái)擴展任務(wù)的任務(wù)控制塊OS_TCB;opt用于設定OSTaskCreateExt();的選項,指定是否允許堆棧的檢驗,是否將堆棧清0,任務(wù)是否要進(jìn)行浮點(diǎn)操作等。
OSStart();函數
void OSStart (void)
該函數負責從任務(wù)就緒表中找出用戶(hù)建立的優(yōu)先級最高的任務(wù)控制塊,并開(kāi)始執行這個(gè)任務(wù)。調用該函數后,軟件就將控制權交給了uC/OS-II的內核,開(kāi)始運行多任務(wù)。在調用該函數之前,必須先建立一個(gè)任務(wù),否則,應用程序將會(huì )崩潰。
NIOS II上的uC/OS-II移植,其實(shí)就這么簡(jiǎn)單。當然了,如果要不斷的深入進(jìn)去,一定大有學(xué)問(wèn)。
評論