UCOSIII移植笔记(三)

此篇博文主要讲述UCOSIII的任务管理功能。
多任务操作系统最主要的就是对任务的管理,包括任务的创建、挂起、删除和调度等。关于UCOSIII任务的创建、挂起、删除和调度可以查看本人另一篇博文。

一、UCOSIII的启动和初始化

将UCOSIII移植到工程之后,就可以启动UCOSIII了,在使用UCOSIII的时候我们需要按照一定的顺序初始化并打开UCOSIII,顺序如下:

  • 第一步,是调用CPU_Init()初始化UCOSIII
  • 第二步,创建任务
    一般情况下,我们在main函数里只创建一个start_task()任务,其它的任务都在start_task()中创建,创建任务的函数是OSTaskCreate()函数。
    注意在调用OSTaskCreate()函数创建任务的时候,一定要调用OS_CRITICAL_ENTER()函数进入临界区,任务创建以后,要调用OS_CRITICAL_EXIT()函数退出临界区。
  • 第三步,调用OSStart()函数来开启UCOSIII

    下面是一个例子:

    OSInit(&err);       //初始化UCOSIII
    OS_CRITICAL_ENTER();//进入临界区
    //创建开始任务
    OSTaskCreate((OS_TCB    * )&StartTaskTCB,       //任务控制块
                 (CPU_CHAR  * )"start task",        //任务名字
                 (OS_TASK_PTR )start_task,          //任务函数
                 (void      * )0,                   //传递给任务函数的参数
                 (OS_PRIO     )START_TASK_PRIO,     //任务优先级
                 (CPU_STK   * )&START_TASK_STK[0],  //任务堆栈基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,   //任务堆栈深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,      //任务堆栈大小
                 (OS_MSG_QTY  )0,                   //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK     )0,                   //当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void      * )0,                   //用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                 (OS_ERR    * )&err);               //存放该函数错误时的返回值
    OS_CRITICAL_EXIT(); //退出临界区  
    OSStart(&err);  //开启UCOSIII

二、任务状态

UCOSIII支持的是单核CPU,不支付多核CPU,这样在某一个时刻就有且只能有一个任务获得CPU的使用权限,其它的任务都会进入其它状态,UCOSIII中的任务有多个状态:

  1. 休眠态
    休眠态就是任务只是以任务函数的方式存在,只是存储区中的一段代码,并未用OSTaskCreate()函数创建这个任务,不受UCOSIII管理。
  2. 就绪态
    任务在就绪表中已经登记,等待获取CPU的使用权。
  3. 运行态
    正在运行的任务。
  4. 等待态
    正在运行的任务需要等待某一个事件,比如信号质量、消息事件等,就会暂时让出CPU的使用权,进入等待事件状态。
  5. 中断服务态
    一个正在执行的任务被中断打断,CPU转而去执行中断服务程序,这时这个任务就会被挂起,进入中断服务状态。
    这里写图片描述

    三、任务控制块

    任务控制块是什么?
    任务控制块OS_TCB,是用来保存任务的信息,我们在使用OSTaskCreate()函数在创建任务的时候就会分配给一个任务控制块,任务控制块是一个结构体,如下:

struct os_tcb{
    CPU_STK     *StkPtr;            //指向当前任务堆栈的栈顶
    void        *ExtPtr;            //指向用户可定义的数据区
    CPU_STK     *StkLimitPtr;       //可指向任务堆栈区中的某个位置
    OS_TCB      *NextPtr;           //nextptr和prevptr用于在任务就绪表建立OC_TCB双向链表
    OC_TCB      *PrevPtr;           
    OS_TCB      *TickNextPtr;       //TickNectPtr和TickPrevPtr可把正在延时或在指定时间内等待某个事件的任务的OC_TCB构成比向链表
    OC_TCB      *TickPrevPtr;
    OS_TICK_SPOKE *TickSpokePtr;    //通过该指针可知道该任务在时钟节拍轮的哪个spoke上
    CPU_CHAR    *NamePtr;           //任务名
    CPU_STK     *StkBasePtr         //任务堆栈基地址
    OS_TASK_PTR *TaskEntryAddr;     //任务代码入口地址
    void        *TaskEntryArg;      //传递给任务的参数


}

四、任务堆栈

在UCOSIII中任务堆栈是一个非常重要的概念,任务堆栈是用来在切换任务和调用其它函数时的保存现场,因此每个任务都有自己的堆栈。
创建任务堆栈的步骤如下:
1、定义一个CPU_STK(任务堆栈)变量,CPU_STK在cpu.h中有定义,其实,CPU_STK就是CPU_INT32U,可以看出一个CPU_STK为4个字节,因此任务堆栈的实际大小应该为我们定义的4倍,例如下面的代码,堆栈的实际大小为64*4=256字节
CPU_STK TASK_STK[64];
不过上面的代码不清楚,不便于修改,我们可以修改成如下:
#define LED1_STK_SIZE 64;
CPU_STK TASK_STK[LED1_STK_SIZE];
我们在使用OSTaskCreate()函数创建任务的时候,就可以把创建的堆栈传递给任务如下代码:

OSTaskCreate((OS_TCB    * )&StartTaskTCB,       //任务控制块
                 (CPU_CHAR  * )"start task",        //任务名字
                 (OS_TASK_PTR )start_task,          //任务函数
                 (void      * )0,                   //传递给任务函数的参数
                 (OS_PRIO     )START_TASK_PRIO,     //任务优先级
                 (CPU_STK   * )&START_TASK_STK[0],  //任务堆栈基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,   //任务堆栈深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,      //任务堆栈大小
                 (OS_MSG_QTY  )0,                   //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK     )0,                   //当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void      * )0,                   //用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                 (OS_ERR    * )&err);               //存放该函数错误时的返回值

上面的代码涉及到任务堆栈的有三个部分分别是任务堆栈基地址、任务堆栈深度限位,任务堆栈大小。所谓基地址,顾名思义就可以理解为基本地址,他是相对偏移量的计算基准。
在创建任务的时候就会初始化任务的堆栈,我们需要提前将CPU的寄存器保存在任务堆栈中,完成这个任务就是OSTaskStkInit()函数。这个函数专门是被OSTaskCreate()函数在创建任务的时候调用的。

五 任务就绪表

UCOSIII中将已经就绪的任务放在任务就绪表里,任务就绪表有两个部分,优先级位映射表OSPrioTal[]和就绪任务列表OSRdyList[]。

1、优先级位映射表
当某一个任务就绪以后就会将优先级位映射表中相应的位置置1,优先级位映射表如下图所示。CPU_DATA(cpu.h)中设置该表元素的位宽度,可以是8位、16位和32位的。
其中任务数目由宏OS_CFG_PRIO_MAX(os_cfg,h)配置的。
这里写图片描述
上图所示的,从左到右优先级逐渐降低,但是每个OSPrioTbl[]数组的元素最低位在右,最高位在左边,比如OSPrioTbl[0] 的bit31 为最高优先级0,bit0为优先级31。之所以这样做,主要是为了支持一条特殊的指令,“计算机前导零CLZ”,使用这条指令可以快递的找到最高铸造级的任务。
有关优先级的操作有3个函数:
OS_PrioGetHighest() //获取就表绪中最高优先级的任务
OS_PrioInser() /将某个任务在就绪表中相对应的位置1
OS_PrioRemove() //将某个任务在就绪表中相对应的位清零

猜你喜欢

转载自blog.csdn.net/s18714804418/article/details/81873847