移植UCOS-II到ARM7TDMI

UCOS-II移植到ARM需要做的工作如下:

<!--[if !supportLists]-->1)  <!--[endif]-->编写一个的与CPU相关的文件OS_CPU_A.S,此文件包含四个函数,这四个函数需用汇编编写。

<!--[if !supportLists]-->a) OSStartHighRdy( ),此函数作用为启动最高优先级任务,由OSStart( )调用。

<!--[if !supportLists]-->b) OSCtxSw( ),任务级别的切换函数,例如当前运行的任务不再是优先级最高时,这时就需要任务的切换(比如当前任务申请一个信号量而被挂起,或由于任务释放资源时,这些时候系统会进行任务的切换)。OSCtxSw( )OSSche( )调用。

<!--[if !supportLists]-->c)<!--[endif]--> OSIntCtxSw( ),此函数用于由中断产生的任务切换。比如系统时钟使得一个更高优先级的任务就绪时,或者中断使得更高优先级就绪时。由于发生中断时,系统的堆栈已经保存了当前任务的上下文,而后中断处理程序又调用OSIntExit( ),最后才由OSIntExit( )调用OSIntCtxSw( )进行任务的切换,这时堆栈中就多了一些内容,需要将这些多余的内容去掉,然后调用OSCtxSw( )

<!--[if !supportLists]-->d) <!--[endif]-->OSTickISR( ),为UCOS-II提供一个时钟资源来实现时间的延时和期满的功能。时钟节拍应该每秒发生10—100次。这与具体的处理器有关。

<!--[if !supportLists]-->2)  <!--[endif]-->编写OS_CPU_C.C文件

要求编写六个简单的函数:

OSTaskStkInit( );

OSTaskCreateHook( );

OSTaskDelHook( );

OSTaskSwHook( );

OSTaskStatHook( );

OSTimeTickHook( );

在这六个函数中最关键的是编写 OSTaskStkInit( ) ,此函数是在任务被创建的时候,由 OSTaskCreate( ) 调用。主要用于初始化任务堆栈,任务堆栈结构与 CPU 的体系结构、编译器密切相关。本次移植的任务堆栈结构如下图(一)


1OS_CPU.H

本移植是将UCOS-II移植到ARMTDMI内核,ARMTDMI7个工作模式,两个指令系统ARMTHUMB

7个工作模式中只选用用户和系统模式。本次移植将实现在用户和系统模式转换,ARMTHUMB指令系统的转换。

       当系统初始化完成后,将进入用户模式,在用户模式下进行任务的创建和运行,然后调用相应的软中断来实现用户和系统模式转换,ARMTHUMB指令系统的转换,以及任务切换等工作。也就是说任务切换是通过软中断来实现的。各个软中断(SWI)服务如下:(OS_TASK_SW( )OSStartHighRdy( )OS_CPU_A.S中实现,其他各功能实现在OS_CPU_C.C中)

__swi(0x0)    void OS_TASK_SW(void);        /*任务级切换函数     */

__swi(0x1)    void OSStartHighRdy(void);    /*运行优先级最高的任务*/

__swi(0x2)    void OS_ENTER_CRITICAL(void); /*进入临界区,关中断  */

__swi(0x3)    void OS_EXIT_CRITICAL(void);  /*推出临界区,开中断  */

__swi(0x80) void ChangToSYSMode(void);      /*任务切换到系统模式  */

__swi(0x81) void ChangToUserMode(void);     /*任务切换到用户模式  */

__swi(0x82) void TaskIsARM(INT8U prio);     /*任务代码是ARM指令  */

__swi(0x83) void TaskIsTHUMB(INT8U prio);   /*任务代码为THUMB指令*/

TaskIsARM( ) TaskIsTHUMB( )使用时应该注意,他们必须在任务建立后但还没运行时被调用。有以下三中解决方法:

<!--[if !supportLists]-->A、 <!--[endif]-->高优先级任务使用默认的指令集;

<!--[if !supportLists]-->B、 <!--[endif]-->改变OSTaskCreateHook( )使任务不处于就绪状态,建立任务后调用函数OSTaskResume( )来使任务进入就绪状态;

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

<!--[if !supportLists]-->1、  <!--[endif]-->OS_CPU_C.C

