本文转载自:http://blog.csdn.net/ceci_zhou/article/details/9787349
在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
本文转载自:http://blog.csdn.net/ceci_zhou/article/details/9835399
ZStack OSAL的事件(event)与消息(message)——part 1 (有关event的那些事)
在这里 http://blog.csdn.net/ceci_prayer/article/details/9787349
二、消息
消息可以理解为带有附加信息的事件。最典型的一类便是按键消息,它同时产生了一个哪个按键被按下了附加信息。所以在OnBoard_SendKeys这个
函数中,不仅向GenericApp发送了事件,还通过调用osal_msg_send函数向GenericApp发送了一个消息,这个消息记录了这个事件的附加信息。
一般来说,一个消息总是和一个事件对应。当协议栈接收到消息后,在ProcessEvent函数中有以下语句:
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
case ……
}
可以看出,消息是被当作系统事件接收的,接收到之后会找到消息对应的event,之后进行相应的处理。
消息的使用与事件类似,但是使用了不同的函数触发:
byte osal_msg_send( byte destination_task, byte *msg_ptr )
这个函数有两个参数分别为接收事件任务的ID,另一个是指向消息的指针。它的功能是向一个任务发送命令或数据消息,此外,这个函数也会触发目标任务的SYS_EVENT_MSG(系统消息任务)。
----------------------------以上是之前学习内容的分割线-----------------------------------
之前对于消息的分析是正确的,但过于笼统了。今天我重新梳理了一下关于消息(message)的思路,在这里记录一下。
首先我们需要明确一个概念,,msg是用来干什么的?msg和event有什么不同?
我们已经知道了,event是一个事件,当这个事件发生以后,会触发相应的事件处理函数。即event是事先定义好的,但不知道会在哪个确定时间点被触发。
而消息不同。顾名思义,消息是用来传递信息的,即有两个主体(如下图中的task1和task2),在这两个主体想要通信的时候,就会用到消息。
上面这个图是我自已总结的有关zstack中消息的用法(如果有不正确的地方希望大家指正^_^)。
step1:osal_msg_allocate
可以看出,task1想要给task2发送消息(这两个任务可能属于一个设备,也可能属于不同的设备,这一点稍后再说),于是task1就得先产生一个msg,这时候就要用到osal_msg_allocation给这个消息分配一个缓存:
uint8 * osal_msg_allocate( uint16 len)
{
osal_msg_hdr_t *hdr;
if ( len == 0 )
return ( NULL );
hdr = (osal_msg_hdr_t *) osal_mem_alloc( (short)(len + sizeof( osal_msg_hdr_t )) );
if ( hdr )
{
hdr->next = NULL;
hdr->len = len;
hdr->dest_id = TASK_NO_TASK;
return ( (uint8 *) (hdr + 1) );
}
else
return ( NULL );
}
注意加粗的代码。首先看一下这个函数的参数len,是什么呢?查手册可以知道,len是msg的长度。那osal_msg_hdr_t *hdr又是什么呢?看一下osal_msg_hdr_t这个数据结构的定义:
typedef struct
{
void *next;
uint16 len;
uint8 dest_id;
} osal_msg_hdr_t;
这个实际上是消息的头部。再看下一句:
hdr = (osal_msg_hdr_t *) osal_mem_alloc( (short)(len + sizeof( osal_msg_hdr_t )) );
这句代码是给消息分配缓存区的,而缓存区的大小是len + sizeof( osal_msg_hdr_t )),也就是消息的大小+消息头的大小。
到这里我们隐约能感觉到,一个消息应该是由两部分组成:消息头和消息的实际内容,消息头是协议栈定义好的,而消息的内容则应该是我们自己添加的。大家再关注一下osal_msg_allocate的代码,可以看出,除了分配缓存以外,它还赋给了消息头一个初始值,但是却没有对消息本身做什么处理。因为消息是要留给大家自己定义的,所以osal_msg_allocate将指向消息头hdr下一位的指针做为函数的返回值,以便大家添加自己的消息代码。
至于消息的具体定义,可以分为两种:系统消息和用户消息。系统消息什么的拜托协议栈就好啦,而用户消息就需要大家根据实际的要求动手了^0^
(在接下来的内容中,完整的消息被认为是已经定义好的,直接拿来用就可以了。)
step2:osal_msg_send
step3:osal_msg_receive