uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)

uCOS II   LPC11C14 上面的移植方法

电子文档: http://download.csdn.net/detail/icegoly/5449031   

第一: OS_CPU.H 的移植 2

1.1  定义 与编译器有关的数据类型 2

1.2 定义中断的实现方式 3

1.3  定义栈的生长方式 5

1.4  宏定义 优先级任务切换 6

1.5  定义开、关中断的函数 6

1.6  声明 5 个函数 6

第二:OS_CPU_C.C的移植 6

2.1 OSTaskStkInt() 任务堆栈初始化函数 6

2.2 OSTaskStkInit() 10

2.3 OSTaskCreateHook() 11

2.4 OSTaskDelHook() 11

2.5 OSTaskSwHook() 11

2.6 OSTaskStatHook() 11

2.7 OSTimeTickHook() 11

第三: OS_CPU_A.S 的移植 11

3.0  说明: 11

3.0.1 NVIC_INT_CTRL:中断控制和状态寄存器 12

3.0.2 NVIC_SCB_SHPR3 系统处理程序优先级寄存器 14

3.0.3  PendSV 15

3.1  实现 中断开、关的汇编 15

3.2 OSStartHighRdy 函数移植 16

:设置 PendSV 异常优先级为最低 17

:初始化 PSP 设置为0 17

:设置 OSRunning=ture 17

:触发 PendSV  异常 17

:开中断 18

3.3 OSCtxSw  任务切换 18

3.4 OSIntCtxSw任务切换 18

3.5 OS_CPU_PendSVHandler   pendsv 异常处理 18

:获取任务的 SP ,如果为  的话则 直接跳到 OS_CPU_PendSVHandler_nosave,去执行 18

:保存 R4-R11  SP 19

:将当前的堆栈指针给当前进程的任务块 19

:然后调用 OSTaskSwHook 19

:获取当前的高优先级 20

F获取当前就绪线程 20

G 得到新任务的Sp和恢复从堆栈 r4-r11 20

H:载入新的SP和返回 21


第一: OS_CPU.H 的移植

1.1  定义 与编译器有关的数据类型

typedef unsigned  char    BOOLEAN ;    /* boolean */

typedef unsigned  char    INT8U ;      /* Unsigned  8 bit quantity 无符号8位整型变量                          */

typedef signed    char    INT8S ;      /* Signed    8 bit quantity 有符号8位整型变量                          */

typedef unsigned  short   INT16U ;     /* Unsigned 16 bit quantity 无符号16位整型变量                          */

typedef signed    short   INT16S ;     /* Signed   16 bit quantity 有符号16位整型变量                          */

typedef unsigned  int     INT32U ;     /* Unsigned 32 bit quantity 无符号32位整型变量                          */

typedef signed    int     INT32S ;     /* Signed   32 bit quantity  有符号32位整型变量                         */

typedef  float           FP32;       /* Single precision floating point    单精度浮点数(32位长度)                */

typedef  double          FP64;       /* Double precision floating point    双精度浮点数(64位长度)                */

typedef unsigned  int     OS_STK ;       /* Each stack entry is 32-bit wide  堆栈是32位宽度                 */

typedef unsigned  int     OS_CPU_SR ;   /* Define size of CPU status register (PSR = 32 bits) */

1.2  定义中断的实现方式   

为了处理临界区代码,必须关中断,等处理完毕后,再开中断。关中断可以避免其他任务或中断进入临界区代码。 uC/OS-II 定义了这两个宏来实现,但注意一条:调用 uC/OS-II 功能函数时,中断应该总是开着的。

)当 OS_CRITICAL_METHOD= = 1 时,简单实现如下:     #define OS_ENTER_CRITICAL() disable_int()       #define OS_EXIT_CRITICAL() enable_int() 但这样有一个问题,如果禁止中断的情况下调用 uC/OS-II 功能函数,那么从功能函数返回时,中断可能变成允许的了,而实际上还是希望是禁止的。  2 )当 OS_CRITICAL_METHOD= = 2 时,实现如下:    #define  OS_ENTER_CRITICAL()                 asm(  PUSH PSW  );             asm(  DI );      #define  OS_EXIT_CRITICAL()             asm(  POP PSW  ); 执行OS_ENTER_CRITICAL() 时,先将中断状态保存到堆栈,然后关中断;执行OS_EXIT_CRITICAL() 时,再从堆栈中恢复原来的中断开 / 关状态。这种方法不会改变中断状态,避免前面的问题。

)当 OS_CRITICAL_METHOD= = 3 时,实现如下:   #define OS_ENTER_CRITICAL()             cpu_sr = get_processor_psw();        disable_interrupts();   #define  OS_EXIT_CRITICAL()        set_ processor_psw(cpu_sr);

