我將uCOS-II 移植到了EPONS 的C33209的平台上,接下來我就基於我移植好的代碼講解如何將uCOS-II從一種MCU移植到另一種MCU。
首先介紹uCOS-II的文件,如下表:
ucos_ii.h
os_cfg.h
os_cpu.h
os_core.c
os_dbg_r.c
os_flag.c
os_mbox.c
os_mem.c
os_mutex.c
os_q.c
os_sem.c
os_task.c
os_time.c
ucos_ii.c
os_cpu_c.c
os_cpu_a.asm
其中我們和硬件平台相關的文件的文件名被加粗了,也就是說若要將uCOS-II移植到新的平台上只要關心以上四個文件就行了。當然你也可以根據需要再添加你自己的和平台相關的文件,事實上我也是這麼做的。在我移植的例子中就添加了四個和平台相關的文件,文件如下表:
crt0.c
drv_rtc.c
vector.c
ext.s
crt0.c是用來初始化系統的比如說MCU的一些特殊寄存器、設置外圍的總線接口,等。drv_rtc.c是用來初始化系統中的一個RTC的,這個RTC可以為內核提供必要的基於時間片調度的時基。同時提供了對RTC開始和停止的操作函數。在我的例子中RTC會每秒產生32次中斷。vector.c顧名思義,它是系統上電後為系統提供矢量入口表的文件,當然也包括中斷向量表。ext.s是為uc/OS-II提供OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()函數的具體實現以及在用戶程序的中斷函數出入時要調用的狀態保護和狀態恢復函數OS_SAVEALL ()和OS_RESTOREALL ()。前面兩個函數的功能是:OS_ENTER_CRITICAL()屏蔽中斷;OS_EXIT_CRITICAL()恢復原來的中斷使能狀態。
1. os_cpu_a.asm的說明
要想順利的移植首先要了解uCOS-II的一些基本概念。
uCOS-II實質上是一個嵌入式操作系統內核,她只負責管理各個任務,為每個任務分配CPU時間,並且負責任務之間的通訊。內核提供的基本服務是任務切換。這是個很重要的概念,可以說你只要掌握了任務切換的本質,可以說你就掌握了移植uCOS-II的技術。至於任務之間的通訊他們是建立在任務切換之上的或者說和系統平台關系不大(當然這也和操作系統通訊機制的實現相關,至少uCOS-II是這樣的)。
接下來我們就有針對性的介紹什麼是uCOS-II裡的任務。一個任務通常是一個無限循環,如下程序所示。
void Task1(void *data)
{
INT8U err;
char *rxmsg;
data = data; /* Prevent compiler warning */
while(1) //這是一個無限循環
{
rxmsg = (char *)OSMboxPend(MAIL1, 0, &err); /* Wait for message from Task #2 */
OSTimeDlyHMSM(0, 0, 1, 0); /* Wait 1 second */
OSMboxPend(MAIL3, 0, &err); /* Wait for message from Task #3*/
OSMboxPost(MAIL2, (void *)1); /* Acknowledge reception of msg*/
}
}
可以通過內核的專用函數來建立、刪除、掛起、激活任務,在這裡我們的重點在如何移植,所以具體的使用方式和原理可以看JEAN J.LABROSSE 著、邵貝貝譯的《uCOS-II:源碼公開的實時嵌入式操作系統》一書(下載見 http://www.linuxidc.com/Linux/2012-07/66057.htm )。
(1). OSCtxSw()函數
在上面的例子裡你也看到了任務和其他的C函數一樣,有函數的返回類型,有形式參數變量,只是任務是絕不會返回。事實上任務也就是一個函數,內核在調度時是以這個函數為基礎的,為了和其他函數區分,我們給了她另外一個名字——任務。也正因她是一個特殊的函數,而且和內核調度直接相關,所以不能隨便返回和被用戶調用,而要用內核的專用函數來“建立”和“刪除”。所謂的“建立任務”其實是在內核處對該函數進行注冊和相關數據結構的填充,比如該函數的入口地址、為函數分配專門的堆棧空間(為什麼要為函數分配專門的地址空間呢?我們馬上就會談到)。“任務調度”就是根據情況(比如時間片被用完),來調用另一個被稱為任務的函數(我們暫時稱之為函數TA),同時停止當前的一個任務(其實也是一個函數,我們稱之為TB)。問題出來了,若內核象普通函數那樣直接調用TA,那麼當內核要重新調用TB時怎麼知道剛才TB執行到哪裡了呢?若內核為TA和TB分配專用的兩塊空間,當內核要調用其他任務(其實就是函數)的時候先將當前任務(函數)運行的地址和狀態保存起來,然後當要返回前再恢復,當然每個被稱之為任務的函數都要有自己獨立的保存運行地址和狀態的空間,以免混亂。那問題就很好解決了。這也就是為什麼任務都有自己的堆棧空間的原因。