新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > μC/OS II針對(duì)TMS320C32的移植

μC/OS II針對(duì)TMS320C32的移植

作者:錢勇 時(shí)間:2004-07-23 來(lái)源: 收藏
摘  要: OS-mC/OS-II是為數(shù)不多的幾個(gè)源代碼公開的操作系統(tǒng)之一,對(duì)它的學(xué)習(xí)、分析能夠給我們帶來(lái)很多概念上的了解和深入。另外mC/OS-II可以移植到很多CPU芯片,本文介紹了把mC/OS-II移值到TI公司浮點(diǎn)DSP芯片的過(guò)程的一些細(xì)節(jié)問(wèn)題。
關(guān)鍵詞: 操作系統(tǒng);mC/OS-II; DSP;移植

嵌入式操作系統(tǒng)mC/OS II是一個(gè)可移植可裁剪、占先式多任務(wù)OS。大部分源代碼用ANSI C語(yǔ)言編寫,只有很少的一部分用匯編語(yǔ)言編寫,使移植工作簡(jiǎn)化。
下面介紹如何將mC/OS-II移植到TI的浮點(diǎn)DSP芯片TMS320C32上。

TMS320C32 DSP芯片介紹
TMS320C3x是TI公司的浮點(diǎn)DSP芯片。TMS320C32是其中的一款,能工作在60MHz的時(shí)鐘頻率下,指令運(yùn)行速度達(dá)到60 MFLOPS,是性價(jià)比很高的浮點(diǎn)處理器,有著廣泛的應(yīng)用。
TMS320C32芯片的組成:浮點(diǎn)CPU、512字節(jié)RAM、2個(gè)DMA通道、1個(gè)串口、2個(gè)定時(shí)器、固化引導(dǎo)程序,另外還有如下的通用寄存器:8個(gè)40bit的寄存器(R0~R7),可以用來(lái)存放32bit的整數(shù),同時(shí)也可以用來(lái)存放40bit的擴(kuò)展精度的浮點(diǎn)數(shù);8個(gè)32bit的輔助寄存器(AR0~AR7),它們的主要作用是存儲(chǔ)地址、參與各種模式的尋址等,當(dāng)然也可以作為一般的寄存器來(lái)使用;狀態(tài)寄存器ST(含全局中斷使能位)、堆棧寄存器SP、中斷標(biāo)志寄存器IF、中斷使能寄存器IE、I/O標(biāo)志寄存器IOF、數(shù)據(jù)頁(yè)指針寄存器DP(每頁(yè)容量為64K)、索引寄存器IR0、IR1、塊寄存器BK、重復(fù)執(zhí)行寄存器RS(塊起始地址)、RE(塊終止地址)、RC(重復(fù)次數(shù))。
要實(shí)現(xiàn)mC/OS-II向TMS320C32的移植,需要C3x的C編譯器支持,否則無(wú)從下手。我們使用的是TI公司的C編譯器Coder Composer V4.10.36。這個(gè)C編譯器允許嵌入行匯編,另外還具有強(qiáng)大的優(yōu)化C編譯的功能。

