1.时间管理
1.1OSTimeDly() 延时程序
void OSTimeDly (INT16U ticks)
-
Ticks 延时脉冲数 ,通常与 OS_TICKS_PER_SEC 相联系以更精确的定时, OS_TICKS_PER_SEC 在 OS_CORE.H 中宏定义,也就是说与处理器相关联
1 <= ticks <= 65535
void OSTimeDly (INT16U ticks)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
if (ticks > 0) /* ticks 有效 */
{
OS_ENTER_CRITICAL(); /* 关中断 */
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) /* 当前任务从就绪表移除 */
{ /* 并检查该优先级组是否都没有就绪任务了 */
OSRdyGrp &= ~OSTCBCur->OSTCBBitY; /* 如果是, OSRdyGrp 的相应位置 0 */
}
OSTCBCur->OSTCBDly = ticks; /* 设置 TCB 的 OSTCBDly 变量为 ticks */
OS_EXIT_CRITICAL(); /* 开中断 */
OSSched(); /* 任务切换 */
}
}
1.1.1 程序流程图
图 1 OSTimeDly() 流程图
1.2OSTimeTick()
void OSTimeTick (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
OSTimeTickHook(); /* Call user definable hook */
#if OS_TIME_GET_SET_EN > 0
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
OSTime++;
OS_EXIT_CRITICAL();
#endif
if (OSRunning == TRUE) {
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) { /* Delayed or waiting for event with TO */
if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* */
OSRdyGrp |= ptcb->OSTCBBitY; /**/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
} else { /* Yes, Leave 1 tick to prevent ... */
ptcb->OSTCBDly = 1; /* ... loosing the task when the ... */
} /* ... suspension is removed. */
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
}
}
1.3OSTimeDlyHMSM() 按时分秒延时函数
INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli)
-
INT8U hours 延时的小时数( INT8U 决定最多延长小于 257 小时的时间)
-
INT8U minutes 延时的分钟数
-
INT8U seconds 延时的秒数
-
INT8U milli 延时的微秒数(最大 999 )
说明的几点
-
OSTimeDly ()能接受的最大 ticks 为 65535 ,导致超过 65536ticks 的 OSTimeDlyHMSM ()将不允许被 OSTimeDlyResume ()。
-
注意 OS_TICKS_PER_SEC 这个宏定义,整个函数的关键点还是它的设置。它表示一秒钟产生多少个 ticks ,也就是频率。一个 100Hz 的时钟, OS_TICKS_PER_SEC 应该定义为 100 。
#if OS_TIME_DLY_HMSM_EN > 0
INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli)
{
INT32U ticks;
INT16U loops;
if (hours > 0 || minutes > 0 || seconds > 0 || milli > 0) {
if (minutes > 59) {
return (OS_TIME_INVALID_MINUTES); /* 计算时分秒微妙等参数是否合法 */
}
if (seconds > 59) {
return (OS_TIME_INVALID_SECONDS);
}
if (milli > 999) {
return (OS_TIME_INVALID_MILLI);
}
ticks = ((INT32U)hours * 3600L + (INT32U)minutes * 60L + (INT32U)seconds) * OS_TICKS_PER_SEC + OS_TICKS_PER_SEC * ((INT32U)milli + 500L / OS_TICKS_PER_SEC) / 1000L;
/* 计算总的延时 ticks */
loops = (INT16U)(ticks / 65536L); /* 计算需要多少个 65536 的循环来完成延时 */
ticks = ticks % 65536L; /* 计算整数倍 65536 延时意外剩余的 ticks 延时 */
OSTimeDly((INT16U)ticks);
while (loops > 0) {
OSTimeDly(32768);
OSTimeDly(32768);
loops--;
}
return (OS_NO_ERR);
}
return (OS_TIME_ZERO_DLY);
}
#endif
1.4OSTimeDlyResume() 延时期任务结束延时程序
INT8U OSTimeDlyResume (INT8U prio)
-
Prio 延时回复任务的优先级
说明
-
任务处在延时中,即便被延时恢复也不一定可以就绪,因为他可能也正被挂起,这种情况在在代码中又体现,也就是说只有在没有被挂起的情况下才能处于就绪状态
#if OS_TIME_DLY_RESUME_EN > 0
INT8U OSTimeDlyResume (INT8U prio)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
if (prio >= OS_LOWEST_PRIO) {
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
ptcb = (OS_TCB *)OSTCBPrioTbl[prio]; /* 获得 prio 的任务 TCB */
if (ptcb != (OS_TCB *)0) { /* 如果 TCB 存在 */
if (ptcb->OSTCBDly != 0) { /* 如果该 TCB 延时不为 0 (正被演示) */
ptcb->OSTCBDly = 0; /* 清除延时数 */
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* 确保未被挂起 */
OSRdyGrp |= ptcb->OSTCBBitY; /* 将任务加入就绪列表 */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OS_EXIT_CRITICAL();
OS_Sched(); /* 进行任务切换 */
} else {
OS_EXIT_CRITICAL(); /* 任务被挂起 不做操作 */
}
return (OS_NO_ERR);
} else {
OS_EXIT_CRITICAL();
return (OS_TIME_NOT_DLY); /* 返回任务未被 delay */
}
}
OS_EXIT_CRITICAL();
return (OS_TASK_NOT_EXIST); /* The task does not exist */
}
#endif
1.4.1 程序流程图
图 2 OSTimeDlyResume() 流程图
1.5系统时间, OSTimeGet()和 OSTimeSet()
OS_EXT volatile INT32U OSTime; /* 系统时间的当前值 ,用 ticks表示 */
#if OS_TIME_GET_SET_EN > 0
void OSTimeSet (INT32U ticks)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_ENTER_CRITICAL();
OSTime = ticks;
OS_EXIT_CRITICAL();
}
#endif
INT32U OSTimeGet (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
INT32U ticks;
OS_ENTER_CRITICAL();
ticks = OSTime;
OS_EXIT_CRITICAL();
return (ticks);
}
#endif
2.TCB 任务控制块
2.1OS_TCBInit() 任务控制块初始化函数
2.1.1 源代码分析
Prio 任务优先级
Ptos 任务栈顶指针 pointer top to stack
Pbos 任务栈底指针 pointer bottom to stack
Id 任务的 ID ( 0-65536)
Stk_size 堆栈在堆栈单元(?)中的尺寸,与 OS_STK的定义有关
INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt)
{
*************
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
*****************///
OS_TCB *ptcb;
OS_ENTER_CRITICAL();
ptcb = OSTCBFreeList; /* 将当前 TCB 加入到空闲 TCB 列表指针 */
if (ptcb != (OS_TCB *)0) { /* 如果当前指针不为空 */
OSTCBFreeList = ptcb->OSTCBNext; /* 更新加入后的空闲列表指针 */
OS_EXIT_CRITICAL();
ptcb->OSTCBStkPtr = ptos; /* 参数付给当前 TCB 需求的参数 */
ptcb->OSTCBPrio = (INT8U)prio;
ptcb->OSTCBStat = OS_STAT_RDY;
ptcb->OSTCBDly = 0;
// 这一段代码放置 待注释和解释
#if OS_TASK_CREATE_EXT_EN > 0
ptcb->OSTCBExtPtr = pext;
ptcb->OSTCBStkSize = stk_size;
ptcb->OSTCBStkBottom = pbos;
ptcb->OSTCBOpt = opt;
ptcb->OSTCBId = id;
#else
pext = pext; /* Prevent compiler warning if not used */
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
#if OS_TASK_DEL_EN > 0
ptcb->OSTCBDelReq = OS_NO_ERR; /* 与 OSTaskDelReq() 函数相联系的变量 */
#endif
// 处理任务就绪列表
ptcb->OSTCBY = prio >> 3; /* 存储就绪优先级组的信息 */
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY]; /* 存储上面变量的具体位 */
ptcb->OSTCBX = prio & 0x07; /* 存储就绪优先级组中的 8 个优先级情况 */
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX]; /* 存储上面变量的具体位情况 */
// 这一段代码放置 待注释和解释
#if OS_EVENT_EN > 0
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */
#endif
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) && (OS_TASK_DEL_EN > 0)
ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0; /* Task is not pending on an event flag */
#endif
#if (OS_MBOX_EN > 0) || ((OS_Q_EN > 0) && (OS_MAX_QS > 0))
ptcb->OSTCBMsg = (void *)0; /* No message received */
#endif
#if OS_VERSION >= 204
OSTCBInitHook(ptcb);
#endif
OSTaskCreateHook(ptcb); /* Call user defined hook */
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = ptcb;
ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
OS_EXIT_CRITICAL();
return (OS_NO_MORE_TCB);
}
2.2TCB 的结构体定义
2.2.1TCB主要的数据
对于普通的 OSTaskCreate()来说, TCB主要的数据有:
-
OS_STK *OSTCKStkPtr 当前栈顶指针
-
Struct os_tcb *OSTCBNext 指向 TCB 列表中当前 PCB 的下一个 TCB
-
Struct os_tcb *OSTCBPrev 指向 TCB 列表中当前 PCB 的上一个 TCB
-
INT16U OSTCBDly; 延迟任务、等待事件等需要的延时,要是任务就绪,其值必须为 0
-
INT8U OSTCBStat; 任务状态 /*******///
-
INT8U OSTCBPrio; 任务优先级
以下四个参数用于定位优先级的就绪表有关
-
INT8U OSTCBX;
-
INT8U OSTCBY;
-
INT8U OSTCBBitX;
-
INT8U OSTCBBitY;
-
#if OS_TASK_DEL_EN > 0 // 如果定义了该变量为 1 默认为定义了
BOOLEAN OSTCBDelReq; 用于与 OSTaskDelReq() 函数相联系,请求删除标志
2.2.2 TCB结构体源代码分析
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
#if OS_TASK_CREATE_EXT_EN > 0
void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */
OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */
INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */
INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
INT16U OSTCBId; /* Task ID (0..65535) */
#endif
struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
OS_EVENT *OSTCBEventPtr; /* 指向 ECB 的指针 */
#endif
**********
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */
#endif
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if OS_TASK_DEL_EN > 0
OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */
#endif
OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */
#endif
**********
INT16U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
INT8U OSTCBStat; /* Task status */
INT8U OSTCBPrio; /* Task priority (0 == highest, 63 == lowest) */
INT8U OSTCBX; /* Bit position in group corresponding to task priority (0..7) */
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */
INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */
#if OS_TASK_DEL_EN > 0
BOOLEAN OSTCBDelReq; /* 用于与 OSTaskDelReq() 函数相联系,请求删除标志 */
#endif
} OS_TCB
2.2.3 (OS_TCB*)0 和 (OS_TCB*)1的说明:
(OS_TCB *)0 可以理解 为在该优先级无任务,因为 UCOS不允许多个任务共一个优先级。 (OS_TCB*)1 则为有任务。
2.2.4 与 TCB相关的全局变量
OS_EXT OS_TCB *OSTCBCur; /* 指向当前 TCB的指针 */
OS_EXT OS_TCB *OSTCBFreeList; /* TCB空闲列表指针 */
OS_EXT OS_TCB *OSTCBHighRdy; /* 最高优先级 TCB指针 R-to-R */
OS_EXT OS_TCB *OSTCBList; /* TCB双向链表指针 */
OS_EXT OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + 1];
/* 创建某优先级 TCB的表,可用于查找相应优先级的 TCB*/
OS_EXT OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]; /* Table of TCBs */
3.临界区中断开关 OS_ENTER_CRITICAL() 和 OS_EXIT_CRITICAL()
OS_ENTER_CRITICAL() 和 OS_EXIT_CRITICAL() //OS_CPU.H 中找到 见书 P213
进入临界代码区之前需要关闭中断
#define OS_CRITICAL_METHOD 2 //系统默认为 2
// 方法一简单执行速度快(只有一条指令),但是有弊端,关闭中断后,若调用 uCOS 系统函数,结束后中断可能将打开,这应该是不允许的。换句话讲,他不允许中断嵌套
#if OS_CRITICAL_METHOD == 1
#define OS_ENTER_CRITICAL() asm CLI /* Disable interrupts*/
#define OS_EXIT_CRITICAL() asm STI /* Enable interrupts */
#endif
// 方法二是先将中断关闭的状态保存到堆栈中,然后关闭中断。与之对应的 OS_EXIT_CRITICAL() 的操作是从堆栈中恢复中断状态。它允许使用中断嵌套,如中断关闭后,调用系统函数,其中也涉及到中断开关,而里面的不影响外面的中断,及允许中断嵌套。
#if OS_CRITICAL_METHOD == 2
#define OS_ENTER_CRITICAL() asm {PUSHF; CLI} /* Disable interrupts */
#define OS_EXIT_CRITICAL() asm POPF /* Enable interrupts */
#endif
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR()) /* Disable interrupts */
#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr)) /* Enable interrupts */
#endif
4.ECB 事件控制块及相关函数
4.1OS_EVENT 结构体的定义
#if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 0)
typedef struct {
INT8U OSEventType; /* 事件控制块类型 */
INT8U OSEventGrp; /* 等待任务列表组信息 */
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待事件发生的任务列表 */
INT16U OSEventCnt; /* 当事件是一个信号量时, OSEventCnt 是用于信号量的计数器 */
void *OSEventPtr; /* Pointer to message or queue structure */
} OS_EVENT;
#endif
4.2EVENT相关宏定义
#define OS_EVENT_TYPE_UNUSED 0
#define OS_EVENT_TYPE_MBOX 1
#define OS_EVENT_TYPE_Q 2
#define OS_EVENT_TYPE_SEM 3
#define OS_EVENT_TYPE_MUTEX 4
#define OS_EVENT_TYPE_FLAG 5
4.3OS_EventWaitListInit() 初始化一个事件控制块
初始化 OS_EVENT 变量的等待任务列表信息
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
void OS_EventWaitListInit (OS_EVENT *pevent)
{
INT8U *ptbl;
pevent->OSEventGrp = 0x00; /* No task waiting on event */
ptbl = &pevent->OSEventTbl[0];
#if OS_EVENT_TBL_SIZE > 0
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 1
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 2
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 3
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 4
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 5
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 6
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 7
*ptbl = 0x00;
#endif
}
#endif
4.4OS_EventTaskRdy()使任务进入就绪态
4.4.1源码分析
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
-
OS_EVENT *pevent 进入就绪态的任务事件指针
-
Void *msg
-
INT8U msk 该参数是用于将 TCB 中的 OSTCBStat 相应位清零,和所发生事件的类型相对应。
#if OS_EVENT_EN > 0
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]; /* 得到等待列表最高优先级信息 */
bity = OSMapTbl[y];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
bitx = OSMapTbl[x];
prio = (INT8U)((y << 3) + x); /* 计算任务优先级 */
if ((pevent->OSEventTbl[y] &= ~bitx) == 0x00) { /* 将任务从等待列表中删去 */
pevent->OSEventGrp &= ~bity; /* 如果是该等待列表组唯一个等待任务,组清零 */
}
ptcb = OSTCBPrioTbl[prio]; /* 找到指向这个优先级的任务 TCB */
ptcb->OSTCBDly = 0; /* 延时变量清零 ,即就绪 */
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* 清除事件控制块 */
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb->OSTCBMsg = msg; /* 将消息发送给等待的 TCB */
#else
msg = msg; /* Prevent compiler warning if not used */
#endif
ptcb->OSTCBStat &= ~msk; /* 清除等待事件的相应状态 */
if (ptcb->OSTCBStat == OS_STAT_RDY) { /* 如果任务未被挂起 */
OSRdyGrp |= bity; /* 将任务加入就绪表 */
OSRdyTbl[y] |= bitx;
}
return (prio);
}
#endif
4.4.2 流程图
图 3 OS_EventTaskRdy() 流程图
4.5OS_EventTaskWait() 任务进入等待某事件发生状态
4.5.1源码分析
void OS_EventTaskWait (OS_EVENT *pevent)
{
OSTCBCur->OSTCBEventPtr = pevent; /* 将 ECB 指针放到 TCB 的 OSTCBEventPtr 中 */
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) { /* 从就绪表中清除 */
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* 加入等待列表 */
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
#endif
4.5.2 流程图
图 4 OSEventTaskWait 流程图
4.6OS_EventTo() 由于等待超时将任务置为就绪态
4.6.1源码分析
#if OS_EVENT_EN > 0
void OS_EventTO (OS_EVENT *pevent)
{
if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) {
pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY; /* 从等待列表中清除 */
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* 将 TCB 的状态位置为就绪 */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* 不再等待事件 */
}
#endif
5.消息
5.1信号量
5.1.1 OSSemCreate()创建信号量
-
-
-
-
源码分析
-
-
-
-
INT16U cnt 信号量计数值
说明:
在 μC/OS-II 中,信号量一旦建立就不能删除了,因此也就不可能将一个已分配的任务控制块再放回到空闲 ECB 链表中
OS_EVENT *OSSemCreate (INT16U cnt)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_EVENT *pevent;
**********
if (OSIntNesting > 0) { /* */
return ((OS_EVENT *)0); /* */
}
******///
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; /* 获得空闲 ECB */
if (OSEventFreeList != (OS_EVENT *)0) { /* 看空闲 PCB 池是否已满 */
/****
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* */
****/
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) { /* 如果得到一个 ECB */
pevent->OSEventType = OS_EVENT_TYPE_SEM; /* 设置 ECB 类型 */
///*******
pevent->OSEventCnt = cnt; /* 设置信号量计数值 */
*******///
pevent->OSEventPtr = (void *)0; /* 从 ECB 空闲列表中脱掉 */
OS_EventWaitListInit(pevent); /* 初始化,令其初始无等待任务 */
}
return (pevent);
}
图 5 OSSemCreate ()流程图
5.1.2 OSSemPend() 任务等待一个信号量
源代码分析
-
OS_EVENT *pevent
-
INT16U timeout
-
INT8U *err
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
if (OSIntNesting > 0) { /* See if called from ISR ... */
*err = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return;
}
#if OS_ARG_CHK_EN > 0
if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */
*err = OS_ERR_PEVENT_NULL;
return;
}
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
*err = OS_ERR_EVENT_TYPE;
return;
}
#endif
OS_ENTER_CRITICAL();
if (pevent->OSEventCnt > 0) { /* If sem. is positive, resource available ... */
pevent->OSEventCnt--; /* ... decrement semaphore only if positive. */
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return;
}
/* Otherwise, must wait until event occurs */
OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* Resource not available, pend on semaphore */
****
OSTCBCur->OSTCBDly = timeout; /* Store pend timeout in TCB */
****
OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
///*** 是否有可能不需要进行任务切换
OS_Sched(); /* Find next highest priority task ready */
OS_ENTER_CRITICAL();
if (OSTCBCur->OSTCBStat & OS_STAT_SEM) { /* Must have timed out if still waiting for event*/
OS_EventTO(pevent);
OS_EXIT_CRITICAL();
*err = OS_TIMEOUT; /* Indicate that didn't get event within TO */
return;
}
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
流程图
图 6 OSSemPend() 流程图
5.1.3 OSSemPost()发送一个信号量
源代码分析
-
OS_EVENT *pevent 信号量的指针
INT8U OSSemPost (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
#if OS_ARG_CHK_EN > 0
if (pevent == (OS_EVENT *)0) { /* 查看 ECB 是否有效 */
return (OS_ERR_PEVENT_NULL);
}
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 查看 ECB 类型是否为消息 */
return (OS_ERR_EVENT_TYPE);
}
#endif
OS_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0x00) { /* 看等待列表中是否有等待该消息的任务 */
OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM); /* 有则将最高 prio 的任务就绪 */
OS_EXIT_CRITICAL();
OS_Sched(); /* 可能发生调度,进行任务切换 */
return (OS_NO_ERR);
}
if (pevent->OSEventCnt < 65535) { /* 如果信号计数值没有溢出的可能 */
pevent->OSEventCnt++; /* 信号量计数值加 1 */
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
OS_EXIT_CRITICAL(); /* 这里信号计数值已发生溢出 */
return (OS_SEM_OVF);
}
流程图
图 7 OSSemPost() 流程图
5.1.4 OSSemAccept() 无等待的请求一个信号量
源码分析
-
OS_EVENT *pevent ECB 的指针,指向请求的信号量
说明:
-
中断服务子程序要请求信号量时,只能用 OSSemAccept() 而不能用 OSSemPend() ,因为中断服务子程序是不允许等待的
#if OS_SEM_ACCEPT_EN > 0
INT16U OSSemAccept (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
INT16U cnt;
#if OS_ARG_CHK_EN > 0
if (pevent == (OS_EVENT *)0) { /* 检查 ECB 是否有效 */
return (0);
}
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 检查事件类型是否正确,是否为消息 */
return (0);
}
#endif
OS_ENTER_CRITICAL();
cnt = pevent->OSEventCnt;
if (cnt > 0) { /* 资源是否有用 */
pevent->OSEventCnt--; /* 如果有用,资源数减一 */
}
OS_EXIT_CRITICAL();
return (cnt); /* 返回原有的资源数,用以说明该资源是否可用,另外也可以看到该资源数的的多少 */
}
#endif
流程图
图 8 OSSemAccept 流程图