lpc2200移植ucos-II总结(四 编写os_cpu_c.c文件)

2.4 编写os_cpu_c.c文件

OSTaskStkInt()任务堆栈初始化函数,在编写此函数之前,必须先确定任务的堆栈结构。而任务的堆栈结构是与CPU的体系结构、编译器有密切的关联。本移植的堆栈结构见图2.1所示。

lpc2200移植ucos-II总结(四 编写os_cpu_c.c文件)_第1张图片

图2.1 任务堆栈结构图

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

OsEnterSum是作者定义的一个全局变量,主要是用它来保存关中断的次数,这样关中断和开中断就可以嵌套了。在调用OS_ENTER_CRITICAL()时,它的值加1,同时关中断。在调用OS_EXIT_CRITICAL()时,它的值减1,同时开中断。每个任务都有独立的OsEnterSum,在任务切换时保存和恢复各自的OsEnterSum。这样,各个任务开关中断的状态可以不同,任务不必过分考虑关中断对别的任务的影响。

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

2. 软件中断异常SWI服务程序C语言部分

参数SWI_Num为功能号,而Regs为指向堆栈中保存寄存器的值的位置。软中断的0、1号功能并没有在这里实现,而是在OS_CUP_A.S中实现。

软中断的C语言部分代码

        void SWI_Exception(int SWI_Num, int *Regs)

