UC/OS-II的移植步驟分析 zqcumt 關(guān)于UC/OS-II的移植網(wǎng)上介紹的已經(jīng)很多了,比較流行的幾款處理器(例如ARM)在網(wǎng)上都可以直接下載移植好的代碼。由于最近選修了一門嵌入式系統(tǒng)的課,用的處理器是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 文件中包含與處理器相關(guān)的常量,宏和結(jié)構(gòu)體的定義。 #ifdef OS_CPU_GLOBALS #define OS_CPU_EXT //全局變量 #else #define OS_CPU_EXT extern #endif /////////////////////////////////////////////////////////////////////////////// 由于不同的處理器有不同的字長,μC/OS-II的移植需要重新定義一系列的數(shù)據(jù)結(jié)構(gòu)。這部分是和處理器相關(guān)的. 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 /////////////////////////////////////////////////////////////////////////////// 與其他實時系統(tǒng)一樣,μC/OS-II在進入系統(tǒng)臨界代碼區(qū)之前要關(guān)閉中斷,等到退出臨界區(qū)后再打開。從而保護核心數(shù)據(jù)不被多任務(wù)環(huán)境下的其他任務(wù)或中斷破壞。Borland C/C++支持嵌入?yún)R編語句,所以加入關(guān)閉/打開中斷的語句是很方便的。μC/OS-II定義了兩個宏用來關(guān)閉/打開中斷:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。下面定義了三種方法,具體的可以查閱相關(guān)書籍. ////////////////////////////////////////////////////////////// #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 //第二種方法,這個是我們用到的,要修改,一般用匯編寫,根據(jù)各個處理器的不同而不同,下面是S #define OS_ENTER_CRITICAL() asm(" ld.w %r4, %psr"); asm(" xld.w %r5, 0xffffffef"); asm(" and %r4, %r5");//關(guān)中斷,保持狀態(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 //堆棧的增長方向,由高相低,這個也是和處理器相關(guān)的,有的處理器堆棧是由低向高變,只要定義為零即可 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 在 μC/OS-II中, 就緒任務(wù)的堆棧初始化應(yīng)該模擬一次中斷發(fā)生后的樣子,堆棧中應(yīng)該按進棧次序設(shè)置好各個寄存器的內(nèi)容。OS_TASK_SW()函數(shù)模擬一次中斷過程,在中斷返回的時候進行任務(wù)切換。中斷服務(wù)程序(ISR)(也稱為例外處理過程)的入口點必須指向匯編函數(shù)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"); / /使用零號中斷來進行任務(wù)切換 /////////////////////////////////////////////////////////////////////////////// 可以注釋掉,主要是用于在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 /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// 具體的實現(xiàn)見第二步, 在OS_CPU_A.ASM中實現(xiàn) 考下面的程序*/ /////////////////////////////////////////////////////////////////////////////// 定義宏OS_TASK_SW(),這個宏實際上被定義為os_CPU_a.s中的函數(shù)OSCtxSw()。由此可以了解OSCtxSw()的任務(wù):保存當前任務(wù)上下文,裝入新任務(wù)上下文。這里并沒有用到模擬軟中斷 #define OS_TASK_SW OSCtxSw /////////////////////////////////////////////////////////////////////////////// // Definitions specific to ARM/uHAL #define SVC32MODE 0x13 //定義空閑任務(wù)堆棧的大小,可以不用定義這部分 #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 聲明這些函數(shù),在后面都有所定義 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中的四個函數(shù): OSStartHighRdy() OSCtxSw() OSIntCtxSw() OSTickISR() //////////////////////////////////////////////////////////////// 該函數(shù)由SStart()函數(shù)調(diào)用,功能是運行優(yōu)先級最高的就緒任務(wù),在調(diào)用OSStart()之前,用戶必須先調(diào)用OSInit(),并且已經(jīng)至少創(chuàng)建了一個任務(wù)(請參考OSTaskCreate()和OSTaskCreateExt()函數(shù))。OSStartHighRdy()默認指針OSTCBHighRdy指向優(yōu)先級最高就緒任務(wù)的任務(wù)控制塊(OS_TCB)(在這之前OSTCBHighRdy已由OSStart()設(shè)置好了)。OSTCBHighRdy->OSTCBStkPtr指向的是任務(wù)堆棧的頂端 OSStartHighRdy: xcall OSTaskSwHook //調(diào)用OSTaskSwHook ,此時OSRunning為FALSE ld.w %r4, 0x1 xld.w %r5, OSRunning xld.b [%r5], %r4 //使OSRunning的狀態(tài)為TRUE ,以后調(diào)用OSTaskSwHook時會先保存寄存器再恢復(fù) xld.w %r5, [OSTCBHighRdy]; ld.w %sp, %r5;//得到最高優(yōu)先級任務(wù)的堆棧指針 xld.w %r4, [%sp+0x0]; ld.w %sp, %r4;//偏移量為0 popn %r15 //恢復(fù)r15-r0,這個是S reti; //返回,此命名執(zhí)行時,處理器會自動恢復(fù)PC和狀態(tài)寄存器的值,至此新任務(wù) /////////////////////////////////////////////////////////////////////////////// OSCtxSw()是一個任務(wù)級的任務(wù)切換函數(shù)(在任務(wù)中調(diào)用,區(qū)別于在中斷程序中調(diào)用的OSIntCtxSw())。它通過執(zhí)行一條軟中斷的指令來實現(xiàn)任務(wù)切換。軟中斷向量指向OSCtxSw()。在μC/OS-II中,如果任務(wù)調(diào)用了某個函數(shù),而該函數(shù)的執(zhí)行結(jié)果可能造成系統(tǒng)任務(wù)重新調(diào)度(例如試圖喚醒了一個優(yōu)先級更高的任務(wù)),則在函數(shù)的末尾會調(diào)用OSSched(),如果OSSched()判斷需要進行任務(wù)調(diào)度,會找到該任務(wù)控制塊OS_TCB的地址,并將該地址拷貝到OSTCBHighRdy,然后通過宏OS_TASK_SW()執(zhí)行軟中斷進行任務(wù)切換。。注意到在此過程中,變量OSTCBCur始終包含一個指向當前運行任務(wù)OS_TCB的指針。大部分解釋同上,只是多了寄存器的保存這一段。 OSCtxSw: xcall OSTaskSwHook //中斷時,PC和寄存器的值S pushn %r15;// Save current task's context xld.w %r4, [OSTCBCur];//指向當前的運行任務(wù) ld.w %r5, %sp; Save the SP to R5 ld.w %sp, %r4;// 保存當前任務(wù)的堆棧指針 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,把任務(wù)優(yōu)先級也保存 xld.w %r5, OSPrioCur ; ld.b [%r5], %r4 xld.w %r4, [OSTCBCur];//載入新的任務(wù) ld.w %sp, %r4 ld.w %r5, [%sp+0x0];//恢復(fù)新任務(wù)的堆棧 ld.w %sp, %r5 popn %r15 ; reti ; //運行新的任務(wù) /////////////////////////////////////////////////////////////////////////////// 在μC/OS-II中,由于中斷的產(chǎn)生可能會引起任務(wù)切換,在中斷服務(wù)程序的最后會調(diào)用OSIntExit()函數(shù)檢查任務(wù)就緒狀態(tài),如果需要進行任務(wù)切換,將調(diào)用OSIntCtxSw()。所以OSIntCtxSw()又稱為中斷級的任務(wù)切換函數(shù)。由于在調(diào)用OSIntCtxSw()之前已經(jīng)發(fā)生了中斷,OSIntCtxSw()將默認CPU寄存器已經(jīng)保存在被中斷任務(wù)的堆棧中了。因此在中斷服務(wù)程序中要保存寄存器,PC和狀態(tài)寄存器的值已經(jīng)被處理器自動保存。OSIntCtxSw()大部分程序和OSCtxSw()相同只是不用保存寄存器,它也可直接用OSCtxSw()來實現(xiàn) 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,把任務(wù)優(yōu)先級也保存 xld.w %r5, OSPrioCur ; ld.b [%r5], %r4 xld.w %r4, [OSTCBCur] //載入新的任務(wù) ld.w %sp, %r4 ld.w %r5, [%sp+0x0] ld.w %sp, %r5 popn %r15 ; reti //Return to new task /////////////////////////////////////////////////////////////////////////////// 和μC/OS-II中的其他中斷服務(wù)程序一樣,OSTickISR()首先在被中斷任務(wù)堆棧中保存CPU寄存器的值,然后調(diào)用OSIntEnter()。μC/OS-II要求在中斷服務(wù)程序開頭調(diào)用OSIntEnter(),其作用是將記錄中斷嵌套層數(shù)的全局變量OSIntNesting加1。如果不調(diào)用OSIntEnter(),直接將OSIntNesting加1也是允許的。OSTickISR()調(diào)用OSTimeTick(),檢查所有處于延時等待狀態(tài)的任務(wù),判斷是否有延時結(jié)束就緒的任務(wù)。在OSTickISR()的最后調(diào)用OSIntExit(),如果在中斷中(或其他嵌套的中斷)有更高優(yōu)先級的任務(wù)就緒,并且當前中斷為中斷嵌套的最后一層。OSIntExit()將進行任務(wù)調(diào)度。注意如果進行了任務(wù)調(diào)度,OSIntExit()將不再返回調(diào)用者,而是用新任務(wù)的堆棧中的寄存器數(shù)值恢復(fù)CPU現(xiàn)場,然后用IRET實現(xiàn)任務(wù)切換。如果當前中斷不是中斷嵌套的最后一層,或中斷中沒有改變?nèi)蝿?wù)的就緒狀態(tài),OSIntExit()將返回調(diào)用者OSTickISR(),最后OSTickISR()返回被中斷的任務(wù)。如果編譯器支持C語言和匯編的混合編程,則這段代碼可以放到OS_CPU_C.C中,針對S void OSTickISR() { asm( " pushn %r15");//保存中斷的任務(wù)的寄存器 /////////////////////////////////////////////////////////////////////////////// 在這個移植中以8位定時器TIME2 作為時鐘節(jié)拍,2MS發(fā)生一次中斷,在中斷向量表vector.c 中在timer2的入口地址處放入(unsigned long)OSTickISR, 發(fā)生中斷后程學將會跳到此程序處執(zhí)行。 *(volatile unsigned char*)0x40285 |= 0x04; // 清除timer2的中斷標志位 OSIntEnter();//處理中斷嵌套曾數(shù)的增加也可以直接 給OSIntNesting加一 if (OSIntNesting == 1) { asm(" ld.w %r4, %sp");//如果嵌套層數(shù)為1 則在當前的任務(wù)控制塊中保存堆棧指針 asm(" xld.w %r10, [OSTCBCur]"); asm(" ld.w %sp, %r10"); asm(" ld.w [%sp+0x0], %r4"); asm(" ld.w %sp, %r4"); } OSTimeTick(); // 給等待延遲時間的任務(wù)的參數(shù)減1 OSIntExit(); //調(diào)用這個函數(shù),如果ISR使更高優(yōu)先級的任務(wù)進入就緒態(tài)或者ISR脫離 // 了中斷嵌套,則此函數(shù)不會返回,而是由進行中斷級任務(wù)切換,否 //此函數(shù)返回OSTickISR,然后恢復(fù)寄存器 asm(" popn %r15");//恢復(fù)寄存器 asm(" reti");//返回中斷的任務(wù)繼續(xù)運行 } /////////////////////////////////////////////////////////////////////////////// 為了更清楚一點這里面的過程,順便付上這里用到TIMER2 的程序,最好有個感性的認識 這部分程序應(yīng)該在驅(qū)動程序里或者放在初始化程序里。始終節(jié)拍中斷的啟動(定時器2的啟動)應(yīng)該放在OSStart()運行之后,但是OSStart()不會返回,所以應(yīng)該放在OSStart()之前建立的任務(wù)中的優(yōu)先級最高的任務(wù)中啟動,如果放在 OSInit()和OSStart() 之間啟動,程序容易崩潰。 /* Prototype */ void init_timer(void); void Start_Timer(void); ///////////////////////////////////////////////////////////////////////////// 定時器2 的初始化,完成定時時間等一下設(shè)置,每隔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中的四個函數(shù): OSStartHighRdy() OSCtxSw() OSIntCtxSw() OSTickISR() 同時對于ARM的開關(guān)中斷(ARMEnableInt,ARMDisableInt)的定義也是放在這個文件下的 關(guān)于ARM的程序就不用解釋那么清楚了,相信英文大家都能看懂,也可以參考上面的程序?qū)崿F(xiàn)的功能都是相同的 /////////////////////////////////////////////////////////////////////////////// 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中恢復(fù)cpsr ///////////////////////////////////////////////////////////////////////////////SVC模式下ARM處理器不會自動保存PC的所以需要自己保存和恢復(fù) LDMFD sp!, {r0-r12,lr,pc} ; pop new task s r0-r12,lr & pc /////////////////////////////////////////////////////////////////////////////// EXPORT OSCtxSw //這個函數(shù)別的文件要用 IMPORT OSPrioCur //這是在別的文件定義的變量,當前任務(wù)優(yōu)先級 IMPORT OSPrioHighRdy //將要恢復(fù)執(zhí)行的任務(wù)的優(yōu)先級 IMPORT OSTCBCur //當前任務(wù)的TCB的指針 IMPORT OSTaskSwHook //調(diào)用用戶定義HOOK IMPORT OSTCBHighRdy //將要恢復(fù)執(zhí)行的任務(wù)的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 // 得到就緒任務(wù)中的最高優(yōu)先級的任務(wù) LDR r5, [r5] STR r5, [r4] //使當前任務(wù)指針指向最高優(yōu)先級的任務(wù) 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} //切換到新的任務(wù) /////////////////////////////////////////////////////////////////////////////// 關(guān)于OSIntCtxSw()就是上面那下半截。這是因為:ARM硬件的中斷時并不自動壓棧任何寄存器,所以免去了恢復(fù)堆棧指針的麻煩;另外,我們最好在進入ISR保存當前任務(wù)現(xiàn)場時一同保存好TCB中的堆棧指針,而不是在OSIntCtxSw()中保存。具體的解釋也可以參考上面這里只是用的寄存器不同而已。 IMPORT OSTaskSwHook OSIntCtxSw BL OSTaskSwHook //調(diào)用OSTaskSwHook函數(shù) LDR r4, =OSTCBHighRdy //得到當前最高優(yōu)先級就緒的任務(wù) 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] //得到新任務(wù)的堆棧指針 LDMFD sp!, {r4} // pop new task cpsr MSR cpsr_cxsf, r4 LDMFD sp!, {r0-r12,lr,pc} //切換到新的任務(wù) /////////////////////////////////////////////////////////////////////////////// 這是 UCOS-II 搶占式調(diào)度ISR的一個標本。當一個優(yōu)先級高的任務(wù)放棄CPU使用權(quán),例如要休眠 10 個 Tick,系統(tǒng)調(diào)度一個低優(yōu)先級的任務(wù)執(zhí)行之。OSTickISR()為休眠的任務(wù)計時,每次執(zhí)行,就把休眠任務(wù)剩余的睡覺時間減去一個Tick數(shù)。如果發(fā)現(xiàn)一個任務(wù)睡夠了,就順便恢復(fù)它為READY態(tài)。做完該做的一切,一個對OSIntExit()的調(diào)用,使調(diào)度發(fā)生了。 EXPORT OSTickISR IMPORT OSIntEnter IMPORT OSTimeTick IMPORT tick_hook IMPORT OSIntExit /////////////////////////////////////////////////////////////////////////////// 注意ARM的IRQ中斷發(fā)生后的PC保存(處理器自動保存LR=PC+4),而不是前面的PC=LR。另外,我們保存的是SVC模式下的現(xiàn)場,中斷后處理器進入IRQ模式,訪問不到SVC模式下的R13(sp),于是在IRQ模式下,只好先另存SPSR和LR,然后盡快退回到SVC模式,這時的R13才是任務(wù)的堆棧指針。在此模式下再將SPSR和LR保存到堆棧中,立即保存所有寄存器。任務(wù)是在SVC模式下運行。關(guān)于時鐘節(jié)拍怎么實現(xiàn)的(如果不是很懂就看下一篇文章關(guān)于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} //恢復(fù)r4中的內(nèi)容 ORR lr, lr, #0x80 // Mask irq for context switching before MSR cpsr_cxsf , lr // 從IRQ模式恢復(fù)到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保存到當前的任務(wù)中 LDR r4, [r4] STR sp, [r4] // OSTCBCur -> stkptr = sp BL OSIntEnter //處理中斷嵌套曾數(shù)的增加也可以直接 給OSIntNesting加一 BL OSTimeTick //調(diào)用ostimetick() BL tick_hook // 我們在Tick_hook()里清除S BL OSIntExit //決定是否進行任務(wù)調(diào)度 ///////////////////////////////////////////////////////////////////////////// 如果返回則繼續(xù)運行此任務(wù) LDMFD sp!, {r4} //pop new task cpsr MSR cpsr_cxsf, r4 LDMFD sp!, {r0-r12,lr,pc} // pop new task r0-r12,lr & pc /////////////////////////////////////////////////////////////////////////////// 定義關(guān)中斷,主要是為了安全訪問臨界區(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中的六個函數(shù): OSTaskStkInit() OSTaskCreateHook() OSTaskDelHook() OSTaskSwHook() OSTaskStatHook() OSTimeTickHook() 實際需要修改的只有OSTaskStkInit()函數(shù),其他五個函數(shù)需要聲明,但不一定有實際內(nèi)容。這五個函數(shù)都是用戶定義的,所以OS_CPU_C.C中沒有給出代碼。如果用戶需要使用這些函數(shù),請將文件OS_CFG.H中的#define constant OS_CPU_HOOKS_EN設(shè)為1,設(shè)為0表示不使用這些函數(shù)。 /////////////////////////////////////////////////////////////////////////////// 這個函數(shù)是很重要的 ,該函數(shù)由OSTaskCreate()或OSTaskCreateExt()調(diào)用,用來初始化任務(wù)的堆棧。初始狀態(tài)的堆棧模擬發(fā)生一次中斷后的堆棧結(jié)構(gòu)。當調(diào)用OSTaskCreate()或OSTaskCreateExt()創(chuàng)建一個新任務(wù)時,需要傳遞的參數(shù)是:任務(wù)代碼的起使地址,參數(shù)指針(pdata),任務(wù)堆棧頂端的地址,任務(wù)的優(yōu)先級。OSTaskCreateExt()還需要一些其他參數(shù),但與OSTaskStkInit()沒有關(guān)系。OSTaskStkInit()只需要以上提到的3個參數(shù)(task, pdata,和ptos)。在這個堆棧初始化函數(shù)中要清楚堆棧中都要保存哪些東西,要留多大的空間,這些都很重要,否則會發(fā)生很嚴重的錯誤。 OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { INT32U *stk; //定義一個指針 opt = opt; /* 這個參數(shù)沒有用,但是為了防止編譯錯誤*/ stk = (INT32U*)ptos; //載入堆棧指針 /////////////////////////////////////////////////////////////////////////// S *stk-- = (INT32U)task; //存放PC的地址,s //////////////////////////////////////////////////////////////////////////// 存放狀態(tài)寄存器,同樣也會被自動保存,設(shè)置為中斷開啟 參考其PSR每位的作用。如果選擇任務(wù)啟動后允許中斷發(fā)生,則所有的任務(wù)運行期間中斷都允許;同樣,如果選擇任務(wù)啟動后禁止中斷,則所有的任務(wù)都禁止中斷發(fā)生,而不能有所選擇。知道為什么嗎?因為啟動的時候,OSStart()調(diào)用的是 OSStartHighRdy,即從堆棧中恢復(fù)PC和SPR以及寄存器中的內(nèi)容,因此第一次堆棧中的放的值決定了spr,其它寄存器的值到?jīng)]有什么關(guān)系。 *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); //返回堆棧指針所指向的地址,恢復(fù)寄存器時候要用 } /////////////////////////////////////////////////////////////////////////////// |
|