以前移值過ucos2到stm32f2系列的單片機,這個單片機是屬於arm的m3內核的。最近在學習永磁同步電機的控制,對於這個電機的控制,有比較多的數學計算,甚至於還有浮點的運算。因此用到了stm32f4系列的單片機,這個單片機內置FPU,能夠用幾條指令就能夠處理單精度的浮點數據,而它是屬於M4內核的。由於原先移植過M3的基礎,想着應該很快會搞定,沒想到移植了幾天的時間才搞清楚,下面就記錄下M3與M4內核的ucos2的移植不一樣之處。其實M3與M4內核相差不大,對於我應用的來講,其實最大的不一樣一是M4的最大主頻提升了,二是M4帶浮點功能。函數
一、ucos2移植到M3核的重點(對於ucos2移植到M3內核詳細的講解以及ucos2的結構後續會單獨寫一篇博客描述)學習
M3覈對於操做系統的支持很好,它有一個NVIC向量中斷控制器,它管理着對CM3的全部中斷請求。其中有幾個隊操做系統很重要的中斷:this
a、SVC(系統服務調用),用於產生系統函數的調用請求,例如操做系統一般不讓用戶程序直接訪問硬件,而是經過提供一些系統服務函數,讓用戶程序使用SVC發出對系統服務函數的呼叫請求,以這種方法調用它們來間接訪問硬件。所以,當用戶程序想要控制特定的硬件時,它就要產生一個SVC異常,而後操做系統提供的SVC異常服務例程獲得執行,它再調用相關的操做系統函數,後者完成用戶程序請求的服務。這與傳統的arm核好比arm7與amr9等的SWI的軟件中斷異常相似。spa
b、PendSV(可懸起的系統中斷),它是能夠像普通的中斷同樣被懸起的(不像SVC那樣會上訪)。OS能夠利用它「緩期執行」一個異常——直到其它重要的任務完成後才執行動做。懸起PendSV 的方法是:手工往NVIC的PendSV懸起寄存器中寫1。懸起後,若是優先級不夠高,則將緩期等待執行。因此它經常 被用來進行任務的切換,UCOS2的任務切換就是在這個中斷裏面實現的。操作系統
c、SysTick定時器被捆綁在NVIC中,用於產生SysTick異常(異常號:15)。SysTick定時器能產生中斷,CM3爲它專門開出一個異常類型,而且在向量表中有它的一席之地。它用於操做系統的滴答時鐘。在之前,操做系統還有全部使用了時基的系統,都必須一個硬件定時器來產生須要的「滴答」中斷,做爲整個系統的時基。滴答中斷對操做系統尤爲重要。例如,操做系統能夠爲多個任務許以不一樣數目的時間片,確保沒有一個任務能霸佔系統;或者把每一個定時器週期的某個時間範圍賜予特定的任務等,還有操做系統提供的各類定時功能,都與這個滴答定時器有關。指針
ucos2對每個任務都會分配一個任務控制塊,任務控制塊的首地址存放着該任務的堆棧指針,它存放的數據的格式如在函數,能夠看到它的堆棧是向下遞增的,也就是說堆棧頂部的位置始終是數值大的地址。首先存放的是M3內核發生異常時自動保存的數據,能夠看到任務的函數地址也被保存在內,它的值其實就是任務發生切換時PC的值,若是後續要切換回這個任務了只要作一次PendSV中斷,切換PSP指針爲這個任務的堆棧指針,當從PendSV中斷返回時task的值就會自動的存放到PC寄存器中,這樣就作到了任務切換。接着保存M3內核發生異常時沒有自動保存的寄存器,剩下的堆棧內容就保存任務函數的一些變量以及函數調用等等。rest
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt) { OS_STK *p_stk; (void)opt; /* 'opt' is not used, prevent warning */ p_stk = ptos; /* Load stack pointer */ /* Registers stacked as if auto-saved on exception */ *(--p_stk) = (OS_STK)0x01000000uL; /* xPSR */ *(--p_stk) = (OS_STK)task; /* Entry Point */ *(--p_stk) = (OS_STK)OS_TaskReturn; /* R14 (LR) */ *(--p_stk) = (OS_STK)0x12121212uL; /* R12 */ *(--p_stk) = (OS_STK)0x03030303uL; /* R3 */ *(--p_stk) = (OS_STK)0x02020202uL; /* R2 */ *(--p_stk) = (OS_STK)0x01010101uL; /* R1 */ *(--p_stk) = (OS_STK)p_arg; /* R0 : argument */ /* Remaining registers saved on process stack */ *(--p_stk) = (OS_STK)0x11111111uL; /* R11 */ *(--p_stk) = (OS_STK)0x10101010uL; /* R10 */ *(--p_stk) = (OS_STK)0x09090909uL; /* R9 */ *(--p_stk) = (OS_STK)0x08080808uL; /* R8 */ *(--p_stk) = (OS_STK)0x07070707uL; /* R7 */ *(--p_stk) = (OS_STK)0x06060606uL; /* R6 */ *(--p_stk) = (OS_STK)0x05050505uL; /* R5 */ *(--p_stk) = (OS_STK)0x04040404uL; /* R4 */ return (p_stk); }
接着看到UCOS2在M3內核下運行時的任務切換代碼,能夠看到任務切換函數,主要是將被打斷任務的堆棧保存好,而後將須要運行的任務的堆棧出棧,任何以從進程堆棧中作出棧操做,進行任務切換code
PendSV_Handler //;中斷處理函數 CPSID I //; Prevent interruption during context switch ;在任務切換時禁止其它中斷 MRS R0, PSP //; PSP is process stack pointer ; CBZ R0, OS_CPU_PendSVHandler_nosave //; Skip register save the first time ;檢查是不是從進程中進來的,若是不是,直接跳到OS_CPU_PendSVHandler_nosave SUBS R0, R0, #0x20 //; Save remaining regs r4-11 on process stack; 保存中斷上下文未自動保存的寄存器 STM R0, {R4-R11} LDR R1, =OSTCBCur // ; OSTCBCur->OSTCBStkPtr = SP; LDR R1, [R1] STR R0, [R1] //; R0 is SP of process being switched out 保存當前任務堆棧指針到當前任務控制塊 //; At this point, entire context of process has been saved OS_CPU_PendSVHandler_nosave PUSH {R14} //; Save LR exc_return value LDR R0, =OSTaskSwHook //; OSTaskSwHook(); BLX R0 //; POP {R14} //;//只是爲了調用OSTaskSwHook函數 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 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDM R0, {R4-R11} // ; Restore r4-11 from new process stack 取出新任務的R4-R11寄存器值 ADDS R0, R0, #0x20 MSR PSP, R0 // ; Load PSP with new process SP 切換新任務指針給PSP ORR LR, LR, #0x04 // ; Ensure exception return uses process stack 從進程堆棧中做出出棧操做 CPSIE I // ; 開啓中斷 BX LR // ; Exception return will restore remaining context 任務切換,切換到新任務被切換時的地址 END
二、ucos2移植到M4核blog
UCOS2在M3與M4運行任務切換時的原理是同樣的,都是利用的PendSV中斷。可是保存的寄存器有所不一樣,由於M4內核能夠支持單精度浮點操做,這樣就會有了浮點相關的寄存器的保存。這就是與M3內核最大的不一樣之處。支持浮點運算的M4內核比M3內核多一個FPU模塊,它裏面有33個寄存器,其中S0-S16是異常上下文會自動保存的。進程
a、M4內核堆棧的初始化,與M3相比多了保存34個寄存的內容
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt) { OS_STK *p_stk; (void)opt; /* 'opt' is not used, prevent warning */ p_stk = ptos; /* Load stack pointer */ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) *(--p_stk) = (INT32U)0x00000000L; //No Name Register *(--p_stk) = (INT32U)0x00001000L; //FPSCR *(--p_stk) = (INT32U)0x00000015L; //s15 *(--p_stk) = (INT32U)0x00000014L; //s14 *(--p_stk) = (INT32U)0x00000013L; //s13 *(--p_stk) = (INT32U)0x00000012L; //s12 *(--p_stk) = (INT32U)0x00000011L; //s11 *(--p_stk) = (INT32U)0x00000010L; //s10 *(--p_stk) = (INT32U)0x00000009L; //s9 *(--p_stk) = (INT32U)0x00000008L; //s8 *(--p_stk) = (INT32U)0x00000007L; //s7 *(--p_stk) = (INT32U)0x00000006L; //s6 *(--p_stk) = (INT32U)0x00000005L; //s5 *(--p_stk) = (INT32U)0x00000004L; //s4 *(--p_stk) = (INT32U)0x00000003L; //s3 *(--p_stk) = (INT32U)0x00000002L; //s2 *(--p_stk) = (INT32U)0x00000001L; //s1 *(--p_stk) = (INT32U)0x00000000L; //s0 #endif /* Registers stacked as if auto-saved on exception */ *(--p_stk) = (OS_STK)0x01000000uL; /* xPSR */ *(--p_stk) = (OS_STK)task; /* Entry Point */ *(--p_stk) = (OS_STK)OS_TaskReturn; /* R14 (LR) */ *(--p_stk) = (OS_STK)0x12121212uL; /* R12 */ *(--p_stk) = (OS_STK)0x03030303uL; /* R3 */ *(--p_stk) = (OS_STK)0x02020202uL; /* R2 */ *(--p_stk) = (OS_STK)0x01010101uL; /* R1 */ *(--p_stk) = (OS_STK)p_arg; /* R0 : argument */ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) *(--p_stk) = (INT32U)0x00000031L; //s31 *(--p_stk) = (INT32U)0x00000030L; //s30 *(--p_stk) = (INT32U)0x00000029L; //s29 *(--p_stk) = (INT32U)0x00000028L; //s28 *(--p_stk) = (INT32U)0x00000027L; //s27 *(--p_stk) = (INT32U)0x00000026L; //s26 *(--p_stk) = (INT32U)0x00000025L; //s25 *(--p_stk) = (INT32U)0x00000024L; //s24 *(--p_stk) = (INT32U)0x00000023L; //s23 *(--p_stk) = (INT32U)0x00000022L; //s22 *(--p_stk) = (INT32U)0x00000021L; //s21 *(--p_stk) = (INT32U)0x00000020L; //s20 *(--p_stk) = (INT32U)0x00000019L; //s19 *(--p_stk) = (INT32U)0x00000018L; //s18 *(--p_stk) = (INT32U)0x00000017L; //s17 *(--p_stk) = (INT32U)0x00000016L; //s16 #endif /* Remaining registers saved on process stack */ *(--p_stk) = (OS_STK)0x11111111uL; /* R11 */ *(--p_stk) = (OS_STK)0x10101010uL; /* R10 */ *(--p_stk) = (OS_STK)0x09090909uL; /* R9 */ *(--p_stk) = (OS_STK)0x08080808uL; /* R8 */ *(--p_stk) = (OS_STK)0x07070707uL; /* R7 */ *(--p_stk) = (OS_STK)0x06060606uL; /* R6 */ *(--p_stk) = (OS_STK)0x05050505uL; /* R5 */ *(--p_stk) = (OS_STK)0x04040404uL; /* R4 */ return (p_stk); }
a、M4內核的任務切換,與M3相比多了保存16個寄存器
PendSV_Handler //;中斷處理函數 CPSID I //; Prevent interruption during context switch ;在任務切換時禁止其它中斷 MRS R0, PSP //; PSP is process stack pointer ; CBZ R0, OS_CPU_PendSVHandler_nosave //; Skip register save the first time ;檢查是不是從進程中進來的,若是不是,直接跳到OS_CPU_PendSVHandler_nosave //;Is the task using the FPU context? If so, push high vfp registers.支持M4內核保存S16-S31寄存器 TST R14, #0x10 IT EQ VSTMDBEQ R0!, {S16-S31} SUBS R0, R0, #0x20 //; Save remaining regs r4-11 on process stack; 保存中斷上下文未自動保存的寄存器 STM R0, {R4-R11} LDR R1, =OSTCBCur // ; OSTCBCur->OSTCBStkPtr = SP; LDR R1, [R1] STR R0, [R1] //; R0 is SP of process being switched out 保存當前任務堆棧指針到當前任務控制塊 //; At this point, entire context of process has been saved OS_CPU_PendSVHandler_nosave PUSH {R14} //; Save LR exc_return value LDR R0, =OSTaskSwHook //; OSTaskSwHook(); BLX R0 //; POP {R14} //;//只是爲了調用OSTaskSwHook函數 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 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDM R0, {R4-R11} // ; Restore r4-11 from new process stack 取出新任務的R4-R11寄存器值 ADDS R0, R0, #0x20 ;Is the task using the FPU context? If so, push high vfp registers.支持M4內核出棧S16-S31寄存器 TST R14, #0x10 IT EQ VLDMIAEQ R0!, {S16-S31} MSR PSP, R0 // ; Load PSP with new process SP 切換新任務指針給PSP ORR LR, LR, #0x04 // ; Ensure exception return uses process stack 從進程堆棧中做出出棧操做 CPSIE I // ; 開啓中斷 BX LR // ; Exception return will restore remaining context 任務切換,切換到新任務被切換時的地址 END
以上就是移值UCOS2到M4核與M3核的區別