【Ucos-III源码分析】——软件定时器

目录

一、软件定时器创建的时候都做了些什么

二、软件定时器在调用启用定时器的时候做了啥

1、OS_TmrUnlink(p_tmr);  


一、软件定时器创建的时候都做了些什么

#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;
    }

第一步还是照旧,根据你传递进来的参数和设置的宏,来判断你的参数是否合理,如果不合理直接返回。

CPU_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             */

    CPU_CRITICAL_EXIT();
    *p_err = OS_ERR_NONE;

第二步,禁用中断的前提下去给你创建的这个软件定时器来赋值,然后给一个变量去自增,在开启中断。这样基本就完成了一个软件定时器。这里创建好的一个软件定时器是一个停止状态。需要调用start去启用定时器才行。


二、软件定时器在调用启用定时器的时候做了啥

#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;
             return (DEF_TRUE);

        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;
             return (DEF_TRUE);

        case OS_TMR_STATE_UNUSED:                           /* Timer not created                                      */
             OSSchedUnlock(&err);
             *p_err = OS_ERR_TMR_INACTIVE;
             return (DEF_FALSE);

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

这里就是软件定时器在启用的时候进行的判断分为(没有错误)

  1. 定时器正在运行当中调用start:那么此时会将定时器从这个timlist中移除之后再重新加入进来,相当于给定时器进行了一次重启
  2. 定时器处于停止状态调用start:那么这时候就会直接将这个软件定时器加入到一个timlist当中去。

这里很关键的就是这两个函数

  1.  OS_TmrUnlink(p_tmr);   
  2. OS_TmrLink(p_tmr, OS_OPT_LINK_DLY); 

1、OS_TmrUnlink(p_tmr);  

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_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;
        }
    }
    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++;
    }
    if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) {     /* Keep track of maximum number of entries in each spoke  */
        p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
    }

简单来说你创建的软件定时器,如果需要开始使用那么就会调用这个函数,这个函数主体功能就是把你创建的软件定时器,去找一个合适的地方去插入一个双向链表中,有很多个双向链表都被一个数组存着,你需要利用哈希算法来算出一个数组下标,那么这个双向就是你需要插入的双向链表,具体你要插入到双向链表的头还是尾还是中间,这就要看你的定时时间的长短来算,不断地比较你和某一个节点的定时时间长短,然后找到一个最合适的地方去插入,最后再把你定时器的状态设置程p_tmr->State   = OS_TMR_STATE_STOPPED;。这最后还一步会存一个最大的存储值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值