μC/OS-III---I笔记4---软件定时器

  软件定时器是在硬件定时器的基础上开发的,通过将一个硬件定时器进行分频及管理就可以的到多个软件定时器。他和时间管理共同组成了系统的时间管理大部分的内容。系统一开始的系统初始化函数OSInit函数内调用了OS_TmrInit()这个函数并在这个函数内创建了一个定时器任务(OS_TmrTask),在这个任务内有等待滴答定时器中断处发布信号量的信号量请求操作。

滴答定时器内的信号量发布函数部分:

#if OS_CFG_TMR_EN > 0u
    OSTmrUpdateCtr--;
    if (OSTmrUpdateCtr == (OS_CTR)0u) {
        OSTmrUpdateCtr = OSTmrUpdateCnt;
        OSTaskSemPost((OS_TCB *)&OSTmrTaskTCB,              /* Signal timer task                                      */
                      (OS_OPT  ) OS_OPT_POST_NONE,
                      (OS_ERR *)&err);
    }
#endif

而OS_TmrInit函数:

************************************************************************************************************************
*                                             INITIALIZE THE TIMER MANAGER
*
* Description: This function is called by OSInit() to initialize the timer manager module.
*
* Argument(s): p_err    is a pointer to a variable that will contain an error code returned by this function.
*
*                           OS_ERR_NONE
*                           OS_ERR_TMR_STK_INVALID       if you didn‘t specify a stack for the timer task
*                           OS_ERR_TMR_STK_SIZE_INVALID  if you didn‘t allocate enough space for the timer stack
*                           OS_ERR_PRIO_INVALID          if you specified the same priority as the idle task
*                           OS_ERR_xxx                   any error code returned by OSTaskCreate()
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_TmrInit (OS_ERR  *p_err)
{
    OS_TMR_SPOKE_IX   i;
    OS_TMR_SPOKE     *p_spoke;

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#if OS_CFG_DBG_EN > 0u
    OSTmrDbgListPtr = (OS_TMR *)0;
#endif

    if (OSCfg_TmrTaskRate_Hz > (OS_RATE_HZ)0) {
        //计算分频系数
        OSTmrUpdateCnt = OSCfg_TickRate_Hz / OSCfg_TmrTaskRate_Hz;
    } else {
        //OSCfg_TmrTaskRate_Hz设置错误 则按默认的时钟节拍1/10
        OSTmrUpdateCnt = OSCfg_TickRate_Hz / (OS_RATE_HZ)10;
    }
    //更新分频计数变量,此处联想到滴答定时器内的那一段程序
    OSTmrUpdateCtr   = OSTmrUpdateCnt;
    //清零
    OSTmrTickCtr     = (OS_TICK)0;
    OSTmrTaskTimeMax = (CPU_TS)0;
    //跟新定时器列表
    for (i = 0u; i < OSCfg_TmrWheelSize; i++) {
        p_spoke                = &OSCfg_TmrWheel[i];
        p_spoke->NbrEntries    = (OS_OBJ_QTY)0;
        p_spoke->NbrEntriesMax = (OS_OBJ_QTY)0;
        p_spoke->FirstPtr      = (OS_TMR   *)0;
    }

                                                            /* ---------------- CREATE THE TIMER TASK --------------- */
   //定时器堆栈基地址
    if (OSCfg_TmrTaskStkBasePtr == (CPU_STK*)0) {
       *p_err = OS_ERR_TMR_STK_INVALID;
        return;
    }
    //定时器任务堆栈检查
    if (OSCfg_TmrTaskStkSize < OSCfg_StkSizeMin) {
       *p_err = OS_ERR_TMR_STK_SIZE_INVALID;
        return;
    }
    //定时器优先级检查
    if (OSCfg_TmrTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) {
       *p_err = OS_ERR_TMR_PRIO_INVALID;
        return;
    }
    //创建定时器任务
    OSTaskCreate((OS_TCB     *)&OSTmrTaskTCB,
                 (CPU_CHAR   *)((void *)"uC/OS-III Timer Task"),
                 (OS_TASK_PTR )OS_TmrTask,
                 (void       *)0,
                 (OS_PRIO     )OSCfg_TmrTaskPrio,
                 (CPU_STK    *)OSCfg_TmrTaskStkBasePtr,
                 (CPU_STK_SIZE)OSCfg_TmrTaskStkLimit,
                 (CPU_STK_SIZE)OSCfg_TmrTaskStkSize,
                 (OS_MSG_QTY  )0,
                 (OS_TICK     )0,
                 (void       *)0,
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),
                 (OS_ERR     *)p_err);
}

OS_TmrInit ()

注意其中的任务创建函数创建了的任务就是上面提到的等待定时器处的调用发布信号量。下面就是创建TmrInit函数创建的任务TmrTask。

