在以uC/OS為操作系統的項目中,系統可能要處理各種不同的中斷請求,如果某個中斷處理程序需要調用uC/OS的各種Post函數向任務發出消息,那么uC/OS建議中斷服務程序的寫法是: 1、保存全部CPU寄存器 2、調用OSIntEnter或OSIntNesting直接加1 3、執行用戶代碼做中斷服務 4、調用OSIntExit 5、恢復所有CPU寄存器 6、執行中斷返回指令 暫且稱為“標準中斷”方式,這種方式實際上是將這個中斷處理加入了任務調度系統,也就是說這個中斷可以引起任務的切換。
如果在中斷處理中沒有調用各種Post函數的話,則可以用一般的、象原來沒有操作系統時的寫法: 1、保存中斷處理程序需要用到的CPU寄存器 2、執行中斷處理 3、恢復保存了的CPU寄存器 4、執行中斷返回指令 暫且稱為“快中斷”方式,按照這種方法定義的中斷永遠不會引起任務切換。
在uC/OS系統中,每個任務都要定義獨立的棧空間,一個棧空間的使用包括5個部分: 1、任務包括的各個函數的調用返回地址 2、任務包括的各個函數中可能在棧上分配的局部變量 3、發生了“標準中斷”方式定義的中斷或任務被掛起時,所要保存的任務上下文 4、發生了“快中斷”方式定義的中斷時,中斷處理程序所需要的棧空間 5、中斷嵌套時,所要保存的中斷嵌套上下文
在這些使用的部分中,1,2,3,4的內存占用量是比較容易估算的,最精確和保險的確定方法是:查看由C生成的asm文件,并計算各個函數的棧使用量。但是第5部分的棧空間使用量是隨中斷嵌套的深度而不斷增加的,是不確定的,一般的方法只能定義一個充分大的棧空間,使之不會溢出。
為每個任務都定義一個充分大的棧空間,這在某些內存稀缺的小項目中是非常痛苦的,有時不得不增擴內存,這就會使成本增加。
我深入研究了uC/OS后,認為,可以將所有任務棧空間使用的第5部分合并,這樣將會大大的降低整個系統對內存的需求。
uC/OS的任務調度是靠OS_Sched和OSIntExit來完成的,這兩個函數中都要先判斷一個叫 OSIntNesting的系統變量,如果OSIntNesting不為0,則不進行任務切換。也就是說:在OSIntNesting為1(當前只有一個中斷在處理中,并且沒有嵌套的中斷)時起,如果發生了嵌套的中斷(不管嵌套的層數有深),那么在所有嵌套的中斷一層一層地都返回直到 OSIntNesting再次為1時止,任務棧是不會切換的(棧指針都在一個任務的棧空間中變化)。
據此,我們可以這樣改動:設置一個緩沖區OSInterruptStk,作為嵌套中斷的棧空間,由所有任務共享,中斷服務程序改為: 1、保存全部CPU寄存器 2、調用OSIntEnter或OSIntNesting直接加1 增加:2.1、判斷OSIntNesting是否等于1,如果不是則轉到3 增加:2.2、將棧指針SP保存到OSTCBCur->OSTCBStkPtr 增加:2.3、將SP指向OSInterruptStk的棧頂(注意棧增長的方向)。 3、執行用戶代碼做中斷服務 4、調用OSIntExit 增加:4.1、判斷OSIntNesting是否等于0,如果不是則轉到5 增加:4.2、從OSTCBCur->OSTCBStkPtr中恢復棧指針SP 5、恢復所有CPU寄存器 6、執行中斷返回指令
并且要修改OSIntCtxSw函數,原始的OSIntCtxSw函數的寫法是: 1、調整棧指針來去掉在調用:OSIntExit,OSIntCtxSw過程中入棧的多余內容 2、將當前任務棧指針保存到OSTCBCur中(OSTCBCur->OSTCBStkPtr = __SP__) 3、如果需要則調用OSTaskSwHook 4、OSTCBCur = OSTCBHighRdy 5、OSPrio = OSPrioHighRdy 6、從OSTCBCur中恢復棧指針(__SP__ = OSTCBCur->OSTCBStkPtr) 7、恢復保存了的CPU寄存器 8、執行中斷返回指令
新的寫法只需將原寫法中的1,2去掉即可,因為1,2步只是保存舊任務的棧指針,而新的寫法中,這些步被移到了“中斷服務程序”中的2.2。
以上的修改已在我的項目中驗證通過了
|