【uCOS-II学习笔记1】启动、创建任务、优先级获取、任务调度

uC/OS-II的启动

/**
  * 多任务的启动时用户通过调用OSStart()实现的
  * 在启动之前,用户至少要建立一个应用任务
  */
int main(void)
{
	OSInit();	/* 初始化uC/OS-II */
	OSTaskCreate(task,(void *)0,(OS_STK *)&TASK_STK[STK_SIZE-1],TASK_PRIO );
	OSStart();	/* 开始任务调度 */
}

1、uC/OS初始化 OSInit

void OSInit(void)
{
	OSInitHookBegin();  /* Call port specific initialization code   */
	
	OS_InitMisc( ); //完成一般变量初始化
	OS_InitRdyList( );//就绪列表初始化
	OS_InitTCBList( );//空闲任务链表OSTCBFreeList建立
	OS_InitEventList( );//事件空闲链表OSEventFreeList建立。

	OS_FlagInit( );  //其它相关功能参数初始化。
	OS_MemInit( );
	OS_QInit( );

	OS_InitTaskIdle( );//创建空闲任务OS_TaskIdle.
	OS_InitTaskStat( );
	OSInitHookEnd( ); /* Call port specific init. code            */

}

①相关变量初始化OS_InitMisc

  • OSIntNesting、OSLockNesting、OSTaskCtr分别是中断嵌套的层数、调取器上锁、任务数,均清零
  • OSRunning,系统任务的运行状态为 OS_FALSE
  • OSCtxSwCtr、OSIdleCtr分别是上下文切换计数和空闲任务计数,均清零

②就绪列表初始化OS_InitRdyList

  • OSRdyGrp、OSRdyTbl[]分别是就绪任务组,就绪任务表清零
  • OSPrioCur、OSPrioHighRdy当前运行任务优先级,最高就绪任务优先级清零
  • OSTCBCur、OSTCBHighRdy当前运行任务控制块,最高就绪任务控制块清零

③任务链表初始化OS_InitTCBList

OSTCBTbl[]任务控制块数组清零,全部变成空闲任务控制块。

  • 然后&OSTCBTbl[i]->OSTCBNext = &OSTCBTbl[i+1]组成单向的空闲任务控制块链表,
  • OSTCBFreeList = &OSTCBTbl[0];OSTCBFreeList 为空闲任务控制块链表表头。
  • OSTCBList = (OS_TCB *)0;OSTCBList 为双向任务控制块链表,创建任务的时候会将这个OS_TCB类型的指针,指向新创建任务的任务控制块,

OSTCBPrioTbl[]对应优先级的任务控制块数组清零。

  • 创建任务时,若该任务优先级为prio,创建任务的任务控制块指针为ptcb,则OSTCBPrioTbl[prio] = ptcb。

2、任务创建 OSTaskCreate

可用过一下两个函数之一建立任务

  • OSTaskCreate();
  • OSTaskCreateExt();

OSTaskCreate的流程为,①初始化任务堆栈、②初始化任务控制块、③如果系统已启动,进行任务调度

/**
  * 代码有删减,去除宏定义等,便于梳理工作流程
  * OSTaskCreate中,①初始化任务堆栈、②初始化任务控制块、③如果系统已启动,进行任务调度
  */
INT8U  OSTaskCreate (void   (*task)(void *p_arg),
                     void    *p_arg,
                     OS_STK  *ptos,
                     INT8U    prio)
{
    OS_ENTER_CRITICAL();//系统进入“临界区”,即关中断

    psp = OSTaskStkInit(task, p_arg, ptos, 0u);	/* Initialize the task's stack */
    err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u);
    if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */
    	OS_Sched();
    }
            
    OS_EXIT_CRITICAL();//系统退出“临界区”,即开中断
}

①初始化任务堆栈 OSTaskStkInit

1. 入栈顺序

根据Cortex-M3 权威指南
中断/异常触发时,硬件自动依次把xPSR, PC, LR, R12以及R3‐R0压入适当的堆栈
R11-R4则需要手动压入堆栈中
Cortex-M3 权威指南-第九章
Cortex-M3先把PC与xPSR的值保存,就可以更早地启动服务例程指令的预取——因为这需要修改PC;同时,也做到了在早期就可以更新xPSR中IPSR位段的值。(时间线不用考虑,只需要了解CM3寄存器组入栈的顺序即可,xPSR, PC, LR, R12以及R3‐R0)