************************************************************************************************************************
*                                                 TIMER MANAGEMENT TASK
*
* Description: This task is created by OS_TmrInit().
*
* Arguments  : none
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_TmrTask (void  *p_arg)
{
    CPU_BOOLEAN          done;
    OS_ERR               err;
    OS_TMR_CALLBACK_PTR  p_fnct;
    OS_TMR_SPOKE        *p_spoke;
    OS_TMR              *p_tmr;
    OS_TMR              *p_tmr_next;
    OS_TMR_SPOKE_IX      spoke;
    CPU_TS               ts;
    CPU_TS               ts_start;
    CPU_TS               ts_end;

    //去警告
    p_arg = p_arg;                                               /* Not using ‘p_arg‘, prevent compiler warning       */
    while (DEF_ON) {
       //请求信号量,信号发布在滴答定时器处
        (void)OSTaskSemPend((OS_TICK )0,                         /* Wait for signal indicating time to update tmrs    */
                            (OS_OPT  )OS_OPT_PEND_BLOCKING,
                            (CPU_TS *)&ts,
                            (OS_ERR *)&err);
        OSSchedLock(&err);
        ts_start = OS_TS_GET();
        //任务运行次数
        OSTmrTickCtr++;                                          /* Increment the current time                        */
        spoke    = (OS_TMR_SPOKE_IX)(OSTmrTickCtr % OSCfg_TmrWheelSize);
        p_spoke  = &OSCfg_TmrWheel[spoke];
        p_tmr    = p_spoke->FirstPtr;
        done     = DEF_FALSE;
        //找出列表里到期的任务
        while (done == DEF_FALSE) {
            if (p_tmr != (OS_TMR *)0) {
                p_tmr_next = (OS_TMR *)p_tmr->NextPtr;           /* Point to next tmr to update because current ...   */
                //到期                                                 /* ... timer could get unlinked from the wheel.      */
                if (OSTmrTickCtr == p_tmr->Match) {              /* Process each timer that expires                   */
                    OS_TmrUnlink(p_tmr);                         /* Remove from current wheel spoke                   */
                    if (p_tmr->Opt == OS_OPT_TMR_PERIODIC) {
                        //周期定时器继续插入定时器列表
                        OS_TmrLink(p_tmr,
                                   OS_OPT_LINK_PERIODIC);        /* Recalculate new position of timer in wheel        */
                    } else {
                        //一次性定时器完成
                        p_tmr->State = OS_TMR_STATE_COMPLETED;   /* Indicate that the timer has completed             */
                    }
                    //取出回调函数并执行
                    p_fnct = p_tmr->CallbackPtr;                 /* Execute callback function if available            */
                    if (p_fnct != (OS_TMR_CALLBACK_PTR)0) {
                        (*p_fnct)((void *)p_tmr,
                                  p_tmr->CallbackPtrArg);
                    }
                    //第一个到期了还要继续检查后面的是否到期
                    p_tmr = p_tmr_next;                          /* See if next timer matches                         */
                } else {
                    //根据定时器的插入规则,第一个未到期后面的肯定没有到期
                    done  = DEF_TRUE;
                }
            } else {
                done = DEF_TRUE;
            }
        }
        //计算运行时间
        ts_end = OS_TS_GET() - ts_start;                         /* Measure execution time of timer task              */
        OSSchedUnlock(&err);
        if (OSTmrTaskTimeMax < ts_end) {
            OSTmrTaskTimeMax = ts_end;
        }
    }
}

#endif

OS_TmrTask

注意代码中的OSTaskSemPend()函数就是和开头的信号发布相对应,当用户创建一个定时器(注意:在进行创建定时器时一定要先定义一个定时器变量一般都是全局的)OSmrCreate()函数是用来创建一个定时器,创建完成后还需要在调用OSTmrStart()来启动定时器,时内部调用的OSTmrLink()函数将按照定时器剩余时间采用哈希算法将其加入定时器列表,其中OSTmrCreate主要有这样几个变量需要注意一下。

  • P_tmr是一个指向自定义的定时器变量
  • Opt 共有2个情况     OS_OPT_TMR_ONE_SHOT 一次性的    OS_OPT_TMR_PERIODIC 重复的
  • dly 对于第一种情况 DIY就是延时时间,第二种情况就是第一次的延时时间,第二次和以后重装值是下一个变量period
  • 对于这里的回调函数,书上给的解释是一个由用户编写系统自动调用的函数,这里放在定时器里就肯定是一个定时一定时间自动调用的函数,这一类函数满足相同的函数签名(函数原型,参数,返回值等)。

具体操作见代码:

/*
************************************************************************************************************************
*                                                   CREATE A TIMER
*
* Description: This function is called by your application code to create a timer.
*
* Arguments  : p_tmr           Is a pointer to a timer control block
*
*              p_name          Is a pointer to an ASCII string that is used to name the timer.  Names are useful for
*                              debugging.
*
*              dly             Initial delay.
*                              If the timer is configured for ONE-SHOT mode, this is the timeout used
*                              If the timer is configured for PERIODIC mode, this is the first timeout to wait for
*                              before the timer starts entering periodic mode
*
*              period          The ‘period‘ being repeated for the timer.
*                              If you specified ‘OS_OPT_TMR_PERIODIC‘ as an option, when the timer expires, it will
*                              automatically restart with the same period.
*
*              opt             Specifies either:
*
*                                  OS_OPT_TMR_ONE_SHOT       The timer counts down only once
*                                  OS_OPT_TMR_PERIODIC       The timer counts down and then reloads itself
*
*              p_callback      Is a pointer to a callback function that will be called when the timer expires.  The
*                              callback function must be declared as follows:
*
*                                  void  MyCallback (OS_TMR *p_tmr, void *p_arg);
*
*              p_callback_arg  Is an argument (a pointer) that is passed to the callback function when it is called.
*
*              p_err           Is a pointer to an error code.  ‘*p_err‘ will contain one of the following:
*
*                                 OS_ERR_NONE
*                                 OS_ERR_ILLEGAL_CREATE_RUN_TIME if you are trying to create the timer after you called
*                                                                  OSSafetyCriticalStart().
*                                 OS_ERR_OBJ_CREATED             if the timer has already been created
*                                 OS_ERR_OBJ_PTR_NULL            is ‘p_tmr‘ is a NULL pointer
*                                 OS_ERR_OBJ_TYPE                if the object type is invalid
*                                 OS_ERR_OPT_INVALID             you specified an invalid option
*                                 OS_ERR_TMR_INVALID_DLY         you specified an invalid delay
*                                 OS_ERR_TMR_INVALID_PERIOD      you specified an invalid period
*                                 OS_ERR_TMR_ISR                 if the call was made from an ISR
*
* Returns    : none
*
* Note(s)    : 1) This function only creates the timer.  In other words, the timer is not started when created.  To
*                 start the timer, call OSTmrStart().
************************************************************************************************************************
*/

void  OSTmrCreate (OS_TMR               *p_tmr,
                   CPU_CHAR             *p_name,
                   OS_TICK               dly,
                   OS_TICK               period,
                   OS_OPT                opt,
                   OS_TMR_CALLBACK_PTR   p_callback,
                   void                 *p_callback_arg,
                   OS_ERR               *p_err)
{
    CPU_SR_ALLOC();

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == DEF_TRUE) {
       *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
        return;
    }
#endif
//不允许在中断中创建定时器
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* See if trying to call from an ISR                      */
       *p_err = OS_ERR_TMR_ISR;
        return;
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_tmr == (OS_TMR *)0) {                             /* Validate ‘p_tmr‘                                       */
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return;
    }
//选择定时模式
    switch (opt) {
        case OS_OPT_TMR_PERIODIC:
            //检查定时时间有效性
             if (period == (OS_TICK)0) {
                *p_err = OS_ERR_TMR_INVALID_PERIOD;
                 return;
             }
             break;

        case OS_OPT_TMR_ONE_SHOT:
             if (dly == (OS_TICK)0) {
                *p_err = OS_ERR_TMR_INVALID_DLY;
                 return;
             }
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
             return;
    }
#endif
    //关中断
    OS_CRITICAL_ENTER();
    //初始化这个定时器数据
    p_tmr->State          = (OS_STATE           )OS_TMR_STATE_STOPPED;     /* Initialize the timer fields             */
    p_tmr->Type           = (OS_OBJ_TYPE        )OS_OBJ_TYPE_TMR;
    p_tmr->NamePtr        = (CPU_CHAR          *)p_name;
    p_tmr->Dly            = (OS_TICK            )dly;
    p_tmr->Match          = (OS_TICK            )0;
    p_tmr->Remain         = (OS_TICK            )0;
    p_tmr->Period         = (OS_TICK            )period;
    p_tmr->Opt            = (OS_OPT             )opt;
    p_tmr->CallbackPtr    = (OS_TMR_CALLBACK_PTR)p_callback;
    p_tmr->CallbackPtrArg = (void              *)p_callback_arg;
    p_tmr->NextPtr        = (OS_TMR            *)0;
    p_tmr->PrevPtr        = (OS_TMR            *)0;
    //仿真调试
#if OS_CFG_DBG_EN > 0u
    OS_TmrDbgListAdd(p_tmr);
#endif
    OSTmrQty++;                                             /* Keep track of the number of timers created             */
    //打开中断并没有任务调度
    OS_CRITICAL_EXIT_NO_SCHED();
   *p_err = OS_ERR_NONE;
   //具体的返回错误见函数的描述
}

OSTmrCreate

其中涉及的几个函数有

1.启动定时器

