IMPORT OSRunning;參數(shù)指示內(nèi)核是否運行的布爾型變量
IMPORT OSPrioCur;參數(shù)當(dāng)前任務(wù)的優(yōu)先級
IMPORT OSPrioHighRdy;參數(shù)優(yōu)先級最高任務(wù)的優(yōu)先級
IMPORT OSTCBCur;任務(wù)控制塊指針指向當(dāng)前正在運行的任務(wù)
IMPORT OSTCBHighRdy;任務(wù)控制塊指針指向最高優(yōu)先級任務(wù)的任務(wù)控制塊
IMPORT OSIntNesting;參數(shù)保存中斷嵌套級
IMPORT OSIntExit;函數(shù)退出軟中斷進行任務(wù)切換
IMPORT OSTaskSwHook;勾函數(shù)任務(wù)切換時運行自己的函數(shù)
;IMPORT外部文件定義的引入供本文件使用(輸入)
;下面是六個需要自己編寫的匯編函數(shù)
EXPORT OSStartHighRdy;運行優(yōu)先級最高的函數(shù)供OSstart調(diào)用
EXPORT OSCtxSw;任務(wù)切換函數(shù)
EXPORT OSIntCtxSw;中斷結(jié)束時進行任務(wù)切換供OSintexit調(diào)用
EXPORT OS_CPU_SR_Save;OS_ENTER_CRITICAL()進入臨界中斷
EXPORT OS_CPU_SR_Restore;OS_EXIT_CRITICAL()退出臨界中斷
EXPORT PendSV_Handler;
;EXPORT本文件定義的供外部文件引用(輸出)
NVIC_INT_CTRL EQU 0xE000ED04 ; 中斷控制及狀態(tài)寄存器ICSR P135
NVIC_SYSPRI2 EQU 0xE000ED22 ; 系統(tǒng)優(yōu)先級寄存器 P132*寫錯?ED22?好像兩個都可以
NVIC_PENDSV_PRI EQU 0xFFFF0000 ; PendSV中斷和系統(tǒng)節(jié)拍中斷
; (都為最低,0xff).
NVIC_PENDSVSET EQU 0x10000000 ; 觸發(fā)軟件中斷的值.掛起PENDSV
PRESERVE8;保證8字節(jié)對齊
AREA |.text|, CODE, READONLY;只讀代碼
THUMB;16位THUMB指令
;********************************************************************************************************
; CRITICAL SECTION METHOD 3 FUNCTIONS
;
; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
; into the CPU's status register.
;
; Prototypes : OS_CPU_SR OS_CPU_SR_Save(void);
; void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
;OS_CPU_SR 大小取決于CPU狀態(tài)寄存器大小32位
;
; Note(s) : 1) These functions are used in general like this:
;
; void Task (void *p_arg)
; {
; #if OS_CRITICAL_METHOD == 3
; OS_CPU_SR cpu_sr;
; #endif
;
; :
; :
; OS_ENTER_CRITICAL();
; :
; :
; OS_EXIT_CRITICAL();
; :
; :
; }
;********************************************************************************************************
;0S_CPU_A.ASM(STM32)這倆個函數(shù)是進出中斷延用的就是防止任務(wù)切換時被意外打斷
;0S_CPU_A.ASM(STM32)沒啥特別的開關(guān)中斷返回。
OS_CPU_SR_Save
MRS R0, PRIMASK ;讀取PRIMASK到R0,R0為返回值
CPSID I ;PRIMASK=1,關(guān)中斷(NMI和硬件FAULT可以響應(yīng))屏蔽所有可屏蔽異常
BX LR ;返回
OS_CPU_SR_Restore
MSR PRIMASK, R0 ;讀取R0到PRIMASK中,R0為參數(shù)
BX LR ;返回
;0S_CPU_A.ASM(STM32)這個函數(shù)是一開始啟動時用的,設(shè)置PENDSV優(yōu)先級最低,這樣只有在沒有其他異常(中斷,事件)運行下進行掛起。
;PendSV可懸起系統(tǒng)調(diào)用確保任務(wù)切換不會打斷中斷
OSStartHighRdy
LDR R4, =NVIC_SYSPRI2 ; set the PendSV exception priority
LDR R5, =NVIC_PENDSV_PRI ;設(shè)置系統(tǒng)服務(wù)優(yōu)先級為最低優(yōu)先級
STR R5, [R4] ;只有在沒有其他異常的情況下才會觸發(fā)系統(tǒng)服務(wù)中斷進行上下文切換
;0S_CPU_A.ASM(STM32)R4寫零這樣第一次運行在PendSV_Handler中將調(diào)用nosave函數(shù),psp一開始為零如果保存psp-32將為不確定值,退出中斷后會跑飛。
MOV R4, #0 ; set the PSP to 0 for initial context switch call
MSR PSP, R4 ;設(shè)置進程堆棧寄存器為0為初始化上下文切換指令
;0S_CPU_A.ASM(STM32)告訴系統(tǒng)ucos初始化完畢可以啟動了
LDR R4, =OSRunning ; OSRunning = TRUE
MOV R5, #1 ;
STRB R5, [R4] ;STRB位傳送指令
;0S_CPU_A.ASM(STM32)STM32有PENDSV 任務(wù)切換是在PENDSV完成的,所以這里要觸發(fā)PENDSV
LDR R4, =NVIC_INT_CTRL ;rigger the PendSV exception (causes context switch)
LDR R5, =NVIC_PENDSVSET ;懸起PENDSV觸發(fā)系統(tǒng)調(diào)用中斷進行任務(wù)調(diào)度
STR R5, [R4]
CPSIE I ;enable interrupts at processor level快速開中斷
;0S_CPU_A.ASM(STM32)這個函數(shù)就是告訴你出錯了,系統(tǒng)崩潰的都會到這里
OSStartHang
B OSStartHang ;should never get here好吧,正常的程序是不會跑到這里的。
;0S_CPU_A.ASM(STM32)管他是中斷切換還是任務(wù)切換,要做的就是觸發(fā)PENDSV
OSCtxSw
PUSH {R4, R5} ;PUSH {R4, R5,LR}
LDR R4, =NVIC_INT_CTRL ;觸發(fā)PendSV異常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5} ;POP {R4, R5,PC}改成這樣可以省略BX
BX LR
;BX跳轉(zhuǎn)并改變指令集
;
OSIntCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;觸發(fā)PendSV異常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
NOP
;0S_CPU_A.ASM(STM32)這個就是任務(wù)切換的地方了,不能被打斷,所以先關(guān)中斷
PendSV_Handler
CPSID I ; Prevent interruption ;during context switch 關(guān)閉中斷防止在切換任務(wù)時打斷任務(wù)切換
;0S_CPU_A.ASM(STM32)硬件仿真的時候進中斷都是用MSP 所以保存還是有必要的。
MRS R0, PSP ; PSP is process stack pointer 如 ;果在用PSP堆棧,則可以忽略保存寄存器,參考CM3權(quán)威中的雙堆棧-白菜注
CBZ R0, PendSV_Handler_Nosave ; Skip register save the first time
;0S_CPU_A.ASM(STM32)如果PSP的值為零則運行PendSV_Handler_Nosave
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
;將PSP的值-32保存在R0
STM R0, {R4-R11} ;一個批量入棧操作將R4到R11全部保存在進程堆棧中地址保存在R0內(nèi)
;MSR MSP,R0
;PUSH {R4-R11}
;0S_CPU_A.ASM(STM32) STM R0, {R4-R11} 可以用上面兩句代替。上面一段把準(zhǔn)備被切掉的函數(shù)的寄存器R4-R7都保存了。
;0S_CPU_A.ASM(STM32) 因為其他寄存器都是自動保存的所以不要管。
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP
;將OSTCBCur指針的地址保存在R1中
LDR R1, [R1]
;從R1中的地址處讀數(shù)據(jù)讀到得就是OSTCBCur指針存儲在R1中
STR R0, [R1] ; R0 is SP of process being switched out R0是當(dāng)前進程堆棧的PSP指針即即將被切換任務(wù)的進程的PSP指針
;將R0中的數(shù)據(jù)(即PSP-32的值)存放在OSTCBCur指針指向的地址中
; At this point, entire context of process has been saved
;0S_CPU_A.ASM(STM32)到這里進程的所有內(nèi)容都保存完畢保存了,包括R4-R11的值和舊的任務(wù)堆棧的PSP地址
;0S_CPU_A.ASM(STM32)這里郁悶了我好久,原來匯編沒有跳轉(zhuǎn)指令會按代碼順序一直運行下去。只要有運行PENDSV HANDLER,一定會運行NOSAVE.仿真時候發(fā)現(xiàn)的。
;0S_CPU_A.ASM(STM32)這里要調(diào)用一個C函數(shù),為了防止LR在C函數(shù)中被改必須要保存下。
PendSV_Handler_Nosave
PUSH {LR} ; Save LR exc_return value
;將R14壓棧 ;保存回調(diào)地址
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
;取勾函數(shù)名所在的地址
BLX R0
;0S_CPU_A.ASM(STM32)BLX 帶鏈接跳轉(zhuǎn)并切換指令集 用戶代碼都是ARM代碼
POP {R14}
;出棧
;0S_CPU_A.ASM(STM32)這一塊完成任務(wù)優(yōu)先級的變換,運行什么任務(wù)是OS判斷這個值決定的了。我之前一直以為這里會跳到將要
;0S_CPU_A.ASM(STM32)運行的任務(wù)
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy
;將參數(shù)OSPrioCur的地址存在R0中
LDR R1, =OSPrioHighRdy
;將參數(shù)OSPrioHighRdy的地址存放在R1中
LDRB R2, [R1] ;R2中保存OSPrioHighRdy的值
;把R1地址中的數(shù)據(jù)取出來存在R2中
STRB R2, [R0]
;把R2中的數(shù)據(jù)存到R0地址對應(yīng)的內(nèi)存空間
;0S_CPU_A.ASM(STM32)任務(wù)換了任務(wù)控制塊也要換了
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1] ;R2中保存的是OSTCBHighRdy這個指針
STR R2, [R0]
;同上
;0S_CPU_A.ASM(STM32)新的任務(wù),有新的進程堆棧
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
;將R2地址中的數(shù)據(jù)存到R0中OSTCBHighRdy這個指針(將要運行任務(wù)的指針)
;0S_CPU_A.ASM(STM32)恢復(fù)新任務(wù)進程堆棧的值。
LDM R0, {R4-R11} ; Restore r4-11 from new process stack
;將R4-R11寄存器的值恢復(fù)到新進程的堆棧下
;POP{R4-R11}
;可以用這句替換上句
ADDS R0, R0, #0x20 ;+32得到新的PSP地址
MSR PSP, R0 ; Load PSP with new process SP
;0S_CPU_A.ASM(STM32)最后把PSP指向新的堆棧位置
;0S_CPU_A.ASM(STM32)到這里又把PSP指針,和R4-R7重新恢復(fù)了 ;0S_CPU_A.ASM(STM32)下面這句應(yīng)該是為了防止意外吧,正常退出都會用PSP堆棧
ORR LR, LR, #0x04 ; Ensure exception return uses process stack
;將LR的第二位置為1并保存到LR確保返回使用進程堆棧。
;0S_CPU_A.ASM(STM32)結(jié)束了,開中斷吧
CPSIE I ; 開中斷
;0S_CPU_A.ASM(STM32)記得跳轉(zhuǎn)
BX LR ; Exception return will restore remaining context
end