移植中所需修改的文件
和CPU相關(guān)的文件主要有四個(gè):C語(yǔ)言文件OS_CPU_C32.C、INCLUDES.H頭文件、頭文件OS_CPU_C32.H和匯編文件OS_CPU_C32.ASM,我們的主要移植工作就是針對(duì)這些文件做一些變動(dòng)。
OS_CPU_C32.H
OS_CPU_C32.H包括typedef、#define定義的CPU相關(guān)信息如下:
#ifndef __OS_CPU_H__
#define __OS_CPU_H__         
typedef  unsigned  char   BOOLEAN;/*布爾量*/
typedef  unsigned  char    INT8U;/* 8位無(wú)符號(hào)數(shù)*/
typedef  signed  char  INT8S;    /* 8位有符號(hào)數(shù)*/
typedef  unsigned  int  INT16U;/* 16位無(wú)符號(hào)數(shù)*/
typedef  signed  int  INT16S; /* 16位有符號(hào)數(shù)*/
typedef  unsigned  long  INT32U;/* 32位無(wú)符號(hào)數(shù)*/
typedef  signed  long  INT32S; /* 32位有符號(hào)數(shù)*/
typedef  float  FP32;/* 32位單精度浮點(diǎn)數(shù) */
typedef  long  double  FP40; /*40位擴(kuò)展精度浮點(diǎn)數(shù)*/
typedef  unsigned  int  OS_STK; /*堆棧入口寬度位32位*/
#define  OS_STK_GROWTH        0 /*堆棧由低地址向高地址增長(zhǎng)*/
#define  OS_CRITICAL_METHOD  1
#if  OS_CRITICAL_METHOD == 1 /*方法一*/
#define  OS_ENTER_CRITICAL() asm ("  AND 0DFFFH,ST ") /*關(guān)全局中斷,進(jìn)入臨界區(qū)*/
#define OS_EXIT_CRITICAL()  asm  ("  OR  02000H,ST ") /*開全局中斷,退出臨界區(qū)*/
#endif
#if  OS_CRITICAL_METHOD == 2  /*方法二*/
/*保存中斷禁止?fàn)顟B(tài)到堆棧,關(guān)全局中斷,進(jìn)入臨界區(qū)*/
#define  OS_ENTER_CRITICAL() { 
asm(" PUSH ST"); 
asm(" AND 0DFFFH,ST");   }
#define  OS_EXIT_CRITICAL()  asm("POP ST")  /* 恢復(fù)中斷禁止?fàn)顟B(tài)*/
#endif
#define  OS_TASK_SW()    asm("TRAP 27")  /*用于任務(wù)切換的軟中斷*/
數(shù)據(jù)類型
由于不同的處理器有不同的字長(zhǎng),所以mC/OS-II的移植包括了一系列的數(shù)據(jù)類型的定義,以確保其可移植性。這里我們定義一些C32以及Code Composer都能識(shí)別、處理的數(shù)據(jù)類型。
C32本質(zhì)上只有4種數(shù)據(jù)類型:32位的無(wú)符號(hào)整數(shù):0_4294967295;32位的有符號(hào)整數(shù):-2147483648_2147483647;32位的浮點(diǎn)單精度浮點(diǎn)數(shù):5.877472e-39_3.4028235e38;40位的擴(kuò)展進(jìn)度浮點(diǎn)數(shù)5.87747175e-39_3.4028236684e38;我們上面定義的8、16位數(shù)實(shí)際上都是32位的。另外C32中,堆棧都是按32位數(shù)據(jù)類型進(jìn)行操作的,所以堆棧數(shù)據(jù)類型OS_STK申明為32位無(wú)符號(hào)整數(shù);
代碼的臨界區(qū)
mC/OS-II在進(jìn)入系統(tǒng)臨界代碼區(qū)之前要關(guān)中斷,避免臨界區(qū)代碼受多任務(wù)或中斷服務(wù)程序的破壞,等到臨界區(qū)代碼執(zhí)行完畢之后,該怎么處理呢?有兩種方案可以供選擇:1)不管關(guān)中斷前中斷使能情況是什么樣子,一律開中斷;2)恢復(fù)關(guān)中斷前中斷使能情況,從一定程度上保證任務(wù)執(zhí)行環(huán)境的完整性。
C32中,狀態(tài)寄存器ST的第13位是全局中斷使能位GIE把該位置0,那么不管什么中斷都不去被響應(yīng),直到臨界區(qū)代碼執(zhí)行完畢為止。(注:C32沒(méi)有不可屏蔽的中斷NMI,對(duì)于別的芯片來(lái)說(shuō),如果有NMI的話,處理辦法就是在這個(gè)中斷服務(wù)程序ISR中對(duì)ST中的GIE位進(jìn)行判斷,如果置0,那么這個(gè)ISR簡(jiǎn)單響應(yīng)一下這個(gè)中斷,大部分處理工作放到GIE置1后馬上去執(zhí)行)。宏OS_ENTER_CRITICAL()把GIE位置0而關(guān)閉所有中斷。
堆棧增長(zhǎng)方向
C32處理器的堆棧是由低地址向高地址遞增,所以O(shè)S_STK_GROWTH應(yīng)該設(shè)置為1;
進(jìn)入任務(wù)切換函數(shù)OS_TASK_SW()的定義
mC/OS-II中,進(jìn)入任務(wù)切換是用函數(shù)OS_TASK_SW()來(lái)實(shí)現(xiàn)的。這個(gè)函數(shù)通過(guò)軟中斷模擬了一次中斷過(guò)程,在這個(gè)中斷服務(wù)程序ISR中實(shí)現(xiàn)任務(wù)的切換,切換的具體實(shí)現(xiàn)在介紹任務(wù)切換函數(shù)OSCtxSw()時(shí)詳細(xì)闡述。C32共有28個(gè)軟中斷可供使用,通過(guò)執(zhí)行匯編指令 TRAP 0、TRAP 1……TRAP27來(lái)產(chǎn)生軟中斷,也稱為TRAP陷阱調(diào)用。這里,我們選擇編號(hào)為27的軟中斷作為進(jìn)入任務(wù)切換的中斷:
#define  OS_TASK_SW()    asm("TRAP 27")
還要注意的一點(diǎn)是這個(gè)中斷服務(wù)程序的入口必須指向函數(shù)OSCtxSw()。
INCLUDES.H文件
INCLUDES.H是主要的頭文件,在大多數(shù).C文件的開始都包含INCLUDES.H文件。不同處理器、不同編譯器、不同庫(kù)文件,需要修改INCLUDES.H文件,刪除不使用的頭文件,添加自己的頭文件。而且,頭文件之間有包含關(guān)系、條件編譯的,一定要排好他們之間的先后順序。,INCLUDES.H文件修改如下:
#ifndef __INCLUDES_H__
#define __INCLUDES_H__       
#include    "OS_CFG.H"
#include    "OS_CPU.H"
#include    "mCOS_II.H"
#include    "C32.H"
#endif
其中C32.H文件包含了4個(gè)頭文件:
#include "Timerdef.H"
#include "SerialPort.H"
#include "Dma.H"
#include "Bus.H"
分別對(duì)C32的定時(shí)器、串口、DMA通道、總線編程用到的數(shù)據(jù)結(jié)構(gòu)進(jìn)行定義。
OS_CPU_C32.ASM文件
本來(lái),這個(gè)匯編文件里面要實(shí)現(xiàn)4個(gè)函數(shù):多任務(wù)啟動(dòng)函數(shù)中調(diào)用的OSStartHighRdy()、中斷任務(wù)切換函數(shù)OSIntCtxSw()、任務(wù)切換函數(shù)OSCtxSw()、時(shí)鐘節(jié)拍服務(wù)函數(shù)OSTickISR();但是這里只實(shí)現(xiàn)后兩個(gè)函數(shù)。前兩個(gè)函數(shù)在OS_CPU_C32.C中實(shí)現(xiàn)。
任務(wù)切換函數(shù):OSCtxSw()
該函數(shù)由任務(wù)切換函數(shù)OS_TASK_SW()進(jìn)入,與中斷程序中調(diào)用的OSIntCtxSw()不同。mC/OS-II中,如果任務(wù)執(zhí)行了某個(gè)函數(shù),其結(jié)果改變了當(dāng)前任務(wù)的狀態(tài)(如OSTaskSuspend()、OSTimeDly())、或者是改變了別的任務(wù)的狀態(tài)(OSTaskResume()、OSTimeDlyResume())都要引起新的任務(wù)調(diào)度:OSSched();在任務(wù)調(diào)度函數(shù)找出新任務(wù)將其控制塊地址放到OSTCBHigRdy后,執(zhí)行OS_TASK_SW()。任務(wù)切換流程:1)硬件進(jìn)入中斷處理:全局中斷使能位置0、返回地址壓棧。2)寄存器值壓入當(dāng)前任務(wù)堆棧;3)修改當(dāng)前任務(wù)控制塊指針OSTCBCur和當(dāng)前任務(wù)優(yōu)先級(jí)OSPrioCur;4)恢復(fù)任務(wù)堆棧中的值到寄存器中;5)執(zhí)行當(dāng)前任務(wù),由RETI指令完成。
時(shí)鐘節(jié)拍函數(shù):OSTickISR()
mC/OS-II中,時(shí)鐘節(jié)拍中斷是一個(gè)非常重要的中斷,因?yàn)檎麄€(gè)操作系統(tǒng)的活動(dòng)都受到它的激勵(lì)。
OSTickISR()的執(zhí)行流程:1)硬件進(jìn)入中斷處理,同上;2)保護(hù)上下文環(huán)境;3)調(diào)用OSIntEnter(),記錄中斷嵌套層數(shù);4)調(diào)用OSTimeTick(),檢查處理各個(gè)任務(wù)的延時(shí),并根據(jù)情況修改就緒任務(wù)表;5)調(diào)用OSIntExit(),檢查就緒任務(wù)表,看是否有比當(dāng)前任務(wù)優(yōu)先級(jí)更高的任務(wù)就緒,如果有,則進(jìn)行調(diào)度;如果沒(méi)有,OSIntExit()返回并恢復(fù)2)所保存的上下文環(huán)境,并執(zhí)行RETI回到被中斷的那個(gè)任務(wù)里繼續(xù)運(yùn)行;如果有,那么OSIntExit()就不返回到這里,具體的情況后面介紹OSIntExit()時(shí)具體闡述。
OS_CPU_C32.C文件
這個(gè)文件里,主要實(shí)現(xiàn)3個(gè)函數(shù):堆棧初始化函數(shù)OSTaskInit()、中斷任務(wù)切換函數(shù)OSIntCtxSw()、多任務(wù)啟動(dòng)函數(shù)中調(diào)用的OSStartHighRdy(),另外還有5個(gè)擴(kuò)展外掛函數(shù):
void    OSTaskCreateHook(OS_TCB ptcb){} /*任務(wù)創(chuàng)建擴(kuò)展外掛函數(shù)*/
void    OSTaskSwHook(void){} /*任務(wù)切換擴(kuò)展外掛函數(shù)*/
void    OSTaskDelHook(OS_TCB *ptcb){}  /*任務(wù)刪除擴(kuò)展外掛函數(shù)*/
void    OSTaskStatHook(void){} /*統(tǒng)計(jì)任務(wù)擴(kuò)展外掛函數(shù)*/
void    OSTimeTickHook(void){}  /*時(shí)鐘節(jié)拍創(chuàng)建擴(kuò)展外掛函數(shù)*/
這幾個(gè)函數(shù)我們這里都處理為空函數(shù),而且還可以通過(guò)在文件OS_CFG.H中設(shè)置OS_CPU_HOOKS_EN為0而不使用這些函數(shù)。我們主要來(lái)討論前三個(gè)函數(shù):
堆棧初始化函數(shù)OSTaskInit()
堆棧初始化函數(shù)OSTaskInit()是由任務(wù)創(chuàng)建函數(shù)OSTaskCreate()或OSTaskCreateExt()來(lái)調(diào)用,用來(lái)初始化任務(wù)堆棧。初始化后的堆棧保存著任務(wù)第一次執(zhí)行時(shí)的上下文環(huán)境,它和中斷后的堆棧神似!這個(gè)函數(shù)最關(guān)鍵的兩個(gè)參數(shù)就是任務(wù)的起始地址void(* task)(void *pd)和任務(wù)使用堆棧的棧頂指針void *ptos;需要注意的是,任務(wù)第一次執(zhí)行時(shí)的某些全局寄存器的值有特殊要求:1)狀態(tài)寄存器ST的初始值必須保證中斷全局使能位GIE為1,從而保證時(shí)鐘節(jié)拍中斷不會(huì)長(zhǎng)時(shí)間被屏蔽,這里我選擇初值為0x2000;2)頁(yè)指針寄存器DP的初始值:如果你選擇Small-Memory模式進(jìn)行編譯時(shí),那么它的初始值應(yīng)該和建立C環(huán)境時(shí)對(duì)DP的初始化值一樣;否則就不要對(duì)這個(gè)DP寄存器進(jìn)行任何保護(hù)處理,這樣也可以的,不過(guò)這樣的話,別的函數(shù)也就要做相應(yīng)的改動(dòng)了;如果選擇了Large-Memory模式的話,那么這個(gè)值的初始化就可以不進(jìn)行了,因?yàn)榫幾g系統(tǒng)在編譯時(shí)會(huì)自動(dòng)插入更新DP的指令的;
中斷任務(wù)級(jí)切換函數(shù)OSIntCtxSw()
mC/OS-II中,中斷的產(chǎn)生可能會(huì)引起任務(wù)的切換,在中斷服務(wù)程序的最后會(huì)調(diào)用OSIntExit()檢查任務(wù)就緒狀態(tài)。如果需要進(jìn)行任務(wù)切換,將調(diào)用OSIntCtxSw()。所以O(shè)SIntCtxSw()又稱為中斷級(jí)的任務(wù)切換函數(shù)。需要注意的是,任何中斷服務(wù)程序ISR前面都要像時(shí)鐘節(jié)拍函數(shù):OSTickISR()流程的第2步那樣保存上下文環(huán)境。OSIntCtxSw()和OSCtxSw()的后半部分幾乎相同,不同之處是:對(duì)當(dāng)前任務(wù)的堆棧指針進(jìn)行調(diào)整!其代碼如下:
asm(" SUBI 5,SP ");
asm(" LDI @_OSTCBCur,AR0 ");
asm(" STI SP,*AR0  ");
這里我們把堆棧指針SP減去5,就是調(diào)整的結(jié)果。
下面我們來(lái)分析一下這個(gè)“5”是怎么得來(lái)的(我們針對(duì)時(shí)鐘節(jié)拍中斷ISR來(lái)進(jìn)行說(shuō)明):
任務(wù)調(diào)用函數(shù)OSIntExit()之前時(shí),當(dāng)前任務(wù)堆棧的情況如圖1所示;調(diào)用OSIntExit函數(shù)后,當(dāng)前任務(wù)堆棧的情況如圖2所示,為什么會(huì)這樣呢?我們可以看看OSIntExit()函數(shù)編譯后的匯編文件就明白了,這個(gè)函數(shù)入口的地方有如下幾條語(yǔ)句:
_OSIntExit:
        push      fp
        ldiu      sp,fp
        push      ar4
