Zstack协议栈OSAL中添加任务事件消息、发送数据详细过程

本文将以在GenericApp项目的基础上,一步一步地建立一个应用;需要实现的任务目标:1、使用Zigbee终端设备捕获串口中的字符“silverze” 。2、当Zigbee终端设备捕获到该字符串后,触发一个Zstack协议栈OSAL的任务消息 Get_Name。3、Zigbee终端设备在OSAL任务处理函数中处理Get_Name消息,并发送“Hello,silverze!”至协调器,协调器通过串口输出接收到Zigbee终端设备发送的内容。

步骤一:Zigbee终端设备捕获字符串

首先,我们要让cc2530的串口在Zstack中使用起来,这一步骤可以参照我之前写的ZStack-CC2530-2.5.1a串口使用笔记一。
Zstack串口能够正常使用起来后,我们新建两个文件Silverze.c、Silverze.h保存在..\GenericApp\source\文件夹下并添加到IAR工程App虚拟目录下。
在MT_UART.c文件中,有一个串口接收数据的回调函数:
void MT_UartProcessZToolData ( uint8 port, uint8 event ) ;为了减少对MT_UART.c文件的修改,我们将该回调函数指向到我们刚才新建立的Silverze.c文件中的函数:void Silverze_UartProcessZToolData ( uint8 port, uint8 event )

具体的函数代码

/***************************************************************************************************
 * @fn      Silverze_UartProcessZToolData
 *
 * @brief   处理UART接收到的数据
 *
 * @param   port     - UART port
 *          event    - Event that causes the callback
 *
 *
 * @return  None
 ***************************************************************************************************/
void Silverze_UartProcessZToolData ( uint8 port, uint8 event )
{
  uint8  ch;
  uint8  cnt = 0;
  uint8* str = "silverze";
  (void)event;  // Intentionally unreferenced parameter

  while (Hal_UART_RxBufLen(port))
  {
    HalUARTRead (port, &ch, 1);

    if(ch == str[cnt])
      cnt++;
    else
      cnt = 0;

    if(cnt == strlen((char*)str))
    {
      HalLedSet( HAL_LED_3, HAL_LED_MODE_TOGGLE );//加入板子上LED toggle,便于调试
      Silverze_SendGetNamdeOverMsg(); //接收到"silverze",发送OSAL任务消息
    }
  }
}

如果一切顺利,我们通过串口助手,发送包含“silverze”的字符串,就能看到开发板上(这个需要特定的开发板,根据使用的板子决定吧!)的LED3 Toggle。

步骤二:触发一个自定义的OSAL任务消息

完成步骤二的内容,后我们可以的应用已经能够通过串口捕获想要捕获的字符串了,而且我也将发送OSAL消息的函数结构给调用了。但这里还只是一个空壳,在完成该函数前;我们要先在Silverze.c文件中编写一个应用注册函数,这个函数很简单:

uint8 registTaskID;

/***************************************************************************************************
 * @fn      Silverze_RegisterTaskID
 *
 * @brief   注册该应用的任务ID
 *
 * @param   task_id  - 该应用任务的ID
 *
 * @return  None
 ***************************************************************************************************/
void  Silverze_RegisterTaskID( uint8 task_id )
{
    registTaskID = task_id;
}

该函数会在GenericApp.c文件中的void GenericApp_Init( uint8 task_id )中调用,同时在Silverze.h文件中声明函数原型:

/*
 * 注册该应用的任务ID
 */ 
extern void  Silverze_RegisterTaskID( uint8 task_id );  

我们在void GenericApp_Init( uint8 task_id )中调用上面的任务注册函数:

void GenericApp_Init( uint8 task_id )
{
  GenericApp_TaskID = task_id;
  GenericApp_NwkState = DEV_INIT;
  GenericApp_TransID = 0;

  // Device hardware initialization can be added here or in main() (Zmain.c).
  // If the hardware is application specific - add it here.
  // If the hardware is other parts of the device add it in main().

  MT_UartInit (); //初始化串口

  GenericApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
  GenericApp_DstAddr.endPoint = 0;
  GenericApp_DstAddr.addr.shortAddr = 0;

  // Fill out the endpoint description.
  GenericApp_epDesc.endPoint = GENERICAPP_ENDPOINT;
  GenericApp_epDesc.task_id = &GenericApp_TaskID;
  GenericApp_epDesc.simpleDesc
            = (SimpleDescriptionFormat_t *)&GenericApp_SimpleDesc;
  GenericApp_epDesc.latencyReq = noLatencyReqs;

  // Register the endpoint description with the AF
  afRegister( &GenericApp_epDesc );

  Silverze_RegisterTaskID( GenericApp_TaskID ); //注册自己的应用任务ID

  // Register for all key events - This app will handle all key events
  RegisterForKeys( GenericApp_TaskID );

  // Update the display
#if defined ( LCD_SUPPORTED )
  HalLcdWriteString( "GenericApp", HAL_LCD_LINE_1 );
#endif

  ZDO_RegisterForZDOMsg( GenericApp_TaskID, End_Device_Bind_rsp );
  ZDO_RegisterForZDOMsg( GenericApp_TaskID, Match_Desc_rsp );

#if defined( IAR_ARMCM3_LM )
  // Register this task with RTOS task initiator
  RTOS_RegisterApp( task_id, GENERICAPP_RTOS_MSG_EVT );
#endif
}

还有两个小步骤需要完成:
a、在Silverze.h头文件中定义一个结构:

typedef struct
{
  osal_event_hdr_t hdr;
  uint8* name;
} getName_t;  

b、在comdef.h中定义宏

#define GET_NAME                  0xE1    //串口获取到指定字符串事件

这里我们为什么要定义GET_NAME为0xE1呢?在ZComdef.h头文件中,我发现有很多系统消息被使用了,而且Zstack提示:

// OSAL System Message IDs/Events Reserved for applications (user applications)
// 0xE0 ?0xFC

所以,我们可以定义自己的应用消息范围0xE0 - 0xFC!

好了,现在准备开始编写Silverze_SendGetNamdeOverMsg( )函数的内容:

/***************************************************************************************************
 * @fn      Silverze_SendGetNamdeOverMsg
 *
 * @brief   发送从串口接收到指定数据后的消息给OSAL应用层
 *
 * @param   None
 *
 * @return  None
 ***************************************************************************************************/
static void Silverze_SendGetNamdeOverMsg( void )
{
  getName_t *msgPtr;

  msgPtr = (getName_t *)osal_msg_allocate( sizeof(getName_t) );//为带发送的消息指针申请内存空间
  if(msgPtr)
  {
    msgPtr->hdr.event = GET_NAME;
    msgPtr->name = "silverze";

    osal_msg_send( registTaskID, (uint8 *)msgPtr); //将消息发给OSAL应用层
  }
}

至此,我们已经将从串口捕获到指定字符串“silverze”的自定义应用消息发送给了GenericApp这个任务,接下来的事情就是到uint16 GenericApp_ProcessEvent( uint8 task_id, uint16 events )函数中处理该应用消息了!
怎么样快速,确定我们上面的工作已经OK了?方法很简单啦,之前我们不是弄了一个LED灯来作为我们程序运行的指示吗。现在只要将之前在void Silverze_UartProcessZToolData ( uint8 port, uint8 event )中的

 HalLedSet( HAL_LED_3, HAL_LED_MODE_TOGGLE );//加入板子上LED toggle,便于调试

注释掉,并移动到uint16 GenericApp_ProcessEvent( uint8 task_id, uint16 events )函数中,而其捕捉到串口字符串后,我们还能看到相同的实验现象。