************************************************************************************************************************
*                                                   START A TIMER
*
* Description: This function is called by your application code to start a timer.
*
* Arguments  : p_tmr    Is a pointer to an OS_TMR
*
*              p_err    Is a pointer to an error code.  ‘*p_err‘ will contain one of the following:
*
*                           OS_ERR_NONE
*                           OS_ERR_OBJ_TYPE            if ‘p_tmr‘ is not pointing to a timer
*                           OS_ERR_TMR_INVALID
*                           OS_ERR_TMR_INACTIVE        if the timer was not created
*                           OS_ERR_TMR_INVALID_STATE   the timer is in an invalid state
*                           OS_ERR_TMR_ISR             if the call was made from an ISR
*
* Returns    : DEF_TRUE      is the timer was started
*              DEF_FALSE     if not or upon an error
*
* Note(s)    : 1) When starting/restarting a timer, regardless if it is in PERIODIC or ONE-SHOT mode, the timer is
*                 linked to the timer wheel with the OS_OPT_LINK_DLY option. This option sets the initial expiration
*                 time for the timer. For timers in PERIODIC mode, subsequent expiration times are handled by
*                 the OS_TmrTask().
************************************************************************************************************************
*/

CPU_BOOLEAN  OSTmrStart (OS_TMR  *p_tmr,
                         OS_ERR  *p_err)
{
    OS_ERR       err;
    CPU_BOOLEAN  success;

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (DEF_FALSE);
    }
#endif
//检查是否在中断内
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* See if trying to call from an ISR                      */
       *p_err = OS_ERR_TMR_ISR;
        return (DEF_FALSE);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_tmr == (OS_TMR *)0) {
       *p_err = OS_ERR_TMR_INVALID;
        return (DEF_FALSE);
    }
#endif
//对传入数据的类型检查
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_tmr->Type != OS_OBJ_TYPE_TMR) {                   /* Make sure timer was created                            */
       *p_err = OS_ERR_OBJ_TYPE;
        return (DEF_FALSE);
    }
#endif
//锁调度器,
    OSSchedLock(&err);
    //根据定时器状态操作
    switch (p_tmr->State) {
        //定时器正在运行--重启
        case OS_TMR_STATE_RUNNING:                          /* Restart the timer                                      */
             OS_TmrUnlink(p_tmr);                           /* ... Stop the timer                                     */
             OS_TmrLink(p_tmr, OS_OPT_LINK_DLY);            /* ... Link timer to timer wheel (see Note #1).           */
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_NONE;
             success = DEF_TRUE;
             break;
        //定时器已经停止--重启
        case OS_TMR_STATE_STOPPED:                          /* Start the timer                                        */
        case OS_TMR_STATE_COMPLETED:
             OS_TmrLink(p_tmr, OS_OPT_LINK_DLY);            /* ... Link timer to timer wheel (see Note #1).           */
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_NONE;
             success = DEF_TRUE;
             break;
        //定时器正已删除返回错误
        case OS_TMR_STATE_UNUSED:                           /* Timer not created                                      */
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_TMR_INACTIVE;
             success = DEF_FALSE;
             break;

        default:
             OSSchedUnlock(&err);
            *p_err = OS_ERR_TMR_INVALID_STATE;
             success = DEF_FALSE;
             break;
    }
    return (success);
}

OSTmrStart

2.加入定时器列表操作

