上篇我们说了协议栈运行的流程,这篇我们看下事件在协议栈中是怎样被处理的。
处理事件有一个很重要的函数,就是 events = (tasksArr[idx])( idx, events ) ,乍一看这只是一个数组,其实tasksArr是一个指向数组的指针,当然我们也可以把 tasksArr[] 看成一个数组。在这个数组中存放的是所有的任务处理函数的入口,双击tasksArr按F12进入后可以看到以下的这段程序:
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_ProcessEvent,
#endif
ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_event_loop,
#endif
zcl_event_loop,
zclSampleTemperatureSensor_event_loop //用户自定义的任务处理函数,是我们自己写应用的地方
};
从这里来调用各个层的任务处理函数,其中我们可以看到 events = (tasksArr[idx])( idx, events ) 这句中的数组 tasksArr[idx] 有一个索引号idx,这个索引号决定了调用第几个任务处理函数,比如 idx = 1,那么调用的就是 macEventLoop 这个任务处理函数。那么问题来了,这个索引号是怎么来的呢? 这段程序的下方还有一段程序:
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
macTaskInit( taskID++ );
nwk_init( taskID++ );
Hal_Init( taskID++ );
#if defined( MT_TASK )
MT_TaskInit( taskID++ );
#endif
APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_Init( taskID++ );
#endif
ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_Init( taskID++ );
#endif
zcl_Init( taskID++ );
zclSampleTemperatureSensor_Init( taskID );
}
这段程序的意义是,为每个任务都打上了一个任务号,这个任务号 taskID 和 idx 是对应的,在各层的初始化中吧每个任务处理函数的任务号保存下来。任务号的数值越小,处理的优先级越高(初始化只进行一次,所以任务号的固定不变的)。其中的这段程序
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
为 tasksEvents[] 这个数组分配了动态内存,大小等于任务的个数,并且把数组中的元素初值赋值为0。
到此,我们了解了 events = (tasksArr[idx])( idx, events ) 中的 tasksArr[] 数组和 索引号 idx 。还有一个量没有看,那就是events。 上偏中我们提到了一段程序如下:
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState);
}
这里要注意的是事件数组 tasksEvents[idx] 一共有16位,每一位用来表示一个事件,其中 0x8000 表示的是 SYS_EVENTS_CMD ,是系统消息,剩余的15位可以由我们用户定义。
从上面的一段程序中可以看到 events = tasksEvents[idx] ,也就是说 events 的值就是事件数组中的值。OK,我们知道了 events = (tasksArr[idx])( idx, events ) 中 events 的值是从 events = tasksEvents[idx]得到的,那么 tasksEvents[idx] 的值是从哪里得到的呢?
我们来看下面的几段程序:
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
/*....省略...*/
return ( osal_msg_enqueue_push( destination_task, msg_ptr, FALSE ) );
}
static uint8 osal_msg_enqueue_push( uint8 destination_task, uint8 *msg_ptr, uint8 push )
{
/*....省略...*/
// Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG );
return ( SUCCESS );
}
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
#endif /* OSAL_PORT2TIRTOS */
{
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s)
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
#ifdef USE_ICALL
ICall_signal(osal_semaphore);
#endif /* USE_ICALL */
return ( SUCCESS );
}
else
{
return ( INVALID_TASK );
}
}
进过一系列的调用之后,我们看到了这样一条语句 tasksEvents[task_id] |= event_flag 这就是对 tasksEvents[task_id]的赋值。也就是说调用 osal_msg_send() 这个函数可以对 tasksEvents[task_id] 赋值。当然我们还有其他的办法,那就是调用函数osal_start_timerEx()(最终也是调用了osal_set_event()函数,这个之后的应用中会用到)。
上述的函数 osal_msg_send( uint8 destination_task, uint8 *msg_ptr ) 有两个参数,分别为目标任务的ID,和指向消息的指针。它的功能是向一个任务发送消息(我们后面来说处理消息的程序)。此外,这个函数会触发目标任务的SYS_EVENT_MSG(系统消息任务),这也方便了对消息的处理。
下篇我们来做一个实验,具体一个用户自定义事件的调用。