目录
2、OS_TmrLink(p_tmr, OS_OPT_LINK_DLY);
一、软件定时器创建的时候都做了些什么
#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);
}
这里就是软件定时器在启用的时候进行的判断分为(没有错误)
- 定时器正在运行当中调用start:那么此时会将定时器从这个timlist中移除之后再重新加入进来,相当于给定时器进行了一次重启
- 定时器处于停止状态调用start:那么这时候就会直接将这个软件定时器加入到一个timlist当中去。
这里很关键的就是这两个函数
- OS_TmrUnlink(p_tmr);
- 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--;
}
这里简单概述一下这里的结构
这里的话就是我们所有的软件定时器都会被一个哈希链表管理,利用定时的时间长短来算出一个哈希值作为数组下标,但是其实这个数组元素也是一个双向链表,然后找到这个软件定时器在双向链表中存储的地方,然后再把它从双向链表中移除出来,最后把软件定时器设置成停滞状态。
2、OS_TmrLink(p_tmr, OS_OPT_LINK_DLY);
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;。这最后还一步会存一个最大的存储值。