************************************************************************************************************************
*                                         INSERT A TIMER INTO THE TIMER WHEEL
*
* Description: This function is called to insert the timer into the timer wheel.  The timer is always inserted at the
*              beginning of the list.
*
* Arguments  : p_tmr          Is a pointer to the timer to insert.
*              -----
*
*              opt            Is either:
*
*                               OS_OPT_LINK_PERIODIC    Means to re-insert the timer after a period expired
*                               OS_OPT_LINK_DLY         Means to insert    the timer the first time
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_TmrLink (OS_TMR  *p_tmr,
                  OS_OPT   opt)
{
    OS_TMR_SPOKE     *p_spoke;
    OS_TMR           *p_tmr0;
    OS_TMR           *p_tmr1;
    OS_TMR_SPOKE_IX   spoke;

    //因为这个列表里的定时器都是运行态
    p_tmr->State = OS_TMR_STATE_RUNNING;
    if (opt == OS_OPT_LINK_PERIODIC) {                      /* Determine when timer will expire                      */
        //周期延时
        p_tmr->Match = p_tmr->Period + OSTmrTickCtr;
    } else {
        //周期延时
        if (p_tmr->Dly == (OS_TICK)0) {
            p_tmr->Match = p_tmr->Period + OSTmrTickCtr;
        } else {
        //单次延时
            p_tmr->Match = p_tmr->Dly    + OSTmrTickCtr;
        }
    }
    //哈希算法按到时剩余长短排序加入定时link
    spoke  = (OS_TMR_SPOKE_IX)(p_tmr->Match % OSCfg_TmrWheelSize);
    p_spoke = &OSCfg_TmrWheel[spoke];
    //一下就是一个双向链表的插入操作
    if (p_spoke->FirstPtr ==  (OS_TMR *)0) {                /* Link into timer wheel                                  */
        p_tmr->NextPtr      = (OS_TMR *)0;
        p_tmr->PrevPtr      = (OS_TMR *)0;
        p_spoke->FirstPtr   = p_tmr;
        p_spoke->NbrEntries = 1u;
    } else {
        p_tmr->Remain  = p_tmr->Match                       /* Compute remaining time for timer                       */
                       - OSTmrTickCtr;
        p_tmr1         = p_spoke->FirstPtr;                 /* Point to current first timer in the list               */
        while (p_tmr1 != (OS_TMR *)0) {
            p_tmr1->Remain = p_tmr1->Match                  /* Compute time remaining of current timer in list        */
                           - OSTmrTickCtr;
            if (p_tmr->Remain > p_tmr1->Remain) {           /* Do we need to insert AFTER current timer in list?      */
                if (p_tmr1->NextPtr  != (OS_TMR *)0) {      /* Yes, are we pointing at the last timer in the list?    */
                    p_tmr1            = p_tmr1->NextPtr;    /* No,  Point to next timer in the list                   */
                } else {
                    p_tmr->NextPtr    = (OS_TMR *)0;
                    p_tmr->PrevPtr    =  p_tmr1;
                    p_tmr1->NextPtr   =  p_tmr;             /* Yes, timer to insert is now new last entry in the list */
                    p_tmr1            = (OS_TMR *)0;        /* Break loop                                             */
                }
            } else {                                        /* Insert before the current timer                        */
                if (p_tmr1->PrevPtr == (OS_TMR *)0) {       /* Are we inserting before the first timer?               */
                    p_tmr->PrevPtr    = (OS_TMR *)0;
                    p_tmr->NextPtr    = p_tmr1;
                    p_tmr1->PrevPtr   = p_tmr;
                    p_spoke->FirstPtr = p_tmr;
                } else {                                    /* Insert in between 2 timers already in the list         */
                    p_tmr0            = p_tmr1->PrevPtr;
                    p_tmr->PrevPtr    = p_tmr0;
                    p_tmr->NextPtr    = p_tmr1;
                    p_tmr0->NextPtr   = p_tmr;
                    p_tmr1->PrevPtr   = p_tmr;
                }
                p_tmr1 = (OS_TMR *)0;                       /* Break loop                                             */
            }
        }
        p_spoke->NbrEntries++;
    }
    // 更新link长
    if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) {     /* Keep track of maximum number of entries in each spoke  */
        p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
    }
}

OS_TmrLink

3.删除定时器

************************************************************************************************************************
*                                                   DELETE A TIMER
*
* Description: This function is called by your application code to delete a timer.
*
* Arguments  : p_tmr          Is a pointer to the timer to stop and delete.
*
*              p_err          Is a pointer to an error code.  ‘*p_err‘ will contain one of the following:
*
*                                 OS_ERR_NONE
*                                 OS_ERR_OBJ_TYPE             ‘p_tmr‘ is not pointing to a timer
*                                 OS_ERR_TMR_INVALID          ‘p_tmr‘ is a NULL pointer
*                                 OS_ERR_TMR_ISR              if the function was called from an ISR
*                                 OS_ERR_TMR_INACTIVE         if the timer was not created
*                                 OS_ERR_TMR_INVALID_STATE    the timer is in an invalid state
*
* Returns    : DEF_TRUE   if the timer was deleted
*              DEF_FALSE  if not or upon an error
************************************************************************************************************************
*/

#if OS_CFG_TMR_DEL_EN > 0u
CPU_BOOLEAN  OSTmrDel (OS_TMR  *p_tmr,
                       OS_ERR  *p_err)
{
    OS_ERR       err;
    CPU_BOOLEAN  success;

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (DEF_FALSE);
    }
#endif
//中断内
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* See if trying to call from an ISR                      */
       *p_err  = OS_ERR_TMR_ISR;
        return (DEF_FALSE);
    }
#endif
//数据检差
#if OS_CFG_ARG_CHK_EN > 0u
    if (p_tmr == (OS_TMR *)0) {
       *p_err = OS_ERR_TMR_INVALID;
        return (DEF_FALSE);
    }
#endif
//内核对象类型检差
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_tmr->Type != OS_OBJ_TYPE_TMR) {                   /* Make sure timer was created                            */
       *p_err = OS_ERR_OBJ_TYPE;
        return (DEF_FALSE);
    }
#endif
//进入临界
    OSSchedLock(&err);
#if OS_CFG_DBG_EN > 0u
    OS_TmrDbgListRemove(p_tmr);
#endif
    OSTmrQty--;                                             /* One less timer                                         */
