uC/OS-III 任务详解(四)
uC/OS系统的任务一般都放在最开始介绍,我放在第四章主要是对模糊的概念作清晰的讲解。
从用户的角度来看,uC/OS-III 中的任务可以分为5 种状态,分别是休眠态、就绪态、运行态、挂起态和中断态,如下表所示。
任务状态之间的具体切换情况如下图所示。
也就是说,任务有五个状态,分别是休眠态、就绪态、运行态、等待态、中断服务态。即任务还没使用函数OSTaskCreate()创建时就是属于休眠态,而一但使用函数OSTaskCreate()创建了任务,并且函数OSStart()之前已经被执行的话,那么任务就属于就绪态了。然后系统会根据就绪表里任务的优先级来只执行最高的任务,而这个被执行的任务也从就绪态变为运行态,这个时刻就是这个优先级最高的任务的CPU独享moment(也就是独自拥有CPU的使用权),直到这个任务被被切换成其他状态(使用OSTimeDly()、OSSemPend()等函数能将运行态的函数变为等待态),那么就绪表里优先级最高的任务就会得到CPU的使用权执行任务,依次循环。
所以我这么分类,如果一个正在执行的任务M(即正在执行优先级最高的任务),它就只有两种情况,A.一直循环执行下去 B.改变自己任务状态。 改变状态也分为 b1.自己任务调用延时类函数或者等待信号值类函数的主动改变; b2.被一个优先级更高的就绪任务H打断的被动改变(也就是只要有优先级更高的就绪任务,那么在下一个时钟节拍到来的的时候,任务H就会剥夺任务1的CPU)。
主动改变的话可以是除运行态之外的任意状态,被动改变就是任务变为挂起态。
还有一种情况就是当两个任务的优先级一样怎么办?这里使用的是时间片轮转调度,就要在文件中使能 OSSchedRoundRobinCfg(DEF_ENABLED,1,&err) 函数,如下图所示。
然后在app.C文件的初始函数中初始化OSSchedRoundRobinCfg(DEF_ENABLED,1,&err) ,这个1代表1*5ms的时间,意思是相同的最高优先级时每个任务轮流运行5ms。
例子如下:
#define LED0_TASK_PRIO 4 //任务优先级;
#define LED0_STK_SIZE 128 //任务堆栈大小,实际大小是:128*4字节
CPU_STK LED0_TASK_STK[LED0_STK_SIZE]; //任务堆栈;
OS_TCB Led0TaskTCB; //任务控制块;
//声明任务函数(一般将一个任务写成一个函数):
void Led0_task(void *p_arg); //注:“p_arg”这个参数基本用不上,但必须得写上
#define LED1_TASK_PRIO 4 //任务优先级;
#define LED1_STK_SIZE 128 //任务堆栈大小,实际大小是:128*4字节
CPU_STK LED1_TASK_STK[LED1_STK_SIZE]; //任务堆栈;
OS_TCB Led1TaskTCB; //任务控制块;
//声明任务函数(一般将一个任务写成一个函数):
void Led1_task(void *p_arg); //注:“p_arg”这个参数基本用不上,但必须得写上
//任务创建函数,注:一般将其它任务的创建放到一个专门创建任务的函数中;
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg; //故意使用一下这个参数,否则编译器会警告说此参数没有使用
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为:1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
//创建LED0任务:
OSTaskCreate( (OS_TCB *) &Led0TaskTCB,
(CPU_CHAR *) "led0 task",
(OS_TASK_PTR) Led0_task,
(void *) 0,
(OS_PRIO) LED0_TASK_PRIO,
(CPU_STK *) &LED0_TASK_STK[0],
(CPU_STK_SIZE) LED0_STK_SIZE/10,
(CPU_STK_SIZE) LED0_STK_SIZE,
(OS_MSG_QTY) 0,
(OS_TICK) 2, //时间片长度,启用时间片轮转调度时有用,这个参数用来设置此任务时间片长度
(void *) 0,
(OS_OPT) OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR *) &err
);
//创建LED1任务:
OSTaskCreate( (OS_TCB *) &Led1TaskTCB,
(CPU_CHAR *) "led1 task",
(OS_TASK_PTR) Led1_task,
(void *) 0,
(OS_PRIO) LED1_TASK_PRIO,
(CPU_STK *) &LED1_TASK_STK[0],
(CPU_STK_SIZE) LED1_STK_SIZE/10,
(CPU_STK_SIZE) LED1_STK_SIZE,
(OS_MSG_QTY) 0,
(OS_TICK) 2, //时间片长度,启用时间片轮转调度时有用,这个参数用来设置此任务时间片长度
(void *) 0,
(OS_OPT) OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR *) &err
);
//OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起"start_task"任务
OS_CRITICAL_EXIT(); //退出临界区
OSTaskDel((OS_TCB *)0,&err);//删除任务自身;(第一个参数填写0,就是删除任务自身,要是填写别的任务的控制块,那就是删除别的任务)
}