原创 s3c2410的ucos操作系统移植实践

2009-3-21 13:33 5083 11 11 分类: MCU/ 嵌入式
 

μC/OS-II大部分代码是用移植性很高的ANSI C语言编写的,只包含一小部分汇编语言代码,因此可以很方便地将它移植到各种不同构架的微处理器上。移植μC/OS-II,所用处理器和该处理器所用的C语言编译器必须满足以下条件:


1.处理器的C编译器能产生可重入代码;


2.处理器支持中断,并且能产生定时中断;


3.C语言就可以开/关中断;


4.处理器能支持一定数量的数据存储硬件堆栈;


5.处理器有将堆栈指针以及其他CPU寄存器的内容读出、并存储到堆栈或内存中去的指令。



首先,由于处理器字长不同为了方便,我们通常要修改下字长定义,s3c2410是32位处理器所以定义如下:


      #define U32 unsigned int
#define U16 unsigned short
#define S32 int
#define S16 short int
#define U8 unsigned char
#define S8 signed char





下面我对以上的移植要求一次进行移植分析:


     1.修改ucos源代码中OS_CPU.S中的代码如下,主要是用开关中断实现代码的可重入要求:




EXPORT OSCPUSaveSR


OSCPUSaveSR



        mrs r0,CPSR       


        orr r1,r0,#NOINT   ;屏蔽irq, fiq


        msr CPSR_c,r1


        mov pc,lr         ;跳回




        EXPORT OSCPURestoreSR


OSCPURestoreSR



        msr CPSR_c,r0


        mov pc,lr




     2.   s<?XML:NAMESPACE PREFIX = ST1 />2c2410支持中断,选择一个定时器作为时钟滴答,来对任务做时间片的调度,有关s3c2410的中断介绍可以参考我得另一片文章。



     产生中断后,代码自动跳转到0x0的中断向量表,然后在从中断向量表中跳到下面的程序,进行中断号的分析,然后利用ucos的中断任务切换到中断服务子程序中。


UCOS_IRQHandler



        stmfd sp!,{r0-r3,r12,lr} ;保存现场



        bl OSIntEnter          ;跳到下面程序,实际上是中断嵌套


        bl C_IRQHandler       ;计算出中断号


        bl OSIntExit          ;中断减1 ,切换最高优先级任务



        ldr r0,=OSIntCtxSwFlag ; 判断是否需要中断切换


        ldr r1,[r0]


        cmp r1,#1


        beq _IntCtxSw         ;调用中断任务切换函数,后面分析



        ldmfd sp!,{r0-r3,r12,lr} ;恢复现场


        subs pc,lr,#4



void OSIntEnter (void)


{


    if (OSRunning == OS_TRUE) {


        if (OSIntNesting < 255u) {


            OSIntNesting++;                      /* 中断记数+1                        */


        }


    }


}


void OSIntExit (void)


{


#if OS_CRITICAL_METHOD == 3                              


    OS_CPU_SR cpu_sr = 0;


#endif





    if (OSRunning == OS_TRUE) {


        OS_ENTER_CRITICAL();


        if (OSIntNesting > 0) {                            有中断,中断数-1


            OSIntNesting--;


        }


        if (OSIntNesting == 0) {                          如果没有中断的话


            if (OSLockNesting == 0) {                     


                OS_SchedNew();                       


                if (OSPrioHighRdy != OSPrioCur) {         


                    OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];


#if OS_TASK_PROFILE_EN > 0


                    OSTCBHighRdy->OSTCBCtxSwCtr++;        


#endif


                    OSCtxSwCtr++;                      将最高优先级的任务调入


                    OSIntCtxSw();                         


                }


            }


        }


        OS_EXIT_CRITICAL();


    }


}




void C_IRQHandler(void)
{
        U32 wTemp;


        wTemp = rINTOFFSET<<2;           根据中断偏移量判断是什么中断


        ((void(*)(void))(*((U32 *)(aISR_EINT0+wTemp))))(); 关键!跳到相应中断服务程序 


}






       其中的UCOS_IRQHandler就是实现中断跳转的关键代码,C语言中将irq中断函数的入口


指向它实现中断跳转到C中对应的服务函数,代码如下:



pISR_IRQ = (U32)UCOS_IRQHandler; 中断入口挂接



      然后初始化一个定时器作为时钟滴答,一般选择100ms左右,产生一次中断溢出,进行


次任务调度。




3.有关堆栈的设置。因为ucos进行任务切换时,就是相当于模拟的做中断操作。所以在切换任务之前必须先保存任务的现场:包括R0~R12,sp,lr,pc,cprs,sprs。然后用sp从旧的任务堆栈指向新的任务的堆栈。(这个堆栈在任务创建的时候已经分配好了)。


    在初始化任务堆栈之前,首先要设置两个参数,在CONFIG文件中: 大小端编译模式,堆栈得生长方向。代码如下:


#define OS_STK_GROWTH 1 ;堆栈向上生长


然后在任务创建时的堆栈初始化函数,在OS_CPU_C.c文件中:


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



    opt    = opt;                           /* 'opt' is not used, prevent warning                      */
    stk    = ptos;                          /* Load stack pointer                                      */
    *(stk) = (OS_STK)task;                  /* Entry Point                                             */
    *(--stk) = (INT32U)0;                   /* lr                                                      */
    *(--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)pdata;               /* r0 : argument                                           */
    *(--stk) = (INT32U)(SVCMODE|0x0);       /* PSR                                                     */
    *(--stk) = (INT32U)(SVCMODE|0x0);       /* SPSR                                                    */


        return (stk);
}


