|
UC/OS-II的移植步驟分析 zqcumt 關于UC/OS-II的移植網上介紹的已經很多了,比較流行的幾款處理器(例如ARM)在網上都可以直接下載移植好的代碼。由于最近選修了一門嵌入式系統的課,用的處理器是EPSON公司的S 大家下載到源碼后,針對Intel 80x86的代碼在uCOS-II\Ix INCLUDES.H 是主頭文件,在所有后綴名為.C的文件的開始都包含INCLUDES.H文件。使用INCLUDES.H的好處是所有的.C文件都只包含一個頭文件,程序簡潔,可讀性強。缺點是.C文件可能會包含一些它并不需要的頭文件,額外的增加編譯時間。與優(yōu)點相比,多一些編譯時間還是可以接受的。用戶可以改寫INCLUDES.H文件,增加自己的頭文件,但必須加在文件末尾。 /////////////////////////////////////////////////////////////////////////////// 一、(1)OS_CPU.H文件的移植 (針對S ////////////////////////////////////////////////////////////////////////// OS_CPU.H 文件中包含與處理器相關的常量,宏和結構體的定義。 #ifdef OS_CPU_GLOBALS #define OS_CPU_EXT //全局變量 #else #define OS_CPU_EXT extern #endif /////////////////////////////////////////////////////////////////////////////// 由于不同的處理器有不同的字長,μC/OS-II的移植需要重新定義一系列的數據結構。這部分是和處理器相關的. typedef unsigned char BOOLEAN; typedef unsigned char INT8U; typedef signed char INT8S; typedef unsigned short INT16U; typedef signed short INT16S; typedef unsigned int INT32U; typedef signed int INT32S; //因為沒有浮點運算所以刪掉 typedef unsigned int OS_STK;//定義 堆棧的 寬度為 16位 typedef unsigned int OS_CPU_SR;//定義 狀態(tài)寄存器的寬度為16位 /////////////////////////////////////////////////////////////////////////////// 下面的部分主要是為了和UC/OS 第一版的兼容 #define BYTE INT8S #define UBYTE INT8U #define WORD INT16S #define UWORD INT16U #define LONG INT32S #define ULONG INT32U /////////////////////////////////////////////////////////////////////////////// 與其他實時系統一樣,μC/OS-II在進入系統臨界代碼區(qū)之前要關閉中斷,等到退出臨界區(qū)后再打開。從而保護核心數據不被多任務環(huán)境下的其他任務或中斷破壞。Borland C/C++支持嵌入匯編語句,所以加入關閉/打開中斷的語句是很方便的。μC/OS-II定義了兩個宏用來關閉/打開中斷:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。下面定義了三種方法,具體的可以查閱相關書籍. ////////////////////////////////////////////////////////////// #define OS_CRITICAL_METHOD 2 //使用第二種方法 /////////////////////////////////////////////////////////////////////////////// #if OS_CRITICAL_METHOD == 1 //第一種方法,由于沒有用到,我們不用去修改,可以注釋掉 #define OS_ENTER_CRITICAL() asm CLI #define OS_EXIT_CRITICAL() asm STI #endif /////////////////////////////////////////////////////////////////////////////// #if OS_CRITICAL_METHOD == 2 //第二種方法,這個是我們用到的,要修改,一般用匯編寫,根據各個處理器的不同而不同,下面是S #define OS_ENTER_CRITICAL() asm(" ld.w %r4, %psr"); asm(" xld.w %r5, 0xffffffef"); asm(" and %r4, %r5");//關中斷,保持狀態(tài)寄存器的其它狀態(tài)不變 asm(" ld.w %psr, %r4"); #define OS_EXIT_CRITICAL() asm(" ld.w %r4, %psr"); asm(" or %r4, 0b10000"); asm(" ld.w %psr, %r4"); //開中斷,保持狀態(tài)寄存器的其它狀態(tài)不變 #endif /////////////////////////////////////////////////////////////////////////////// #if OS_CRITICAL_METHOD == 3 //第三種方法,由于沒有用到,我們不用去修改,可以直接注視掉 #define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR()) #define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr)) #endif /////////////////////////////////////////////////////////////////////////// #define OS_STK_GROWTH 1 //堆棧的增長方向,由高相低,這個也是和處理器相關的,有的處理器堆棧是由低向高變,只要定義為零即可 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 在 μC/OS-II中, 就緒任務的堆棧初始化應該模擬一次中斷發(fā)生后的樣子,堆棧中應該按進棧次序設置好各個寄存器的內容。OS_TASK_SW()函數模擬一次中斷過程,在中斷返回的時候進行任務切換。中斷服務程序(ISR)(也稱為例外處理過程)的入口點必須指向匯編函數OSCtxSw(),參看文件OS_CPU_A.ASM.在中斷向量表vector.c的代碼中修改向量表如下 (unsigned long) OSCtxSw, // 48 12 software exception 0 /////////////////////////////////////////////////////////////////////// #define uCOS 0 #define OS_TASK_SW() asm(" int 0"); / /使用零號中斷來進行任務切換 /////////////////////////////////////////////////////////////////////////////// 可以注釋掉,主要是用于在PC機上模擬時鐘節(jié)拍 OS_CPU_EXT INT8U OSTickDOSCtr; //全局變量 //////////////////////////////////////////////////////////////////////////// 可以注釋掉 #if OS_CRITICAL_METHOD == 3 OS_CPU_SR OSCPUSaveSR(void); void OSCPURestoreSR(OS_CPU_SR cpu_sr); #endif /////////////////////////////////////////////////////////////////////////////// (2)OS_CPU.H文件的移植 (針對ARM核的S /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// 具體的實現見第二步, 在OS_CPU_A.ASM中實現 考下面的程序*/ /////////////////////////////////////////////////////////////////////////////// 定義宏OS_TASK_SW(),這個宏實際上被定義為os_CPU_a.s中的函數OSCtxSw()。由此可以了解OSCtxSw()的任務:保存當前任務上下文,裝入新任務上下文。這里并沒有用到模擬軟中斷 #define OS_TASK_SW OSCtxSw /////////////////////////////////////////////////////////////////////////////// // Definitions specific to ARM/uHAL #define SVC32MODE 0x13 //定義空閑任務堆棧的大小,可以不用定義這部分 #define SEMIHOSTED_STACK_NEEDS 1024 // idle task stack size (words) #ifdef SEMIHOSTED #define OS_IDLE_STK_SIZE (32+SEMIHOSTED_STACK_NEEDS) #else #define OS_IDLE_STK_SIZE 32 #endif // defined in os_cpu_a.s 聲明這些函數,在后面都有所定義 extern void OSCtxSw(void); // task switch routine extern void OSIntCtxSw(void); // interrupt context switch extern void ARMDisableInt(void); // disable global interrupts extern void ARMEnableInt(void); // enable global interrupts extern void OSTickISR(void); // timer interrupt routine /////////////////////////////////////////////////////////////////////////////// 二、(1)OS_CPU.A.S文件的移植 (針對S /////////////////////////////////////////////////////////////////////////////// μC/OS-II 的移植需要用戶改寫OS_CPU_A.ASM中的四個函數: OSStartHighRdy() OSCtxSw() OSIntCtxSw() OSTickISR() //////////////////////////////////////////////////////////////// 該函數由SStart()函數調用,功能是運行優(yōu)先級最高的就緒任務,在調用OSStart()之前,用戶必須先調用OSInit(),并且已經至少創(chuàng)建了一個任務(請參考OSTaskCreate()和OSTaskCreateExt()函數)。OSStartHighRdy()默認指針OSTCBHighRdy指向優(yōu)先級最高就緒任務的任務控制塊(OS_TCB)(在這之前OSTCBHighRdy已由OSStart()設置好了)。OSTCBHighRdy->OSTCBStkPtr指向的是任務堆棧的頂端 OSStartHighRdy: xcall OSTaskSwHook //調用OSTaskSwHook ,此時OSRunning為FALSE ld.w %r4, 0x1 xld.w %r5, OSRunning xld.b [%r5], %r4 //使OSRunning的狀態(tài)為TRUE ,以后調用OSTaskSwHook時會先保存寄存器再恢復 xld.w %r5, [OSTCBHighRdy]; ld.w %sp, %r5;//得到最高優(yōu)先級任務的堆棧指針 xld.w %r4, [%sp+0x0]; ld.w %sp, %r4;//偏移量為0 popn %r15 //恢復r15-r0,這個是S reti; //返回,此命名執(zhí)行時,處理器會自動恢復PC和狀態(tài)寄存器的值,至此新任務 /////////////////////////////////////////////////////////////////////////////// OSCtxSw()是一個任務級的任務切換函數(在任務中調用,區(qū)別于在中斷程序中調用的OSIntCtxSw())。它通過執(zhí)行一條軟中斷的指令來實現任務切換。軟中斷向量指向OSCtxSw()。在μC/OS-II中,如果任務調用了某個函數,而該函數的執(zhí)行結果可能造成系統任務重新調度(例如試圖喚醒了一個優(yōu)先級更高的任務),則在函數的末尾會調用OSSched(),如果OSSched()判斷需要進行任務調度,會找到該任務控制塊OS_TCB的地址,并將該地址拷貝到OSTCBHighRdy,然后通過宏OS_TASK_SW()執(zhí)行軟中斷進行任務切換。。注意到在此過程中,變量OSTCBCur始終包含一個指向當前運行任務OS_TCB的指針。大部分解釋同上,只是多了寄存器的保存這一段。 OSCtxSw: xcall OSTaskSwHook //中斷時,PC和寄存器的值S pushn %r15;// Save current task's context xld.w %r4, [OSTCBCur];//指向當前的運行任務 ld.w %r5, %sp; Save the SP to R5 ld.w %sp, %r4;// 保存當前任務的堆棧指針 ld.w [%sp+0x0], %r5 ; //Save the SP to OSTCBCur xld.w %r4, [OSTCBHighRdy] ; //OSTCBCur = OSTCBHighRdy xld.w %r5, OSTCBCur; ld.w [%r5], %r4 ; xld.w %r4, [OSPrioHighRdy]; //OSPrioCur = OSPrioHighRdy,把任務優(yōu)先級也保存 xld.w %r5, OSPrioCur ; ld.b [%r5], %r4 xld.w %r4, [OSTCBCur];//載入新的任務 ld.w %sp, %r4 ld.w %r5, [%sp+0x0];//恢復新任務的堆棧 ld.w %sp, %r5 popn %r15 ; reti ; //運行新的任務 /////////////////////////////////////////////////////////////////////////////// 在μC/OS-II中,由于中斷的產生可能會引起任務切換,在中斷服務程序的最后會調用OSIntExit()函數檢查任務就緒狀態(tài),如果需要進行任務切換,將調用OSIntCtxSw()。所以OSIntCtxSw()又稱為中斷級的任務切換函數。由于在調用OSIntCtxSw()之前已經發(fā)生了中斷,OSIntCtxSw()將默認CPU寄存器已經保存在被中斷任務的堆棧中了。因此在中斷服務程序中要保存寄存器,PC和狀態(tài)寄存器的值已經被處理器自動保存。OSIntCtxSw()大部分程序和OSCtxSw()相同只是不用保存寄存器,它也可直接用OSCtxSw()來實現 OSIntCtxSw: xcall OSTaskSwHook ; //Call user defined task switch hook xld.w %r4, [OSTCBHighRdy] ;// OSTCBCur = OSTCBHighRdy xld.w %r5, OSTCBCur ; ld.w [%r5], %r4 ; xld.w %r4, [OSPrioHighRdy] ; //OSPrioCur = OSPrioHighRdy,把任務優(yōu)先級也保存 xld.w %r5, OSPrioCur ; ld.b [%r5], %r4 xld.w %r4, [OSTCBCur] //載入新的任務 ld.w %sp, %r4 ld.w %r5, [%sp+0x0] ld.w %sp, %r5 popn %r15 ; reti //Return to new task /////////////////////////////////////////////////////////////////////////////// 和μC/OS-II中的其他中斷服務程序一樣,OSTickISR()首先在被中斷任務堆棧中保存CPU寄存器的值,然后調用OSIntEnter()。μC/OS-II要求在中斷服務程序開頭調用OSIntEnter(),其作用是將記錄中斷嵌套層數的全局變量OSIntNesting加1。如果不調用OSIntEnter(),直接將OSIntNesting加1也是允許的。OSTickISR()調用OSTimeTick(),檢查所有處于延時等待狀態(tài)的任務,判斷是否有延時結束就緒的任務。在OSTickISR()的最后調用OSIntExit(),如果在中斷中(或其他嵌套的中斷)有更高優(yōu)先級的任務就緒,并且當前中斷為中斷嵌套的最后一層。OSIntExit()將進行任務調度。注意如果進行了任務調度,OSIntExit()將不再返回調用者,而是用新任務的堆棧中的寄存器數值恢復CPU現場,然后用IRET實現任務切換。如果當前中斷不是中斷嵌套的最后一層,或中斷中沒有改變任務的就緒狀態(tài),OSIntExit()將返回調用者OSTickISR(),最后OSTickISR()返回被中斷的任務。如果編譯器支持C語言和匯編的混合編程,則這段代碼可以放到OS_CPU_C.C中,針對S void OSTickISR() { asm( " pushn %r15");//保存中斷的任務的寄存器 /////////////////////////////////////////////////////////////////////////////// 在這個移植中以8位定時器TIME2 作為時鐘節(jié)拍,2MS發(fā)生一次中斷,在中斷向量表vector.c 中在timer2的入口地址處放入(unsigned long)OSTickISR, 發(fā)生中斷后程學將會跳到此程序處執(zhí)行。 *(volatile unsigned char*)0x40285 |= 0x04; // 清除timer2的中斷標志位 OSIntEnter();//處理中斷嵌套曾數的增加也可以直接 給OSIntNesting加一 if (OSIntNesting == 1) { asm(" ld.w %r4, %sp");//如果嵌套層數為1 則在當前的任務控制塊中保存堆棧指針 asm(" xld.w %r10, [OSTCBCur]"); asm(" ld.w %sp, %r10"); asm(" ld.w [%sp+0x0], %r4"); asm(" ld.w %sp, %r4"); } OSTimeTick(); // 給等待延遲時間的任務的參數減1 OSIntExit(); //調用這個函數,如果ISR使更高優(yōu)先級的任務進入就緒態(tài)或者ISR脫離 // 了中斷嵌套,則此函數不會返回,而是由進行中斷級任務切換,否 //此函數返回OSTickISR,然后恢復寄存器 asm(" popn %r15");//恢復寄存器 asm(" reti");//返回中斷的任務繼續(xù)運行 } /////////////////////////////////////////////////////////////////////////////// 為了更清楚一點這里面的過程,順便付上這里用到TIMER2 的程序,最好有個感性的認識 這部分程序應該在驅動程序里或者放在初始化程序里。始終節(jié)拍中斷的啟動(定時器2的啟動)應該放在OSStart()運行之后,但是OSStart()不會返回,所以應該放在OSStart()之前建立的任務中的優(yōu)先級最高的任務中啟動,如果放在 OSInit()和OSStart() 之間啟動,程序容易崩潰。 /* Prototype */ void init_timer(void); void Start_Timer(void); ///////////////////////////////////////////////////////////////////////////// 定時器2 的初始化,完成定時時間等一下設置,每隔2ms發(fā)生一次的中斷 void init_timer(void) { *(volatile unsigned char *)0x4014E |= 0x *(volatile unsigned char *)0x40169 = 0x92;/ *(volatile unsigned char *)0x40168 |= 0x02; *(volatile unsigned char *)0x40285 &= 0xFB; *(volatile unsigned char *)0x40275 |= 0x04; } /////////////////////////////////////////////////////////////////////////////// 啟動定時器2 void Start_Timer(void) { *(volatile unsigned char*)0x40168 |= 0x01; } ////////////////////////////////////////////////////////////////////////////// (2)OS_CPU.A.S文件的移植 (針對ARM核的S ///////////////////////////////////////////////////////////////////////////// μC/OS-II 的移植需要用戶改寫OS_CPU_A.ASM中的四個函數: OSStartHighRdy() OSCtxSw() OSIntCtxSw() OSTickISR() 同時對于ARM的開關中斷(ARMEnableInt,ARMDisableInt)的定義也是放在這個文件下的 關于ARM的程序就不用解釋那么清楚了,相信英文大家都能看懂,也可以參考上面的程序實現的功能都是相同的 /////////////////////////////////////////////////////////////////////////////// EXPORT OSStartHighRdy IMPORT OSTaskSwHook IMPORT OSTCBHighRdy IMPORT OSRunning OSStartHighRdy BL OSTaskSwHook //Call user-defined hook function LDR r4,=OSRunning // Indicate that multitasking has started MOV r5, #1 STRB r5, [r4] // OSRunning = true LDR r4, =OSTCBHighRdy // Get highest priority task TCB address LDR r4, [r4] // get stack pointer LDR sp, [r4] // switch to the new stack LDMFD sp!, {r4} ;// CPSR特殊,只能用MRS或MSR在寄存器間操作 MSR cpsr_cxsf, r4 //從r4中恢復cpsr ///////////////////////////////////////////////////////////////////////////////SVC模式下ARM處理器不會自動保存PC的所以需要自己保存和恢復 LDMFD sp!, {r0-r12,lr,pc} ; pop new task s r0-r12,lr & pc /////////////////////////////////////////////////////////////////////////////// EXPORT OSCtxSw //這個函數別的文件要用 IMPORT OSPrioCur //這是在別的文件定義的變量,當前任務優(yōu)先級 IMPORT OSPrioHighRdy //將要恢復執(zhí)行的任務的優(yōu)先級 IMPORT OSTCBCur //當前任務的TCB的指針 IMPORT OSTaskSwHook //調用用戶定義HOOK IMPORT OSTCBHighRdy //將要恢復執(zhí)行的任務的TCB指針 OSCtxSw STMFD sp!, {lr} // push pc (lr is actually be pushed in place of PC) 因為是從OS_Sched() BL到這里的 STMFD sp!, {r0-r12,lr} // push lr & register file MRS r4, cpsr // CPSR特殊,只能用MRS或MSR在寄存器間操作 STMFD sp!, {r4} // push current psr LDR r4, =OSTCBCur // Get current task TCB address LDR r5, [r4] STR sp, [r5] // store sp in preempted tasks s TCB ///////////////////////////////////////////////////////////////////////////// 以下程序段和OSIntCtxSw相同,可以共用一段 BL OSTaskSwHook // call Task Switch Hook LDR r5, =OSTCBHighRdy // 得到就緒任務中的最高優(yōu)先級的任務 LDR r5, [r5] STR r5, [r4] //使當前任務指針指向最高優(yōu)先級的任務 OSTCBCur = OSTCBHighRdy LDR r6, =OSPrioHighRdy LDRB r6, [r6] LDR r4, =OSPrioCur STRB r6, [r4] //保存優(yōu)先級到當前的優(yōu)先級 LDR sp, [r5] //get new task s stack pointer LDMFD sp!, {r4} //pop new task cpsr MSR cpsr_cxsf, r4 LDMFD sp!, {r0-r12,lr,pc} //切換到新的任務 /////////////////////////////////////////////////////////////////////////////// 關于OSIntCtxSw()就是上面那下半截。這是因為:ARM硬件的中斷時并不自動壓棧任何寄存器,所以免去了恢復堆棧指針的麻煩;另外,我們最好在進入ISR保存當前任務現場時一同保存好TCB中的堆棧指針,而不是在OSIntCtxSw()中保存。具體的解釋也可以參考上面這里只是用的寄存器不同而已。 IMPORT OSTaskSwHook OSIntCtxSw BL OSTaskSwHook //調用OSTaskSwHook函數 LDR r4, =OSTCBHighRdy //得到當前最高優(yōu)先級就緒的任務 LDR r4, [r4] LDR r5, =OSTCBCur STR r4, [r5] // OSTCBCur = OSTCBHighRdy LDR r6, =OSPrioHighRdy LDRB r6, [r6] LDR r5, =OSPrioCur STRB r6, [r5] // OSPrioCur = OSPrioHighRdy LDR sp, [r4] //得到新任務的堆棧指針 LDMFD sp!, {r4} // pop new task cpsr MSR cpsr_cxsf, r4 LDMFD sp!, {r0-r12,lr,pc} //切換到新的任務 /////////////////////////////////////////////////////////////////////////////// 這是 UCOS-II 搶占式調度ISR的一個標本。當一個優(yōu)先級高的任務放棄CPU使用權,例如要休眠 10 個 Tick,系統調度一個低優(yōu)先級的任務執(zhí)行之。OSTickISR()為休眠的任務計時,每次執(zhí)行,就把休眠任務剩余的睡覺時間減去一個Tick數。如果發(fā)現一個任務睡夠了,就順便恢復它為READY態(tài)。做完該做的一切,一個對OSIntExit()的調用,使調度發(fā)生了。 EXPORT OSTickISR IMPORT OSIntEnter IMPORT OSTimeTick IMPORT tick_hook IMPORT OSIntExit /////////////////////////////////////////////////////////////////////////////// 注意ARM的IRQ中斷發(fā)生后的PC保存(處理器自動保存LR=PC+4),而不是前面的PC=LR。另外,我們保存的是SVC模式下的現場,中斷后處理器進入IRQ模式,訪問不到SVC模式下的R13(sp),于是在IRQ模式下,只好先另存SPSR和LR,然后盡快退回到SVC模式,這時的R13才是任務的堆棧指針。在此模式下再將SPSR和LR保存到堆棧中,立即保存所有寄存器。任務是在SVC模式下運行。關于時鐘節(jié)拍怎么實現的(如果不是很懂就看下一篇文章關于ARM中斷處理的詳細分析)。 LINK_SAVE DCD 0 //申請一個字單元用0來初始化這個字 PSR_SAVE DCD 0 //地址為LINK_SAVE+4 OSTickISR STMFD sp!, {r4} //這里的sp是IRQ 模式下的,將r4壓入堆棧 /////////////////////////////////////////////////////////////////////////// 另存IRQ模式下的SPSR和LR,以便在SVC模式下也能訪問,相當于一個中介作用 LDR r4, =LINK_SAVE STR lr, [r4] //LINK_SAVE = lr,保存lr,此lr為IRQ模式下 MRS lr, spsr //lr=spsr STR lr, [r4, #4] // PSR_SAVE = spsr_irq,保存spsr //////////////////////////////////////////////////////////////////////////// LDMFD sp!, {r4} //恢復r4中的內容 ORR lr, lr, #0x80 // Mask irq for context switching before MSR cpsr_cxsf , lr // 從IRQ模式恢復到SVC模式 //////////////////////////////////////////////////////////////////////////// SUB sp, sp, #4 // Space for給PC保留位置 STMFD sp!, {r0-r12, lr} //保存寄存器和lr LDR r4, =LINK_SAVE //r4= lr_irq LDR lr, [r4, #0] //lr=lr_irq SUB lr, lr, #4 // PC = LINK_SAVE - 4,這個一定要正確 /////////////////////////////////////////////////////////////////////////////// 將PC= LR-4存回到堆棧中,剛才跳過了PC 4字節(jié)的空間 (R1到R12再加lr共占了14個字) STR lr, [sp, #(14*4)] //sp=sp+14*4, 因為堆棧是從高地址向低地址遞減 PC=LR -4 LDR r4, [r4, #4] // r4 = PSR_SAVE, STMFD sp!, {r4} // save CPSR of the task LDR r4, =OSTCBCur //將sp保存到當前的任務中 LDR r4, [r4] STR sp, [r4] // OSTCBCur -> stkptr = sp BL OSIntEnter //處理中斷嵌套曾數的增加也可以直接 給OSIntNesting加一 BL OSTimeTick //調用ostimetick() BL tick_hook // 我們在Tick_hook()里清除S BL OSIntExit //決定是否進行任務調度 ///////////////////////////////////////////////////////////////////////////// 如果返回則繼續(xù)運行此任務 LDMFD sp!, {r4} //pop new task cpsr MSR cpsr_cxsf, r4 LDMFD sp!, {r0-r12,lr,pc} // pop new task r0-r12,lr & pc /////////////////////////////////////////////////////////////////////////////// 定義關中斷,主要是為了安全訪問臨界區(qū)的資源 EXPORT ARMDisableInt ARMDisableInt MRS r0, cpsr STMFD sp!, {r0} // push current PSR ORR r0, r0, #0xC0 MSR cpsr_c, r0 //disable IRQ Int s MOV pc, lr //返回 /////////////////////////////////////////////////////////////////////////////// 定義開中斷 EXPORT ARMEnableInt ARMEnableInt LDMFD sp!, {r0} // pop current PSR MSR cpsr_c, r0 //restore original cpsr MOV pc, lr //返回 /////////////////////////////////////////////////////////////////////////////// 三、(1)OS_CPU.C.C文件的移植 (針對S /////////////////////////////////////////////////////////////////////////////// μC/OS-II 的移植需要用戶改寫OS_CPU_C.C中的六個函數: OSTaskStkInit() OSTaskCreateHook() OSTaskDelHook() OSTaskSwHook() OSTaskStatHook() OSTimeTickHook() 實際需要修改的只有OSTaskStkInit()函數,其他五個函數需要聲明,但不一定有實際內容。這五個函數都是用戶定義的,所以OS_CPU_C.C中沒有給出代碼。如果用戶需要使用這些函數,請將文件OS_CFG.H中的#define constant OS_CPU_HOOKS_EN設為1,設為0表示不使用這些函數。 /////////////////////////////////////////////////////////////////////////////// 這個函數是很重要的 ,該函數由OSTaskCreate()或OSTaskCreateExt()調用,用來初始化任務的堆棧。初始狀態(tài)的堆棧模擬發(fā)生一次中斷后的堆棧結構。當調用OSTaskCreate()或OSTaskCreateExt()創(chuàng)建一個新任務時,需要傳遞的參數是:任務代碼的起使地址,參數指針(pdata),任務堆棧頂端的地址,任務的優(yōu)先級。OSTaskCreateExt()還需要一些其他參數,但與OSTaskStkInit()沒有關系。OSTaskStkInit()只需要以上提到的3個參數(task, pdata,和ptos)。在這個堆棧初始化函數中要清楚堆棧中都要保存哪些東西,要留多大的空間,這些都很重要,否則會發(fā)生很嚴重的錯誤。 OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { INT32U *stk; //定義一個指針 opt = opt; /* 這個參數沒有用,但是為了防止編譯錯誤*/ stk = (INT32U*)ptos; //載入堆棧指針 /////////////////////////////////////////////////////////////////////////// S *stk-- = (INT32U)task; //存放PC的地址,s //////////////////////////////////////////////////////////////////////////// 存放狀態(tài)寄存器,同樣也會被自動保存,設置為中斷開啟 參考其PSR每位的作用。如果選擇任務啟動后允許中斷發(fā)生,則所有的任務運行期間中斷都允許;同樣,如果選擇任務啟動后禁止中斷,則所有的任務都禁止中斷發(fā)生,而不能有所選擇。知道為什么嗎?因為啟動的時候,OSStart()調用的是 OSStartHighRdy,即從堆棧中恢復PC和SPR以及寄存器中的內容,因此第一次堆棧中的放的值決定了spr,其它寄存器的值到沒有什么關系。 *stk-- = (INT32U)0x00000010; *stk-- = (INT32U)0; //存R15中的值 *stk-- = (INT32U)0; //--R14 *stk-- = (INT32U)0; //--R13 *stk-- = (INT32U)0; //--R12 *stk-- = (INT32U)0; //--R11 *stk-- = (INT32U)0; //--R10 *stk-- = (INT32U)0; //--R9 *stk-- = (INT32U)0; //--R8 *stk-- = (INT32U)0; //--R7 *stk-- = (INT32U)0; //--R6 *stk-- = (INT32U)0; //--R5 *stk-- = (INT32U)0; //--R4 *stk-- = (INT32U)0; //--R3 *stk-- = (INT32U)0; //--R2 *stk-- = (INT32U)0; //--R1 *stk = (INT32U)0; //--R0 return ((OS_STK *)stk); //返回堆棧指針所指向的地址,恢復寄存器時候要用 } /////////////////////////////////////////////////////////////////////////////// |
|
|