UC/OS -II 移植對比

 

轉載於:httpzq2007.blog.hexun.com9534277_d.html

 

UC/OS-II 的移植步驟分析

                                             zqcumt    07-4-15

關於UC/OS-II 的移植網上介紹的已經很多了,比較流行的幾款處理器(例如ARM )在網上都可以直接下載移植好的代碼。由於最近選修了一門嵌入式系統的課,用的處理器是EPSON 公司的S1C33 系列,做實驗的時候要進行操作系統的移植,這個週末花了一天半的時間學習了一下,因爲畢業設計的時候做過ARM 上的移植,於是將兩者比較了一下,給出一般的移植要點。由於將來實驗還要設計到GUI 的移植以及文件系統的移植和網絡協議的移植,我會將自己的學習筆記都記錄下來。

大家下載到源碼後,針對Intel 80x86 的代碼在uCOS-II/Ix86L 目錄下。代碼是80x86 實模式,且在編譯器大模式下編譯的。移植部分的代碼可在下述文件中找到:OS_CPU.H , OS_CPU_C.C , OS_CPU_A.ASM 。大家可以參考這個例子,對它進行修改。

INCLUDES.H 是主頭文件,在所有後綴名爲.C 的文件的開始都包含INCLUDES.H 文件。使用INCLUDES.H 的好處是所有的.C 文件都只包含一個頭文件,程序簡潔,可讀性強。缺點是.C 文件可能會包含一些它並不需要的頭文件,額外的增加編譯時間。與優點相比,多一些編譯時間還是可以接受的。用戶可以改寫INCLUDES.H 文件,增加自己的頭文件,但必須加在文件末尾。

///////////////////////////////////////////////////////////////////////////////

一、(1)OS_CPU.H 文件的移植 ( 針對S1C33209)

//////////////////////////////////////////////////////////////////////////

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;// 定義 狀態寄存器的寬度爲16

///////////////////////////////////////////////////////////////////////////////

下面的部分主要是爲了和UC/OS 第一版的兼容

#define BYTE           INT8S          

#define UBYTE          INT8U  

#define WORD           INT16S 

#define UWORD          INT16U

#define LONG           INT32S

#define ULONG          INT32U

///////////////////////////////////////////////////////////////////////////////

與其他實時系統一樣,µC/OS-II 在進入系統臨界代碼區之前要關閉中斷,等到退出臨界區後再打開。從而保護核心數據不被多任務環境下的其他任務或中斷破壞。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 // 第二種方法, 這個是我們用到的, 要修改, 一般用匯編寫, 根據各個處理器的不同而不同, 下面是S1C33 系列的彙編

 

#define  OS_ENTER_CRITICAL() 

asm(" ld.w %r4, %psr");

asm(" xld.w %r5, 0xffffffef");

asm(" and %r4, %r5");// 關中斷, 保持狀態寄存器的其它狀態不變

asm(" ld.w %psr, %r4"); 

             

#define  OS_EXIT_CRITICAL()

asm(" ld.w %r4, %psr");

asm(" or %r4, 0b10000");

asm(" ld.w %psr, %r4"); // 開中斷, 保持狀態寄存器的其它狀態不變

#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, 就緒任務的堆棧初始化應該模擬一次中斷髮生後的樣子,堆棧中應該按進棧次序設置好各個寄存器的內容。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 機上模擬時鐘節拍

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 核的S3C44BOX )

///////////////////////////////////////////////////////////////////////////////
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /*8
位無符號整數*/
typedef signed char INT8S; /*8
位有符號整數*/
typedef unsigned short INT16U; /*16
位有符號整數*/
typedef signed short INT16S; /*16
位無符號整數*/
typedef unsigned long INT32U; /*32
位無符號整數*/
typedef signed long INT32S; /*32
位有符號整數*/
typedef float FP32; /*
單精度浮點數*/
typedef double FP64; /*
雙精度浮點數*/
///////////////////////////////////////////////////////////////////////////
typedef unsigned int OS_STK;/*
堆棧入口寬度爲16*/ARM 處理器相關的代碼:

///////////////////////////////////////////////////////////////////////////////

