参考文章:
http://bbs.feibit.com/thread-3420-1-1.html
https://e2e.ti.com/support/wireless_connectivity/f/158/t/189014
以osal_start_timerEx(uint8 taskID, uint16 event_id, uint16 timeout_value)函数为出发点,看看时间到了,事件是怎么被置位的。
OSAL.Timers.c: osal_start_timerEx(uint8 taskID, uint16 event_id, uint16 timeout_value) { ....... newTimer = osalAddTimer( taskID, event_id, timeout_value ); ...... }
函数最核心的就是上面这条语句,猜想z-stack中应该也有一个定时器队列(类似消息队列),这个队列应该是按心跳计时?
——————————————————————————————
OSAL.Timers.c: osalAddTimer( uint8 task_id, uint16 event_flag, uint16 timeout ) { newTimer = osalFindTimer( task_id, event_flag ); if ( newTimer ) { // Timer is found - update it. newTimer->timeout = timeout; return ( newTimer ); } else { newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) ); if ( newTimer ) { newTimer->task_id = task_id; newTimer->event_flag = event_flag; newTimer->timeout = timeout; newTimer->next = (void *)NULL; newTimer->reloadTimeout = 0; if ( timerHead == NULL ) { timerHead = newTimer; } else { srchTimer = timerHead; while ( srchTimer->next ) srchTimer = srchTimer->next; srchTimer->next = newTimer; } return ( newTimer ); } else return ( (osalTimerRec_t *)NULL ); }
函数开始先寻找这个定时任务是不是已经存在了,如果存在进入if条件,更新剩余时间值,返回。如果不存在进入else,生成新的定时任务,并填充osalTimerRec_t结构体。
这里还有一个判断定时队列不是空的,如果空的就把这个新生成的定时任务设为头结点;如果队列不空,则遍历队列,找到最后一个结点,把newTimer插到队尾。
——————————————————————————————————
OSAL.Timers.c: osalFindTimer( uint8 task_id, uint16 event_flag ) { ....... while ( srchTimer ) { if ( srchTimer->event_flag == event_flag &&srchTimer->task_id == task_id ) break; srchTimer = srchTimer->next; } return ( srchTimer );}
由于采用独热码编码,事件编码类型又是uint16的,所以每个任务下可以定义最多15个事件(有一个0x8000是强制事件),“srchTimer->event_flag == event_flag &&srchTimer->task_id == task_id”这一句也能看出定时队列是以事件为节点的。
——————————————————————————————————
说了这么多,还只是说了一个定时事件是怎么插入到定时队列的。对于插入之后时间的更新还没说,时间到了怎么设置事件标志也没说。
我想,既然是定时事件,那么事件的时间肯定要倒计时的,倒计时应该出现在一个循环中,自然而然想到了osal_run_system( void )。进入找找看。
—————————————————————————————————
OSAL_Clock.c: 在osal_run_system()中有osalTimeUpdate( ) { ...... tmp = macMcuPrecisionCount(); if ( tmp != previousMacTimerTick ) { ticks320us = (tmp - previousMacTimerTick) & 0xffffffffu; //两次运行间隔 previousMacTimerTick = tmp; tmp = (ticks320us * 8) + remUsTicks; CONVERT_320US_TO_MS_ELAPSED_REMAINDER( tmp, elapsedMSec, remUsTicks ); if ( elapsedMSec ) { osalClockUpdate( elapsedMSec ); osalTimerUpdate( elapsedMSec ); } } }
tmp为计数器,函数返回的是溢出次数,每次溢出的单位是320us,就是说tmp值记录了每一次调用这个函数时候timer2计数器经过了多少次320us。
只要系统不是刚上电运行,if的判断语句就为真,因为tmp随着系统运行不断变大的(没搞懂什么时候溢出?)两值相减得出了两次运行到这里的心跳差。
把tmp赋值给previousMacTimerTick ,用来进行下一次运行到这里的比较。
接下来代码的解释参考http://bbs.feibit.com/thread-3420-1-1.html的解释,里面非常清楚。
经过CONVERT_320US_TO_MS_ELAPSED_REMAINDER这个宏定义之后,将上次运行之后经过的时间保存到elapsedMSec中(单位是ms)。
——————————————————————————————————
OSAL_Timers.c: osalTimerUpdate( uint16 updateTime ) { ...... if ( timerHead != NULL ) //如果定时队列不空 { srchTimer = timerHead; //把队列头结点赋值 prevTimer = (void *)NULL; while ( srchTimer ) { osalTimerRec_t *freeTimer = NULL; //生成一个自由定时器,用来保存剩余时间为0的节点 ...... if (srchTimer->timeout <= updateTime) //剩余时间小于已经经过的时间 { srchTimer->timeout = 0; } else //剩余时间大于经过时间的则减去经过时间 { srchTimer->timeout = srchTimer->timeout - updateTime; } if ( (srchTimer->timeout == 0) && (srchTimer->reloadTimeout) && (srchTimer->event_flag) ) { osal_set_event( srchTimer->task_id, srchTimer->event_flag ); srchTimer->timeout = srchTimer->reloadTimeout; } // 这个函数很有意思,前面被置0的任务不是在这里被osal_set_event的,因为osal_start_timerEx()里面的osalAddTimer()的函数体里面有newTimer->reloadTimeout = 0。意思就是说每个加到定时器队列的节点的reloadTimeout值都是0,那么肯定进不去这个if语句,那这个语句要来干嘛?(估计是有其他层,里面会改动这个值,但我还没看到) ```if ( srchTimer->timeout == 0 || srchTimer->event_flag == 0 ) { if ( prevTimer == NULL ) //如果删除的是头结点 timerHead = srchTimer->next; else //删除的不是头结点 prevTimer->next = srchTimer->next; freeTimer = srchTimer; //保存“定时时间到”的节点 srchTimer = srchTimer->next; //往下一个节点走,这样才能遍历完整个队列 }else //更新了之后剩余时间不为0 { prevTimer = srchTimer; srchTimer = srchTimer->next; } if ( freeTimer ) //这里才是osal_set_event()的地方 { if ( freeTimer->timeout == 0 ) { osal_set_event( freeTimer->task_id, freeTimer->event_flag ); }//为什么要加这个if呢,因为如果一轮遍历下来都没有时间为0的事件,那么开头生成的结构体就要释放,否则内存会挤满 osal_mem_free( freeTimer ); } }}
这样子的话,主循环每次进入osalTimeUpdate( )就是更新队列里面的节点的时间,实现了定时任务的倒计时。
联系客服