這個工做主要是根據網友的經驗資料來學習移植的。總的來講須要下面幾個參考資料。架構
ARM Cortex-m3權威指南app
官方移植文檔資料框架
網友移植成功經驗資料ide
這三種資料在個人資源上傳裏面都能找到。函數
我在官網上下的是官方已經移植好的到STM32F103評估板的資料,不少部分已經實現了,因此須要改動的地方不多,不一樣的地方能夠參考第三種網友移植成功經驗資料,裏面有詳細說明。我下的是ucosii2.86版本。學習
下面說說移植過程:ui
移植主要涉及到兩個源文件,os_cpu_c.c和os_cpu_a.asm,os_cpu.h。其餘文件是ucosii核心文件,不須要修改。還有兩個配置文件app_cfg.h和os_cfg.h。this
整個文件框架構成圖以下,用的是IAR。操作系統
這就是對照上面的說明而設置的目錄結構。指針
下面談主要移植的部分。主要集中在os_cpu_a.asm這個文件中,os_cpu.h主要涉及到一些宏的配置。os_cpu_c.c中主要有一個函數必須寫出,就是堆棧初始化函數。
先說os_cpu.h中須要注意的地方。
下面是開關中斷的宏
#define OS_CRITICAL_METHOD 3 #if OS_CRITICAL_METHOD == 3 #define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();} #define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);} #endif
CM3堆棧方向高到底遞減,設置爲1,還有一個任務切換的宏。
#define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory on ARM */ #define OS_TASK_SW() OSCtxSw()
還有幾個關於systick的函數須要註釋掉,下面的os_cpu_c.c會說道緣由。
// /* See OS_CPU_C.C */ //void OS_CPU_SysTickHandler(void); //void OS_CPU_SysTickInit(void); // // /* See BSP.C */ //INT32U OS_CPU_SysTickClkFreq(void);
看os_cpu_c.c。
裏面有些函數是鉤子函數,根據須要寫。若是不須要能夠不寫。
下面是堆棧初始化函數。
/* ********************************************************************************************************* * INITIALIZE A TASK'S STACK * * Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the * stack frame of the task being created. This function is highly processor specific. * * Arguments : task is a pointer to the task code * * p_arg is a pointer to a user supplied data area that will be passed to the task * when the task first executes. * * ptos is a pointer to the top of stack. It is assumed that 'ptos' points to * a 'free' entry on the task stack. If OS_STK_GROWTH is set to 1 then * 'ptos' will contain the HIGHEST valid address of the stack. Similarly, if * OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address * of the stack. * * opt specifies options that can be used to alter the behavior of OSTaskStkInit(). * (see uCOS_II.H for OS_TASK_OPT_xxx). * * Returns : Always returns the location of the new top-of-stack once the processor registers have * been placed on the stack in the proper order. * * Note(s) : 1) Interrupts are enabled when your task starts executing. * 2) All tasks run in Thread mode, using process stack. ********************************************************************************************************* */ OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt) { OS_STK *stk; (void)opt; /* 'opt' is not used, prevent warning */ stk = ptos; /* Load stack pointer */ /* Registers stacked as if auto-saved on exception */ *(stk) = (INT32U)0x01000000L; /* xPSR */ *(--stk) = (INT32U)task; /* Entry Point */ *(--stk) = (INT32U)0xFFFFFFFEL; /* R14 (LR) (init value will cause fault if ever used)*/ *(--stk) = (INT32U)0x12121212L; /* R12 */ *(--stk) = (INT32U)0x03030303L; /* R3 */ *(--stk) = (INT32U)0x02020202L; /* R2 */ *(--stk) = (INT32U)0x01010101L; /* R1 */ *(--stk) = (INT32U)p_arg; /* R0 : argument */ /* Remaining registers saved on process stack */ *(--stk) = (INT32U)0x11111111L; /* R11 */ *(--stk) = (INT32U)0x10101010L; /* R10 */ *(--stk) = (INT32U)0x09090909L; /* R9 */ *(--stk) = (INT32U)0x08080808L; /* R8 */ *(--stk) = (INT32U)0x07070707L; /* R7 */ *(--stk) = (INT32U)0x06060606L; /* R6 */ *(--stk) = (INT32U)0x05050505L; /* R5 */ *(--stk) = (INT32U)0x04040404L; /* R4 */ return (stk); }
由於CM3支持的堆棧地址是從高到低遞減的,因此裏面都是--。並且CM3中斷自動入棧順序爲xPSR,PC,R14,R12,R3~R0,這個能夠參考CM3權威指南。因此放在前面。後面R11~R4須要咱們手動入棧。最後返回棧頂指針。
OS_CPU_C.c中還有一些東西須要咱們註釋掉,由於官方的資料是根據評估板來寫的,若是移植到咱們有STM32固件庫的平臺上,則須要作些改變,這是參考網友移植經驗知道的。這些須要註釋掉的東西主要是跟systic這個定時器有關。
下面是須要註釋掉的地方。
//void OS_CPU_SysTickInit (void) //{ // INT32U cnts; // // // cnts = OS_CPU_SysTickClkFreq() / OS_TICKS_PER_SEC; // // OS_CPU_CM3_NVIC_ST_RELOAD = (cnts - 1); // /* Enable timer. */ // OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE; // /* Enable timer interrupt. */ // OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN; //}
//void OS_CPU_SysTickHandler (void) //{ // OS_CPU_SR cpu_sr; // // // OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */ // OSIntNesting++; // OS_EXIT_CRITICAL(); // // OSTimeTick(); /* Call uC/OS-II's OSTimeTick() */ // // OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */ //}
//#define OS_CPU_CM3_NVIC_ST_CTRL (*((volatile INT32U *)0xE000E010)) /* SysTick Ctrl & Status Reg. */ //#define OS_CPU_CM3_NVIC_ST_RELOAD (*((volatile INT32U *)0xE000E014)) /* SysTick Reload Value Reg. */ //#define OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018)) /* SysTick Current Value Reg. */ //#define OS_CPU_CM3_NVIC_ST_CAL (*((volatile INT32U *)0xE000E01C)) /* SysTick Cal Value Reg. */ // //#define OS_CPU_CM3_NVIC_ST_CTRL_COUNT 0x00010000 /* Count flag. */ //#define OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004 /* Clock Source. */ //#define OS_CPU_CM3_NVIC_ST_CTRL_INTEN 0x00000002 /* Interrupt enable. */ //#define OS_CPU_CM3_NVIC_ST_CTRL_ENABLE 0x00000001 /* Counter mode. */
os_cpu.c.c中須要注意到的地方差很少就是這些了。
下面是os_cpu_a.asm.這裏面的東西最多了,也相對難理解,這就須要仔細看CM3權威指南這個資料了。
這是上面開關中斷宏的彙編語言實現。
OS_CPU_SR_Save MRS R0, PRIMASK ; Set prio int mask to mask all (except faults) CPSID I BX LR OS_CPU_SR_Restore MSR PRIMASK, R0 BX LR
啓動最高優先級函數,只調用一次,在OSStart()中被調用。
;******************************************************************************************************** ; START MULTITASKING ; void OSStartHighRdy(void) ; ; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause ; the first task to start. ; ; 2) OSStartHighRdy() MUST: ; a) Setup PendSV exception priority to lowest; ; b) Set initial PSP to 0, to tell context switcher this is first run; ; c) Set OSRunning to TRUE; ; d) Trigger PendSV exception; ; e) Enable interrupts (tasks will run with interrupts enabled). ;******************************************************************************************************** OSStartHighRdy LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority LDR R1, =NVIC_PENDSV_PRI STRB R1, [R0] MOVS R0, #0 ; Set the PSP to 0 for initial context switch call MSR PSP, R0 LDR R0, =OSRunning ; OSRunning = TRUE MOVS R1, #1 STRB R1, [R0] LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] CPSIE I ; Enable interrupts at processor level OSStartHang B OSStartHang ; Should never get here
主要是手動懸起PendSV中斷,設置OSRunning爲1,而後開啓中斷以後好進入PendSV中斷處理函數中進行任務切換,開始運行ucosii。PendSV這個是須要掌握的,才能理解CM3是如何實現操做系統的管理的。
下面是兩個任務切換函數,一個是任務級間切換,一個是中斷與任務間切換,雖然代碼相同,但意義不一樣,這主要就跟上面說道的PendSV中斷有關了。由於這個中斷的存在,咱們的任務切換中實現的主要工做就是觸發PendSV中斷,讓這個中斷去處理任務切換相關細節。
;******************************************************************************************************** ; PERFORM A CONTEXT SWITCH (From task level) ; void OSCtxSw(void) ; ; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function ; triggers the PendSV exception which is where the real work is done. ;******************************************************************************************************** OSCtxSw LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LR ;******************************************************************************************************** ; PERFORM A CONTEXT SWITCH (From interrupt level) ; void OSIntCtxSw(void) ; ; Notes: 1) OSIntCtxSw() is called by OSIntExit() when it determines a context switch is needed as ; the result of an interrupt. This function simply triggers a PendSV exception which will ; be handled when there are no more interrupts active and interrupts are enabled. ;******************************************************************************************************** OSIntCtxSw LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LR
下面就是很是重要的PendSV中斷處理函數了。
;******************************************************************************************************** ; HANDLE PendSV EXCEPTION ; void OS_CPU_PendSVHandler(void) ; ; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing ; context switches with Cortex-M3. This is because the Cortex-M3 auto-saves half of the ; processor context on any exception, and restores same on return from exception. So only ; saving of R4-R11 is required and fixing up the stack pointers. Using the PendSV exception ; this way means that context saving and restoring is identical whether it is initiated from ; a thread or occurs due to an interrupt or exception. ; ; 2) Pseudo-code is: ; a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch); ; b) Save remaining regs r4-r11 on process stack; ; c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP; ; d) Call OSTaskSwHook(); ; e) Get current high priority, OSPrioCur = OSPrioHighRdy; ; f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy; ; g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr; ; h) Restore R4-R11 from new process stack; ; i) Perform exception return which will restore remaining context. ; ; 3) On entry into PendSV handler: ; a) The following have been saved on the process stack (by processor): ; xPSR, PC, LR, R12, R0-R3 ; b) Processor mode is switched to Handler mode (from Thread mode) ; c) Stack is Main stack (switched from Process stack) ; d) OSTCBCur points to the OS_TCB of the task to suspend ; OSTCBHighRdy points to the OS_TCB of the task to resume ; ; 4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we ; know that it will only be run when no other exception or interrupt is active, and ; therefore safe to assume that context being switched out was using the process stack (PSP). ;******************************************************************************************************** OS_CPU_PendSVHandler 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 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} 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 ADDS R0, R0, #0x20 MSR PSP, R0 ; Load PSP with new process SP ORR LR, LR, #0x04 ; Ensure exception return uses process stack CPSIE I BX LR ; Exception return will restore remaining context END
下面的說的就是上面忽略的部分systick,做爲操做系統的心臟,咱們須要本身來組織這個中斷。
在STM32庫中的終端處理文件stm32f10x_it.c中有個Systick_Handler()函數,在裏面添加處理。
/** * @brief This function handles SysTick Handler. * @param None * @retval None */ void SysTick_Handler(void) { OSIntEnter(); OSTimeTick(); OSIntExit(); }
在主函數main.c中添加systick初始化函數
static void Systick_init(void) { RCC_ClocksTypeDef rcc_clocks; RCC_GetClocksFreq(&rcc_clocks); SysTick_Config(rcc_clocks.HCLK_Frequency / OS_TICKS_PER_SEC); }
最後還有一個須要改動的就是要把STM32啓動文件中的全部PendSV_Handler替換成OS_CPU_ PendSVHandler,由於UCOS默認移植文件中使用的是OS_CPU_ PendSVHandler。