==================== 这里注意的是 OS_CRITICAL_METHOD   ucos 提供了 3 种方法实现,第一种方法是直接简单的开关中断方式,但是一旦嵌套会发生意外,比如:

view plaincopy to clipboardprint? ········· 10 ········ 20 ········ 30 ········ 40 ········ 50········ 60 ········ 70 ········ 80 ········ 90 ········ 100 ······· 110 ······· 120 ······· 130······· 140 ······· 150 void Task (void *data)           .........               OS_ENTER_CRITICAL();        // 进入临界区 1          // 调用了某一函数 , 该函数也需要进入临界区 :       {         OS_ENTER_CRITICAL();          ................        OS_EXIT_CRITICAL();        }       // 这里就自然变成废墟了       ...........       // 临界区切掉       OS_EXIT_CRITICAL();             }    void Task (void *data) {      .........     

    OS_ENTER_CRITICAL();      // 进入临界区 1

    // 调用了某一函数 , 该函数也需要进入临界区 :     {      OS_ENTER_CRITICAL();        ................       OS_EXIT_CRITICAL();      }     // 这里就自然变成废墟了     ...........     // 临界区切掉     OS_EXIT_CRITICAL();         }

此方法太多弊端 , 所以新内核中看不到她的影子了

于是出现第二种方法 , 执行 OS_ENTER_CRITICAL() 时首先保存中断状态到堆栈中 , 然后关中断 , 执行 OS_EXIT_CRITICAL() 时,再从堆栈中恢复原来的中断开/ 关状态。这种方法不会改变中断状态 , 由于用到堆栈 , 这样会带来隐忧 , 看邵贝贝翻译的有这样说:

        “ 但是,用户在使用这种方法的时候还得十分小心,因为如果用户在调用象OSTimeDly() 之类的服务之前就禁止中断,很有可能用户的应用程序会崩溃。发生这种情况的原因是任务被挂起直到时间期满,而中断是禁止的,因而用户不可能获得节拍中断!很明显,所有的 PEND 调用都会涉及到这个问题,用户得十分小心。一个通用的办法是用户应该在中断允许的情况下调用 µ C/OS-  的系统服务! 

第 3 种方法直接保存到任务局部变量中去

#if     OS_CRITICAL_METHOD == 3#define OS_ENTER_CRITICAL()  (cpu_sr = OSCPUSaveSR())#define OS_EXIT_CRITICAL()   (OSCPURestoreSR(cpu_sr)) #endif 避免了使用堆栈

#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

1.3  定义栈的生长方式

 /*   堆栈是从上往下长的, 0 -从下往上的生长方式          */

 

#define  OS_STK_GROWTH        1                       /* Stack grows from HIGH to LOW memory on ARM    */

1.4  宏定义 优先级任务切换

就绪任务的堆栈初始化应该模拟一次中断发生后的样子,堆栈中应该按进栈次序设置好各个寄存器的内容。OS_TASK_SW()函数模拟一次中断过程,在中断返回的时候进行任务切换

#define  OS_TASK_SW()         OSCtxSw()

1.5  定义开、关中断的函数  

#if OS_CRITICAL_METHOD == 3           /* See OS_CPU_A.ASM   */         

   OS_CPU_SR  OS_CPU_SR_Save(void);

   void         OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);

#endif

1.6  声明 5 个函数

   void OSCtxSw(void)             //任务切换

  void OSIntCtxSw(void)          //和 OSCtxSw  可以一致

  void OSStartHighRdy(void)      //创建任务之后 ,运行优先级最高的任务

  OS_CPU_SR  OS_CPU_SR_Save(void)       //开中断

  void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr)    //关中断


第二:OS_CPU_C.C的移植

      实际需要修改的只有 OSTaskStkInit()函数,其他五个函数需要声明,但不一定有实际内容。这五个函数都是用户定义的,所以OS_CPU_C.C中没有给出代码。如果用户需要使用这些函数,请将文件 OS_CFG.H 中的 #define   constant OS_CPU_HOOKS_EN 设为1,设为0表示不使用这些函数

2.1 OSTaskStkInt() 任务堆栈初始化函数  

