《電子技術(shù)應(yīng)用》
您所在的位置:首頁(yè) > 嵌入式技術(shù) > 設(shè)計(jì)應(yīng)用 > LPC21XX移植UCOS-II小結(jié)
LPC21XX移植UCOS-II小結(jié)
摘要: 文章標(biāo)題:LPC21XX移植UCOS-II小結(jié),。中國(guó)IT實(shí)驗(yàn)室嵌入式開(kāi)發(fā)頻道提供最全面的嵌入式開(kāi)發(fā)培訓(xùn)及行業(yè)的信息、技術(shù)以及相關(guān)資料的下載.
Abstract:
Key words :

    1.在uC/OS-II的幫助手冊(cè)內(nèi),,作者特地強(qiáng)調(diào)絕對(duì)不能在OSInit()或者OSStart()內(nèi)

    調(diào)用Timer初始化程序,,那會(huì)破壞系統(tǒng)的可移植性同時(shí)帶來(lái)性能上的損失。

    所以,,一個(gè)折中的辦法就是:

    在優(yōu)先級(jí)最高的程序內(nèi)調(diào)用,,這樣可以保證當(dāng)OSStart()調(diào)用系統(tǒng)內(nèi)部函數(shù)

    OSStartHighRdy()開(kāi)始多任務(wù)后,首先執(zhí)行的就是Timer初始化程序,?;蛘?/p>

    專(zhuān)門(mén)開(kāi)一個(gè)優(yōu)先級(jí)最高的任務(wù),只做一件事情,,那就是執(zhí)行 Timer初始化,,

    之后通過(guò)調(diào)用OSTaskSuspend()將自己掛起來(lái),永遠(yuǎn)不再執(zhí)行,。不過(guò)這樣會(huì)

    浪費(fèi)一個(gè)TCB空間,。對(duì)于那些RAM吃緊的系統(tǒng)來(lái)說(shuō),還是不用為好,。

    2.(三) 一些重要的uC/OS-II API介紹

    任何一個(gè)操作系統(tǒng)都會(huì)提供大量的API供程序員使用,,uC/OS-II也不例外。由于uC/OS-II面向

    的是嵌入式開(kāi)發(fā),,并不要求大而全,,所以?xún)?nèi)核提供的API也就大多和多任務(wù)息息相關(guān)。

    主要的有以下幾類(lèi):

    1)任務(wù)類(lèi)

    2)消息類(lèi)

    3)同步類(lèi)

    4)時(shí)間類(lèi)

    5)臨界區(qū)與事件類(lèi)

    我個(gè)人認(rèn)為對(duì)于初級(jí)程序員而言,,任務(wù)類(lèi)和時(shí)間類(lèi)是必須要首先掌握的兩種類(lèi)型的API.

    下面我就來(lái)介紹比較重要的:

    1)OSTaskCreate函數(shù)

    這個(gè)函數(shù)應(yīng)該至少再main函數(shù)內(nèi)調(diào)用一次,,在OSInit函數(shù)調(diào)用之后調(diào)用。作用就是創(chuàng)建

    一個(gè)任務(wù),。目前有四個(gè)參數(shù),,分別是任務(wù)的入口地址,任務(wù)的參數(shù),, 任務(wù)堆棧的首地址和

    任務(wù)的優(yōu)先級(jí),。調(diào)用本函數(shù)后,系統(tǒng)會(huì)首先從TCB空閑列表內(nèi)申請(qǐng)一個(gè)空的TCB指針,,然后

    將會(huì)根據(jù)用戶給出參數(shù)初始化任務(wù)堆棧,,并在內(nèi)部的任務(wù)就緒表內(nèi)標(biāo)記該任務(wù)為就緒狀態(tài)。

    最后返回,,這樣一個(gè)任務(wù)就創(chuàng)建成功了,。

    2)OSTaskSuspend函數(shù)

    這個(gè)函數(shù)很簡(jiǎn)單,一看名字就該明白它的作用,,它可以將指定的任務(wù)掛起,。如果掛起的是

    當(dāng)前任務(wù)的話,那么還會(huì)引發(fā)系統(tǒng)執(zhí)行任務(wù)切換先導(dǎo)函數(shù)OSShed來(lái)進(jìn)行一次任務(wù)切換,。

    這個(gè)函數(shù)只有一個(gè)參數(shù),,那就是指定任務(wù)的優(yōu)先級(jí)。那為什么是優(yōu)先級(jí)呢,?事實(shí)上在系統(tǒng)

    內(nèi)部,,優(yōu)先級(jí)除了表示一個(gè)任務(wù)執(zhí)行的先后次序外,還起著分別每一個(gè)任務(wù)的作用,,換句話

    說(shuō),,優(yōu)先級(jí)也就是任務(wù)的ID.所以u(píng)C/OS-II不允許出現(xiàn)相同優(yōu)先級(jí)的任務(wù)。

    3)OSTaskResume函數(shù)

    這個(gè)函數(shù)和上面的函數(shù)正好相反,,它用于將指定的已經(jīng)掛起的函數(shù)恢復(fù)成就緒狀態(tài),。如果

    恢復(fù)任務(wù)的優(yōu)先級(jí)高于當(dāng)前任務(wù),那么還為引發(fā)一次任務(wù)切換,。其參數(shù)類(lèi)似 OSTaskSuspend

    函數(shù),,為指定任務(wù)的優(yōu)先級(jí)。需要特別說(shuō)明是,,本函數(shù)并不要求和OSTaskSuspend函數(shù)成對(duì)使

    用,。

    4)OS_ENTER_CRITICAL宏

    很多人都以為它是個(gè)函數(shù),其實(shí)不然,,仔細(xì)分析一下OS_CPU.H文件,,它和下面馬上要談到的

    OS_EXIT_CRITICAL都是宏。他們都是涉及特定 CPU的實(shí)現(xiàn)。一般都被替換為一條或者幾條

    嵌入式匯編代碼,。由于系統(tǒng)希望向上層程序員隱藏內(nèi)部實(shí)現(xiàn),,故而一般都宣稱(chēng)執(zhí)行此條指

    令后系統(tǒng)進(jìn)入臨界區(qū)。其實(shí),, 它就是關(guān)個(gè)中斷而已,。這樣,只要任務(wù)不主動(dòng)放棄CPU使用權(quán),,

    別的任務(wù)就沒(méi)有占用CPU的機(jī)會(huì)了,,相對(duì)這個(gè)任務(wù)而言,它就是獨(dú)占了,。所以說(shuō)進(jìn)入臨界區(qū)了,。

    這個(gè)宏能少用還是少用,因?yàn)樗鼤?huì)破壞系統(tǒng)的一些服務(wù),,尤其是時(shí)間服務(wù),。并使系統(tǒng)對(duì)外界響

    應(yīng)性能降低。

    5)OS_EXIT_CRITICAL宏

    這個(gè)是和上面介紹的宏配套使用另一個(gè)宏,,它在系統(tǒng)手冊(cè)里的說(shuō)明是退出臨界區(qū),。其實(shí)它就

    是重新開(kāi)中斷。需要注意的是,,它必須和上面的宏成對(duì)出現(xiàn),,否則會(huì)帶來(lái)意想不到的后果。

    最壞的情況下,,系統(tǒng)會(huì)崩潰,。我們推薦程序員們盡量少使用這兩個(gè)宏調(diào)用,因?yàn)樗麄兊拇_會(huì)

    破壞系統(tǒng)的多任務(wù)性能,。

 

 

    6)OSTimeDly函數(shù)

    這應(yīng)該程序員們調(diào)用最多的一個(gè)函數(shù)了,,這個(gè)函數(shù)完成功能很簡(jiǎn)單,就是先掛起當(dāng)起當(dāng)前任務(wù),,

    然后進(jìn)行任務(wù)切換,,在指定的時(shí)間到來(lái)之后,將當(dāng)前任務(wù)恢復(fù)為就緒狀態(tài),,但是并不一定運(yùn)行,,

    如果恢復(fù)后是優(yōu)先級(jí)最高就緒任務(wù)的話,那么運(yùn)行之,。簡(jiǎn)單點(diǎn)說(shuō),,就是可以任務(wù)延時(shí)一定時(shí)間

    后再次執(zhí)行它,或者說(shuō),,暫時(shí)放棄CPU的使用權(quán),。一個(gè)任務(wù)可以不顯式的調(diào)用這些可以導(dǎo)致放棄CPU使用權(quán)的API,但那樣多任務(wù)性能會(huì)大大降低,因?yàn)榇藭r(shí)僅僅依靠時(shí)鐘機(jī)制在進(jìn)行任務(wù)切換,。一個(gè)好的任務(wù)應(yīng)該在完成一些操作主動(dòng)放棄使用權(quán),,好東西要大家分享嘛!

    3.我們推薦程序員們盡量少使用OS_ENTER_CRITICAL宏和 OS_EXIT_CRITICAL宏兩個(gè)宏調(diào)用,,

    因?yàn)樗麄兊拇_會(huì)破壞系統(tǒng)的多任務(wù)性能,。why,?,?

    4.在以u(píng)C/OS為操作系統(tǒng)的項(xiàng)目中,系統(tǒng)可能要處理各種不同的中斷請(qǐng)求,,如果某個(gè)中斷處理

    程序需要調(diào)用uC/OS的各種Post函數(shù)向任務(wù)發(fā)出消息,,那么uC/OS建議中斷服務(wù)程序的寫(xiě)法是:

    1、保存全部CPU寄存器

    2,、調(diào)用OSIntEnter或OSIntNesting直接加1

    3,、執(zhí)行用戶代碼做中斷服務(wù)

    4、調(diào)用OSIntExit

    5,、恢復(fù)所有CPU寄存器

    6,、執(zhí)行中斷返回指令

    暫且稱(chēng)為“標(biāo)準(zhǔn)中斷”方式,這種方式實(shí)際上是將這個(gè)中斷處理加入了任務(wù)調(diào)度系統(tǒng),,也就是

    說(shuō)這個(gè)中斷可以引起任務(wù)的切換,。

    如果在中斷處理中沒(méi)有調(diào)用各種Post函數(shù)的話,則可以用一般的,、象原來(lái)沒(méi)有操作系統(tǒng)時(shí)的

    寫(xiě)法:

    1,、保存中斷處理程序需要用到的CPU寄存器

    2、執(zhí)行中斷處理

    3,、恢復(fù)保存了的CPU寄存器

    4,、執(zhí)行中斷返回指令

    暫且稱(chēng)為“快中斷”方式,按照這種方法定義的中斷永遠(yuǎn)不會(huì)引起任務(wù)切換,。

    在uC/OS系統(tǒng)中,,每個(gè)任務(wù)都要定義獨(dú)立的棧空間,,一個(gè)??臻g的使用包括5個(gè)部分:

    1、任務(wù)包括的各個(gè)函數(shù)的調(diào)用返回地址

    2,、任務(wù)包括的各個(gè)函數(shù)中可能在棧上分配的局部變量

    3,、發(fā)生了“標(biāo)準(zhǔn)中斷”方式定義的中斷或任務(wù)被掛起時(shí),所要保存的任務(wù)上下文

    4,、發(fā)生了“快中斷”方式定義的中斷時(shí),,中斷處理程序所需要的棧空間

    5、中斷嵌套時(shí),,所要保存的中斷嵌套上下文

    在這些使用的部分中,,1,2,,3,,4的內(nèi)存占用量是比較容易估算的,最精確和保險(xiǎn)的確定

    方法是:查看由C生成的asm文件,,并計(jì)算各個(gè)函數(shù)的棧使用量,。但是第5部分的棧空間使用

    量是隨中斷嵌套的深度而不斷增加的,,是不確定的,,一般的方法只能定義一個(gè)充分大的棧

    空間,使之不會(huì)溢出,。

    為每個(gè)任務(wù)都定義一個(gè)充分大的??臻g,這在某些內(nèi)存稀缺的小項(xiàng)目中是非常痛苦的,,

    有時(shí)不得不增擴(kuò)內(nèi)存,,這就會(huì)使成本增加。

    我深入研究了uC/OS后,,認(rèn)為,,可以將所有任務(wù)棧空間使用的第5部分合并,,這樣將會(huì)大大的

    降低整個(gè)系統(tǒng)對(duì)內(nèi)存的需求,。

    uC/OS的任務(wù)調(diào)度是靠OS_Sched和 OSIntExit來(lái)完成的,這兩個(gè)函數(shù)中都要先判斷一個(gè)叫

    OSIntNesting的系統(tǒng)變量,,如果OSIntNesting不為0,,則不進(jìn)行任務(wù)切換。也就是說(shuō):

    在OSIntNesting為1(當(dāng)前只有一個(gè)中斷在處理中,,并且沒(méi)有嵌套的中斷)時(shí)起,,

    如果發(fā)生了嵌套的中斷(不管嵌套的層數(shù)有深),那么在所有嵌套的中斷一層一層地都返回

    直到 OSIntNesting再次為1時(shí)止,,任務(wù)棧是不會(huì)切換的(棧指針都在一個(gè)任務(wù)的??臻g中變

    化)。

    據(jù)此,,我們可以這樣改動(dòng):設(shè)置一個(gè)緩沖區(qū)OSInterruptStk,,作為嵌套中斷的棧空間

    由所有任務(wù)共享,,中斷服務(wù)程序改為:

    1,、保存全部CPU寄存器

    2,、調(diào)用OSIntEnter或OSIntNesting直接加1

    增加:2.1、判斷OSIntNesting是否等于1,,如果不是則轉(zhuǎn)到3

    增加:2.2,、將棧指針SP保存到OSTCBCur->OSTCBStkPtr

    增加:2.3、將SP指向OSInterruptStk的棧頂(注意棧增長(zhǎng)的方向),。

    3,、執(zhí)行用戶代碼做中斷服務(wù)

    4、調(diào)用OSIntExit

    增加:4.1,、判斷OSIntNesting是否等于0,,如果不是則轉(zhuǎn)到5

    增加:4.2、從OSTCBCur->OSTCBStkPtr中恢復(fù)棧指針SP

    5,、恢復(fù)所有CPU寄存器

    6,、執(zhí)行中斷返回指令

 

    并且要修改OSIntCtxSw函數(shù),,原始的OSIntCtxSw函數(shù)的寫(xiě)法是:

    1,、調(diào)整棧指針來(lái)去掉在調(diào)用:OSIntExit,OSIntCtxSw過(guò)程中入棧的多余內(nèi)容

    2,、將當(dāng)前任務(wù)棧指針保存到OSTCBCur中(OSTCBCur->OSTCBStkPtr = __SP__)

    3,、如果需要?jiǎng)t調(diào)用OSTaskSwHook

    4、OSTCBCur = OSTCBHighRdy

    5,、OSPrio = OSPrioHighRdy

    6,、從OSTCBCur中恢復(fù)棧指針(__SP__ = OSTCBCur->OSTCBStkPtr)

    7、恢復(fù)保存了的CPU寄存器

    8,、執(zhí)行中斷返回指令

    新的寫(xiě)法只需將原寫(xiě)法中的1,,2去掉即可,因?yàn)?,,2步只是保存舊任務(wù)的棧指針,,而新的寫(xiě)

    法中,這些步被移到了“中斷服務(wù)程序”中的2.2.

    5.注意arm Image for uCOSII for lpc213x 模板中的TargetInit()

    對(duì)于很多使用ZLG arm Image for uCOSII for lpc213x 模板的初學(xué)者,,常常會(huì)置疑使用該模板

    后自動(dòng)生成的target.c文件,,和在程序中調(diào)用的TargetInit()函數(shù),我和 Zgpswh都是如此,,這

    個(gè)問(wèn)題當(dāng)初困擾了很久:當(dāng)用戶程序中不調(diào)用TargetInit()時(shí),,發(fā)現(xiàn)內(nèi)核能運(yùn)行,但是等待機(jī)制

    失靈,,調(diào)用 TargetInit(),,很多硬件中斷打不開(kāi),后來(lái),,在很多熱心人的指點(diǎn)下解決了,,現(xiàn)重新

    總結(jié)如下:

    請(qǐng)仔細(xì)察看ZLG模板里的target.c文件,,這里的TargetInit()如下:

    void TargetInit(void)

    {

    OS_ENTER_CRITICAL();

    srand((uint32)TargetInit),;

    VICInit(),;

    Timer0Init();

    OS_EXIT_CRITICAL(),;

    }

    其中的Timer0Init(),;用于硬件定時(shí)器0的初始化,事實(shí)上,,ZLG的移植代碼的μC/OS-Ⅱ的時(shí)鐘節(jié)拍是

    通過(guò)定時(shí)器0提供的,,不在主程序里調(diào)用這個(gè)函數(shù),μC/OS-Ⅱ的時(shí)鐘源就無(wú)法打開(kāi),;但是,,沒(méi)有開(kāi)啟

    時(shí)鐘源的μC/OS-Ⅱ是同樣能運(yùn)行的,只是內(nèi)核提供的延時(shí)和等待時(shí)限機(jī)制都不起作用,,系統(tǒng)雖能將

    就運(yùn)行,,但因沒(méi)調(diào)用TargetInit()而使內(nèi)核功能不健全。

    請(qǐng)注意,,TargetInit()中的另一個(gè)函數(shù)VICInit ()是用來(lái)中斷的初始化,,它其中含有對(duì)UART0中斷的

    分配,在用戶程序里需要根據(jù)使用的硬件中斷修改這部分代碼,,否則,,這些硬件中斷無(wú)法開(kāi)啟;

    再者,,在不調(diào)用TargetInit()的時(shí)候,,硬件的中斷初始化是在硬件初始化函數(shù)中完成,

    這也就是Zgpswh提到的現(xiàn)象:不調(diào)用TargetInit()內(nèi)核運(yùn)行異常,,調(diào)用了卻開(kāi)不了UART0的中斷,。

    解決的方法如下:

    這在《arm嵌入式系統(tǒng)基礎(chǔ)教程》的430頁(yè)7.4.3節(jié)中論述的很清楚:

    ……關(guān)鍵在于把程序與芯片相關(guān)中斷源掛接,使芯片在產(chǎn)生相應(yīng)的中斷后會(huì)調(diào)用相應(yīng)的處理程序,。

    這需要做兩方面事情:

    1. 增加匯編接口的支持,。……

    2. 初始化向量中斷控制器。……

 

    按照一下方法完成中斷源的的掛接:

    1,、增加匯編接口的支持,。方法是修改IRQ.s文件,在末尾添加本句代碼:

    UART0_Handler HANDLER UART0_Exception

    追加定義了通用串口0 中斷句柄,。

    2,、初始化向量中斷控制器。將target.c文件中的VICInit()修改如下:

    void VICInit(void)

    { extern void IRQ_Handler(void),;

    extern void Timer0_Handler(void),;

    extern void UART0_Handler(void),;

    VICIntEnClr = 0xffffffff;

    VICDefVectAddr =(uint32)IRQ_Handler,;

    VICVectAddr0 =(uint32)Timer0_Handler,;

    VICVectCntl0 =(0x20 | 0x04);

    VICIntEnable = 1<<4,;

    VICVectAddr14 =(uint32)UART0_Handler,;

    VICVectCntl14 =(0x20 | 0x06);

    VICIntEnable = 1<<6,;

    }

    此為初始化向量中斷控制器,。包括定時(shí)器0和串口0,特別要注意的是,,一定不可以省略對(duì)定

    時(shí)器0的初始化

    中斷里,,不可以調(diào)用延時(shí)

    請(qǐng)注意,中斷里面是不支持等待機(jī)制的,。請(qǐng)用自己編的一個(gè)軟件延時(shí),,問(wèn)題就可以解決了。

    中斷源掛接正確是沒(méi)問(wèn)題的,。

    6.問(wèn)

    void TargetInit(void)

    {

    OS_ENTER_CRITICAL(),;

    srand((uint32)TargetInit);

    VICInit(),;

    Timer0Init();

    OS_EXIT_CRITICAL(),;

    }

    在此單獨(dú)用srand()函數(shù)有什么作用(用了有什么好處,,不用又會(huì)怎樣),一般srand()用于

    給rand()設(shè)定種子(即srand給定rand運(yùn)算式子的第一個(gè)值),。查了FAQ(P22)僅僅說(shuō)明了

    seed的譯文,。

    答2

    你說(shuō)的沒(méi)有錯(cuò),他就是用來(lái)設(shè)置隨機(jī)數(shù)的種子,。

    每次編譯一次,,void TargetInit(void)函數(shù)在Ram或者Flash中的地址都不一樣

    (即種子也不一樣)。如果你在程序中不用隨機(jī)函數(shù)rand(),,那么srand()

    在這里對(duì)你來(lái)說(shuō)是沒(méi)有意義的,,如果你要用rand(),那么每次編譯程序后你的rand()

    產(chǎn)生的隨機(jī)數(shù)就不一樣,。當(dāng)然,,你也可以自己種種子咯。

    答3

    srand((uint32) TargetInit),;是初始化uc/os-ii隨機(jī)數(shù)函數(shù)rand(),;也就是說(shuō)給隨機(jī)數(shù)一個(gè)基值,,以后調(diào)用相關(guān)隨機(jī)數(shù)函數(shù)時(shí),隨機(jī)數(shù)就在此基值的基礎(chǔ)上改變,。

    只要不調(diào)用rand()函數(shù),,就可以去掉這句。

    7.

    在arm上移植操作系統(tǒng)有一點(diǎn)需要注意:建立任務(wù)的任務(wù),,最好不要做復(fù)雜的工作,。

    頻繁的訪問(wèn)其他硬件或者做時(shí)序要求比較嚴(yán)的工作容易造成系統(tǒng)死機(jī),希望大家多

    多注意,。我的做法是:將建立任務(wù)的任務(wù),,閑置起來(lái),但是不能進(jìn)入死循環(huán),。方法

    是:利用一個(gè)空郵箱,,讓任務(wù)無(wú)限期的等待,這樣可以實(shí)現(xiàn)與其他任務(wù)的切換,。

    8.//定義與編譯器無(wú)關(guān)的數(shù)據(jù)類(lèi)型

    typedef unsigned char BOOLEAN,;//布爾變量

    typedef unsigned char INT8U;//無(wú)符號(hào)8位整型變量

    typedef signed char INT8S,;//有符號(hào)8位整型變量

    typedef unsigned short INT16U,;//無(wú)符號(hào)16位整型變量

    typedef signed short INT16S;//有符號(hào)16位整型變量

    typedef unsigned int INT32U,;//無(wú)符號(hào)32位整型變量

    typedef signed int INT32S,;//有符號(hào)32位整型變量

    typedef float FP32;//單精度浮點(diǎn)數(shù)(32位長(zhǎng)度)

    typedef double FP64,;//雙精度浮點(diǎn)數(shù)(64位長(zhǎng)度)

    typedef INT32U OS_STK,;//堆棧是32位寬度

    注:這里為什么用typedef,因?yàn)槿绻?define,,那么代碼中的每一個(gè)相應(yīng)的類(lèi)型都會(huì)被替代,,

    很有可能會(huì)出現(xiàn)問(wèn)題,畢竟他只是一個(gè)替代的關(guān)系,,且編譯時(shí)間會(huì)增加,,而用typedef則不會(huì),

    它就相當(dāng)于我們C++里面的引用,,一樣的思維,,在這里面就是說(shuō)多了一個(gè)名稱(chēng)。還有要注意的是

    我們?cè)趺粗纔nsigned char 就是無(wú)符號(hào)8位整型變量,,可以arm公司里面下載ADS_CompilerGuide_D.PDF

    文件,,或者在你所裝的ADS1.2目錄里面有一個(gè)文件夾叫PDF,打開(kāi)它就可

此內(nèi)容為AET網(wǎng)站原創(chuàng),,未經(jīng)授權(quán)禁止轉(zhuǎn)載,。