{

    OS_TCB   *ptcb;

    switch(SWI_Num)

    {

         //case 0x00: /* 任务切换函数OS_TASK_SW,参考os_cpu_s.s文件  */

         //    break;

         //case 0x01: /* 启动任务函数OSStartHighRdy,参考os_cpu_s.s文件 */

         //    break;

     case 0x02:       /* 关中断函数OS_ENTER_CRITICAL(),参考os_cpu.h文件 */

            __asm    /*C语言内嵌汇编指令关键字,通过__asm关键字可以在C程序中内嵌汇编程序*/

            {

                MRS     R0, SPSR      /*读取SPSR寄存器的值,保存到r0中

                ORR     R0, R0, #NoInt /*寄存器r0与立即数NoInt相或,并保存到r0中,

                MSR     SPSR_c, R0    /*将r0的值写入SPSR[7:0],关中断

            }

            OsEnterSum++;

            break;

 

     case 0x03:     /* 开中断函数OS_EXIT_CRITICAL(),参考os_cpu.h文件 */

            if (--OsEnterSum == 0)

            {

                __asm

                {

                    MRS     R0, SPSR

                    BIC     R0, R0, #NoInt

                    MSR     SPSR_c, R0

                }

            }

            break;

    

        case 0x80:                      /* 任务切换到系统模式 */

            __asm

            {

                MRS     R0, SPSR

                BIC     R0, R0,  #0x1f  /* 将R0的低5位清零,其它位不变 */

                ORR     R0, R0, #SYS32Mode   

                MSR     SPSR_c, R0

            }

            break;

        case 0x81:                      /* 任务切换到用户模式 */

            __asm

            {

                MRS     R0, SPSR

                BIC     R0, R0, #0x1f

                ORR     R0, R0, #USR32Mode   

                MSR     SPSR_c, R0

            }

            break;

        case 0x82:                      /* 任务是ARM代码     */

            if (Regs[0] <= OS_LOWEST_PRIO)

            {

                ptcb = OSTCBPrioTbl[Regs[0]];/*ptcb指向当前任务堆栈的首

地址,也就是当前任务堆栈结构的OsEnterSum

                if (ptcb != NULL)

                {

                    ptcb -> OSTCBStkPtr[1] &= ~(1 << 5) /*OSTCNStkPtr指向任务堆栈的栈顶,所以OSTCBStkPtr[1]指向任务堆栈的SPSR寄存器,通过改变SPSR相应位切换处理器模式

                }

            }

            break;

        case 0x83:                          /* 任务是THUMB代码 */

            if (Regs[0] <= OS_LOWEST_PRIO)

            {

                ptcb = OSTCBPrioTbl[Regs[0]];

                if (ptcb != NULL)

                {

                    ptcb -> OSTCBStkPtr[1] |= (1 << 5);

                }

            }

            break;

        default:

            break;

}

3. 宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()

这里需要对关中断函数和开中断函数作些解释。与所有的实时内核一样,μC/OS-Ⅱ需要先禁止中断再访问代码的临界段,并且在访问完毕后重新允许中断。这就使得μC/OS-Ⅱ能够保护临界段代码免受多任务或中断服务破坏。实现OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()这2个宏的定义有3种方法,本移植采用的是第三种方法,即在OS_CPU.H文件中使OS_CRITICAL_MEHTOD等于3,这种方法是利用某些编译器提供的扩展功能,用户可以得到当前处理器状态字的值,并将其保存在C函数的局部变量之中,这个变量可以用于恢复PSW,而本ARM内核关中断和开中断时,是通过改变程序状态寄存器CPSR中的相应控制位实现。由于使用了软件中断,程序状态寄存器 CPSR保存到程序状态保存寄存器 SPSR中,软件中断退出时会将SPSR恢复到CPSR中。所以程序只要改变程序状态保存寄存器SPSR中相应的控制位就可以了。

4. OSStartHighRdy

uC/OS-II启动多任务环境的函数是OSStart()。用户在调用OSStart()之前,必须已经建立了一个或更多任务。OSStart()最终调用函数OSStartHighRdy()运行多任务启动前优先级最高的任务,OSStartHighRdy()的代码如下。

  void OSStartHighRdy(void)

{

    _OSStartHighRdy();

}

通过函数_OSStartHighRdy()进入软中断SoftwareInterrupt,最终调用__OSStartHighRdy。__OSStartHighRdy代码如下所示

__OSStartHighRdy

        MSR     CPSR_c, #(NoInt | SYS32Mode);/*关中断,切换到系统模式

        /*告诉uC/OS-II自身已经运行,将1保存到OSRunning*/

        LDR     R4, =OSRunning /*将OSRunning的地址加载到R4,

R4里存放的是OSRunning的地址

        MOV     R5, #1

        STRB    R5, [R4]       /*将R5的值保存到R4指定的地址中,

只存储1字节数据

        BL      OSTaskSwHook   ;调用钩子函数

 

        LDR     R6, =OSTCBHighRdy  /*将OSTCBHighRdy的地址加载到R6

        LDR    R6, [R6]     /*加载R6指定地址上的数据(字),放入R6中

        B       OSIntCtxSw_1

5. 移植增加的特定函数

根据ARM核心的特点和移植目标,为此增加了两个处理器模式转换函数(ChangTo-SYSMode()、ChangeToUSRMode())和两个任务初始指令集设置函数(TaskIsARM()、TaskIsTHUMBle())。他们都是通过中断指令SWI转换到系统模式,通过软件中断服务程序实现的。

ChangeToSYSMode()把当前任务转换到系统模式,ChangeToUSRMode()把当前任务转换到用户模式。她们改变程序状态保存寄存器SPSR的相应位段,而程序状态保存寄存器会在软件中断退出时复制到程序状态寄存器CPSR,任务的处理器模式就改变了。

本移植增加了两个函数TaskIsARM()和TaskIsTHUMB()用于改变任务建立时默认的指令集。函数TaskIsARM()用于声明指定优先级的任务的第一条指令是ARM指令集中的指令,而函数TaskIsTHUMB()用于声明指定优先级的任务的第一条指令是THUMB指令集中的指令,她们都有唯一的参数,即需要改变的任务的优先级。值得注意的是,这两个函数必须在相应的任务建立后但还没运行时调用。这样,如果在低优先级的任务中创建高优先级的任务就十分危险了。此时,解决的方法有三种:

(1) 高优先级任务使用默认的指令集;

(2) 改变函数OSTaskCreateHook()使任务默认不是出于就绪状态,建立任务后调用函数OSTaskResume()来使任务进入就绪状态。

(3) 建立任务时禁止任务切换,调用函数TaskIsARM()或TaskIsTHUMB()后再允许任务切换。

6. …Hook()函数

uC/OS-II有很多由用户编写的…Hook()函数,它在移植中全为空函数,用户就可以按照uC/OS-II的要求修改它。

你可能感兴趣的:(lpc2200移植ucos-II总结(四 编写os_cpu_c.c文件))