Z-STACK事件与消息分析

xiaoxiao2021-02-28  64

在OSAL中每个任务都有一个任务初始化函数和任务的事件处理回调函数,而每一层中都是一个任务在处理,而每一层中也都对应着一个事件的处理函数,具体这个OSAL中支持多少个任务?好像程序中也没有定义。在uC/OS中支持64个任务。例如SampleApp_Init(taskID) SampleApp_ProcessEvent(byte task_id,UNINT16 events),在Z-Stack1.4.3-1.2.1中可以通过向 const pTaskEventHandlerFn tasksArr[] = { macEventLoop, nwk_event_loop, Hal_ProcessEvent, #if defined( MT_TASK ) MT_ProcessEvent, #endif   APS_event_loop,   ZDApp_event_loop,   SampleApp_ProcessEvent };    数组中添加一个任务的事件处理函数,和在void osalInitTasks( void )函数中添加一个SampleApp_Init( taskID );初始化函数来添加一个任务,这在前面的文章中也有具体讲到,我们知道在一个任务中可以有16个事件,其中SYS_EVENT_MSG是系统的事件,是协议栈中定义好的,也是一个强制事件,在它的下面还有子集,也就是我们消息,那么一个事件可以有256个消息,从0x00—0xFF,我们在Sample_App中看到的,KEY_CHANGE应该是属于SYS_EVENT_MSG事件下的消息,其实这样说不是很准确,它只是包括在传递的消息中,表现的形式还是一个事件,但是它不属于一个任务中16个事件之一,本质上还是SYS_EVENT_MSG下的子事件。在ZcomDef.h文件中这样定义了KEY_CHANGE #define KEY_CHANGE                0xC0    // Key Events osal.h文件中,定义了下面两个数据结构 typedef struct {   void   *next;   uint16 len;   byte   dest_id; } osal_msg_hdr_t; typedef struct {   uint8  event;   uint8  status; } osal_event_hdr_t;    第一个是关于消息的数据结构,另一个是关于事件的数据结构。接收消息和发送消息主要通过下面两个函数实现。    函数osal_msg_send( byte destination_task, byte *msg_ptr )task_id的任务发送消息,在这里消息放进了消息队列中,然后调用了  osal_set_event( destination_task, SYS_EVENT_MSG );通知系统有任务的事件没有处理,这里我们可以看到传递的事件是SYS_EVENT_MSG,那是不是意味着这个  osal_msg_send( byte destination_task, byte *msg_ptr )函数触发事件时,只能是SYS_EVENT_MSG类型的,也就是系统的,比如我们自己定义的SAMPLEAPP_SEND_PERIOCDIC_MSG_EVT事件,必须要用osal_start_timerEx()函数来触发,从止前看到有例子程序中,都是这种处理行为。   byte osal_msg_send( byte destination_task, byte *msg_ptr ) {   ................................   OSAL_MSG_ID( msg_ptr ) = destination_task;     // queue message当一个新消息封装好了以后,就要加入消息队列 osal_qHead是消息队列的头指针   osal_msg_enqueue( &osal_qHead, msg_ptr );     // Signal the task that a message is waiting   osal_set_event( destination_task, SYS_EVENT_MSG );     return ( ZSUCCESS ); }    其中#define OSAL_MSG_ID(msg_ptr)      ((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id 这里我们看到了有一个减一的操作,其中osal_msg_hdr_t 就是前面提到的那个消息的数据结构。其实,消息结构:消息头结构+消息数据结构(Key),在* osal_msg_allocate( uint16 len )函数中,我们可以看到,分配的内存空间实际上是消息头+数据其长度。但在hdr并没有指向消息头,而是了指向了消息数据。而(osal_msg_hdr_t *) (msg_ptr)这条语句的含义是将该指针强制转换为消息头结构的指针,这样的话,-1后指针将向上移动消息头结构大小的偏移量,也就是恰好指向消息头结构处,而且因为是强制为消息头结构指针,自然就找到消息头中的dest_id成员,并将destination_task赋值给它。 byte * 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;   #if defined( OSAL_TOTAL_MEM )     osal_msg_cnt++; #endif     return ( (byte *) (hdr + 1) );   }   else     return ( NULL ); } 这是消息的接收函数, byte *osal_msg_receive( byte task_id ) {   osal_msg_hdr_t *listHdr;   osal_msg_hdr_t *prevHdr=0;   halIntState_t   intState;     // Hold off interrupts   HAL_ENTER_CRITICAL_SECTION(intState);     // Point to the top of the queue   listHdr = osal_qHead;     // Look through the queue for a message that belongs to the asking task 以任务id号遍历消息队列,如果消息队列不为空的话   while ( listHdr != NULL )   {     if ( (listHdr - 1)->dest_id == task_id ) //这里的减一操作,还是和前面的分析相一致     {       break;     }     prevHdr = listHdr;     listHdr = OSAL_MSG_NEXT( listHdr );   }     // Did we find a message?   if ( listHdr == NULL )   {     // Release interrupts     HAL_EXIT_CRITICAL_SECTION(intState);     return NULL;   }     // Take out of the link list 把消息从消息队列中去除   osal_msg_extract( &osal_qHead, listHdr, prevHdr );     // Release interrupts   HAL_EXIT_CRITICAL_SECTION(intState);     return ( (byte*) listHdr ); //如果找到相对应的taskid的消息,返回其指针 }    在例子中有MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );这个函数是接收消息的数据,根据接收到的消息判断其中消息包括的SYS_EVENT_MSG子事件。 补充:    在这里有从网上找到了几个关于消息和事件的比喻,感觉还是挺准确,现在摘抄一下。 1)消息与事件的联系。事件是驱动任务去执行某些操作的条件,当系统产生了一个事件,将这个传递给相应的任务后,任务才能执行一个相应的操作。但是某些事件在 它发生的同时,又伴随着一些附加信息的产生。任务的事件处理函数在处理这个事件的时候,还需要参考其附加信息。最典型的一类便是按键消息,它同时产生了一 个哪个按键被按下了附加信息。所以在OnBoard_SendKeys这个函数中,不仅向GenericApp发送了事件,还通过调用 osal_msg_send函数向GenericApp发送了一个消息,这个消息记录了这个事件的附加信息。在 GenericApp_ProcessEvent中,通过    {         MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );    }   获取了这样一个消息,然后再进一步处理。   OSAL在后台维护了一个消息队列,每一个消息都会被放到这个消息队列中去,当任务接收到事件以后,从消息队列中获取属于自己的消息,然后进行处理。 --------------------------------------------------来自于outman的深入浅出OSAL多任务的分配 2)事件数组:消息操作完成后,还需要向系统发个通知,也就是说通知系统某某任务有消息来了,你去处理一下。事件数组和任务是一一对应的,就是说每个任务都对 应事件数组中的一个单元。对于Key来说,就是往事件数组中对应与Hal任务的单元中放入SYS_EVENT_MSG    可以这样理解:一个小区100户,每户一个邮箱,那么小区就有100个任务,100个邮箱构成事件数组。消息是包裹,事件就是包裹通知单,包裹通知单送达 某户的邮箱,这户人家查看邮箱,发现有包裹通知单,就去领出包裹,领出包裹后拆开其实就是解析消息后再决定是吃掉它,用掉它,回覆它还是丢掉它等等。    消息的解析:正如上面包裹的例子一样,收到包裹后你一定要打开包裹看看是什么东西再决定下步的行动,反应到消息上,就是消息的解析,实际上是封装的反过程。l     还是以Key为例:Hal任务的事件处理函数是Hal_ProcessEvent(),在这个函数中收到SYS_EVENT_MSG后会调用osal_msg_receive(Hal_TaskID);这条语句来进行解析,以下不在叙述。
转载请注明原文地址: https://www.6miu.com/read-41908.html

最新回复(0)