OS_STK * OSTaskStkInit  (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { OS_STK *stk;opt = opt; /* 'opt'  没有使用。作用是避免编译器警告  */ stk = ptos; /*  获取堆栈指针  */ /*  建立任务环境, ADS1.2 使用满递减堆栈  */*stk = (OS_STK) task; /* pc */ *--stk = (OS_STK) task; /* lr */ *--stk = 0; /* r12 */ *--stk = 0; /* r11 */ *--stk = 0; /* r10 */ *--stk = 0; /* r9 */ *--stk = 0; /* r8 */ *--stk = 0; /* r7 */ *--stk = 0; /* r6 */ *--stk = 0; /* r5 */ *--stk = 0; /* r4 */ *--stk = 0; /* r3 */ *--stk = 0; /* r2 */ *--stk = 0; /* r1 */ *--stk = (unsigned int) pdata; /* r0, 第一个参数使用R0 传递  */ *--stk = (USER_USING_MODE|0x00); /* spsr ,允许  IRQ, FIQ  中断 */ *--stk = 0; /*  关中断计数器 OsEnterSum; */ return (stk); }================================== *stk = (OS_STK) task; /* pc */ *--stk = (OS_STK) task; /* lr */ 干什么用的?不是很明白================================== *--stk = 0; /* r8 */ *--stk = 0; /* r7 */ *--stk = 0; /* r6 */ *--stk = 0; /* r5 */ *--stk = 0; /* r4 */ *--stk = 0; /* r3 */ *--stk = 0; /* r2 */ *--stk = 0; /* r1 */ 好像跟绍贝贝翻译的那本书上说法的不太一样,不是要保存寄存器吗?怎么都置 0 了?

答  1:

初始化的时候当然要清零了

答  2:

置0并且压入堆栈了。等下弹出来就象复位后一样~~~~

答  3:

嗯,也是这样也对 书上说的,要保存寄存器的值 就像刚发生过中断一样 仔细想想初始化一个任务没必要那样做

答  4:

还有  *--stk = 0; /* r12 */ *--stk = 0; /* r11 */ *--stk = 0; /* r10 */ *--stk = 0; /* r9 */*--stk = 0; /* r8 */ *--stk = 0; /* r7 */ *--stk = 0; /* r6 */ *--stk = 0; /* r5 */ *--stk = 0; /* r4 */*--stk = 0; /* r3 */ *--stk = 0; /* r2 */ *--stk = 0; /* r1 */+++++++++++++++++++++++++++++ 可是,那些语句和后面的注释怎么就一一对应了?

答  5:

这些数据现在是保存在的堆栈中的,等下任务开始执行时,就从堆栈中弹出,弹出来刚好放在后面注释中的寄存器中。因为这些是工作寄存器,全部要压栈或者从堆栈中恢复的。

答  6:

还有 ==================================*stk = (OS_STK) task; /* pc */ *--stk = (OS_STK) task; /* lr */ 干什么用的?不是很明白

答  7:

后面不是有注释么?分别对应着 pc 寄存器和 lr 寄存器而 lr 寄存器在函数返回时,就会返回到任务入口处了。至于 pc ,为何要设置,这个我就不清楚了,也许是随意弄上去的一句,并没什么实际意义,只是为了把 R15 (即 PC )也弄一下吧。

说明:用户创建任务时, OSTasKCreat() 会调用 OSTaskStkInt() 函数初始化该任务的堆栈,并把返回的堆栈指针保存到该任务的 TCB 结构中的最前面的参数OSTCBStkPtr 中,当该任务要被恢复时,任务切换函数从其 TCB 块中取得其任务堆栈指针,依次将堆栈内容弹到处理器对应的 CPSR  r0  r1    r12,lr,pc的寄存器中,完成现场的恢复和程序指针 PC 的返回。  

uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)_第1张图片

uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)_第2张图片

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);

}


2.2 OSTaskStkInit()

下面的任务可以只声明不编写

2.3 OSTaskCreateHook()

2.4 OSTaskDelHook()

2.5 OSTaskSwHook()

2.6 OSTaskStatHook()

2.7 OSTimeTickHook()

第三: OS_CPU_A.S 的移植

3.0  说明:

NVIC_INT_CTRL   EQU     0xE000ED04                             

                                ; Interrupt control state register.

;NVIC_SYSPRI14   EQU     0xE000ED22                            

                                ; System priority register (priority 14).

NVIC_SCB_SHPR3  EQU     0xE000ED20                            

                            ; PendSV priority value (lowest).

NVIC_PENDSV_PRI  EQU     0x00FF0000

NVIC_PENDSVSET   EQU     0x10000000   

UCOS-II 启动多任务环境的函数是 OSStart ();用户在调用 OSStart ()前,必须已经建立了一个或多个任务。 OSStart ()最终调用 OSStartHighRdy () 运行多任务启动前优先级最高的任务。  

OSStartHighRdy

ldr     r0, =NVIC_SCB_SHPR3

ldr     r1, [r0]