具體的實現見第二步, OS_CPU_A.ASM 中實現
#define OS_ENTER_CRITICAL () ARMDisableInt() /*
關中斷在 OS_CPU.A.S 中定義,可以參

     考下面的程序 */
#define OS_EXIT_CRITICAL () ARMEnableInt() /*
開啓中斷*/
#define OS_STK_GROWTH 1 /*
堆棧由高地址向低地址增長*/

///////////////////////////////////////////////////////////////////////////////

定義宏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 文件的移植 ( 針對S1C33209)

///////////////////////////////////////////////////////////////////////////////

µC/OS-II 的移植需要用戶改寫OS_CPU_A.ASM 中的四個函數:

OSStartHighRdy()

OSCtxSw()

OSIntCtxSw()

OSTickISR()

////////////////////////////////////////////////////////////////

該函數由SStart() 函數調用,功能是運行優先級最高的就緒任務,在調用OSStart() 之前,用戶必須先調用OSInit() ,並且已經至少創建了一個任務(請參考OSTaskCreate() OSTaskCreateExt() 函數)。OSStartHighRdy() 默認指針OSTCBHighRdy 指向優先級最高就緒任務的任務控制塊(OS_TCB )(在這之前OSTCBHighRdy 已由OSStart() 設置好了)。OSTCBHighRdy->OSTCBStkPtr 指向的是任務堆棧的頂端

 

OSStartHighRdy:

  xcall   OSTaskSwHook  // 調用OSTaskSwHook ,此時OSRunningFALSE

ld.w    %r4, 0x1

xld.w    %r5, OSRunning

xld.b   [%r5], %r4  // 使OSRunning 的狀態爲TRUE ,以後調用OSTaskSwHook 時會先保存寄存器再恢復

xld.w  %r5, [OSTCBHighRdy];

ld.w   %sp, %r5;// 得到最高優先級任務的堆棧指針

xld.w  %r4, [%sp+0x0];

ld.w   %sp, %r4;// 偏移量爲0

popn   %r15     // 恢復r15-r0, 這個是S1C33209 的彙編語句

reti;      // 返回,此命名執行時,處理器會自動恢復PC 和狀態寄存器的值,至此新任務

///////////////////////////////////////////////////////////////////////////////

OSCtxSw() 是一個任務級的任務切換函數(在任務中調用,區別於在中斷程序中調用的OSIntCtxSw() )。 它通過執行一條軟中斷的指令來實現任務切換。軟中斷向量指向OSCtxSw() 。在 µC/OS-II 中,如果任務調用了某個函數,而該函數的執行結果可能造成系統任務重新調度(例如試圖喚醒了一個優先級更高的任務),則在函數的末尾會調用OSSched() , 如果OSSched() 判斷需要進行任務調度,會找到該任務控制塊OS_TCB 的地址,並將該地址拷貝到OSTCBHighRdy ,然後通過宏OS_TASK_SW() 執行軟中斷進行任務切換。。注意到在此過程中,變量OSTCBCur 始終包含一個指向當前運行任務OS_TCB 的指針。大部分解釋同上,只是多了寄存器的保存這一段。

OSCtxSw:   

xcall   OSTaskSwHook // 中斷時,PC 和寄存器的值S1C33209 處理器已經自動保存了

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 ,把任務優先級也保存

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() 函數檢查任務就緒狀態,如果需要進行任務切換,將調用OSIntCtxSw() 。所以OSIntCtxSw() 又稱爲中斷級的任務切換函數。由於在調用OSIntCtxSw() 之前已經發生了中斷,OSIntCtxSw() 將默認CPU 寄存器已經保存在被中斷任務的堆棧中了。因此在中斷服務程序中要保存寄存器,PC 和狀態寄存器的值已經被處理器自動保存。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 ,把任務優先級也保存

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() ,其作用是將記錄中斷嵌套層數的全局變量OSIntNesting1 。如果不調用OSIntEnter() ,直接將OSIntNesting1 也是允許的。OSTickISR() 調用OSTimeTick() ,檢查所有處於延時等待狀態的任務,判斷是否有延時結束就緒的任務。 OSTickISR() 的最後調用OSIntExit() ,如果在中斷中(或其他嵌套的中斷)有更高優先級的任務就緒,並且當前中斷爲中斷嵌套的最後一層。OSIntExit() 將進行任務調度。注意如果進行了任務調度,OSIntExit() 將不再返回調用者,而是用新任務的堆棧中的寄存器數值恢復CPU 現場,然後用IRET 實現任務切換。如果當前中斷不是中斷嵌套的最後一層,或中斷中沒有改變任務的就緒狀態,OSIntExit() 將返回調用者OSTickISR() ,最後OSTickISR() 返回被中斷的任務。如果編譯器支持C 語言和彙編的混合編程,則這段代碼可以放到 OS_CPU_C.C 中,針對S1C33209 的移植這部分放在OS_CPU_C.C 中。爲了連續性就在這裏順便寫吧。