//定时器膈个数--
    switch (p_tmr->State) {
        //定时器在运行
        case OS_TMR_STATE_RUNNING:
             OS_TmrUnlink(p_tmr);                           /* Remove from current wheel spoke                        */
             //清空定时器
             OS_TmrClr(p_tmr);
             //开启调度器
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_NONE;
             success = DEF_TRUE;
             break;
        //定时器已经被删或完成,也就是已经脱离定时器列表
        case OS_TMR_STATE_STOPPED:                          /* Timer has not started or ...                           */
        case OS_TMR_STATE_COMPLETED:                        /* ... timer has completed the ONE-SHOT time              */
             OS_TmrClr(p_tmr);                              /* Clear timer fields                                     */
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_NONE;
             success = DEF_TRUE;
             break;
        //定时器已经删除
        case OS_TMR_STATE_UNUSED:                           /* Already deleted                                        */
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_TMR_INACTIVE;
             success = DEF_FALSE;
             break;

        default:
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_TMR_INVALID_STATE;
             success = DEF_FALSE;
             break;
    }
    return (success);
}
#endif

OSTmrDel

4.脱离定时器列表

************************************************************************************************************************
*                                         REMOVE A TIMER FROM THE TIMER WHEEL
*
* Description: This function is called to remove the timer from the timer wheel.
*
* Arguments  : p_tmr          Is a pointer to the timer to remove.
*              -----
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_TmrUnlink (OS_TMR  *p_tmr)
{
    OS_TMR_SPOKE    *p_spoke;
    OS_TMR          *p_tmr1;
    OS_TMR          *p_tmr2;
    OS_TMR_SPOKE_IX  spoke;

    //哈希算法找到目标定时器
    spoke   = (OS_TMR_SPOKE_IX)(p_tmr->Match % OSCfg_TmrWheelSize);
    p_spoke = &OSCfg_TmrWheel[spoke];
    //双向链表删除节点操作
    if (p_spoke->FirstPtr == p_tmr) {                       /* See if timer to remove is at the beginning of list     */
        p_tmr1            = (OS_TMR *)p_tmr->NextPtr;
        p_spoke->FirstPtr = (OS_TMR *)p_tmr1;
        if (p_tmr1 != (OS_TMR *)0) {
            p_tmr1->PrevPtr = (OS_TMR *)0;
        }
    } else {
        p_tmr1          = (OS_TMR *)p_tmr->PrevPtr;         /* Remove timer from somewhere in the list                */
        p_tmr2          = (OS_TMR *)p_tmr->NextPtr;
        p_tmr1->NextPtr = p_tmr2;
        if (p_tmr2 != (OS_TMR *)0) {
            p_tmr2->PrevPtr = (OS_TMR *)p_tmr1;
        }
    }
    p_tmr->State   = OS_TMR_STATE_STOPPED;
    p_tmr->NextPtr = (OS_TMR *)0;
    p_tmr->PrevPtr = (OS_TMR *)0;
    p_spoke->NbrEntries--;
}

OS_TmrUnlink

5.停止定时器

************************************************************************************************************************
*                                                    STOP A TIMER
*
* Description: This function is called by your application code to stop a timer.
*
* Arguments  : p_tmr          Is a pointer to the timer to stop.
*
*              opt           Allows you to specify an option to this functions which can be:
*
*                               OS_OPT_TMR_NONE            Do nothing special but stop the timer
*                               OS_OPT_TMR_CALLBACK        Execute the callback function, pass it the callback argument
*                                                          specified when the timer was created.
*                               OS_OPT_TMR_CALLBACK_ARG    Execute the callback function, pass it the callback argument
*                                                          specified in THIS function call
*
*              callback_arg  Is a pointer to a ‘new‘ callback argument that can be passed to the callback function
*                               instead of the timer‘s callback argument.  In other words, use ‘callback_arg‘ passed in
*                               THIS function INSTEAD of p_tmr->OSTmrCallbackArg
*
*              p_err          Is a pointer to an error code.  ‘*p_err‘ will contain one of the following:
*                               OS_ERR_NONE
*                               OS_ERR_OBJ_TYPE            if ‘p_tmr‘ is not pointing to a timer
*                               OS_ERR_OPT_INVALID         if you specified an invalid option for ‘opt‘
*                               OS_ERR_TMR_INACTIVE        if the timer was not created
*                               OS_ERR_TMR_INVALID         ‘p_tmr‘ is a NULL pointer
*                               OS_ERR_TMR_INVALID_STATE   the timer is in an invalid state
*                               OS_ERR_TMR_ISR             if the function was called from an ISR
*                               OS_ERR_TMR_NO_CALLBACK     if the timer does not have a callback function defined
*                               OS_ERR_TMR_STOPPED         if the timer was already stopped
*
* Returns    : DEF_TRUE       If we stopped the timer (if the timer is already stopped, we also return DEF_TRUE)
*              DEF_FALSE      If not
************************************************************************************************************************
*/

CPU_BOOLEAN  OSTmrStop (OS_TMR  *p_tmr,
                        OS_OPT   opt,
                        void    *p_callback_arg,
                        OS_ERR  *p_err)
{
    OS_TMR_CALLBACK_PTR  p_fnct;
    OS_ERR               err;
    CPU_BOOLEAN          success;

#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (DEF_FALSE);
    }