2. M3寄存器组

在这里插入图片描述

  • R0-R12:通用寄存器
    R0‐R12 都是 32 位通用寄存器,用于数据操作。
  • Banked R13: 两个堆栈指针
    Cortex‐M3 拥有两个堆栈指针,然而它们是 banked,因此任一时刻只能使用其中的一个。
    • 主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包
      括中断服务例程)
    • 进程堆栈指针(PSP):由用户的应用程序代码使用。
  • R14:连接寄存器
    当呼叫一个子程序时,由 R14 存储返回地址
  • R15:程序计数寄存器
    指向当前的程序地址。如果修改它的值,就能改变程序的执行流

3. OSTaskStkInit()

OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
    OS_STK *p_stk;

    p_stk      = ptos + 1u;                                     /* Load stack pointer                                   */
                                                                /* Align the stack to 8-bytes.                          */
    p_stk      = (OS_STK *)((OS_STK)(p_stk) & 0xFFFFFFF8u);
                                                                /* Registers stacked as if auto-saved on exception      */
    *(--p_stk) = (OS_STK)0x01000000uL;                          /* xPSR                                                 */
    *(--p_stk) = (OS_STK)task;                                  /* Entry Point                                          */
    *(--p_stk) = (OS_STK)OS_TaskReturn;                         /* R14 (LR)                                             */
    *(--p_stk) = (OS_STK)0x12121212uL;                          /* R12                                                  */
    *(--p_stk) = (OS_STK)0x03030303uL;                          /* R3                                                   */
    *(--p_stk) = (OS_STK)0x02020202uL;                          /* R2                                                   */
    *(--p_stk) = (OS_STK)0x01010101uL;                          /* R1                                                   */
    *(--p_stk) = (OS_STK)p_arg;                                 /* R0 : argument                                        */
    
                                                                /* Remaining registers saved on process stack           */
    *(--p_stk) = (OS_STK)0x11111111uL;                          /* R11                                                  */
    *(--p_stk) = (OS_STK)0x10101010uL;                          /* R10                                                  */
    *(--p_stk) = (OS_STK)0x09090909uL;                          /* R9                                                   */
    *(--p_stk) = (OS_STK)0x08080808uL;                          /* R8                                                   */
    *(--p_stk) = (OS_STK)0x07070707uL;                          /* R7                                                   */
    *(--p_stk) = (OS_STK)0x06060606uL;                          /* R6                                                   */
    *(--p_stk) = (OS_STK)0x05050505uL;                          /* R5                                                   */
    *(--p_stk) = (OS_STK)0x04040404uL;                          /* R4                                                   */
    
    return (p_stk);
}

②初始化任务控制块TCBInit

找一个空闲任务控制块,写入传入的参数,然后将任务块加入就绪任务控制块链表

