【18】从0到1教你写uC/OS-III————>时基列表

  1. 简述:
    1. 所有的实时系统都需要一个能提供周期性时间的时间源,叫做时基周期或系统周期;
    2. 当时基任务执行时,它会遍历队列中所有等待期满的任务、等待事件超时的任务;
    3. 时基任务会就绪时基列表中的那些期满、超时的任务;
    4. 时基列表中包含了一个表OSCfg_TickWheel[]和一个计数器OSTickCtr;
    5. 这个表由OS_CFG_TICK_WHELL_SIZE记录,记录数取决于处理器的RAM及应用中的最大任务数
    6. 推荐值为所有任务/4,不推荐使用偶数,如果算出来是偶数,则加1变成质数,事实上质数是一个很好的选择;
  2. 例程
    1. 时基列表初始化
      /*********************************************************
       * 函数名:void OS_TickListInit(void); 
       * 描述  :时基列表初始化
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_TickListInit(void)
      {
          OS_TICK_SPOKE_IX    i;
          OS_TICK_SPOKE   *p_spoke;
          
          for(i = 0u; i < OSCfg_TickWheelSize; i++)
          {
              p_spoke         =   (OS_TICK_SPOKE  *)&OSCfg_TickWheel[i];
              p_spoke->FirstPtr = (OS_TCB         *)0;
              p_spoke->NbrEntries = (OS_OBJ_QTY   )0u;
              p_spoke->NbrEntriesMax = (OS_OBJ_QTY )0u;
          }
      }

       

    2. 向时基列表中插入一个任务TCB
      /*********************************************************
       * 函数名:void OS_TickListInsert(OS_TCB *p_tcb, OS_TICK time);
       * 描述  :时基列表中插入一个任务TCB,根据延时时间的大小升序排列
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_TickListInsert(OS_TCB *p_tcb, OS_TICK time)
      {
          OS_TICK_SPOKE_IX    spoke;
          OS_TICK_SPOKE       *p_spoke;
          OS_TCB              *p_tcb0;
          OS_TCB              *p_tcb1;
          
          p_tcb->TickCtrMatch = OSTickCtr + time;
          p_tcb->TickRemain = time;
          
          spoke = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize);
          p_spoke = &OSCfg_TickWheel[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;
          }
          //如果插入的不是第一个节点,则按照TickRemain大小升序排列
          else
          {
              //获取第一个节点指针
              p_tcb1 = p_spoke->FirstPtr;
              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;
                      }
                      //跳出while循环
                      p_tcb1 = (OS_TCB    *)0;
                  }
              } 
              //节点成功插入
              p_spoke->NbrEntries++;        
          }
          //刷新NbrEntriesMax的值
          if(p_spoke->NbrEntriesMax < p_spoke->NbrEntries)
          {
              p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
          }
          
          //任务TCB中的TickSpokePtr回指根节点
          p_tcb->TickSpokePtr = p_spoke;
      }

       

    3. 从时基列表中删除一个指定的TCB节点
      /*********************************************************
       * 函数名:void OS_TickListRemove(OS_TCB *p_tcb);
       * 描述  :从时基列表中删除一个指定的TCB节点
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_TickListRemove(OS_TCB *p_tcb)
      {
          OS_TICK_SPOKE   *p_spoke;
          OS_TCB          *p_tcb1;
          OS_TCB          *p_tcb2;
          
          //获取任务TCB所在链表的根指针
          p_spoke = p_tcb->TickSpokePtr;
          
          //确保任务在链表中
          if(p_spoke != (OS_TICK_SPOKE *)0)
          {
              //将剩余的时间清0
              p_tcb->TickRemain = (OS_TICK)0u;
              //要移除的刚好是第一个节点
              if(p_spoke->FirstPtr == p_tcb)
              {
                  //更新第一个节点,原来的第一个节点需要被移除
                  p_tcb1  =   (OS_TCB *)p_tcb->TickNextPtr;
                  p_spoke->FirstPtr = p_tcb1;
                  if(p_tcb1 != (OS_TCB *)0)
                  {
                      p_tcb1->TickPrevPtr = (OS_TCB *)0;
                  }
              }
              //要移除的不是第一个节点
              else
              {   
                  //保存要移除的节点的前后节点的指针
                  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;
                  }
              }
              
              //复位任务TCB中时基列表相关的字段成员
              p_tcb->TickNextPtr = (OS_TCB    *)0;
              p_tcb->TickPrevPtr = (OS_TCB    *)0;
              p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
              p_tcb->TickCtrMatch = (OS_TICK  )0u;
              
              //节点减1
              p_spoke->NbrEntries--;
          }
      }

       

    4. 更新时基计数器OSTickCtr
      /*********************************************************
       * 函数名:void OS_TickListUpdate(void); 
       * 描述  :
                  在每个SysTick周期到来时在OSTimeTick()被调用
                  用于更新时基计数器OSTickCtr,扫描时基列表中的任务延时是否到期
      
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_TickListUpdate(void)
      {
          OS_TICK_SPOKE_IX    spoke;
          OS_TICK_SPOKE       *p_spoke;
          OS_TCB              *p_tcb;
          OS_TCB              *p_tcb_next;
          CPU_BOOLEAN         done;
          
          //保存当前中断值
          CPU_SR_ALLOC();
          //进入临界段
          OS_CRITICAL_ENTER();
          
          //时基计数器++
          OSTickCtr++;
          
          spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize);
          p_spoke = &OSCfg_TickWheel[spoke];
          
          p_tcb   =  p_spoke->FirstPtr;
          done    =  DEF_FALSE;
          
          while(done == DEF_FALSE)
          {
              if(p_tcb != (OS_TCB *)0)
              {
                  p_tcb_next = p_tcb->TickNextPtr;
                  
                  p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;
                  
                  //节点延时时间到
                  if(OSTickCtr == p_tcb->TickCtrMatch)
                  {
                      //让任务就绪
                      OS_TaskRdy(p_tcb);
                  }
                  else
                  {
                      //如果第一个节点延时期未满,则退出while循环
                      //因为链表是根据升序排列的,第一个节点延时期未满,那后面的肯定未满
                      done = DEF_TRUE;
                  }
                  //如果第一个节点延时期满,则继续遍历链表,看看还有没有延时期满的任务
                  //如果有,则让它就绪
                  p_tcb = p_tcb_next;
              }
              else
              {
                  done = DEF_TRUE;
              }
          }
        
          //退出临界段
          OS_CRITICAL_EXIT();   
      }

       

  3. 总结
    1. 看简述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值