1.在uC/OS-II的幫助手冊內(nèi),作者特地強調(diào)絕對不能在OSInit()或者OSStart()內(nèi)
調(diào)用Timer初始化程序,,那會破壞系統(tǒng)的可移植性同時帶來性能上的損失。
所以,,一個折中的辦法就是:
在優(yōu)先級最高的程序內(nèi)調(diào)用,,這樣可以保證當(dāng)OSStart()調(diào)用系統(tǒng)內(nèi)部函數(shù)
OSStartHighRdy()開始多任務(wù)后,首先執(zhí)行的就是Timer初始化程序,?;蛘?/p>
專門開一個優(yōu)先級最高的任務(wù),只做一件事情,,那就是執(zhí)行 Timer初始化,,
之后通過調(diào)用OSTaskSuspend()將自己掛起來,永遠不再執(zhí)行。不過這樣會
浪費一個TCB空間,。對于那些RAM吃緊的系統(tǒng)來說,,還是不用為好。
2.(三) 一些重要的uC/OS-II API介紹
任何一個操作系統(tǒng)都會提供大量的API供程序員使用,,uC/OS-II也不例外,。由于uC/OS-II面向
的是嵌入式開發(fā),并不要求大而全,,所以內(nèi)核提供的API也就大多和多任務(wù)息息相關(guān),。
主要的有以下幾類:
1)任務(wù)類
2)消息類
3)同步類
4)時間類
5)臨界區(qū)與事件類
我個人認為對于初級程序員而言,任務(wù)類和時間類是必須要首先掌握的兩種類型的API.
下面我就來介紹比較重要的:
1)OSTaskCreate函數(shù)
這個函數(shù)應(yīng)該至少再main函數(shù)內(nèi)調(diào)用一次,,在OSInit函數(shù)調(diào)用之后調(diào)用,。作用就是創(chuàng)建
一個任務(wù)。目前有四個參數(shù),,分別是任務(wù)的入口地址,,任務(wù)的參數(shù), 任務(wù)堆棧的首地址和
任務(wù)的優(yōu)先級,。調(diào)用本函數(shù)后,,系統(tǒng)會首先從TCB空閑列表內(nèi)申請一個空的TCB指針,然后
將會根據(jù)用戶給出參數(shù)初始化任務(wù)堆棧,,并在內(nèi)部的任務(wù)就緒表內(nèi)標(biāo)記該任務(wù)為就緒狀態(tài),。
最后返回,這樣一個任務(wù)就創(chuàng)建成功了,。
2)OSTaskSuspend函數(shù)
這個函數(shù)很簡單,,一看名字就該明白它的作用,它可以將指定的任務(wù)掛起,。如果掛起的是
當(dāng)前任務(wù)的話,,那么還會引發(fā)系統(tǒng)執(zhí)行任務(wù)切換先導(dǎo)函數(shù)OSShed來進行一次任務(wù)切換。
這個函數(shù)只有一個參數(shù),,那就是指定任務(wù)的優(yōu)先級,。那為什么是優(yōu)先級呢?事實上在系統(tǒng)
內(nèi)部,,優(yōu)先級除了表示一個任務(wù)執(zhí)行的先后次序外,,還起著分別每一個任務(wù)的作用,換句話
說,,優(yōu)先級也就是任務(wù)的ID.所以uC/OS-II不允許出現(xiàn)相同優(yōu)先級的任務(wù),。
3)OSTaskResume函數(shù)
這個函數(shù)和上面的函數(shù)正好相反,它用于將指定的已經(jīng)掛起的函數(shù)恢復(fù)成就緒狀態(tài),。如果
恢復(fù)任務(wù)的優(yōu)先級高于當(dāng)前任務(wù),,那么還為引發(fā)一次任務(wù)切換,。其參數(shù)類似 OSTaskSuspend
函數(shù),為指定任務(wù)的優(yōu)先級,。需要特別說明是,,本函數(shù)并不要求和OSTaskSuspend函數(shù)成對使
用。
4)OS_ENTER_CRITICAL宏
很多人都以為它是個函數(shù),,其實不然,,仔細分析一下OS_CPU.H文件,它和下面馬上要談到的
OS_EXIT_CRITICAL都是宏,。他們都是涉及特定 CPU的實現(xiàn),。一般都被替換為一條或者幾條
嵌入式匯編代碼。由于系統(tǒng)希望向上層程序員隱藏內(nèi)部實現(xiàn),,故而一般都宣稱執(zhí)行此條指
令后系統(tǒng)進入臨界區(qū),。其實,, 它就是關(guān)個中斷而已,。這樣,只要任務(wù)不主動放棄CPU使用權(quán),,
別的任務(wù)就沒有占用CPU的機會了,,相對這個任務(wù)而言,它就是獨占了,。所以說進入臨界區(qū)了,。
這個宏能少用還是少用,因為它會破壞系統(tǒng)的一些服務(wù),,尤其是時間服務(wù),。并使系統(tǒng)對外界響
應(yīng)性能降低。
5)OS_EXIT_CRITICAL宏
這個是和上面介紹的宏配套使用另一個宏,,它在系統(tǒng)手冊里的說明是退出臨界區(qū),。其實它就
是重新開中斷。需要注意的是,,它必須和上面的宏成對出現(xiàn),,否則會帶來意想不到的后果。
最壞的情況下,,系統(tǒng)會崩潰,。我們推薦程序員們盡量少使用這兩個宏調(diào)用,因為他們的確會
破壞系統(tǒng)的多任務(wù)性能,。
6)OSTimeDly函數(shù)
這應(yīng)該程序員們調(diào)用最多的一個函數(shù)了,,這個函數(shù)完成功能很簡單,就是先掛起當(dāng)起當(dāng)前任務(wù),,
然后進行任務(wù)切換,,在指定的時間到來之后,,將當(dāng)前任務(wù)恢復(fù)為就緒狀態(tài),但是并不一定運行,,
如果恢復(fù)后是優(yōu)先級最高就緒任務(wù)的話,,那么運行之。簡單點說,,就是可以任務(wù)延時一定時間
后再次執(zhí)行它,,或者說,暫時放棄CPU的使用權(quán),。一個任務(wù)可以不顯式的調(diào)用這些可以導(dǎo)致放棄CPU使用權(quán)的API,,但那樣多任務(wù)性能會大大降低,因為此時僅僅依靠時鐘機制在進行任務(wù)切換,。一個好的任務(wù)應(yīng)該在完成一些操作主動放棄使用權(quán),,好東西要大家分享嘛!
3.我們推薦程序員們盡量少使用OS_ENTER_CRITICAL宏和 OS_EXIT_CRITICAL宏兩個宏調(diào)用,,
因為他們的確會破壞系統(tǒng)的多任務(wù)性能,。why?,?
4.在以uC/OS為操作系統(tǒng)的項目中,,系統(tǒng)可能要處理各種不同的中斷請求,如果某個中斷處理
程序需要調(diào)用uC/OS的各種Post函數(shù)向任務(wù)發(fā)出消息,,那么uC/OS建議中斷服務(wù)程序的寫法是:
1,、保存全部CPU寄存器
2、調(diào)用OSIntEnter或OSIntNesting直接加1
3,、執(zhí)行用戶代碼做中斷服務(wù)
4,、調(diào)用OSIntExit
5、恢復(fù)所有CPU寄存器
6,、執(zhí)行中斷返回指令
暫且稱為“標(biāo)準(zhǔn)中斷”方式,,這種方式實際上是將這個中斷處理加入了任務(wù)調(diào)度系統(tǒng),也就是
說這個中斷可以引起任務(wù)的切換,。
如果在中斷處理中沒有調(diào)用各種Post函數(shù)的話,,則可以用一般的、象原來沒有操作系統(tǒng)時的
寫法:
1,、保存中斷處理程序需要用到的CPU寄存器
2,、執(zhí)行中斷處理
3、恢復(fù)保存了的CPU寄存器
4,、執(zhí)行中斷返回指令
暫且稱為“快中斷”方式,,按照這種方法定義的中斷永遠不會引起任務(wù)切換。
在uC/OS系統(tǒng)中,,每個任務(wù)都要定義獨立的??臻g,,一個棧空間的使用包括5個部分:
1,、任務(wù)包括的各個函數(shù)的調(diào)用返回地址
2,、任務(wù)包括的各個函數(shù)中可能在棧上分配的局部變量
3、發(fā)生了“標(biāo)準(zhǔn)中斷”方式定義的中斷或任務(wù)被掛起時,,所要保存的任務(wù)上下文
4,、發(fā)生了“快中斷”方式定義的中斷時,中斷處理程序所需要的??臻g
5,、中斷嵌套時,所要保存的中斷嵌套上下文
在這些使用的部分中,,1,,2,3,,4的內(nèi)存占用量是比較容易估算的,,最精確和保險的確定
方法是:查看由C生成的asm文件,并計算各個函數(shù)的棧使用量,。但是第5部分的??臻g使用
量是隨中斷嵌套的深度而不斷增加的,是不確定的,,一般的方法只能定義一個充分大的棧
空間,使之不會溢出,。
為每個任務(wù)都定義一個充分大的??臻g,這在某些內(nèi)存稀缺的小項目中是非常痛苦的,,
有時不得不增擴內(nèi)存,,這就會使成本增加。
我深入研究了uC/OS后,,認為,,可以將所有任務(wù)棧空間使用的第5部分合并,,這樣將會大大的
降低整個系統(tǒng)對內(nèi)存的需求,。
uC/OS的任務(wù)調(diào)度是靠OS_Sched和 OSIntExit來完成的,這兩個函數(shù)中都要先判斷一個叫
OSIntNesting的系統(tǒng)變量,,如果OSIntNesting不為0,,則不進行任務(wù)切換。也就是說:
在OSIntNesting為1(當(dāng)前只有一個中斷在處理中,,并且沒有嵌套的中斷)時起,,
如果發(fā)生了嵌套的中斷(不管嵌套的層數(shù)有深),,那么在所有嵌套的中斷一層一層地都返回
直到 OSIntNesting再次為1時止,任務(wù)棧是不會切換的(棧指針都在一個任務(wù)的??臻g中變
化),。
據(jù)此,我們可以這樣改動:設(shè)置一個緩沖區(qū)OSInterruptStk,,作為嵌套中斷的??臻g
由所有任務(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的棧頂(注意棧增長的方向),。
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ù)的寫法是:
1,、調(diào)整棧指針來去掉在調(diào)用:OSIntExit,,OSIntCtxSw過程中入棧的多余內(nèi)容
2、將當(dāng)前任務(wù)棧指針保存到OSTCBCur中(OSTCBCur->OSTCBStkPtr = __SP__)
3,、如果需要則調(diào)用OSTaskSwHook
4,、OSTCBCur = OSTCBHighRdy
5、OSPrio = OSPrioHighRdy
6,、從OSTCBCur中恢復(fù)棧指針(__SP__ = OSTCBCur->OSTCBStkPtr)
7,、恢復(fù)保存了的CPU寄存器
8、執(zhí)行中斷返回指令
新的寫法只需將原寫法中的1,,2去掉即可,,因為1,2步只是保存舊任務(wù)的棧指針,,而新的寫
法中,,這些步被移到了“中斷服務(wù)程序”中的2.2.
5.注意arm Image for uCOSII for lpc213x 模板中的TargetInit()
對于很多使用ZLG arm Image for uCOSII for lpc213x 模板的初學(xué)者,常常會置疑使用該模板
后自動生成的target.c文件,,和在程序中調(diào)用的TargetInit()函數(shù),,我和 Zgpswh都是如此,,這
個問題當(dāng)初困擾了很久:當(dāng)用戶程序中不調(diào)用TargetInit()時,發(fā)現(xiàn)內(nèi)核能運行,,但是等待機制
失靈,,調(diào)用 TargetInit(),很多硬件中斷打不開,,后來,,在很多熱心人的指點下解決了,現(xiàn)重新
總結(jié)如下:
請仔細察看ZLG模板里的target.c文件,,這里的TargetInit()如下:
void TargetInit(void)
{
OS_ENTER_CRITICAL(),;
srand((uint32)TargetInit);
VICInit(),;
Timer0Init(),;
OS_EXIT_CRITICAL();
}
其中的Timer0Init(),;用于硬件定時器0的初始化,,事實上,ZLG的移植代碼的μC/OS-Ⅱ的時鐘節(jié)拍是
通過定時器0提供的,,不在主程序里調(diào)用這個函數(shù),,μC/OS-Ⅱ的時鐘源就無法打開;但是,,沒有開啟
時鐘源的μC/OS-Ⅱ是同樣能運行的,,只是內(nèi)核提供的延時和等待時限機制都不起作用,系統(tǒng)雖能將
就運行,,但因沒調(diào)用TargetInit()而使內(nèi)核功能不健全,。
請注意,TargetInit()中的另一個函數(shù)VICInit ()是用來中斷的初始化,,它其中含有對UART0中斷的
分配,在用戶程序里需要根據(jù)使用的硬件中斷修改這部分代碼,,否則,,這些硬件中斷無法開啟;
再者,,在不調(diào)用TargetInit()的時候,,硬件的中斷初始化是在硬件初始化函數(shù)中完成,
這也就是Zgpswh提到的現(xiàn)象:不調(diào)用TargetInit()內(nèi)核運行異常,,調(diào)用了卻開不了UART0的中斷,。
解決的方法如下:
這在《arm嵌入式系統(tǒng)基礎(chǔ)教程》的430頁7.4.3節(jié)中論述的很清楚:
……關(guān)鍵在于把程序與芯片相關(guān)中斷源掛接,使芯片在產(chǎn)生相應(yīng)的中斷后會調(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,;
}
此為初始化向量中斷控制器,。包括定時器0和串口0,特別要注意的是,,一定不可以省略對定
時器0的初始化
中斷里,,不可以調(diào)用延時
請注意,中斷里面是不支持等待機制的,。請用自己編的一個軟件延時,,問題就可以解決了。
中斷源掛接正確是沒問題的,。
6.問
void TargetInit(void)
{
OS_ENTER_CRITICAL(),;
srand((uint32)TargetInit);
VICInit(),;
Timer0Init(),;
OS_EXIT_CRITICAL();
}
在此單獨用srand()函數(shù)有什么作用(用了有什么好處,,不用又會怎樣),,一般srand()用于
給rand()設(shè)定種子(即srand給定rand運算式子的第一個值)。查了FAQ(P22)僅僅說明了
seed的譯文,。
答2
你說的沒有錯,,他就是用來設(shè)置隨機數(shù)的種子。
每次編譯一次,void TargetInit(void)函數(shù)在Ram或者Flash中的地址都不一樣
(即種子也不一樣),。如果你在程序中不用隨機函數(shù)rand(),,那么srand()
在這里對你來說是沒有意義的,如果你要用rand(),,那么每次編譯程序后你的rand()
產(chǎn)生的隨機數(shù)就不一樣,。當(dāng)然,你也可以自己種種子咯,。
答3
srand((uint32) TargetInit),;是初始化uc/os-ii隨機數(shù)函數(shù)rand();也就是說給隨機數(shù)一個基值,,以后調(diào)用相關(guān)隨機數(shù)函數(shù)時,,隨機數(shù)就在此基值的基礎(chǔ)上改變。
只要不調(diào)用rand()函數(shù),,就可以去掉這句,。
7.
在arm上移植操作系統(tǒng)有一點需要注意:建立任務(wù)的任務(wù),最好不要做復(fù)雜的工作,。
頻繁的訪問其他硬件或者做時序要求比較嚴的工作容易造成系統(tǒng)死機,,希望大家多
多注意。我的做法是:將建立任務(wù)的任務(wù),,閑置起來,,但是不能進入死循環(huán)。方法
是:利用一個空郵箱,,讓任務(wù)無限期的等待,,這樣可以實現(xiàn)與其他任務(wù)的切換。
8.//定義與編譯器無關(guān)的數(shù)據(jù)類型
typedef unsigned char BOOLEAN,;//布爾變量
typedef unsigned char INT8U,;//無符號8位整型變量
typedef signed char INT8S;//有符號8位整型變量
typedef unsigned short INT16U,;//無符號16位整型變量
typedef signed short INT16S,;//有符號16位整型變量
typedef unsigned int INT32U;//無符號32位整型變量
typedef signed int INT32S,;//有符號32位整型變量
typedef float FP32,;//單精度浮點數(shù)(32位長度)
typedef double FP64;//雙精度浮點數(shù)(64位長度)
typedef INT32U OS_STK,;//堆棧是32位寬度
注:這里為什么用typedef,因為如果用#define,,那么代碼中的每一個相應(yīng)的類型都會被替代,,
很有可能會出現(xiàn)問題,畢竟他只是一個替代的關(guān)系,,且編譯時間會增加,,而用typedef則不會,,
它就相當(dāng)于我們C++里面的引用,一樣的思維,,在這里面就是說多了一個名稱,。還有要注意的是
我們怎么知道unsigned char 就是無符號8位整型變量,可以arm公司里面下載ADS_CompilerGuide_D.PDF
文件,,或者在你所裝的ADS1.2目錄里面有一個文件夾叫PDF,,打開它就可