浅谈ZigBee消息机制

来源:互联网 发布:linux解压缩命令 编辑:程序博客网 时间:2024/06/06 13:56

本章简单的介绍一下ZigBee的消息机制,如果说的有问题还请多多指正。

通俗的说,ZigBee的消息机制就好比这有一排抽屉,当我要向某个任务或者事件发送消息时,就把该消息和对应的数据放到抽屉中,在任务轮训时,某个任务轮训按个查看抽屉中是否有自己需要的消息,有就取出并且处理,没有就结束,等待下一次轮训。

ZigBee消息链表如下图所示:(注:消息机制实现代码在osal.c中)


图1 ZigBee消息队列

值得注意的是,消息队列中每条消息都有一个系统消息头,结构体为osal_msg_hdr_t,包括三个数据类型,包括下一条消息的指针、消息长度和任务ID。消息头下有一个事件消息头,结构体为osal_event_hdr_t,该消息头包括事件的掩码和状态。使用过程中特别注意,通过系统函数获取的消息指针为事件消息头(osal_event_hdr_t)对应的地址,具体可看图1。

 

下面以串口接收到数据解包后将数据发到应用层为例,来介绍一下ZigBee的消息机制是怎么实现的。

【MT_UART.c】串口接收到数据后,首先要申请该消息对应的空间内存,申请多少要看数据有多少。

App_pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof (mtOSALSerialData_t) +MT_RPC_FRAME_HDR_SZ + App_LEN_Token );


其中 [MT_RPC_FRAME_HDR_SZ + App_LEN_Token]为串口数据长度,MT_RPC_FRAME_HDR_SZ包括数据长度、命令码0、命令码1三个数据,App_LEN_Token表示应用层数据,在此不再赘述。申请的该指针返回的为mtOSALSerialData_t,对应的图2中event的地址。

 

再看osal_msg_allocate( uint16 len )这个函数。


<span style="font-family: Arial, Helvetica, sans-serif;">uint8 * osal_msg_allocate( uint16 len )</span>
{  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 );}

该函数在申请内存时,添加了sizeof( osal_msg_hdr_t )长度,该长度及图1中消息时间头,并且赋予了该消息头的next指针,长度len和dest_id任务ID。函数返回的指针为hdr+1,从我们正常的角度看,即申请了一片内存,返回内存指针,在内存指针前加上了任务消息头。因此这就是在处理任务消息头时都要-1的原因。


图3 任务消息处理函数

 

这样就生成一条消息。但是该工作并没有完成,需要将该消息放入消息对垒并且通知对应的任务ID取出该消息。

 

通过查看串口代码,可发现当数据校验正确后,要调用该函数。

osal_msg_send( App_TaskID, (byte *)App_pMsg );

该函数代码如下:

uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr ){  if ( msg_ptr == NULL )    return ( INVALID_MSG_POINTER );  if ( destination_task >= tasksCnt )  {    osal_msg_deallocate( msg_ptr );    return ( INVALID_TASK );  }  // Check the message header  if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||       OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )  {    osal_msg_deallocate( msg_ptr );    return ( INVALID_MSG_POINTER );  }  OSAL_MSG_ID( msg_ptr ) = destination_task;  // queue message  osal_msg_enqueue( &osal_qHead, msg_ptr );  // Signal the task that a message is waiting  osal_set_event( destination_task, SYS_EVENT_MSG );  return ( SUCCESS );}

除去一些判断信息,该函数最重要的操作有三个,赋值任务ID、消息入队列和设置任务事件。

赋值任务ID不在赘述,即将 该条消息中的dest_id赋值为要通知的任务ID。

消息入队列的操作也比较简单,结合图1,通过查找链表的链尾,将该条消息的event地址赋值给链尾消息的*next即可。

设置任务事件即置位相应的任务ID和事件掩码。

 

 

将消息如队列后,对应的取消息即为该过程的逆操作。以应用层取串口数据为例。

【sampleApp.c】应用层调用函数

MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );

该函数代码如下:

uint8 *osal_msg_receive( uint8 task_id ){  osal_msg_hdr_t *listHdr;  osal_msg_hdr_t *prevHdr = NULL;  osal_msg_hdr_t *foundHdr = NULL;  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  while ( listHdr != NULL )  {    if ( (listHdr - 1)->dest_id == task_id )    {      if ( foundHdr == NULL )      {        // Save the first one        foundHdr = listHdr;      }      else      {        // Second msg found, stop looking        break;      }    }    if ( foundHdr == NULL )    {      prevHdr = listHdr;    }    listHdr = OSAL_MSG_NEXT( listHdr );  }  // Is there more than one?  if ( listHdr != NULL )  {    // Yes, Signal the task that a message is waiting    osal_set_event( task_id, SYS_EVENT_MSG );  }  else  {    // No more    osal_clear_event( task_id, SYS_EVENT_MSG );  }  // Did we find a message?  if ( foundHdr != NULL )  {    // Take out of the link list    osal_msg_extract( &osal_qHead, foundHdr, prevHdr );  }  // Release interrupts  HAL_EXIT_CRITICAL_SECTION(intState);  return ( (uint8*) foundHdr );}

该函数就不在一一翻译了,大体意思为:

查找消息链表,链表头为osal_qHead,查看链表中消息的任务消息头,如果消息头为查找的任务ID,则跳出循环,并且将该条消息 从链表中剔除,如果没有找到,返回NULL。找到该条消息后下一步就是进行处理了。

 

到此ZigBee的消息机制基本就结束了,其中还有一些消息处理的其它函数,也比较简单,就不在一一描述了,有需要的可以自行查看。


1 0