ldr     r2, =NVIC_PENDSV_PRI

orrs    r1, r1, r2

str     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 异常优先级为最低

ldr     r0, =NVIC_SCB_SHPR3

ldr     r1, [r0]

ldr     r2, =NVIC_PENDSV_PRI

orrs    r1, r1, r2

str     r1, [r0]

:初始化 PSP 设置为0

    MOVS    R0, #0                                              ; Set the PSP to 0 for initial context switch call

    MSR     PSP, R0

:设置 OSRunning=ture

    LDR     R0, =OSRunning                                      ; OSRunning = TRUE

    MOVS    R1, #1

    STRB    R1, [R0]

:触发 PendSV  异常

    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)

    LDR     R1, =NVIC_PENDSVSET

    STR     R1, [R0]

:开中断

 CPSIE   I 

3.3 OSCtxSw  任务切换

当操作系统要切换任务的时候,就触发一个pendsv异常。

OSCtxSw

LDR     R0, =NVIC_INT_CTRL                                

  ; Trigger the PendSV exception (causes context switch)

    LDR     R1, =NVIC_PENDSVSET

    STR     R1, [R0]

    BX      LR

3.4 OSIntCtxSw任务切换

和 3.3  用一样的代码

3.5 OS_CPU_PendSVHandler   pendsv 异常处理  

) 1  pendsv 用于使上下文切换。这是一个值得推荐的方法进行

Cortex-M3 上下文切换 ,这是因为 Cortex-M3 自动保存在任何例外处理器上下文的一半,使相同的异常返回 ,所以保存R4-R11  SP 是必须的。 使用 pendsv 异常这种方式意味着上下文保存和恢复是相同的,无论是由一个线程或发生中断或异常。

) 2 、代码实现步骤

:获取任务的 SP ,如果为  的话则 直接跳到  OS_CPU_PendSVHandler_nosave,去执行

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

cmp r0, #0

beq OS_CPU_PendSVHandler_nosave

:保存 R4-R11  SP

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack

    ;STM     R0, {R4-R11}

stm     r0!, {r4-r7}

mov     r1, r8

mov     r2, r9

mov     r3, r10

mov     r4, r11

stm     r0!, {r1-r4}

subs    r0, r0, #0x20

:将当前的堆栈指针给当前进程的任务块

    LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;

    LDR     R1, [R1]

    STR     R0, [R1]                                            ; R0 is SP of process being switched out

:然后调用 OSTaskSwHook 

mov     r0, lr

push    {r0}

ldr     r0, =OSTaskSwHook

blx     r0

:获取当前的高优先级

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;

    LDR     R1, =OSPrioHighRdy

    LDRB    R2, [R1]

    STRB    R2, [R0]

F获取当前就绪线程

    LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;

    LDR     R1, =OSTCBHighRdy

    LDR     R2, [R1]

    STR     R2, [R0]

G 得到新任务的Sp和恢复从堆栈 r4-r11

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;

    ;LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack

ldm     r0!, {r4-r7}

ldm     r0!, {r1-r3}

mov     r8, r1

mov     r9, r2

mov     r10, r3

ldm     r0!, {r1}

mov     r11, r1

H:载入新的SP和返回

    ;ADDS    R0, R0, #0x20

    MSR     PSP, R0                                             ; Load PSP with new process SP

    ;ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack

    mov     r0, lr

movs    r1, #0x04

orrs    r0, r0, r1

mov     lr, r0

CPSIE   I     // 更改处理器状态,使能中断

    BX      LR                                                  ; Exception return will restore remaining context

                          

                            ; Value to trigger PendSV exception.

3.0.1 NVIC_INT_CTRL:中断控制和状态寄存器  
uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)_第3张图片
uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)_第4张图片

3.0.2 NVIC_SCB_SHPR3系统处理程序优先级寄存器

SHPR2-SHPR3寄存器设置优先级可配置的异常处理程序的优先级级别( 0~3)。

SHPR2-SHPR3是字可访问的。有关它们的属性请见表 22.31 的寄存器小结。

利用 CMSIS 访问系统异常的优先级级别要用到以下 CMSIS 函数:

[1] uint32_t NVIC_GetPriority(IRQn_Type IRQn)

[2] void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

输入参数 IRQn 是 IRQ  编号,更多信息请见表 22.10 。


uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)_第5张图片

系统故障处理程序、优先级域以及每个处理程序的寄存器如下所示:

uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)_第6张图片


3.0.3  PendSV

PendSV 是为系统级服务提供的中断驱动。在一个操作系统环境中,当没有其他异常正在执行时,可以使用 PendSV 来进行上下文的切换。

