1.时钟节拍列表由一个时钟节拍轮(,见os_cfg_app.c中的OSCfg_TickWheel[])和一个计数器OSTickCtr构成。
2.时钟节拍轮是一个数组,其中数组的元素的类型为OS_TICK_SPOKE,它的大小由OS_CFG_TICK_WHEEL_SIZE设定。
建议OS_CFG_TICK_WHEEL_SIZE的大小为任务数目的1/4左右,不要把它的值与时钟节拍的频率成倍数关系,最好为素数。
注:时钟节拍轮的每个元素也成为表项。
3.OS_TICK_SPOKE结构体
struct os_tick_spoke {
OS_TCB *FirstPtr; // 指向该表项上的等待任务构成的双向链表
OS_OBJ_QTY NbrEntries; // 该表项上等待的任务的数目
OS_OBJ_QTY NbrEntriesMax; // 该表项上等待的任务的最大数目
};
4.OS_TickListInsert()函数的功能是把任务添加到时钟节拍列表,该函数的定义位于os_tick.c文件中,209行 - 306行
void OS_TickListInsert (OS_TCB *p_tcb,
OS_TICK time,
OS_OPT opt,
OS_ERR *p_err)
{
OS_TICK tick_delta;
OS_TICK tick_next;
OS_TICK_SPOKE *p_spoke;
OS_TCB *p_tcb0;
OS_TCB *p_tcb1;
OS_TICK_SPOKE_IX spoke;
if (opt == OS_OPT_TIME_MATCH) { // 绝对模式
tick_delta = time - OSTickCtr - 1u; // 计算剩余时间
if (tick_delta > OS_TICK_TH_RDY) { // 若延时已经发生
p_tcb->TickCtrMatch = (OS_TICK )0u;
p_tcb->TickRemain = (OS_TICK )0u;
p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
*p_err = OS_ERR_TIME_ZERO_DLY; // 不做时,直接返回
return;
}
p_tcb->TickCtrMatch = time; // 匹配延时时间长度
p_tcb->TickRemain = tick_delta + 1u; // 剩余延时时间长度
} else if (time > (OS_TICK)0u) { // 延时长度不为0
if (opt == OS_OPT_TIME_PERIODIC) { // 周期模式
tick_next = p_tcb->TickCtrPrev + time; // 下一个周期的时间
tick_delta = tick_next - OSTickCtr - 1u; // 距下一个周期还剩多长时间
if (tick_delta < time) { // 若下一个周期未发生
p_tcb->TickCtrMatch = tick_next; // 使用下一个周期的时间
} else { // 下一个周期已发生
p_tcb->TickCtrMatch = OSTickCtr + time; // 重置任务的时钟节拍匹配值
}
p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;
p_tcb->TickCtrPrev = p_tcb->TickCtrMatch;
} else { // 相对模式
p_tcb->TickCtrMatch = OSTickCtr + time;
p_tcb->TickRemain = time;
}
} else { // 延时长度为0
p_tcb->TickCtrMatch = (OS_TICK )0u;
p_tcb->TickRemain = (OS_TICK )0u;
p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
*p_err = OS_ERR_TIME_ZERO_DLY; // 不做延时,直接返回
return;
}
spoke = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize); // 获取表项
p_spoke = &OSCfg_TickWheel[spoke];
if (p_spoke->NbrEntries == (OS_OBJ_QTY)0u) { // 若表项中的任务链表为空
p_tcb->TickNextPtr = (OS_TCB *)0;
p_tcb->TickPrevPtr = (OS_TCB *)0;
p_spoke->FirstPtr = p_tcb;
p_spoke->NbrEntries = (OS_OBJ_QTY)1u;
} else { // 表项中的任务链表不为空
p_tcb1 = p_spoke->FirstPtr; // 指针指向表项的第一个任务的任务控制块OS_TCB
while (p_tcb1 != (OS_TCB *)0) { // 若指针不为空
p_tcb1->TickRemain = p_tcb1->TickCtrMatch - OSTickCtr; // 计算任务的剩余时间
if (p_tcb->TickRemain > p_tcb1->TickRemain) { // 指针继续向后移
if (p_tcb1->TickNextPtr != (OS_TCB *)0) {
p_tcb1 = p_tcb1->TickNextPtr;
} else { // 到达链表尾部,在链表尾部插入
p_tcb->TickNextPtr = (OS_TCB *)0;
p_tcb->TickPrevPtr = p_tcb1;
p_tcb1->TickNextPtr = p_tcb;
p_tcb1 = (OS_TCB *)0; // 跳出循环
}
} else { // 插入到当前任务的前面
if (p_tcb1->TickPrevPtr == (OS_TCB *)0) { // 在链表头部插入
p_tcb->TickPrevPtr = (OS_TCB *)0;
p_tcb->TickNextPtr = p_tcb1;
p_tcb1->TickPrevPtr = p_tcb;
p_spoke->FirstPtr = p_tcb;
} else { // 在链表中间插入
p_tcb0 = p_tcb1->TickPrevPtr;
p_tcb->TickPrevPtr = p_tcb0;
p_tcb->TickNextPtr = p_tcb1;
p_tcb0->TickNextPtr = p_tcb;
p_tcb1->TickPrevPtr = p_tcb;
}
p_tcb1 = (OS_TCB *)0; // 跳出循环
}
}
p_spoke->NbrEntries++; // 等待的任务的数目加1
}
if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) {
p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
}
p_tcb->TickSpokePtr = p_spoke; // 任务回指到时钟节拍轮的表项上
*p_err = OS_ERR_NONE;
}
4.OS_TickListRemove()函数的功能是把任务从时钟节拍列表中删除,该函数的定义位于os_tick.c文件中,326行 - 357行
void OS_TickListRemove (OS_TCB *p_tcb)
{
OS_TICK_SPOKE *p_spoke;
OS_TCB *p_tcb1;
OS_TCB *p_tcb2;
p_spoke = p_tcb->TickSpokePtr; // 根据OS_TCB中的成员TickSpoketPtr获取任务所在时钟节拍轮的表项
if (p_spoke != (OS_TICK_SPOKE *)0) { // 若表项不为空
p_tcb->TickRemain = (OS_TICK)0u; // 任务的剩余时间长度设为0
if (p_spoke->FirstPtr == p_tcb) { // 若OS_TCB在链表的头部
p_tcb1 = (OS_TCB *)p_tcb->TickNextPtr;
p_spoke->FirstPtr = p_tcb1;
if (p_tcb1 != (OS_TCB *)0) {
p_tcb1->TickPrevPtr = (void *)0;
}
} else { // OS_TCB在链表中
p_tcb1 = p_tcb->TickPrevPtr;
p_tcb2 = p_tcb->TickNextPtr;
p_tcb1->TickNextPtr = p_tcb2;
if (p_tcb2 != (OS_TCB *)0) {
p_tcb2->TickPrevPtr = p_tcb1;
}
}
p_tcb->TickNextPtr = (OS_TCB *)0; // 清空OS_TCB中与时钟节拍有关的成员
p_tcb->TickPrevPtr = (OS_TCB *)0;
p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
p_tcb->TickCtrMatch = (OS_TICK )0u;
p_spoke->NbrEntries--;
}
}
5.OS_TickListUpdate()函数的功能是更新时钟节拍列表,该函数的定义位于os_tick.c文件中,404行 - 506行
void OS_TickListUpdate (void)
{
CPU_BOOLEAN done;
OS_TICK_SPOKE *p_spoke;
OS_TCB *p_tcb;
OS_TCB *p_tcb_next;
OS_TICK_SPOKE_IX spoke;
CPU_TS ts_start;
CPU_TS ts_end;
CPU_SR_ALLOC(); // 宏定义,声明变量cpu_sr,用来临时保存CPU的状态寄存器的值
OS_CRITICAL_ENTER(); // 进入临界区
ts_start = OS_TS_GET(); // 获取时间戳
OSTickCtr++; // 计数器加1
spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize); // 获取表项的位置
p_spoke = &OSCfg_TickWheel[spoke]; // 获取表项的地址
p_tcb = p_spoke->FirstPtr; // 获取表项中的第一个OS_TCB
done = DEF_FALSE;
while (done == DEF_FALSE) {
if (p_tcb != (OS_TCB *)0) { // 若OS_TCB不为空
p_tcb_next = p_tcb->TickNextPtr; // 获取下一个OS_TCB
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY: // 就绪
case OS_TASK_STATE_PEND: // 等待
case OS_TASK_STATE_SUSPENDED: // 被挂起
case OS_TASK_STATE_PEND_SUSPENDED: // 等待且被挂起
break;
case OS_TASK_STATE_DLY: // 延时
p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr; // 计算剩余的时间长度
if (OSTickCtr == p_tcb->TickCtrMatch) { // 若延时时间到
p_tcb->TaskState = OS_TASK_STATE_RDY; // 改变任务的状态未就绪
OS_TaskRdy(p_tcb); // 把任务添加到任务就绪表中
} else {
done = DEF_TRUE; // 延时时间未到
}
break;
case OS_TASK_STATE_PEND_TIMEOUT: // 带超时检测的等待
p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr; // 计算剩余的时间长度
if (OSTickCtr == p_tcb->TickCtrMatch) {
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endif
p_tcb->TS = OS_TS_GET();
OS_PendListRemove(p_tcb); // 从任务挂起表中删除该任务
OS_TaskRdy(p_tcb); // 把任务添加到任务就绪表中
p_tcb->TaskState = OS_TASK_STATE_RDY; // 任务的状态改为就绪
p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT;
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
} else {
done = DEF_TRUE; // 延时时间未到
}
break;
case OS_TASK_STATE_DLY_SUSPENDED: // 延时且被挂起
p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) {
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; // 任务的状态改为被挂起
OS_TickListRemove(p_tcb); // 把任务从时钟节拍轮中删除
} else {
done = DEF_TRUE; // 延时时间未到
}
break;
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED: // 带超时检测的等待且被挂起
p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;
if (OSTickCtr == p_tcb->TickCtrMatch) {
#if (OS_MSG_EN > 0u)
p_tcb->MsgPtr = (void *)0;
p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endif
p_tcb->TS = OS_TS_GET();
OS_PendListRemove(p_tcb); // 把任务从任务挂起表中删除
OS_TickListRemove(p_tcb); // 把任务从时钟节拍列表中删除
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; // 任务的状态改为被挂起
p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT;
p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING;
} else {
done = DEF_TRUE; // 延时未结束
}
break;
default:
break;
}
p_tcb = p_tcb_next;
} else {
done = DEF_TRUE;
}
}
ts_end = OS_TS_GET() - ts_start; // 测量时钟节拍任务的执行时间
if (ts_end > OSTickTaskTimeMax) {
OSTickTaskTimeMax = ts_end;
}
OS_CRITICAL_EXIT(); // 退出临界区
}