#endif
//中断内
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {                        /* See if trying to call from an ISR            */
       *p_err = OS_ERR_TMR_ISR;
        return (DEF_FALSE);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_tmr == (OS_TMR *)0) {
       *p_err = OS_ERR_TMR_INVALID;
        return (DEF_FALSE);
    }
#endif
//内核对象类型检差
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_tmr->Type != OS_OBJ_TYPE_TMR) {                             /* Make sure timer was created                  */
       *p_err = OS_ERR_OBJ_TYPE;
        return (DEF_FALSE);
    }
#endif
//锁调度器进入临界区
    OSSchedLock(&err);
    switch (p_tmr->State) {
        case OS_TMR_STATE_RUNNING:
             OS_TmrUnlink(p_tmr);                                     /* Remove from current wheel spoke              */
            *p_err = OS_ERR_NONE;
             switch (opt) {
                 case OS_OPT_TMR_CALLBACK:
                      p_fnct = p_tmr->CallbackPtr;                         /* Execute callback function ...           */
                      if (p_fnct != (OS_TMR_CALLBACK_PTR)0) {              /* ... if available                        */
                        (*p_fnct)((void *)p_tmr, p_tmr->CallbackPtrArg);   /* Use callback arg when timer was created */
                      } else {
                         *p_err = OS_ERR_TMR_NO_CALLBACK;
                      }
                      break;
                //停止时调用回调函数
                 case OS_OPT_TMR_CALLBACK_ARG:
                      p_fnct = p_tmr->CallbackPtr;                    /* Execute callback function if available ...   */
                      if (p_fnct != (OS_TMR_CALLBACK_PTR)0) {
                        (*p_fnct)((void *)p_tmr, p_callback_arg);     /* .. using the ‘callback_arg‘ provided in call */
                      } else {
                         *p_err = OS_ERR_TMR_NO_CALLBACK;
                      }
                      break;
                //停止时不调用回调函数
                 case OS_OPT_TMR_NONE:
                      break;

                 default:
                     OSSchedUnlock(&err);
                    *p_err = OS_ERR_OPT_INVALID;
                     return (DEF_FALSE);
             }
            //退临界
             OSSchedUnlock(&err);
             success = DEF_TRUE;
             break;
        //定时已经完成or停止
        case OS_TMR_STATE_COMPLETED:                                  /* Timer has already completed the ONE-SHOT or  */
        case OS_TMR_STATE_STOPPED:                                    /* ... timer has not started yet.               */
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_TMR_STOPPED;
             success = DEF_TRUE;
             break;
        //定时器已删除
        case OS_TMR_STATE_UNUSED:                                     /* Timer was not created                        */
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_TMR_INACTIVE;
             success = DEF_FALSE;
             break;

        default:
             OSSchedUnlock(&err);
            *p_err   = OS_ERR_TMR_INVALID_STATE;
             success = DEF_FALSE;
             break;
    }
    return (success);
}

OSTmrStop

还有就是一些不常用的函数比如,定时器剩余时间获取,定时状态获取等函数从函数描述就可以知道其用法。

软件定时器设计的数据结构:

最后整个定时工作的原理就是,在系统初始化时候的TmrInit()函数将配置分频系数OSTmrUpdatecnt并创建一个任务OS_TmrTask任务等待信号量,在滴答定时器内检查内次到达分频系数减到0的时候Post信号给OS_TmrTask任务,此任务就可以继续执行检查是哪一个定时器到期此处同样使用哈希求余算法快速查找,查找到定时器到期根据是否为周期定时器进行相应的操作并调用对应函数的回调函数;在创建一个定时器并启动时系统会将这个定时器加入定时器列表里在OS_TmrTask任务里查找,对于定时器列表是到时间剩余长短排列,剩余时间越短排序越靠前,因此在检查定时器 是否到期时,如果第一个没有到期就不用检查后面的定时器是否到期,同理如果前面的定时器到期了,就还要继续检查后面的定时器是否到期。

时间: 2024-10-11 01:45:49

μC/OS-III---I笔记4---软件定时器的相关文章

μC/OS-II中使用软件定时器

在试着将μC/OS-II移植到ARM7芯片(LPC2138)上的过程中,发现使用OSTmrCreate创建的OSTmr始终都不能执行CallbackFunction,OS版本是v2.85,最后是这么解决的. 在文档<uCOS-II-RefMan.PDF>中找到了关于“OSTmrSignal()”这个函数的一段描述: OSTmrSignal() is called either by a task or an ISR to indicate that it’s time to update th

uC/OS-III-11.0-uC/OS-III软件定时器

1.软件定时器管理 uC/OS-III提供了软件定时器服务(相关代码在OS_TMR.C中).当设置OS_CFG.H中的OS_CFG_TMR_EN为1时软件定时器服务被使能. 2.uC/OS-III 定时器的分辨率决定于时基频率,也就是变量OS_CFG_TMR_TASK_RATE_HZ的值,它是以 Hz为单位的.如果时基任务的频率设置为 10Hz,所有定时器的分辨率为十分之一秒.事实上, 这是用于定时器的推荐值. 定时器用于不精确时间尺度的任务. 3.uC/OS-III提供了一些函数用于管理定时器