INT8U  OS_TCBInit (INT8U    prio,
                   OS_STK  *ptos,
                   OS_STK  *pbos,
                   INT16U   id,
                   INT32U   stk_size,
                   void    *pext,
                   INT16U   opt)
{
    OS_TCB    *ptcb;	//创建任务控制块指针
    OS_ENTER_CRITICAL();
    
    /**
      * 关于OSTCBFreeList,可以看一下OS_InitTCBList()函数,该函数在OSInit()中被调用。
      * 在OS_InitTCBList()函数中,将空闲任务控制块数组OSTCBTbl[]先清0,
      * 然后将OSTCBTbl[]中的前一个空闲任务块元素的OSTCBNext指向下一个空闲任务块元素
      * OSTCBFreeList = &OSTCBTbl[0];
      */
 	/* 这里就是将找一个空闲任务控制块 */
    ptcb = OSTCBFreeList;                                  /* Get a free TCB from the free TCB list    */
    if (ptcb != (OS_TCB *)0) {
    	/* OSTCBFreeList重新指向下一个空闲任务块 */
        OSTCBFreeList            = ptcb->OSTCBNext;        /* Update pointer to free TCB list          */
        OS_EXIT_CRITICAL();
        /* 将当前任务堆栈的栈顶指针赋值给任务控制块 */
        ptcb->OSTCBStkPtr        = ptos;                   /* Load Stack pointer in TCB                */
        /* 优先级赋值给任务控制块 */
        ptcb->OSTCBPrio          = prio;                   /* Load task priority into TCB              */
        /* 状态先不看 */
        ptcb->OSTCBStat          = OS_STAT_RDY;            /* Task is ready to run                     */
        ptcb->OSTCBStatPend      = OS_STAT_PEND_OK;        /* Clear pend status                        */
        ptcb->OSTCBDly           = 0u;                     /* Task is not delayed                      */
        
        /* 这里都是为了保存优先级,为了能更快的找到任务优先级最高的已就绪任务 */
        /* 放在“③任务调度”总结优先级问题 */
		/* A- 这里任务优先级小于64 */
        ptcb->OSTCBY             = (INT8U)(prio >> 3u);
        ptcb->OSTCBX             = (INT8U)(prio & 0x07u);
        /* B- Pre-compute BitX and BitY */
        ptcb->OSTCBBitY          = (OS_PRIO)(1uL << ptcb->OSTCBY);
        ptcb->OSTCBBitX          = (OS_PRIO)(1uL << ptcb->OSTCBX);

		/* OSTCBInitHook\OSTaskCreateHook,这俩不知道干嘛,好像用户自定义的东西,后面再看 */
        OSTCBInitHook(ptcb); /* ????????? */
        OS_ENTER_CRITICAL();
        OSTCBPrioTbl[prio] = ptcb;	/* 将赋值好的任务控制块放入对应优先级的任务控制块数组中 */
        OS_EXIT_CRITICAL();
        OSTaskCreateHook(ptcb); /* Call user defined hook ????????? */


        OS_ENTER_CRITICAL();
        /* OSTCBList指向的是上一个创建的任务的任务控制块 */
        /* 将ptcb放入就绪任务控制块链表的头部 */
        ptcb->OSTCBNext = OSTCBList;                       /* Link into TCB chain                      */
        ptcb->OSTCBPrev = (OS_TCB *)0;         
        if (OSTCBList != (OS_TCB *)0) {
            OSTCBList->OSTCBPrev = ptcb;
        }
        /* OSTCBList指向最新创建的任务控制块 */
        OSTCBList               = ptcb;
        
		/* 这里为了能更快的找到任务优先级最高的已就绪任务 */
		/* 将优先级分8组,每组八个优先级 */
		/* 放在“③任务调度”总结优先级问题 */
        OSRdyGrp               |= ptcb->OSTCBBitY;         /* Make task ready to run                   */
        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
        
        OSTaskCtr++;                                       /* Increment the #tasks counter             */
        OS_EXIT_CRITICAL();
        return (OS_ERR_NONE);
    }
    OS_EXIT_CRITICAL();
    return (OS_ERR_TASK_NO_MORE_TCB);
}

③任务调度OS_Sched/任务优先级

创建任务的时候,如果多任务已开始,则查找优先级最高的任务。
判断当前‘正在进行的任务’是不是比‘所有的已就绪任务’的优先级更高,
不是最高,则进行一次任务调度;
是最高,则跳过。

void  OS_Sched (void)
{
    OS_ENTER_CRITICAL();
    /* 在已就绪的任务中,找到最高优先级OSPrioHighRdy */
    OS_SchedNew();
    /* 根据优先级OSPrioHighRdy,将OSTCBHighRdy指向该任务的控制块 */
    /* OSTCBPrioTbl[prio] = ptcb;在任务创建的时候赋值的 */
    OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
    /* 任务切换 */
    if (OSPrioHighRdy != OSPrioCur) {          	/* No Ctx Sw if current task is highest rdy  */
           OSCtxSwCtr++;                        /* Increment context switch counter          */
          OS_TASK_SW();                         /* Perform a context switch                  */
    }
    OS_EXIT_CRITICAL();
}
//============================================================================================
static  void  OS_SchedNew (void)
{
    INT8U   y;
    y             = OSUnMapTbl[OSRdyGrp];
    OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
}
//============================================================================================
#define  OS_TASK_SW()         OSCtxSw()

/**
  * OSCtxSw 在 os_cpu_a.asm 中
  * 啃一下Cortex-M3 权威指南,再来总结
  */
PUBLIC  OSCtxSw

OSCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

保存就绪优先级时

