uC/OS-III之时钟节拍列表

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();  // 退出临界区
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值