在此文件中主要是编写OSTaskStkInit( )由于任务堆栈都已经确定好了,如图(一)。很容易写出代码,其代码如下:

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

    OS_STK *stk;

    opt = opt;            /* ''opt'' is not used, prevent warning  */

    stk=(OS_STK *)ptos;   /* Load stack pointer  */

    *stk = (OS_STK)task;  /* PC   (High address)   */

    *--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, the first parameter is transfered by R0  */

    *--stk = 0x10;      /* CPSR  ,enable IRQ and FIQ interrupt  (Low address)*/

    return ((void *)stk);
}

其余的勾挂函数,在以后具体的应用中根据具体的情况编写。

 

<!--[if !supportLists]-->1、  <!--[endif]-->OS_CPU_A.S

对于所有的任务级的切换,都是使用SWI软中断来实现。因此首先编写如下的汇编代码,此为SWI的汇编与C

的接口代码。软中断发生后PC指向0x08处,因此可以在0x08处放一条跳转语句指向处理程序SwiFirstHandler,然后通过SwiFirstHandler调用SwiSecondHandler( )来实现其他的功能SwiFirstHandler如程序清单1

程序清单1 软中断代码的汇编部分

SwiFirstHandler    

       LDR   SP, =SVCStack             ;

       STMFD SP!, {R0-R3, R12, LR}     ;保护任务环境

 

       MOV  R1, SP    ; Set pointer to parameters

       MRS  R3, SPSR   ; SPSR保存到R3,其实SPSR就是任务运行时的CPSR

      

       TST  R3,#T_BIT   ;判断是否是THUNMB            

       LDRNEH R0, [LR, #-2]        ; THUMB

       BICNE           R0, R0, #0xFF00

      

       LDREQ          R0, [LR, #-4]  ; ARM

       BICEQ           R0, R0, #0xFF000000

      

       ;R0 存放 SWI 功能码


       CMP R0, #1                         

       LDRLO PC,=OS_TASK_SW  ; SWI 码是 0,进行任务的切换,在OS_CPU_A.S中实现

       LDREQ  PC,=OSStartHighRdy ; SWI 码是 1,启动任务,在OS_CPU_A.S中实现

      

       BL SwiSecondHandler  ; 在在OS_CPU_C.C中实现

       LDMFD  SP!, {R0-R3,R12,PC}^   ; 没有进行任务切换返回到当前任务

注意①,我们知道在处理器处理软中断时,处理器的工作模式本来就是SVC模式,自然SP也是指向SVC模式的指针,这里为什么还要从新把SVC模式指针赋给SP呢?这里主要是因为在启动代码程序中,将SVC模式指针修改了,所以这里要从新将SVC模式指针赋给SP。这部分启动任务程序在OS_CPU_A.S中。
还需注意的是本段代码并不在
OS_CPU_A.S实现,而被放到启动代码文件InitTarget.s实现的。

2.1OSStartHighRdy()的实现

用伪码描述OSStartHighRdy()如下:

void OSStartHighRdy (void)

{

Call user definable OSTaskSwHook();

Get the stack pointer of the task to resume:

Stack pointer = OSTCBHighRdy->OSTCBStkPtr;

OSRunning = TRUE;

Restore all processor registers from the new task''s stack;

Execute a return from interrupt instruction;

}

OSStartHighRdy( )OSStart()调用,用于启动高优先级的任务。OSStartHighRdy( )所要做的工作是将图(一)中任务堆栈的初始值直接拷贝到相应寄存器中即可完成。

 

 

2.2OS_TASK_SW( )OSIntCtxSw( )

       由于arm处理器和所用编译器的特点,OS_TASK_SW( )OSIntCtxSw( )可用相同的代码编写。

虽然OS_TASK_SW( )OSIntCtxSw( )是在两个不同的模式下被调用,OS_TASK_SW( )是在SVC模式下,而OSIntCtxSw( )是在中断模式下被调用,但是还是可以用相同代码来处理。关键是两种模式堆栈所保存的任务环境要相同,所以对于IRQ模式,也需要做一些保存任务环境的工作,具体采用以下代码来实现,如程序清单2。当发生中断时PC指向0x18处,在此处放一条跳转到IsrIRQ指令。

 

程序清单2

IsrIRQ                          ; using I_ISPR register.

    sub      lr,lr,#4           ; reserved for return

    stmfd   sp!,{r0-r3,r12,lr}  ; restore the contexts of task to be                                     ;interrupted

       ; IMPORTANT CAUTION

       ; if I_ISPC isn't used properly, I_ISPR can be 0 in this routine.

   ldr   r0,=OSIntNesting        ; OSIntNesting+1

   ldrb  r1,[r0]

   add   r1,r1,#1

   strb  r1,[r0]

   ldr   r1,= I_ISPR

   ldr   r1,[r1]

   

cmp r1,#0x0 ; if the idel mode work_around is used r9 may 0 sometimes

    beq      %F2

    mov     r0,#0x0

0

    movs    r1,r1,lsr #1

    bcs      %F1

    add      r0,r0,#4

    b         %B0

1

    ldr       r1,=ADC    ; ADC开始是对于44B0芯片的全部中断表,这个表如程序清单3

    add      r1,r1,r0    ; r1+r0就是找到是什么发生了中断请求

    ldr       pc,[r1]    ; 调用中断处理程序

    mrs         r3,spsr

 bl            OSIntExit                                                       

2    

   ldmfd   sp!,{r0-r3,r12,pc}   ; 无任务切换返回

 

程序清单3

ADC         dcd 0

RTC         dcd 0

UTXD1       dcd 0

UTXD0       dcd 0

SIO         dcd 0

IIC         dcd 0

URXD1       dcd 0

URXD0       dcd 0

TIMER5      dcd 0

TIMER4      dcd 0

TIMER3      dcd 0

TIMER2      dcd 0

TIMER1      dcd 0

TIMER0      dcd OSTickISR

UERR01      dcd 0

WDT         dcd 0

BDMA1       dcd 0

BDMA0       dcd 0

ZDMA1       dcd 0

ZDMA0       dcd 0

TICK        dcd 0

EINT4567    dcd 0

EINT3       dcd 0

EINT2       dcd 0

EINT1       dcd 0

EINT0       dcd 0  

 

需要注意的是这段代码和SwiFirstHandler一样在InitTarget.s中编写,这段代码是按照S 3C 44B0来编写的,采用的是非向量中断模式。在具体的应用时,中断服务程序的地址自行安排。TIMER0指向OSTickISR也就是说用TIMER0来做操作系统的时钟。

经过 IsrIRQ SwiFirstHandler 的处理,不管是在 SVC 模式还是在 IRQ 模式,任务都以图(二)的形式保存到各自的堆栈中,这样就可以用相同的程序来处理 OS_TASK_SW( ) OSIntCtxSw( ) 了。

程序清单4将给出如何保存被切换任务的环境

 

程序清单 <!--[if !vml]--><!--[endif]-->4

        LDR     R2,[SP,#20]               ;得到 PC

        LDR     R12,[SP,#16]              ;得到R12

        MRS     R0,CPSR   ;将当前的状态保存到R0,这样就不管是SVC

;IRQ模式

       

        MSR     CPSR_c,#SYSMODE                  ;切换到系统模式

        MOV     R1,LR

        STMFD   SP!,{R1-R2}                      ;//保存PC,LR,R12-R4

        STMFD   SP!,{R4-R12}

       

        MSR     CPSR_c,R0                        ;//返回先前的模式

        LDMFD   SP!,{R4-R7}

        MSR     R3,SPSR                          ;//被切换任务的CPSR

ADD     SP,SP,#8                         ;// 因为刚才PCR12还没弹出栈

     

        MSR     CPSR_c,#SYSMODE

        STMFD   SP!,{R4-R7}              ;//保存 PC,LR,R3-R0

        STMFD   SP!,{R3}                 ;//保存 CPSR

       

LDR     R1,=OSTCBCur                                    ;//OSTCBCur->OSTCBStkPtr = Stack pointer

        LDR     R1,[R1]

        STR     SP,[R1]

以上的代码就将被挂起任务的上下文保存到了自己的堆栈中。下面的的工作就是将更高优先级的就绪任务从任务堆栈中复制出来执行。以下代码略。 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值