然后該函數(shù)又調(diào)用中斷切換函數(shù)OSIntCtxSw(),這時(shí)當(dāng)前任務(wù)堆棧的的情況如圖3所示;OSIntCtxSw()編譯后的匯編文件入口的地方有如下幾條語(yǔ)句:
_OSIntCtxSw:
        push      fp
由此可見,當(dāng)別的任務(wù)需要調(diào)度時(shí),當(dāng)前任務(wù)需要把自己的堆棧指針SP調(diào)整到調(diào)用OSIntExit()之前的值,從圖上可以看出只要把當(dāng)前任務(wù)的堆棧指針的值減去“5”便可。
多任務(wù)啟動(dòng)函數(shù)中調(diào)用的OSStartHighRdy()
這個(gè)函數(shù)只在多任務(wù)啟動(dòng)函數(shù)中調(diào)用一次,主要用來(lái)把多任務(wù)啟動(dòng)時(shí)優(yōu)先級(jí)最高的就緒任務(wù)的上下文環(huán)境從堆?;謴?fù)過(guò)來(lái)。mC/OS-II系統(tǒng)啟動(dòng)時(shí)至少創(chuàng)建了一個(gè)任務(wù)__空閑任務(wù),實(shí)際應(yīng)用當(dāng)然還要?jiǎng)?chuàng)建別的用戶任務(wù)。OSStart()首先從這些任務(wù)中查找出優(yōu)先級(jí)最高的就緒任務(wù),并把它的任務(wù)控制塊的地址賦給OSTCBHighRdy,然后調(diào)用OSStartHighRdy()來(lái)運(yùn)行OSTCBHighRdy指向的那個(gè)任務(wù)控制塊所對(duì)應(yīng)的任務(wù)。其流程是:1)該函數(shù)的返回地址壓入堆棧,注意,這里提到的堆棧是mC/OS-II系統(tǒng)使用的堆棧,而與其他任何任務(wù)使用的堆棧沒(méi)有任何關(guān)系,而且這個(gè)地址壓不壓棧意義已經(jīng)不大,因?yàn)椴豢赡茉購(gòu)姆祷剡@里返回回去;2)變量OSRunning賦值為True,標(biāo)志多任務(wù)已經(jīng)啟動(dòng);3)從任務(wù)初始化過(guò)的堆棧中恢復(fù)上下文環(huán)境,代碼和上面的雷同;4)執(zhí)行RETI指令運(yùn)行這個(gè)任務(wù);

