ucosii在CM3内核中的移植

本篇文章基于STM32f107芯片进行移植,使用相同内核cortex-m3的都可参考

移植要做的就是和cpu相关的代码,cpu上层的代码都是固定的。ucosii中与cpu相关的代码在那里呢?

ucosii在CM3内核中的移植_第1张图片

移植相关的文件就是上述三个文件。

1、头文件os_cpu.h,主要功能:

(1)声明数据类型,增强代码的移植行

typedef unsigned char  BOOLEAN;
typedef unsigned char  INT8U;			/* Unsigned  8 bit quantity       */
typedef signed   char  INT8S;			/* Signed    8 bit quantity       */
typedef unsigned short INT16U;			/* Unsigned 16 bit quantity       */
typedef signed   short INT16S;			/* Signed   16 bit quantity       */
typedef unsigned int   INT32U;			/* Unsigned 32 bit quantity       */
typedef signed   int   INT32S;			/* Signed   32 bit quantity       */
typedef float          FP32;			/* Single precision floating point*/
typedef double         FP64;			/* Double precision floating point*/

//STM32是32位位宽的,这里OS_STK和OS_CPU_SR都应该为32位数据类型
typedef unsigned int   OS_STK;			/* Each stack entry is 32-bit wide*/
typedef unsigned int   OS_CPU_SR;		/* Define size of CPU status register*/

(2)定义临界段,也就是开中断关中断函数

#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

(3)定义栈的增长方向

栈的增长需要实现约定好,ARM中一般采取满减的方式。

//定义栈的增长方向.
//CM3中,栈是由高地址向低地址增长的,所以OS_STK_GROWTH设置为1
#define  OS_STK_GROWTH        1      /* Stack grows from HIGH to LOW memory on ARM    */

(4)定义任务切换的宏

//任务切换宏,由汇编实现.
#define  OS_TASK_SW()         OSCtxSw()

任务切换的代码在os_cpu.asm中实现。

2、os_cpu_c.c 文件

os_cpu_c.c中最重要的函数是栈的初始化函数,用于在创建任务时初始化任务栈,对R0-R12通用寄存器,sp指针,lr寄存器,程序状态寄存器xPSR,以及程序入口地址初始化。

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)OS_TaskReturn;           /* 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);
}

3. os_cpu_a.asm文件

该文件主要处理任务切换、中断相关的函数。有以下三个函数需要处理:

          OSStartHighRdy               
          OSCtxSw
          OSIntCtxSw

这三个函数最终都会用到PendSV_Handler。

OSStartHighRdy():运行优先级最高的就绪任务

这个函数只会执行一次。是在OSStart()函数当中运行的。函数运行的时间点是在创建任务完毕,开始执行任务时从当前所有就绪的任务当中找到优先级最高的任务来执行。

;/**************************************************************************************
;* 函数名称: OSStartHighRdy
;*
;* 功能描述: 使用调度器运行第一个任务
;* 
;* 参    数: None
;*
;* 返 回 值: None
;**************************************************************************************/  

OSStartHighRdy
        LDR     R4, =NVIC_SYSPRI2      ; set the PendSV exception priority 设置PENDSV中断优先级,最低
        LDR     R5, =NVIC_PENDSV_PRI
        STR     R5, [R4]

        MOV     R4, #0                 ; set the PSP to 0 for initial context switch call。设置PSP栈指针为0,PendSV_Handler中用来判断是否是第一次执行任务切换
        MSR     PSP, R4

        LDR     R4, =OSRunning         ; OSRunning = TRUE  设置OSRunnind为真,表明os已经运行起来了,在一些地方会用到
        MOV     R5, #1
        STRB    R5, [R4]

                                       ;切换到最高优先级的任务
        LDR     R4, =NVIC_INT_CTRL     ;rigger the PendSV exception (causes context switch)
        LDR     R5, =NVIC_PENDSVSET    ;这里就要置PENDSV中断触发,中断开启后就能进入PENDSV中断了。
        STR     R5, [R4]

        CPSIE   I                      ;enable interrupts at processor level
OSStartHang
        B       OSStartHang            ;should never get here
OSCtxSw()任务切换函数
这里是在线程中切换函数时的入口,最终任务切换在PendSV_Handler中完成
;/**************************************************************************************
;* 函数名称: OSCtxSw
;*
;* 功能描述: 任务级上下文切换         
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/
  
OSCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL  	;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
		POP     {R4, R5}
        BX      LR
OSIntCtxSw()中断中切换任务

这里是在中断中切换函数时的入口,最终任务切换在PendSV_Handler中完成,代码和上面的代码是基本一样的,区别在于在执行该函数时硬件无需再保存cpu寄存器,因为在调用之前已经发生了中断,已经保存过了。

;/**************************************************************************************
;* 函数名称: OSIntCtxSw
;*
;* 功能描述: 中断级任务切换
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/

OSIntCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL      ;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
		POP     {R4, R5}
        BX      LR
        NOP
PendSV_Handler,真正的任务切换函数

这里是任务真正的切换函数,任务切换要完成的工作是,

1、保存当前任务的R4-R11和PSP到任务栈中,(R0-R3,R12,LR,PC,xPSR已经被硬件自动压栈保存)。

2、找到当前就绪任务中优先级最高的任务,找到这个任务的PSP

3、以找到的任务PSP为线索,将该任务的R4-R11出栈

4、最后,LR记得设置成合适的值,才能回到线程模式下,使用线程栈PSP。LR的具体意义参考这篇文章cortex-m4内核知识讲解

出中断,硬件自动将当前任务的R0-R3,R12,LR,PC,xPSR出栈。完成任务切换

;/**************************************************************************************
;* 函数名称: OSPendSV
;*
;* 功能描述: OSPendSV is used to cause a context switch.
;*
;* 参    数: None
;*
;* 返 回 值: None
;***************************************************************************************/

PendSV_Handler
    CPSID   I                                                   ; 关中断,防止被打断
    MRS     R0, PSP                                             ; PSP is process stack pointer 如果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈-白菜注
    CBZ     R0, PendSV_Handler_Nosave		                ; Skip register save the first time,若是第一次任务切换(psp=0,在OSStartHighRdy中设置过),直接执行下一段程序,
	
    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}                                        ;将R4-R11入栈保存

    LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out将PSP指针保存到任务块中

                                                                ; At this point, entire context of process has been saved
PendSV_Handler_Nosave
    PUSH    {R14}                                               ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook()找到最高优先级任务
    BLX     R0
    POP     {R14} 

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]                                            ;这里找到下一个任务的栈指针PSP

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack,将R4-R11出栈
    ADDS    R0, R0, #0x20

		
    MSR     PSP, R0                                             ; Load PSP with new process SP R4-R11出栈后,更新一下PSP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack 
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context
	NOP
    end  
	

任务切换的压栈出栈细节,这篇文章讲的不错,一目了然

http://blog.csdn.net/_xiao/article/details/78481598




你可能感兴趣的:(RTOS)