Ucos源码分析
1.Ucos源码分析------任务控制块与任务调度
2.Ucos源码分析------事件控制块与事件控制
3.Ucos源码分析------信号量
4.Ucos源码分析------邮箱与队列
5.Ucos源码分析------事件标志组
6.Ucos源码分析------内存管理
7.Ucos源码分析------临界区与中断管理
8.Ucos源码分析------OS启动
9.Ucos总结
1.事件控制块
事件控制块成员 | |
---|---|
OSEventType | 定义事件的具体类型 |
OSEventCnt | 信号量的计数器 |
*OSEventPtr | 定义邮箱或者消息队列时,指向一个消息或消息队列控制块 |
OSEventGrp | 等待任务所在的组 |
OSEventType | 构成事件控制块的等待任务列表,类似于任务就绪表 |
OSEventType
宏名 | 说明 |
---|---|
OS_EVENT_SEM | 信号量 |
OS_EVENT_TYPE_UNUSED | 未分配 |
OS_EVENT_TYPE_MBOX | 消息邮箱 |
OS_EVENT_TYPE_Q | 消息队列 |
OS_EVENT_TYPE_MUTEX | 互斥信号量 |
任务与事件着重关注:任务状态的变化;就绪表的变化;事件控制块等待任务表的变化
2.事件的基本函数
2.1任务等待事件
将任务从就绪表移除
将事件控制块的等待任务表置位
void OS_EventTaskWait (OS_EVENT *pevent)
{
//设置任务控制块的事件指针
OSTCBCur->OSTCBEventPtr = pevent; /* Store pointer to event control block in TCB */
//将任务从就绪表移除
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) { /* Task no longer ready */
OSRdyGrp &= ~OSTCBCur->OSTCBBitY; /* Clear event grp bit if this was only task pending */
}
//等待任务表置位
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */
//等待任务组置位
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
2.2等待事件任务就绪
将任务从事件控制块的等待任务表移除
将任务状态相应的等待事件清楚
将任务加入到就绪表
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
{
OS_TCB *ptcb;
INT8U x;
INT8U y;
INT8U bitx;
INT8U bity;
INT8U prio;
//获取等待该事件的任务的最高优先级
y = OSUnMapTbl[pevent->OSEventGrp]; /* Find highest prio. task waiting for message */
bity = OSMapTbl[y];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
bitx = OSMapTbl[x];
prio = (INT8U)((y << 3) + x); /* Find priority of task getting the msg */
//将该任务从事件等待列表移除
if ((pevent->OSEventTbl[y] &= ~bitx) == 0x00) { /* Remove this task from the waiting list */
pevent->OSEventGrp &= ~bity; /* Clr group bit if this was only task pending */
}
//获取该任务任务控制块
ptcb = OSTCBPrioTbl[prio]; /* Point to this task's OS_TCB */
//将该任务的任务控制块的延时清零,防止系统滴答定时器导致任务再次进入就绪
ptcb->OSTCBDly = 0; /* Prevent OSTimeTick() from readying task */
//移除该任务的任务控制块的事件
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Unlink ECB from this task */
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb->OSTCBMsg = msg; /* Send message directly to waiting task */
#else
msg = msg; /* Prevent compiler warning if not used */
#endif
//清除调用该函数的事件类型的位,进一步判断任务是不是手动挂起
ptcb->OSTCBStat &= ~msk; /* Clear bit associated with event type */
//清除该事件后,如果任务处于就绪态,并且没有手动挂起
if (ptcb->OSTCBStat == OS_STAT_RDY) { /* See if task is ready (could be susp'd) */
//将任务加入到就绪列表
OSRdyGrp |= bity; /* Put task in the ready to run list */
OSRdyTbl[y] |= bitx;
}
return (prio);
}
2.3任务事件等待超时
将任务从事件控制块的等待任务表移除
将任务状态设置为就绪态
将任务加入到就绪列表是在OSTimeTick** 实现
void OS_EventTO (OS_EVENT *pevent)
{
//从事件等待列表移除
if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) {
pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
}
//修改当前任务为就绪态
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set status to ready */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* No longer waiting for event */
}
因为OSTaskSuspend与OSTaskResume 是手动挂起和手动恢复的,和后面的文章不好分类,在这哪里出来。
3.任务挂起
INT8U OSTaskSuspend (INT8U prio) 实现任务手动挂起
参数prio :指定要获取挂起的任务优先级
如果挂起当前任务进行任务调度,挂起当前任务可以传入当前任务的优先级或者
OS_PRIO_SELF,OS_PRIO_SELF的优势是不需要知道当前任务优先级。
INT8U OSTaskSuspend (INT8U prio)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
BOOLEAN self;
OS_TCB *ptcb;
#if OS_ARG_CHK_EN > 0
if (prio == OS_IDLE_PRIO) { /* Not allowed to suspend idle task */
return (OS_TASK_SUSPEND_IDLE);
}
if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { /* Task priority valid ? */
return (OS_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (prio == OS_PRIO_SELF) { /* See if suspend SELF */
//获取当前任务的优先级
//self = TRUE; 需进行调度
prio = OSTCBCur->OSTCBPrio;
self = TRUE;
}
//判断是否是调用此函数的任务的任务优先级
else if (prio == OSTCBCur->OSTCBPrio) { /* See if suspending self */
self = TRUE;
} else {
self = FALSE; /* No suspending another task */
}
//获取待挂起任务的任务控制块
ptcb = OSTCBPrioTbl[prio];
if (ptcb == (OS_TCB *)0) { /* Task to suspend must exist */
OS_EXIT_CRITICAL();
return (OS_TASK_SUSPEND_PRIO);
}
//任务从就绪表移除
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0x00) { /* Make task not ready */
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
//添加挂起状态
ptcb->OSTCBStat |= OS_STAT_SUSPEND; /* Status of task is 'SUSPENDED' */
OS_EXIT_CRITICAL();
//如果是当前任务被挂起,需要进行任务调度
if (self == TRUE) { /* Context switch only if SELF */
OS_Sched();
}
return (OS_NO_ERR);
}
4.挂起任务的恢复
用 OSTaskSuspend()挂起的任务只能用 OSTaskResume ()唤醒
如果挂起恢复后,任务为就绪态,此时将任务加入就绪表,并进行任务调度
NT8U OSTaskResume (INT8U prio)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
#if OS_ARG_CHK_EN > 0
if (prio >= OS_LOWEST_PRIO) { /* Make sure task priority is valid */
return (OS_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
//获取任务控制块
ptcb = OSTCBPrioTbl[prio];
//判断任务控制块是否有效
if (ptcb == (OS_TCB *)0) { /* Task to suspend must exist */
OS_EXIT_CRITICAL();
return (OS_TASK_RESUME_PRIO);
}
// 判断任务是否处于挂起状态
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) != OS_STAT_RDY) { /* Task must be suspended */
//ptcb->OSTCBStat &= ~OS_STAT_SUSPEND 清除任务挂起状态位
//ptcb->OSTCBDly == 0 代表任务处于不等待事件情况
// 恢复手动挂起 任务没有等待事件 条件1 = 1;条件2 =1;
// 恢复手动挂起并且任务等待的事件 已 得到 条件1 = 1;条件2 =1;
// 恢复手动挂起并且任务等待的事件 未 得到 条件1 = 0;条件2 =0;
if (((ptcb->OSTCBStat &= ~OS_STAT_SUSPEND) == OS_STAT_RDY) && /* Remove suspension */
(ptcb->OSTCBDly == 0)) { /* Must not be delayed */
//任务加入就绪表
OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OS_EXIT_CRITICAL();
//系统调度
OS_Sched();
} else {
OS_EXIT_CRITICAL();
}
return (OS_NO_ERR);
}
OS_EXIT_CRITICAL();
return (OS_TASK_NOT_SUSPENDED);
}