兩點(diǎn)補(bǔ)充說(shuō)明
編譯器的編譯選項(xiàng)
在移植過(guò)程中,除了要熟悉mC/OS-II內(nèi)核原理和目標(biāo)芯片之外,還要熟悉C編譯器提供編譯選項(xiàng)的使用。使用不當(dāng),會(huì)給移植帶來(lái)大麻煩。上面在介紹堆棧初始化函數(shù)也提及到有關(guān)DP寄存器的處理問(wèn)題上,就是涉及到編譯選項(xiàng)的選擇:是選用Small-Memory還是Large-Memory模式,這個(gè)取決于編譯選項(xiàng)-mb的打開和關(guān)閉。
中斷函數(shù)的編寫
在分析函數(shù)OSIntCtxSw()時(shí),我們提到分析的結(jié)果是針對(duì)時(shí)鐘中斷服務(wù)函數(shù)得出的。大家應(yīng)該注意到這個(gè)函數(shù)是匯編語(yǔ)言寫的,那么我們寫別的中斷處理程序時(shí),如果用C語(yǔ)言來(lái)寫的話,要不要注意些什么呢?回答是肯定的。因?yàn)榫幾g器在處理C語(yǔ)言寫的中斷服務(wù)函數(shù)時(shí)會(huì)作出一些特殊的處理:在這個(gè)ISR入口處插入壓棧指令,把部分全局寄存器的值壓入堆棧,具體哪些,因函數(shù)的不同而有所不同。這就干擾了我們保存上下文環(huán)境的工作,如果不進(jìn)行處理,任務(wù)調(diào)度時(shí)會(huì)出現(xiàn)難以想象的問(wèn)題。解決這個(gè)問(wèn)題的辦法就是要讓C編譯器認(rèn)為這個(gè)中斷服務(wù)函數(shù)是一般的函數(shù),那么它就不會(huì)在函數(shù)入口處插入一系列的壓棧指令。C編譯器Code Composer規(guī)定,凡是函數(shù)名為c_intnm(其中n、m是小于9正整數(shù))的函數(shù)都是中斷函數(shù),在編譯這些函數(shù)時(shí)都作出特殊的處理,為此,我們避免為中斷處理函數(shù)取這樣的名字就可以了。
但是,編譯在處理一般函數(shù)時(shí)還是要做一定的處理的,譬如:
        push      fp
        ldiu      sp,fp
        push      ar4