OSTCBY= prio >> 3; // OSTCBY= 0x00
OSTCBX= prio & 0x07; // OSTCBX= 0x05

OSTCBBitY = 1uL << OSTCBY; // OSTCBBitY = 0x01
OSTCBBitX = 1uL << OSTCBX; // OSTCBBitX = 0x05

OSRdyGrp |= OSTCBBitY ; // OSRdyGrp = 0x01 (00000001)
OSRdyTbl[OSTCBY] |= OSTCBBitX; // OSRdyTbl[0] = 00100000

  • 优先级小于64,prio bit5~bit0为有效位。
  • 将高三位bit5 ~ bit3作为优先级的组别放入OSRdyGrp ,OSRdyGrp 是一个8位的无符号数,每一位bit代表一个组。
  • 将低三位bit2 ~ bit0放入OSRdyTbl[OSTCBY] 。
  • 优先级为5时,第一组有任务就绪,OSRdyGrp |= 0x01 ,OSRdyTbl[0]的bit5置1,OSRdyTbl[0] |= 00100000;
  • 优先级为20时,第三组有任务就绪,OSRdyGrp |= 0x04 ,OSRdyTbl[2]的bit4置1,OSRdyTbl[0] |= 00010000;
    在这里插入图片描述

查找就绪任务的最高优先级时

  • OSUnMapTbl就是将0-255每个数据中最低位为1的位数一一列举出来
  • 优先级不能为0,0x00中没有任何一位为1 OSUnMapTbl[0]无效
    y = OSUnMapTbl[OSRdyGrp]; y 就是最小的就绪优先级的组数
    x = OSUnMapTbl[OSRdyTbl[y]], x 是确认y组中最小的优先级。
    OSPrioHighRdy = (y<<3) + x;

无需遍历,查表即可获得最高就绪优先级,速度MAX。

/*
*********************************************************************************************************
*                                      PRIORITY RESOLUTION TABLE
*
* Note: Index into table is bit pattern to resolve highest priority
*       Indexed value corresponds to highest priority bit position (i.e. 0..7)
*********************************************************************************************************
*/

INT8U  const  OSUnMapTbl[256] = {
    0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F                   */
    7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u  /* 0xF0 to 0xFF                   */
};

3、开始任务调度 OSStart

代码流程

  • 找到优先级最高的任务OS_SchedNew
  • 将当前正在运行的优先级和任务控制块,赋值为上一步找到的任务。
  • 任务切换OSStartHighRdy
void  OSStart (void)
{
    if (OSRunning == OS_FALSE) {
        OS_SchedNew();                               /* Find highest priority's task priority number   */
        OSPrioCur     = OSPrioHighRdy;
        OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run    */
        OSTCBCur      = OSTCBHighRdy;
        OSStartHighRdy();                            /* Execute target specific code to start task     */
    }
}

OSStartHighRdy

;********************************************************************************************************
;                                         START MULTITASKING
;                                      void OSStartHighRdy(void)
;
; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause
;              the first task to start.
;
;           2) OSStartHighRdy() MUST:
;              a) Setup PendSV exception priority to lowest;
;              b) Set initial PSP to 0, to tell context switcher this is first run;
;              c) Set the main stack to OS_CPU_ExceptStkBase
;              d) Set OSRunning to TRUE;
;              e) Trigger PendSV exception;
;              f) Enable interrupts (tasks will run with interrupts enabled).
;********************************************************************************************************

OSStartHighRdy

; 设置PendSV中断优先级
    LDR     R0, =NVIC_SYSPRI14                                  ; Set the PendSV exception priority
    LDR     R1, =NVIC_PENDSV_PRI
    STRB    R1, [R0]
 
; 初始化PSP=0
    MOVS    R0, #0                                              ; Set the PSP to 0 for initial context switch call
    MSR     PSP, R0

; 初始化MSP地址,MSP=OS_CPU_ExceptStkBase
    LDR     R0, =OS_CPU_ExceptStkBase                           ; Initialize the MSP to the OS_CPU_ExceptStkBase
    LDR     R1, [R0]
    MSR     MSP, R1    

;OSRunning = 1 表示系统开始运行
    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                                                   ; Enable interrupts at processor level

OSStartHang
    B       OSStartHang                                         ; Should never get here
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页