在zstack中,有两种方式在OSAL的任务(task)中添加自定义的功能:事件(event)和消息(message)。
这篇主要讲讲和event有关的事,和message有关的事请移步
ZStack OSAL的事件(event)与消息(message)——part2
这里http://blog.csdn.net/ceci_prayer/article/details/9835399
一、事件
事件是驱动任务去执行某些操作的条件,当系统产生了一个事件,将这个触发传递给相应的任务后,任务才能执行一个相应的操作。
OSAL通过一个16位宽度的数组来管理事件,意味着OSAL最多可以支持16个事件,其中最高位(0x08000,SYS_EVENT_MSG)系统保留,用户可以使用的事件有15个。
事件的使用很简单:2)在需要触发事件的地方调用osal_set_event(task_id, event_flag) ,这个函数有两个参数,一个是接收事件任务的ID,另一个参数指定事件ID.
3)在相应任务的处理函数,检查该事件执行相应代码即可。
4)清除事件标识。
一个event被调用的过程:
1. main() -> osal_start_system()在ZMain.c中;
2.osal_start_system() -> XX_event在OSAL.C中。
第一步简单易懂,重要的是第二步的实现。
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
uint8 idx = 0;
osalTimeUpdate();
Hal_ProcessPoll(); // This replaces MT_SerialPoll() and osal_check_timer().
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{ /* idx 越小,任务的优先级越高 */
break; /* 较高优先级的任务总是优先处理 */
}
} while (++idx < tasksCnt);
/* 得到了待处理的具有最高优先级的任务索引号 idx */
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); /* 进入临界区---保存EA状态然后置EA = 0 */
events = tasksEvents[idx]; //处理该idx的task的event
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState); /* 退出临界区---恢复EA状态 */
events = (tasksArr[idx])( idx, events ); //调用该idx个任务的事件处理函数(函数指针指向的函数)
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState);
}
加粗的代码就是上述的第二步,可以看出,这一步并不是直接调用的,而是根据不同的参数(idx,events)来动态调用的。这里涉及到一个很重要的结构体tasksArr[]:
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
ZDApp_event_loop,
ControlWifi_ProcessEvent,
GenericApp_ProcessEvent
};
可以看出这个结构体是一个数组,但是是一个什么类型的数组呢?pTaskEventHandlerFn类型的。这个类型有代表什么的?接下来我们找到这个类型的定义看一下:
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
又见函数指针类型。所以,tasksArr[]中的每一个元素都是一个指向函数的指针。但是,这个结构体还是没有解决我们调用_ProcessEvent或者别的event的问题。
这时候,就要回到我们之前加粗的那个语句了:events = (tasksArr[idx])( idx, events )。可以看出,这个语句是调用tasksArr[]中第idx个函数,然后把返回值赋给events(events是若干个标志位,每个标志位代表一个事件的发生与否)。至于调用的是哪个函数,就跟参数(idx,events)有关了,而这个参数的选取,具体见这行代码之前的若干代码。
-----------------------------------------补充的分割线----------------------------------------
之前我们讲到了一个事件的处理函数是怎么被调用的,但是一个事件是怎么被触发的,我只是寥寥写了一些。这种事,不写下来总会忘记的,所以还是补充一下有关事件触发的那些事。
还记得events这个参数吗?events里对应的标志位决定着相应的事件处理函数被调用,只要某一个事件的标志位变成了1,处理函数就被调用。那么什么时候这个标志位会从0变成1呢?
(以下代码显示了这些标志位都被初始化为0
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
)
这是由最开始时提到的那个重要的API osal_set_event(task_id, event_flag)完成的。你想要在哪些动作完成后触发这个事件,就调用这个函数,将相应的标志位置1,那么在之后协议栈运行的过程中就会根据需要调用这个事件的处理函数了。
ps:如果是消息的话,使用osal_msg_send可以完成类似的功能。有关方面,请移步
这里http://blog.csdn.net/ceci_prayer/article/details/9835399