μC/OS-Ⅱ从任务的建立到运行
前言
最近在学习ucosii实时操作系统,记录下学习过程,以待日后查阅.
tips:本文所用ucosii版本为V2.86,硬件平台为STM32.
一、任务建立到运行整体流程
μC/OS-Ⅱ任务从建立到运行的整体流程如下所述:
1. μC/OS-Ⅱ初始化: OSInit();
2. 创建任务: Create_Task();
3. 开始运行OS: OSStart();
4. 开启时钟节拍:systick_init();
整体例程
int main(void)
{
OSInit(); // μC/OS-Ⅱ初始化
OSTaskCreate(Task1,\
0,\
&Task1_Stk[Task1_STK_SIZE-1],\
Task1_PRIO); //创建任务
OSStart(); // 开始运行OS
systick_init(); //开启时钟节拍
return 0;
}
二、分步功能实现
1.μC/OS-Ⅱ初始化
在调用μC/OS-Ⅱ的任何其它服务之前,μC/OS-Ⅱ要求用户首先调用系统初始化函数OSIint()。OSIint()初始化μC/OS-Ⅱ
所有的变量和数据结构(见 OS_CORE.C)。
OSInit()会自动建立空闲任务 idle task,这个任务总是处于就绪态的。
OS_InitTaskIdle(); /* Create the Idle Task */
空闲任务OSTaskIdle()的优先级总是设成最低,即 OS_LOWEST_PRIO。也就是说当前系统中无其他任务运行时,idle task 空闲任务就会运行。
如果统计任务允许 OS_TASK_STAT_EN 和任务建立扩展允许都设为 1,则 OSInit()还会建立统计任务OSTaskStat()并且让其进入就绪态。OSTaskStat 的优先级总是设为 OS_LOWEST_PRIO-1。
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
下图为调用 OSInit()之后,一些μC/OS-Ⅱ变量和数据结构之间的关系。
空闲任务和统计任务的任务控制块(OS_TCBs)是用双向链表链接在一起的。OSTCBList 指向这个链表的起始处。当建立一个任务时,这个任务总是被放在这个链表的起始处。换句话说,OSTCBList 总是指向最后建立的那个任务。链的终点指向空字符 NULL(也就是零)。
因为这两个任务都处在就绪态,在就绪任务表 OSRdyTbl[]中的相应位是设为 1 的。还有,因为这两个任务的相应位是在 OSRdyTbl[]的同一行上,即属同一组,故 OSRdyGrp 中只有 1 位是设为 1 的。(关于OSRdyTbl[]和OSRdyGrp接下来会讲到)。
μC/OS-Ⅱ还初始化了空任务控制块缓冲区,该缓冲区为单向链表,允许μC/OS-Ⅱ从缓冲区中迅速得到或释放一个缓冲区中的元素。
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
注意,空任务控制块在空缓冲区中的数目取决于最多任务数 OS_MAX_TASKS,这个最多任务数是在 OS_CFG.H 文件中定义的。μC/OS-Ⅱ自动安排总的系统任务数 OS_N_SYS_TASKS(见文件μC/OS-Ⅱ.H)。控制块 OS_TCB 的数目也就自动确定了。
系统初始化成功后就该进行下一步创建任务了。
2.创建任务
任务可以在多任务调度开始前建立,也可以在其它任务的执行过程中被建立。 在开始多任务调度(即调用 OSStart())前,用户必须建立至少一个任务。任务不能由中断服务程序(ISR)来建立。
OSTaskCreate()为任务创建函数,其代码如下。
INT8U OSTaskCreate (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio)
{
OS_STK *psp;
INT8U err;
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */
return (OS_ERR_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Make sure we don't create the task from within an ISR */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */
OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL();
psp = OSTaskStkInit(task, p_arg, ptos, 0); /* Initialize the task's stack */
err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);
if (err == OS_ERR_NONE) {
if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */
OS_Sched();
}
} else {
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */
OS_EXIT_CRITICAL();
}
return (err);
}
OS_EXIT_CRITICAL();
return (OS_ERR_PRIO_EXIST);
}
从中可以知道,OSTaskCreate()需要四个参数:
- task 是任务代码的指针;
- pdata 是当任务开始执行时传递给任务的参数的指针;
- ptos是分配给任务的堆栈的栈顶指针;
- prio 是分配给任务的优先级。
以main函数中的
OSTaskCreate(Task1, 0,&Task1_Stk[Task1_STK_SIZE-1],Task1_PRIO);
为例。
Task1函数如下:
static void Task1(void *para)
{
para = para;
while(1)
{
//在此处添加要做的工作
OSTimeDlyHMSM(0,0,0,50); //50ms延时,释放cpu控制权
}
}
Task1任务的优先级定为30:
#define Task1_PRIO 30
在创建Task1时,首先要对该任务的优先级的有效性进行检测:
if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */
return (OS_ERR_PRIO_INVALID);
}
当设置的优先级比定义的最低优先级还小时(数值越大优先级越小)就会返回优先级不可用错误。
确定当前创建任务时不在中断中:
if (OSIntNesting > 0) { /* Make sure we don't create the task from within an ISR */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
当创建任务是在中断中时就会返回OS_ERR_TASK_CREATE_ISR
错误。
接下来才是创建任务的重点
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* 确定任务的优先级不是已存在的优先级 */
OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* 设置该优先级为已占用 */
OS_EXIT_CRITICAL();
psp = OSTaskStkInit(task, p_arg, ptos, 0);/* 建立任务的堆栈*/
err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);
if (err == OS_ERR_NONE) {
if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */
OS_Sched();
}
} else {
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */
OS_EXIT_CRITICAL();
}
return (err);
}
这部分判断当前任务的优先级是否为已经被其他任务占用的优先级,一个优先级只能有一个任务。然后就是OSTaskStkInit
建立任务的堆栈,该函数是与处理器的硬件体系相关的函数。OS_TCBInit
负责初始化任务的tcb
,这部分与任务的调度有极大的关,重点来说一下这个函数中有关任务调度的部分。截取如下:
#if OS_LOWEST_PRIO <= 63
ptcb->OSTCBY = (INT8U)(prio >> 3); /* Pre-compute X, Y, BitX and BitY */
ptcb->OSTCBX = (INT8U)(prio & 0x07);
ptcb->OSTCBBitY = (INT8U)(1 << ptcb->OSTCBY);
ptcb->OSTCBBitX = (INT8U)(1 << ptcb->OSTCBX);
#else
.
.
.
#endif
.
.
.
OSTCBPrioTbl[prio] = ptcb;
.
.
.
OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OSTaskCtr++; /* Increment the #tasks counter */
OS_TCB结构体定义在ucos_ii.h文件中