為此,我們仔細(xì)分析編譯器編譯生成的匯編代碼,并對(duì)堆棧作相應(yīng)的調(diào)整。然后在函數(shù)結(jié)束的地方嵌入asm(" RETI");語(yǔ)句結(jié)束。這樣中斷程序就可以正確執(zhí)行。
它的框架如下:
void Int0ISR(void)
{
asm(" SUBI N,SP") /*這個(gè)N的值要看具體的程序來(lái)定,一般等于1或2*/

asm(" RETI")   /*用自己的返回指令返回*/
}
結(jié)語(yǔ)
在移植和運(yùn)行的mC/OS-II過(guò)程中,也許還會(huì)有新的問(wèn)題出現(xiàn),遇到問(wèn)題時(shí)要仔細(xì)分析,分析堆棧的使用、中斷的影響,分析編譯器生成的匯編代碼,就可以解決這些問(wèn)題,從而實(shí)現(xiàn)mC/OS-II的可靠運(yùn)行?!?

參考文獻(xiàn)
1. ‘mC/OS-II——源碼公開的實(shí)時(shí)嵌入式操作系統(tǒng)’,邵貝貝譯,中國(guó)電力出版社 2001.
2. ‘TMS320C3x/4x Optimizing C Compiler User’s Guide’ TEXAS INSTRUMENTS.
3. ‘單片機(jī)與嵌入式系統(tǒng)應(yīng)用’雜志,2001  NO.12.
linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)


關(guān)鍵詞: 嵌入式

評(píng)論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