定位到uCOS-II/Source/os_task.c,该文件是任务的相关操作:
1. 修改任务优先级函数OSTaskChangePrio()
OSTaskChangePrio()适用于用户动态改变一个任务的优先级,但新的优先级必须符合要求。
#if OS_TASK_CHANGE_PRIO_EN > 0u
INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio)
{
#if (OS_EVENT_EN) /* 事件控制块配置宏 */
OS_EVENT *pevent;
#if (OS_EVENT_MULTI_EN > 0u) /* 多事件控制块配置宏 */
OS_EVENT **pevents;
#endif
#endif
OS_TCB *ptcb;
INT8U y_new;
INT8U x_new;
INT8U y_old;
OS_PRIO bity_new;
OS_PRIO bitx_new;
OS_PRIO bity_old;
OS_PRIO bitx_old;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u; //为CPU状态寄存器分配存储器
#endif
#if OS_ARG_CHK_EN > 0u //允许参数检查
//旧优先级数值不合法
if (oldprio >= OS_LOWEST_PRIO) {
if (oldprio != OS_PRIO_SELF) {
return (OS_ERR_PRIO_INVALID);
}
}
//新优先级不合法
if (newprio >= OS_LOWEST_PRIO) {
return (OS_ERR_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL(); //进入临界区
if (OSTCBPrioTbl[newprio] != (OS_TCB *)0) { //不等于0说明已经存在优先级跟新优先级一样的任务了
OS_EXIT_CRITICAL();
return (OS_ERR_PRIO_EXIST);
}
//要改变优先级的任务是任务自己本身
if (oldprio == OS_PRIO_SELF) { //OS_PRIO_SELF虽为0xff,但它指代任务自己
oldprio = OSTCBCur->OSTCBPrio;
}
//获取旧的优先级的任务的TCB
ptcb = OSTCBPrioTbl[oldprio];
//旧优先级的任务的TCB为空
if (ptcb == (OS_TCB *)0) {
OS_EXIT_CRITICAL();
return (OS_ERR_PRIO);
}
//旧优先级的任务的TCB已经被保留/占领
if (ptcb == OS_TCB_RESERVED) {
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_NOT_EXIST);
}
#if OS_LOWEST_PRIO <= 63u
y_new = (INT8U)(newprio >> 3u); //通过优先级数值得到优先级组,即OSRdyTbl[]的下标Y
x_new = (INT8U)(newprio & 0x07u); //通过优先级数值得到OSRdyTbl[Y]成员的具体哪一位X
#else
y_new = (INT8U)((INT8U)(newprio >> 4u) & 0x0Fu);
x_new = (INT8U)(newprio & 0x0Fu);
#endif
bity_new = (OS_PRIO)(1uL << y_new); //bity_new和bitx_new目的方便在就绪表中登记就绪
bitx_new = (OS_PRIO)(1uL << x_new); //两个数值替代了稍微老点的版本的OSMapTbl[]表格
OSTCBPrioTbl[oldprio] = (OS_TCB *)0; /* 旧优先级的TCB不再使用,置空 */
/* 将任务放在newprio指定的TCB中 */
OSTCBPrioTbl[newprio] = ptcb;
/* 更改任务的优先级相关的参数 */
y_old = ptcb->OSTCBY;
bity_old = ptcb->OSTCBBitY;
bitx_old = ptcb->OSTCBBitX;
if ((OSRdyTbl[y_old] & bitx_old) != 0u) { //若旧优先级的任务为就绪态
OSRdyTbl[y_old] &= (OS_PRIO)~bitx_old; //设置旧优先级的任务为非就绪态。(这步很重要,因为就旧任务的TCB已经为空了,
//若不更改为非就绪态,那么它将会得到系统的调度,一旦被调度就运行出错了)
if (OSRdyTbl[y_old] == 0u) { //设置旧优先级的任务的任务组
OSRdyGrp &= (OS_PRIO)~bity_old;
}
//既然旧优先级的任务原先就处于就绪态,那么更改完优先级后还是还原其为就绪态
OSRdyGrp |= bity_new; /* Make new priority ready to run */
OSRdyTbl[y_new] |= bitx_new;
}
#if (OS_EVENT_EN) /事件控制使能
pevent = ptcb->OSTCBEventPtr;
if (pevent != (OS_EVENT *)0) { //若pevent等于0说明原优先级的任务跟event没有关联,即没有等待信号量、Mutex那些
//反之,将旧优先级的任务的pevent迁移到新优先级的任务的pevent
//清空
pevent->OSEventTbl[y_old] &= (OS_PRIO)~bitx_old; /* Remove old task prio from wait list */
if (pevent->OSEventTbl[y_old] == 0u) {
pevent->OSEventGrp &= (OS_PRIO)~bity_old;
}
//迁移
pevent->OSEventGrp |= bity_new; /* Add new task prio to wait list */
pevent->OSEventTbl[y_new] |= bitx_new;
}
#if (OS_EVENT_MULTI_EN > 0u)
//一个任务可以等待多个事件,将这些事件放在一起,用OSTCBEventMultiPtr指向它们
if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) {
pevents = ptcb->OSTCBEventMultiPtr;
pevent = *pevents;
while (pevent != (OS_EVENT *)0) {
pevent->OSEventTbl[y_old] &= (OS_PRIO)~bitx_old; /* Remove old task prio from wait lists */
if (pevent->OSEventTbl[y_old] == 0u) {
pevent->OSEventGrp &= (OS_PRIO)~bity_old;
}
pevent->OSEventGrp |= bity_new; /* Add new task prio to wait lists */
pevent->OSEventTbl[y_new] |= bitx_new;
pevents++;
pevent = *pevents;
}
}
#endif
#endif
ptcb->OSTCBPrio = newprio; /* Set new task priority */
//更新任务的TCB中与优先级相关的就绪表坐标参数
ptcb->OSTCBY = y_new;
ptcb->OSTCBX = x_new;
ptcb->OSTCBBitY = bity_new;
ptcb->OSTCBBitX = bitx_new;
OS_EXIT_CRITICAL();
if (OSRunning == OS_TRUE) {
OS_Sched(); /* 优先级发生改变,自然需要重新调度 */
}
return (OS_ERR_NONE);
}
#endif
2. 创建任务函数OSTaskCreate()
uCOS-II支持动态创建和静态创建任务:
(1) 静态创建:在应用程序调用OSStart()之前创建好所有任务,OSStart()函数将全局变量OSRunning置一后系统开始调度
(2) 动态创建:在OSStart()之后创建任务
一般我们使用的是静态创建任务的方式。uCOS-II是一款RTOS,为保证实时性,不可在ISR中创建任务。
uCOS-II是通过任务控制块TCB来管理任务的,所以创建一个任务的工作实质上是创建一个TCB,并通过TCB将任务代码和任务堆栈关联起来形成一个完整的任务。当然还要将被创建的任务为就绪态并引发一次调度。
#if OS_TASK_CREATE_EN > 0u
INT8U OSTaskCreate (void (*task)(void *p_arg), //指向任务代码
void *p_arg, //传递给任务代码的参数
OS_STK *ptos, //指向任务堆栈的栈顶
INT8U prio) //任务的优先级
{
OS_STK *psp;
INT8U err;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508 //IEC61508是标准,主要与风险和安全相关,不细究
if (OSSafetyCriticalStartFlag == OS_TRUE) {
OS_SAFETY_CRITICAL_EXCEPTION();
return (OS_ERR_ILLEGAL_CREATE_RUN_TIME);
}
#endif
#if OS_ARG_CHK_EN > 0u //参数合法性判断使能
if (prio > OS_LOWEST_PRIO) {
return (OS_ERR_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting > 0u) { //不可在中断ISR中创建任务
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { //不等于0表示已经存在任务,其优先级与要创建的任务的优先级一致。
//等于0说明不存在
OSTCBPrioTbl[prio] = OS_TCB_RESERVED; //先RESERVED任务控制块,RESERVED即占领
//占领了位置了,下来可以放心操作该任务的相关设置
OS_EXIT_CRITICAL(); //进入临界区
psp = OSTaskStkInit(task, p_arg, ptos, 0u); //初始化栈
err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u); //初始化该任务的TCB
if (err == OS_ERR_NONE) {
if (OSRunning == OS_TRUE) { //如果os已经在运行了,引发调度
OS_Sched();
} //如果os还没运行呢?这里并没有这个else。不必着急,等下APP调用OSStart()时候会开启调度,这是系统的首次调度
} else { //创建任务失败,让出占领TCB的位置
OS_ENTER_CRITICAL();