(十)(2) Z-Stack中OSAL定时器事件触发流程分析
我们先看一下osal_start_timerEx()函数,是怎么调用到最后的osal_set_event()函数,触发事件处理的。下面是osal_start_timerEx()函数的源代码,从中间我们并没有看到有关osal_set_events()函数的相关信息。当然这个函数中没有直接的调用该函数,那osal_set_events()函数,是怎么和我们的osal_start_timerEx()函数联系起来的呢?我们应该先从系统中的主循环开始查找其中的奥妙。 byte osal_start_timerEx( byte taskID, UINT16 event_id, UINT16 timeout_value ) { halIntState_t intState;
osalTimerRec_t *newTimer;
HAL_ENTER_CRITICAL_SECTION( intState );
// Hold off interrupts. // Add timer newTimer = osalAddTimer( taskID, event_id, timeout_value );
if ( newTimer ) { #ifdef POWER_SAVING // Update timer registers osal_retune_timers();
(void)timerActive;
#endif // Does the timer need to be started? if ( timerActive == FALSE ) { osal_timer_activate( TRUE );
} } 先在osal_start_system( void )函数开始时调用Hal_ProcessPoll();
因为Hal_ProcessPoll()函数是在一个死循环中,所以每过一定的时间就会执行到。在Z-Stack OSAL中这个时种节奏定义是1ms,由8bits HW_TIMER4来控制,这些都可以有程序员进行修改,我们先看一下Hal_ProcessPoll()函数。 void Hal_ProcessPoll () { /* Timer Poll检测定时器中断溢出标志*/ HalTimerTick();
/* UART Poll */ #if (defined HAL_UART) && (HAL_UART == TRUE) HalUARTPoll();
#endif } 在这是就会有一个疑问是在什么时候溢出呢?我们知道每个系统都会有一个节拍,也就是系统的时钟,在OSAL中也不例外,这个时钟就是由Timer4提供的计时的。 在void InitBoard(byte level)函数中调用下面这个函数,进行了配置 HalTimerConfig (OSAL_TIMER,// 8bit timer2 HAL_TIMER_MODE_CTC,// Clear Timer on Compare HAL_TIMER_CHANNEL_SINGLE,// Channel 1 - default HAL_TIMER_CH_MODE_OUTPUT_COMPARE,// Output Compare mode OnboardTimerIntEnable,// Use interrupt Onboard_TimerCallBack);
// Channel Mode OSAL的Timer定义好之后,就要启动Timer,在网络层初始化函数nwk_init(taskID++)执行完后,Timer启动了,也就是说在网络层的初始化函数中启动了Timer,网络层的代码是不开源的,所以也就无法看到其真正的启动代码。 每当1ms心跳来临时,Timer4的中断标志置位,这样在OSAL的死循环中,通过一开始的Hal_ProcessPoll()函数检测到这中断标志位,在Hal_ProcessPoll ()函数中调用了HalTimerTick();
函数,这个函数是专门用来检测是否有硬件定时器溢出的。如果定时器有溢出的话,就要调用halProcessTimerX ();
(X表示 1 3 4).这里假设是HalProcessTimer4(),在HalProcessTimer4()中,通过下面的一句话,调用了函数 halTimerSendCallBack (HAL_TIMER_2, HAL_TIMER_CHANNEL_B, HAL_TIMER_CH_MODE_OUTPUT_COMPARE);
在halTimerSendCallBack中调用了真正的回调函数, if (halTimerRecord[hwtimerid].callBackFunc) (halTimerRecord[hwtimerid].callBackFunc) (timerId, channel, channelMode);
这个回调函数通过 uint8 HalTimerConfig (uint8 timerId, uint8 opMode, uint8 channel, uint8 channelMode,bool intEnable, halTimerCBack_t cBack) 也就是前面的那个函数。
进行的配置。在InitBoard()函数中调用了函数HalTimerConfig()。 也就是调用了Onboard_TimerCallBack()函数,在这个函数中调用了osal_update_timers();
函数,这个函数又调用了osalTimerUpdate( tmr_decr_time );
这个函数中又调用了osal_set_events()函数。 osalTimerUpdate( tmr_decr_time )函数会去轮询Timer事件链表,Timer事件链表是下面这样一个数据结构,next指向下一个Timer事件,timerout值表明本Timer事件还需要timerout个心跳才需要被处理,因为些处的心跳是1ms,所以也就是说还需要timerout个ms才处理。也就是检测timeout是否小于1ms,如果小于1ms,则发出event_flag这个消息到消息队列,这个消息隶属于task_id这个任务,如果不大于1ms,说明该Timer事件还不到处理的时间,则Timeout= Timeout-1,然后继续等待下一次心跳,Timer事件链表的维护是通过osal_start_timerEx()这个函数来实现的。这样就把前面的osalTimerUpdate( tmr_decr_time )函数和我们应用程序中经常调用的osal_start_timerEx()函数联系在了一起, typedef struct
{
void *next;
UINT16 timeout;
UINT16 event_flag;
byte task_id;
} osalTimerRec_t;
如果到达定时器的定时值,也就是说Timeout< 1ms后,就会发送一个event_flag这个消息给消息队列,这样在主循环中就可以检测到这个消息,并且检测到这个消息是属于那个任务的,然后,调用相应的消息处理函数。
推荐阅读
- 热闹中的孤独
- Shell-Bash变量与运算符
- 三十年后的广场舞大爷
- JS中的各种宽高度定义及其应用
- 2021-02-17|2021-02-17 小儿按摩膻中穴-舒缓咳嗽
- 深入理解Go之generate
- 异地恋中,逐渐适应一个人到底意味着什么()
- 一百二十三夜,请嫁给我
- 我眼中的佛系经纪人
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售