ucosiii浅析内核对象-软件定时器

内核对象和各种内核机制的函数接口都在os.h里声明,实现在各自的.c文件,比如os_tmr.c和os_time.c. C语言全局变量一般会默认初始化:局部变量如若不初始化,会分配垃圾数据的:建议使用时都手动初始化. 其实使用内核对象时,就类似与使用任务,只不过在创建对象之前,要先声明一个内核对象. 好了,上面闲聊了几句,今天来说说ucosiii的几个内核对象. 首先说"软件定时器",其实单纯的讲就是定时作用,这里我们要注意的就是,使用它方法和使用任务类似:那么我们就先来分析分析任务的执

基于链表的软件定时器实现(转)

软件定时器在实际应用比较重要,本文旨在实现一种便于移植,易扩展功能,效率高的软件定时器.本定时器是基于排序链表,将最近将触发的定时器置于链表头,后续新增定时器将计算出其合适位置插入. 主要数据结构及数据 typedef struct m_tm_tcb_struct { uint32_t time; //初次触发时间 uint32_t period; //周期时间,如果是只执行1次,则设为0 void *pdata; //定时器私有参数 m_timeout_handler phandler; //

软件定时器

软件定时器是 uC/OS 操作系统的一个内核对象,软件定时器是基于时钟节拍和系统管理创建的软件性定时器,理论上可以创建无限多个,但精准度肯定比硬件定时稍逊一筹.使用硬件定时器往往需要查阅芯片的相关数据手册,比较繁琐,而使用 uC/OS 的软件定时非常方便. 软件定时器启动之后是由软件定时器任务 OS_TmrTask() 统一管理,在创建软件定时器之前必须先使能软件定时器和配置软件定时器的相关参数. 软件定时器的使能位于"os_cfg.h": /* -------------------

uC/OS-III 软件定时器(三)

软件定时器是uC/OS 操作系统的一个内核对象,软件定时器是基于时钟节拍和系统管理创建的软件性定时器,理论上可以创建无限多个,操作简单,但精准度肯定比硬件定时稍逊一筹. 原理和实现过程 要用到的函数: OSTmrCreate () //创建定时器 OSTmrDel () //删除定时器 OSTmrStart () //开启定时器 OSTmrStop () //停止定时器 使用定时器前先使能,软件定时器的使能位于“os_cfg.h”. 其有关参数的配置位于“os_cpu_app.h”. OSTmr

毫秒,微妙级别软件定时器

单片机开发中,软件定时器是常用的工具.定时执行特定任务和延时功能,都可以用软件定时器实现. 常见的延时函数的实现做法有: 1. 使用空指令进行延时,通过控制空指令的执行次数,进行延时.优点:不需要占用系统外设.缺点:系统运行指定个空指令的时间不稳定,中途出现的中断处理会严重影响计时的精确性. 2.使用单片机的定时器外设,设定特定的时间产生中断,进行计时.优点:计时准确,不受其他中断影响计时.缺点:浪费单片机外设资源,并且延时处理不能嵌套调用,灵活性不够. 这里要介绍的是利用单片机内部的sysTi

Mac OS X学习笔记

命令行 软连接:$ sudo ln -s /home/user/bin/node-echo.js /usr/local/bin/node-echo 使环境变量的修改立即生效:source ~/.bash_profile hosts修改:sudo vi /etc/hosts 让node有root权限: $ sudo chown root /usr/local/bin/node $ sudo chmod +s /usr/local/bin/node 印象笔记 cmd+shift+d = date,

实现自己的软件定时器

为什么要实现软件定时器: 在芯片平台上,地址空间也是相当宝贵的,如果保留了更多的硬件定时器的话,就需要更多的地址空间,那么我们能不能作个折中方案呢?答案是肯定的,我们可以使用一个硬件定时器,来模拟实现一个软件定时器,可以满足更多的定时需求,需要注意的一点就是软件定时器精度可能会有稍微误差,因为会涉及到任务调度.锁中断等,在对定时精度要求不高的场景,可以考虑使用软件定时器.Linux内核中的timer_list精度为10ms,这里我们来实现一套精度为1ms的软件定时器(当然可以实现精度为微秒级的,

高效软件定时器的设计

软件定时器在协议栈等很多场景都有广泛的应用,有时候会有大量的定时器同时处于工作状态,它们的超时时间各异,要高效的保证每个定时器都能够较为准确的超时并执行到其回调函数并不是一件易事.本文分析嵌入式实时操作系统Nucleus的定时器方案,它巧妙的管理了一条按照相对时间来排序的双向链表,避免每次tick中断都要遍历链表检查超时和更新剩余时间,实现了一种相当高效的软件定时器. 结构体TM_TCB来表示动态创建的定时器,其定义如下 typedef struct TM_TCB_STRUCT { /*Nucl