void OSTickISR()

{   asm( " pushn %r15");// 保存中斷的任務的寄存器

///////////////////////////////////////////////////////////////////////////////

在這個移植中以8 位定時器TIME2 作爲時鐘節拍,2MS 發生一次中斷,在中斷向量表vector.c 中在timer2 的入口地址處放入(unsigned long)OSTickISR,   發生中斷後程學將會跳到此程序處執行。

*(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 使更高優先級的任務進入就緒態或者ISR 脫離

             // 了中斷嵌套,則此函數不會返回,而是由進行中斷級任務切換,否

// 此函數返回OSTickISR ,然後恢復寄存器

    asm(" popn %r15");// 恢復寄存器

    asm(" reti");// 返回中斷的任務繼續運行

}

///////////////////////////////////////////////////////////////////////////////

爲了更清楚一點這裏面的過程,順便付上這裏用到TIMER2 的程序,最好有個感性的認識

這部分程序應該在驅動程序裏或者放在初始化程序裏。始終節拍中斷的啓動(定時器2 的啓動)應該放在OSStart() 運行之後,但是OSStart() 不會返回,所以應該放在OSStart() 之前建立的任務中的優先級最高的任務中啓動,如果放在 OSInit()OSStart() 之間啓動,程序容易崩潰。

/* Prototype */

void init_timer(void);

void Start_Timer(void);

/////////////////////////////////////////////////////////////////////////////

定時器2 的初始化,完成定時時間等一下設置,每隔2ms 發生一次的中斷

void init_timer(void)

{

*(volatile unsigned char *)0x4014E |= 0x0F;

*(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 核的S3C44BOX )

/////////////////////////////////////////////////////////////////////////////

µ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           // 這是在別的文件定義的變量 當前任務優先級

        IMPORT  OSPrioHighRdy      // 將要恢復執行的任務的優先級

        IMPORT  OSTCBCur           // 當前任務的 TCB 的指針

        IMPORT  OSTaskSwHook       // 調用用戶定義 HOOK

         IMPORT  OSTCBHighRdy       // 將要恢復執行的任務的 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       // 得到就緒任務中的最高優先級的任務

       LDR  r5, [r5]

        STR   r5, [r4]         // 使當前任務指針指向最高優先級的任務

OSTCBCur = OSTCBHighRdy

         LDR r6, =OSPrioHighRdy      

       LDRB     r6, [r6]

       LDR r4, =OSPrioCur          

         STRB     r6, [r4]                // 保存優先級到當前的優先級

         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  // 得到當前最高優先級就緒的任務

        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的一個標本。當一個優先級高的任務放棄CPU使用權,例如要休眠 10 個 Tick,系統調度一個低優先級的任務執行之。OSTickISR()爲休眠的任務計時,每次執行,就把休眠任務剩餘的睡覺時間減去一個Tick數。如果 發現一個任務睡夠了,就順便恢復它爲READY態。做完該做的一切,一個對OSIntExit()的調用,使調度發生了。

EXPORT OSTickISR

    IMPORT  OSIntEnter

    IMPORT  OSTimeTick

    IMPORT  tick_hook  

    IMPORT  OSIntExit

///////////////////////////////////////////////////////////////////////////////

注意 ARM IRQ 中斷髮生後的 PC 保存(處理器自動保存 LR=PC+4 ),而不是前面的 PC LR 。另外,我們保存的是 SVC 模式下的現場,中斷後處理器進入 IRQ 模式,訪問不到 SVC 模式下的 R13(sp) ,於是在 IRQ 模式下,只好先另存 SPSR LR ,然後儘快退回到 SVC 模式,這時的 R13 纔是任務的堆棧指針。在此模式下再將 SPSR LR 保存到堆棧中,立即保存所有寄存器。任務是在 SVC 模式下運行。關於時鐘節拍怎麼實現的(如果不是很懂就看下一篇文章關於 ARM 中斷處理的詳細分析)。

LINK_SAVE   DCD     0    // 申請一個字單元用0 來初始化這個字

PSR_SAVE    DCD     0    // 地址爲LINK_SAVE+4

OSTickISR

    STMFD   sp!, {r4}                // 這裏的spIRQ 模式下的,將r4 壓入堆棧

///////////////////////////////////////////////////////////////////////////

另存 IRQ 模式下的 SPSR LR ,以便在 SVC 模式下也能訪問,相當於一箇中介作用

    LDR     r4, =LINK_SAVE

    STR     lr, [r4]                //LINK_SAVE = lr ,保存lr,lrIRQ 模式下

    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 字節的空間 (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() 裏清除 S3C44B0x Tick_Int_Pend                           函數在 main.c 裏,是另加的

    BL  OSIntExit         // 決定是否進行任務調度

/////////////////////////////////////////////////////////////////////////////

  如果返回則繼續運行此任務

    LDMFD   sp!, {r4}              //pop new task cpsr

    MSR     cpsr_cxsf, r4

    LDMFD   sp!, {r0-r12,lr,pc}       // pop new task r0-r12,lr & pc

///////////////////////////////////////////////////////////////////////////////

定義關中斷,主要是爲了安全訪問臨界區的資源

  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 文件的移植 ( 針對S1C33209)

///////////////////////////////////////////////////////////////////////////////

µ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() 調用,用來初始化任務的堆棧。初始狀態的堆棧模擬發生一次中斷後的堆棧結構。 當調用OSTaskCreate() OSTaskCreateExt() 創建一個新任務時,需要傳遞的參數是:任務代碼的起使地址,參數指針(pdata ) ,任務堆棧頂端的地址,任務的優先級。OSTaskCreateExt() 還需要一些其他參數,但與OSTask­StkInit() 沒有關係。OSTaskStkInit() 只需要以上提到的3 個參數(task , pdata ,ptos )。在這個堆棧初始化函數中要清楚堆棧中都要保存哪些東西,要留多大的空間,這些都很重要,否則會發生很嚴重的錯誤。

 

OS_STK  *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)

{

    INT32U *stk;   // 定義一個指針

    opt    = opt;    /* 這個參數沒有用,但是爲了防止編譯錯誤*/

stk    = (INT32U*)ptos;                 // 載入堆棧指針

///////////////////////////////////////////////////////////////////////////

S1c33 處理器是在入棧時,先變化sp ,再向當前的sp 指向的地址寫入數據。出棧時是先彈出數據,再變化sp

*stk-- = (INT32U)task;             // 存放PC 的地址,s1c33209 的處理器會自動保存

////////////////////////////////////////////////////////////////////////////

存放狀態寄存器,同樣也會被自動保存,設置爲中斷開啓 參考其PSR 每位的作用。 如果選擇任務啓動後允許中斷髮生,則所有的任務運行期間中斷都允許;同樣,如果選擇任務啓動後禁止中斷,則所有的任務都禁止中斷髮生,而不能有所選擇。知道爲什麼嗎?因爲啓動的時候,OSStart ()調用的是 OSStartHighRdy ,即從堆棧中恢復PCSPR 以及寄存器中的內容,因此第一次堆棧中的放的值決定了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);       // 返回堆棧指針所指向的地址,恢復寄存器時候要用

}

///////////////////////////////////////////////////////////////////////////////

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章