此篇博文主要讲述UCOSIII的任务管理功能。
多任务操作系统最主要的就是对任务的管理,包括任务的创建、挂起、删除和调度等。关于UCOSIII任务的创建、挂起、删除和调度可以查看本人另一篇博文。
一、UCOSIII的启动和初始化
将UCOSIII移植到工程之后,就可以启动UCOSIII了,在使用UCOSIII的时候我们需要按照一定的顺序初始化并打开UCOSIII,顺序如下:
第三步,调用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中的任务有多个状态:
中断服务态
一个正在执行的任务被中断打断,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() //将某个任务在就绪表中相对应的位清零