一直想寫個類似uCOS的OS,近段時間考研復習之余忙里偷閑,總算有點成果了。言歸正傳,我覺得OS最難的部分首先便是上下文切換的問題,他和MCU的架構有關,所以對於不同的MCU,這部分需要移植。一旦這個問題解決了,整個OS算是成功了一半了,當然,是對於簡單的OS。
好了,要寫一個OS,首先需要一個開發板和仿真器。我的開發板是STM32F4DISCOVERY,自帶ST-LINK V2仿真器,板載MCU為STM32F407VGT6,支持FPU,32位ARM Cortex-M4F核,1024KB FLASH,192 KB RAM,總之很強大。對STM32其他系列,本OS幾乎不需修改修改就可使用。開發環境為IAR for ARM 6.5,如果是MDK的話,也是大同小異,匯編部分需要修改。
研究了一下UCOS-II的Cortex-M4的Port部分,覺得很好,就直接拿來用了,修改的很少。首先我們來看一下這一部分幾個比較重要的函數,打開os_cpu_a.asm文件,定位到下面的地方,注釋我改成中文了。當OS初始化完畢后,執行OSStart,OSStart最后調用OSStartHighRdy函數,注意在此之前的線程模式和異常模式的堆棧都是MSP,在此之后線程模式的堆棧是PSP,異常模式的堆棧仍是MSP。
OSStartHighRdy LDR R0, =NVIC_SYSPRI14 ; 設置PendSV的異常中斷優先級 LDR R1, =NVIC_PENDSV_PRI STRB R1, [R0] MOVS R0, #0 ; 初始化PSP=0 MSR PSP, R0 LDR R0, =OS_CPU_ExceptStkBase ; 初始化異常堆棧MSP地址 LDR R1, [R0] MSR MSP, R1 LDR R0, =OSRunning ; 置OSRunning = TRUE MOVS R1, #1 STRB R1, [R0] LDR R0, =NVIC_INT_CTRL ; 觸發PendSV異常 (引起上下文切換) LDR R1, =NVIC_PENDSVSET STR R1, [R0] CPSIE I ; 開啟中斷,於是進入PendSV異常 OSStartHang B OSStartHang ; 正常情況下,不應運行到這
其中最核心的函數應該是OS_CPU_PendSVHandler了,它處理PendSV中斷,完成上下文切換。
OS_CPU_PendSVHandler CPSID I ; 關中斷 MRS R0, PSP ; 獲得PSP CBZ R0, OS_CPU_PendSVHandler_nosave; PSP為0跳到OS_CPU_PendSVHandler_nosave,即不保存上文,直接進入下文。 ; 問什么呢,因為首次調用,是沒有上文的。 ; 保存上文 SUBS R0, R0, #0x20 ; 因為寄存器是32位的,4字節對齊,自動壓棧的寄存器有8個,所以偏移為8*0x04=0x20 STM R0, {R4-R11} ; 除去自動壓棧的寄存器外,需手動將R4-R11壓棧 LDR R1, =OSTCBCur ; 保存上文的SP指針 OSTCBCur->OSTCBStkPtr = SP; LDR R1, [R1] STR R0, [R1] OS_CPU_PendSVHandler_nosave ; 切換下文 PUSH {R14} ; LR壓棧,下面要調用C函數 LDR R0, =OSTaskSwHook ; 調用OSTaskSwHook(); BLX R0 POP {R14} LDR R0, =OSPrioCur ; 置OSPrioCur = OSPrioHighRdy; LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] LDR R0, =OSTCBCur ; 置OSTCBCur = OSTCBHighRdy; LDR R1, =OSTCBHighRdy LDR R2, [R1] STR R2, [R0] LDR R0, [R2] ; R0中的值為新任務的SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDM R0, {R4-R11} ; 手動彈出 R4-R11 ADDS R0, R0, #0x20 MSR PSP, R0 ; PSP = 新任務SP ORR LR, LR, #0x04 ; 確保異常返回后使用PSP CPSIE I BX LR ; 退出異常,從PSP彈出xPSR,PC,LR,R0-R3,進入新任務運行
之后我們在此基礎上構建自己的OS,首先完成兩個任務互相調用,然后是加入SysTick的任務調度,最后加入信號量,郵箱等功能。
