微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 无线和射频 > TI Zigbee设计交流 > Z-Stack协议中事件和消息分析

Z-Stack协议中事件和消息分析

时间:10-02 整理:3721RD 点击:
在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);这条语句来进行解析,以下不在叙述。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top