uint16 GenericApp_ProcessEvent( uint8 task_id, uint16 events )
{
    ......
  if ( events & SYS_EVENT_MSG )
  {
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
    while ( MSGpkt )
    {
      switch ( MSGpkt->hdr.event )
      {
      case ZDO_CB_MSG:
        GenericApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
        break;
        ......   
      case GET_NAME:
        HalLedSet( HAL_LED_3, HAL_LED_MODE_TOGGLE );//加入板子上LED toggle,便于调试
        break;

      default:
        break;
      }
      ......
}

步骤三:发送数据给协调器

利用Zstack将数据发送出去,只需将终端设备、协调器建立连接后;调用

AF_DataRequest() 函数即可发送数据

根据该函数的参数,在GenericApp.c文件中定义目标地址类型参数,修改簇数组:

// This list should be filled with Application specific Cluster IDs.
const cId_t GenericApp_ClusterList[GENERICAPP_MAX_CLUSTERS] =
{
  GENERICAPP_CLUSTERID,
  SILVERZE_CLUSTERID       //增加一个簇,在发送串口捕获成功信息时使用
};

afAddrType_t Silverze_DstAddr;//定义串口捕获字符串,数据发送地址类型

在GenericApp.h 增加一个簇 #define SILVERZE_CLUSTERID 2
并将簇的最大值修改为对应大小,以及在将簇数组初始化。

#define GENERICAPP_MAX_CLUSTERS       2 //增加一个簇 SILVERZE_CLUSTERID
#define GENERICAPP_CLUSTERID          1
#define SILVERZE_CLUSTERID            2

在void GenericApp_Init( uint8 task_id )函数中,初始化Silverze_DstAddr:

  Silverze_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
  Silverze_DstAddr.endPoint = 1;
  Silverze_DstAddr.addr.shortAddr = 0;

接下来定义一个发送数据的函数,在该函数中调用AF_DataRequest()实现数据发送。

static void GenericApp_SendSilverzeMsg(  char* name )
{
  char hello[] = "Hello,"; //此处定义 char* hello = "Hello";strcat()函数会产生bug
  char* say_hello = strcat( hello, name ); //拼接字符串为"Hello,silverze";此处注意strcat的使用!!

 // printf("say_hello = %s\n", say_hello);

  if ( AF_DataRequest( &Silverze_DstAddr, &GenericApp_epDesc,
                       SILVERZE_CLUSTERID,
                       strlen(say_hello) + 1, 
                       (uint8*)say_hello, 
                       &GenericApp_TransID,
                       AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
  {
    // Successfully requested to be sent.
  }
  else
  {
    // Error occurred in request to send.
  }

}

程序编写至此;我本以为在uint16 GenericApp_ProcessEvent( uint8 task_id, uint16 events )函数中的switch-case语句中进行调用、发送即可:

      case AF_INCOMING_MSG_CMD:
        GenericApp_MessageMSGCB( MSGpkt );
        break;
      case GET_NAME:
        GenericApp_SendSilverzeMsg( ((getName_t*)MSGpkt)->name ); 
        break;

当然, GenericApp_MessageMSGCB( MSGpkt ) 函数已经修改成如下:

static void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
  switch ( pkt->clusterId )
  {
  case GENERICAPP_CLUSTERID:
    // "the" message
#if defined( LCD_SUPPORTED )
    HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" );
#elif defined( WIN32 )
    WPRINTSTR( pkt->cmd.Data );
#endif

#if debug
    printf("绑定成功后,接收到周期数据!\n");
#endif
    break;

  case SILVERZE_CLUSTERID: 
#if debug
    HalLedSet( HAL_LED_3, HAL_LED_MODE_TOGGLE );//加入板子上LED toggle,便于调试
#endif 
    printf("%s\n", pkt->cmd.Data);
    break;
  }
}

此时,调试程序;发现ZStack触发不了 AF_INCOMING_MSG_CMD事件,后面想起该GenericApp需要操作开发板上按键,才能进行终端与协调绑定,建立连接。这里为了减少硬件的差异,我就让终端启动完成后,自动发起绑定请求,在uint16 GenericApp_ProcessEvent( uint8 task_id, uint16 events )函数中添加下面代码:

if ( events & GENERICAPP_SEND_MSG_EVT )
  { 

    if(cnt == 0)
    {
      cnt++;
      dstAddr.addrMode = Addr16Bit;
      dstAddr.addr.shortAddr = 0x0000; // Coordinator
      ZDP_EndDeviceBindReq( &dstAddr, NLME_GetShortAddr(),
                            GenericApp_epDesc.endPoint,
                            GENERICAPP_PROFID,
                            GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
                            GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
                            FALSE );
    }

步骤四:协调器接收接收数据后,串口打印

完成,上面终端设备与协调器绑定后,且 AF_INCOMING_MSG_CMD事件的处理函数GenericApp_MessageMSGCB对应的SILVERZE_CLUSTERID簇id打印接收到的数据即可实现了。

你可能感兴趣的:(CC2530)