在进入 PendSV 处理函数时:

( 1  xPSR  PC  LR  R12  R0  R3 已经在处理栈中被保存。

( 2 )处理模式切换到线程模式。

( 3 )栈是主堆栈。

由于 PendSV 在系统中被设置为最低优先级,因此只有当没有其他异常或者中断在执行时才会被执行。

uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法)_第7张图片

OS_CPU_SR_Save

MRS     R0, PRIMASK                                        

CPSID   I

    BX      LR

OS_CPU_SR_Restore

    MSR     PRIMASK, R0

    BX      LR


3.1  实现 中断开、关的汇编

UCOS-II 启动多任务环境的函数是 OSStart ();用户在调用 OSStart ()前,必须已经建立了一个或多个任务。 OSStart ()最终调用 OSStartHighRdy ()运行多任务启动前优先级最高的任务。  

OSStartHighRdy

ldr     r0, =NVIC_SCB_SHPR3

ldr     r1, [r0]

ldr     r2, =NVIC_PENDSV_PRI

orrs    r1, r1, r2

str     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 异常优先级为最低

ldr     r0, =NVIC_SCB_SHPR3

ldr     r1, [r0]

ldr     r2, =NVIC_PENDSV_PRI

orrs    r1, r1, r2

str     r1, [r0]

:初始化 PSP 设置为0

    MOVS    R0, #0                                              ; Set the PSP to 0 for initial context switch call

    MSR     PSP, R0

:设置 OSRunning=ture

    LDR     R0, =OSRunning                                      ; OSRunning = TRUE

    MOVS    R1, #1

    STRB    R1, [R0]

:触发 PendSV  异常

    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)

    LDR     R1, =NVIC_PENDSVSET

    STR     R1, [R0]

:开中断

 CPSIE   I 

3.3 OSCtxSw  任务切换

当操作系统要切换任务的时候,就触发一个pendsv异常。

OSCtxSw

LDR     R0, =NVIC_INT_CTRL                                

  ; Trigger the PendSV exception (causes context switch)

    LDR     R1, =NVIC_PENDSVSET

    STR     R1, [R0]

    BX      LR

3.4 OSIntCtxSw任务切换

和 3.3  用一样的代码

3.5 OS_CPU_PendSVHandler   pendsv 异常处理  

) 1  pendsv 用于使上下文切换。这是一个值得推荐的方法进行

Cortex-M3 上下文切换 ,这是因为 Cortex-M3 自动保存在任何例外处理器上下文的一半,使相同的异常返回 ,所以保存R4-R11  SP 是必须的。 使用 pendsv 异常这种方式意味着上下文保存和恢复是相同的,无论是由一个线程或发生中断或异常。

) 2 、代码实现步骤

:获取任务的 SP ,如果为  的话则 直接跳到 OS_CPU_PendSVHandler_nosave,去执行

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

cmp r0, #0

beq OS_CPU_PendSVHandler_nosave

:保存 R4-R11  SP

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack

    ;STM     R0, {R4-R11}

stm     r0!, {r4-r7}

mov     r1, r8

mov     r2, r9

mov     r3, r10

mov     r4, r11

stm     r0!, {r1-r4}

subs    r0, r0, #0x20

:将当前的堆栈指针给当前进程的任务块

    LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;

    LDR     R1, [R1]

    STR     R0, [R1]                                            ; R0 is SP of process being switched out

:然后调用 OSTaskSwHook 

mov     r0, lr

push    {r0}

ldr     r0, =OSTaskSwHook

blx     r0

:获取当前的高优先级

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;

    LDR     R1, =OSPrioHighRdy

    LDRB    R2, [R1]

    STRB    R2, [R0]

F获取当前就绪线程

    LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;

    LDR     R1, =OSTCBHighRdy

    LDR     R2, [R1]

    STR     R2, [R0]

G 得到新任务的Sp和恢复从堆栈 r4-r11

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;

    ;LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack

ldm     r0!, {r4-r7}

ldm     r0!, {r1-r3}

mov     r8, r1

mov     r9, r2

mov     r10, r3

ldm     r0!, {r1}

mov     r11, r1

H:载入新的SP和返回

    ;ADDS    R0, R0, #0x20

    MSR     PSP, R0                                             ; Load PSP with new process SP

    ;ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack

    mov     r0, lr

movs    r1, #0x04

orrs    r0, r0, r1

mov     lr, r0

CPSIE   I     // 更改处理器状态,使能中断

    BX      LR                                                  ; Exception return will restore remaining c

你可能感兴趣的:(uCOS的移植过程(uCOS II 在LPC11C14上面的移植方法))