4.有关任务调度方面的移植:


    首先就是当运行OSSTART()后,系统要进入优先级最高的任务中,即第一个任务,这短代码的移植如下:


    OSStartHighRdy


        bl OSTaskSwHook             ;


        ldr r4,=OSRunning           ; 运行多任务
        mov r5,#1
        strb r5,[r4]


        ldr r4,=OSTCBHighRdy        ; 得到最高优先级任务的TCB地址


        ldr r4,[r4]                 ; 得到其堆栈地址
        ldr sp,[r4]                 ; 切换到新任务的堆栈


        ldmfd sp!,{r4}              ; 把新任务堆栈的内容环境载进当片CPU
        msr SPSR_cxsf,r4
        ldmfd sp!,{r4}              ; pop new task's psr
        msr CPSR_cxsf,r4
        ldmfd sp!,{r0-r12,lr,pc}    ; pop new task's r0-r12,lr & pc


     其次时对任务调度函数的移植,主要是在时间片到时,读取就绪表中优先级最高的任务,并切换到该任务的环境中:


OSCtxSw
        ;保存要被切换出去的任务环境
        stmfd sp!,{lr}              ; push pc (lr should be pushed in place of PC)
        stmfd sp!,{r0-r12,lr}       ; push lr & register file
        mrs r4,cpsr
        stmfd sp!,{r4}              ; push current psr
        mrs r4,spsr
        stmfd sp!,{r4}              ; push current spsr


        ;把就绪表中优先级 最高的任务载入到当前任务指针指向


       ; OSPrioCur = OSPrioHighRdy
        ldr r4,=OSPrioCur
        ldr r5,=OSPrioHighRdy
        ldrb r6,[r5]
        strb r6,[r4]
       


        ;得到当前任务的TCB地址
        ; Get current task TCB address
        ldr r4,=OSTCBCur
        ldr r5,[r4]
        str sp,[r5]                 ; store sp in preempted tasks's TCB


        bl OSTaskSwHook             ; call Task Switch Hook


        ;得到最高优先级任务的地址,并把堆栈指针指向它的堆栈地址


        ; Get highest priority task TCB address
        ldr r6,=OSTCBHighRdy
        ldr r6,[r6]
        ldr sp,[r6]                 ; get new task's stack pointer


        ;调出新任务的环境实现切换


        ; OSTCBCur = OSTCBHighRdy
        str r6,[r4]                 ; set new current task TCB address


        ldmfd sp!,{r4}              ; pop new task's spsr
        msr SPSR_cxsf,r4
        ldmfd sp!,{r4}              ; pop new task's psr
        msr CPSR_cxsf,r4
        ldmfd sp!,{r0-r12,lr,pc}    ; pop new task's r0-r12,lr & pc


       最后是中断级的任务切换这种情况发生在,在中断服务程序中报告了一个或多个事件的发生,而这些事件的发生使一些更高优先级的任务进入就绪状态。因此在中断退出时不应该返回到被中断的任务,而应该返回到就绪态中优先级最高的任务。函数OSIntCtxSw()的主要功能是完成中断级的任务切换。与任务级的任务切换不同的时,在进入中断后,所有寄存器的值都被入栈了。因此在进入任务级切换时寄存器的入栈操作应该再有。另外,在中断服务程序中调用了函数OSIntExit()OSIntCtxSw(),因此应该调整当前任务的堆栈指针,让任务下次再被执行时返回中断服务程序而不是返回函数OSIntExit()OSIntCtxSw()。剩下的任务就是进行任务切换了,这些代码同函数OSCtxSw()的大部分代码都相同。


    _IntCtxSw
       


        ;对堆栈指针的调整


        mov r1,#0
        str r1,[r0]


        ldmfd sp!,{r0-r3,r12,lr}
        stmfd sp!,{r0-r3}
        mov r1,sp
        add sp,sp,#16
        sub r2,lr,#4


        mrs r3,spsr
        orr r0,r3,#NOINT
        msr spsr_c,r0


        ldr r0,=.+8
        movs pc,r0


         ;以下代码和正常的任务切换过程完全一样


        stmfd sp!,{r2}              ; push old task's pc
        stmfd sp!,{r4-r12,lr}       ; push old task's lr,r12-r4
        mov r4,r1                   ; Special optimised code below
        mov r5,r3
        ldmfd r4!,{r0-r3}
        stmfd sp!,{r0-r3}           ; push old task's r3-r0
        stmfd sp!,{r5}              ; push old task's psr
        mrs r4,spsr
        stmfd sp!,{r4}              ; push old task's spsr
       
        ; OSPrioCur = OSPrioHighRdy
        ldr r4,=OSPrioCur
        ldr r5,=OSPrioHighRdy
        ldrb r5,[r5]
        strb r5,[r4]
       
        ; Get current task TCB address
        ldr r4,=OSTCBCur
        ldr r5,[r4]
        str sp,[r5]                 ; store sp in preempted tasks's TCB


        bl OSTaskSwHook             ; call Task Switch Hook


        ; Get highest priority task TCB address
        ldr r6,=OSTCBHighRdy
        ldr r6,[r6]
        ldr sp,[r6]                 ; get new task's stack pointer


        ; OSTCBCur = OSTCBHighRdy
        str r6,[r4]                 ; set new current task TCB address


        ldmfd sp!,{r4}              ; pop new task's spsr
        msr SPSR_cxsf,r4
        ldmfd sp!,{r4}              ; pop new task's psr
        msr CPSR_cxsf,r4


        ldmfd sp!,{r0-r12,lr,pc}    ; pop new task's r0-r12,lr & pc


          这样我们就可以实现ucos在s3c2410的移植了。注意如果要是在ram中仿真时,必须把中断向量表预先烧录到0x0处才能正常实现中断的跳转。

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
11
关闭 站长推荐上一条 /3 下一条