7天快速入门Zigbee:无线传输与接收

7天快速入门Zigbee:无线传输与接收



1. 概述

  这篇文章主要想让大家了解Zigbee的无线传输机制。了解Z-Stack协议栈中如何发送数据,如何接受数据和处理数据。

2. 解析Zigbee通信机制

  当子设备(终端节点或路由器)加入协调器的网络后,它们之间便可以相互通信了。Zigbee设备通信的实质是Zigbee设备端口与另一个Zigbee设备端口之间的通信。只要知道目标设备的网络地址和端口号就可以调用数据发送函数
“AF_DataRequest()” 发送数据给目标设备的端口,再由该端口绑定的任务来处理该数据。如下图所示。
这里写图片描述
  Zigbee设备提供给用户使用的端口为1~240号端口。若用户要使用某端口收发数据,则需要使用afRegister()函数在设备上注册该端口,注册完成后便可以使用该端口收发数据。
接下来我们分析一下这个端口注册函数“afStatus_t afRegister( endPointDesc_t *epDesc )”如何使用。
先看一下afRegister()函数的形参端口描述符“epDesc”,它包含了该端口的所有信息。

typedef struct
{
  uint8 endPoint;       // 端口号
  uint8 *task_id;       // 该端口绑定的任务的任务ID,发送至此端口的数据都由此任务处理
  SimpleDescriptionFormat_t *simpleDesc;    // 简单描述符:存储该端口更多的信息
  afNetworkLatencyReq_t latencyReq;         // 固定为noLatencyReqs
} endPointDesc_t;

下面是简单描述符所包含的信息:

typedefstruct 
{ 
  byte EndPoint;                // 端口号 
  uint16 AppProfId;             // 应用规范ID 
  uint16 AppDeviceId;           // 特定规范ID的设备类型 
  byte AppDevVer:4;             // 特定规范ID的设备的版本 
  byte Reserved:4;              // AF_V1_SUPPORTusesforAppFlags:4. 
  byte AppNumInClusters;        // 输入簇ID的个数 
  cId_t *pAppInClusterList;     // 输入簇ID的列表 
  byte AppNumOutClusters;       // 输出簇ID的个数 
  cId_t *pAppOutClusterList;    // 输出簇ID的列表 
}SimpleDescriptionFormat_t;

  我们首先在我们自己的“任务初始化函数”中注册一个端口用来通信,应用层代码是基于我们上一篇文章《7天快速入门Zigbee:如何在协议栈中从零建立自己的任务》写的代码。
———————————————————————– Gateway.c ———————————————————————–

#include "OSAL.h"
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDProfile.h"

#include "GenericApp.h"
#include "DebugTrace.h"

#if !defined( WIN32 ) || defined( ZBIT )
  #include "OnBoard.h"
#endif

#include "Gateway.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// 任务ID
byte g_gateway_taskid = 0;

/* 新添加代码 START */
// 定义端口描述符
endPointDesc_t g_gateway_epdesc = {0};
// 定义简单描述符
const cId_t Gateway_InClusterList[] =
{
  TRANSMISSION_CLUSTERID
};
#define GATEWAY_MAX_INCLUSTERS ( sizeof( Gateway_InClusterList )/
                                    sizeof( Gateway_InClusterList[0] ))

const cId_t Gateway_OutClusterList[] =
{
  TRANSMISSION_CLUSTERID
};
#define GATEWAY_MAX_OUTCLUSTERS ( sizeof( Gateway_OutClusterList )/
                                    sizeof( Gateway_OutClusterList[0] ))

const SimpleDescriptionFormat_t g_gateway_simpledesc =
{
  GATEWAY_ENDPOINT,                 //  int Endpoint;
  GATEWAY_PROFID,                   //  uint16 AppProfId[2];
  GATEWAY_DEVICEID,                 //  uint16 AppDeviceId[2];
  GATEWAY_DEVICE_VERSION,           //  int   AppDevVer:4;
  GATEWAY_FLAGS,                    //  int   AppFlags:4;
  GATEWAY_MAX_INCLUSTERS,           //  byte  AppNumInClusters;
  (cId_t *)Gateway_InClusterList,   //  byte *pAppInClusterList;
  GATEWAY_MAX_OUTCLUSTERS,          //  byte  AppNumInClusters;
  (cId_t *)Gateway_OutClusterList   //  byte *pAppInClusterList;
};
/* 新添加代码 END */
/*********************************************************************
 * GLOBAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void Init_IndicatorLight(void);
/*********************************************************************
 * EXTERN VARIABLES
 */

/*********************************************************************
 * EXTERN FUNCTIONS
 */


void Gateway_Init( uint8 task_id )
{
  g_gateway_taskid = task_id;

/* 新添加代码 START */
  // 填充端口描述符
  g_gateway_epdesc.endPoint = GATEWAY_ENDPOINT;
  g_gateway_epdesc.task_id = &g_gateway_taskid;
  g_gateway_epdesc.simpleDesc = (SimpleDescriptionFormat_t *)&g_gateway_simpledesc;
  g_gateway_epdesc.latencyReq = noLatencyReqs;
  // 注册该端口
  afRegister(&g_gateway_epdesc);
/* 新添加代码 END */

  // 初始化LED灯
  Init_IndicatorLight();
  // 通知g_gateway_taskid任务有LED灯闪烁事件发生
  osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
}

uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
  if ( events & EVENT_FLASH_LED )
  {
    // 置反LED灯
    if(P1_0==1)
    {
      P1_0 = 0;
    }
    else
    {
      P1_0 = 1;
    }
    // 定时500毫秒后通知g_gateway_taskid任务有LED灯闪烁事件发生
    osal_start_timerEx(g_gateway_taskid, EVENT_FLASH_LED, 500);
    return (events ^ EVENT_FLASH_LED);
  }

  return 0;
}

static void Init_IndicatorLight(void)
{
  // P1_0,LED1,低电平亮,高电平灭
  P1SEL &= ~(1<<0);
  P1DIR |= (1<<0);      // IO口方向输出
  P1_0 = 1;             // LED灯灭
}

———————————————————————– Gateway.h ———————————————————————–

#ifndef __GATEWAY_H
#define __GATEWAY_H

#ifdef __cplusplus
extern "C"
{
#endif

/*********************************************************************
 * MACROS
 */
// 自定义事件
#define EVENT_FLASH_LED 0x0001

/* 新添加代码 START */
// 简单描述符信息
#define GATEWAY_ENDPOINT           8
#define GATEWAY_PROFID             0x0F04
#define GATEWAY_DEVICEID           0x0001
#define GATEWAY_DEVICE_VERSION     0
#define GATEWAY_FLAGS              0
#define TRANSMISSION_CLUSTERID     0x01
/* 新添加代码 END */

/*********************************************************************
 * CONSTANTS
 */

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 *  VARIABLES
 */

/*********************************************************************
 *  FUNCTIONS
 */
void Gateway_Init( uint8 task_id );
uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events );

#ifdef __cplusplus
}
#endif
#endif

  至此我们就已经在Zigbee设备上成功注册了端口8作为我们的数据收发端口,端口绑定的任务是我们上一篇文章建立的任务。

3. 数据发送

  上面我们已经注册了端口8作为我们数据的收发端口。那么接下来我们再来分析一下这个全协议栈唯一的数据发送函数“AF_DataRequest()”

afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP, uint16 cID, 
                            uint16 len, uint8 *buf, uint8 *transID, uint8 options, uint8 radius )

    dstAddr:填充目标设备的网络地址和目标端口号。
    srcEP:本身发送端口的端口描述符
    cID:ClusterID,这个我们以后再分析
    len:要发送数据的长度
    buf:要发送的数据
    transID:此条发送命令的发送ID
    options:后面分析,默认AF_DISCV_ROUTE
    radius:后面分析,默认AF_DEFAULT_RADIUS
  我们的终端设备可以利用这个函数发送数据给协调器,协调器的网络地址已知固定为0x0000,上面已注册端口8为数据收发端口。
  为了方便我们以后的编程,从现在开始我们将3种设备类型分为3个协议栈分开编程。
  整体的编程思路为终端节点每隔1秒发送一次字符串数据“Enddevice”给协调器,协调器收到数据后置反LED灯开关状态。

终端节点编程:

———————————————————————– Gateway.h ———————————————————————–

……
// 定义周期发送数据事件
#define EVENT_PERIOD_SEND_DATA      0x0002
// Cluster
#define TRANSMISSION_CLUSTERID      0x0001
……

———————————————————————– Gateway.c ———————————————————————–

void Gateway_Init( uint8 task_id )
{   
    ……
  // 通知g_gateway_taskid任务有LED灯闪烁事件发生
  //  osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);    // 关掉上一篇文章的LED灯闪烁功能
  // 开始定期发送数据给协调器
  osal_set_event(g_gateway_taskid, EVENT_PERIOD_SEND_DATA);
}

uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
  ……
  // 处理周期性发送数据事件
  if( events & EVENT_PERIOD_SEND_DATA )
  {
    uint8 data[] = "Enddevice";
    // 目标地址为协调器
    afAddrType_t dstaddr = {0};
    dstaddr.addrMode = (afAddrMode_t)Addr16Bit; // 单播
    dstaddr.addr.shortAddr = 0x0000;            // 目标地址为协调器
    dstaddr.endPoint = GATEWAY_ENDPOINT;        // 目标端口为收发数据端口8
    // 发送数据
    AF_DataRequest(&dstaddr,                    // 目标设备地址和端口
                   &g_gateway_epdesc,           // 发送设备的端口描述符
                   TRANSMISSION_CLUSTERID,      // 数据传输Cluster
                   sizeof(data),               // 要发送数据的大小
                   data,                        // 要发送的数据
                   &g_transid,                  // 此条发送命令的发送ID
                   AF_DISCV_ROUTE,              // 后面分析,默认AF_DISCV_ROUTE
                   AF_DEFAULT_RADIUS);          // 后面分析,默认AF_DEFAULT_RADIUS

    // 每隔1秒发送一次字符串数据“Enddevice”给协调器
    osal_start_timerEx(g_gateway_taskid, EVENT_PERIOD_SEND_DATA, 1000);

    return (events ^ EVENT_PERIOD_SEND_DATA);
  }
  ……
}

4. 数据接收

  协调器设备要接收来自终端设备的数据。

协调器编程

———————————————————————– Gateway.c ———————————————————————–

uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
  afIncomingMSGPacket_t *MSGpkt;

  if ( events & SYS_EVENT_MSG )
  {
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( g_gateway_taskid );
    while ( MSGpkt )
    {
      switch ( MSGpkt->hdr.event )
      {
        // 接收无线数据事件
        case AF_INCOMING_MSG_CMD:
          // 处理无线数据函数
          Gateway_MessageMSGCB(MSGpkt);
          break;

        default:
          break;
      }
      // Release the memory
      osal_msg_deallocate( (uint8 *)MSGpkt );
      // Next
      MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( g_gateway_taskid );
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }
  ……
}

5. 数据处理

  协调器设备接收到终端设备的无线数据后,置反一次LED灯的开关状态。

协调器编程:

———————————————————————– Gateway.h ———————————————————————–

……
// Cluster
#define TRANSMISSION_CLUSTERID      0x0001
……

———————————————————————– Gateway.c ———————————————————————–

// 声明无线数据处理函数
static void Gateway_MessageMSGCB( afIncomingMSGPacket_t *pkt );

void Gateway_Init( uint8 task_id )
{   
  ……
  // 通知g_gateway_taskid任务有LED灯闪烁事件发生
//  osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);  // 关掉上一篇文章的LED灯闪烁功能
}

uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
  ……
  if ( events & EVENT_FLASH_LED )
  {
    ……
    // 定时500毫秒后通知g_gateway_taskid任务有LED灯闪烁事件发生
//    osal_start_timerEx(g_gateway_taskid, EVENT_FLASH_LED, 500);   // 关闭自动闪烁LED功能
    return (events ^ EVENT_FLASH_LED);
  }
  ……
}

static void Gateway_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
  switch ( pkt->clusterId )
  {
    case TRANSMISSION_CLUSTERID:
      {
        // 置反LED灯开关状态
        osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
      }
      break;
  }
}

  分别按下终端设备和协调器设备工程的编译按钮,0错误0警告,然后我们将程序分别烧录到两个CC2530设备中,等待设备自动组网后,协调器就可以收到终端设备每秒钟发送过来的数据,然后置反LED灯开关状态,我们就可以观察协调器上的LED灯1秒钟闪烁一次了。
  软件源码的下载地址在下面的评论区有给出。
大家的支持就是我分享技术的动力,希望大家需转载时能附上原作者的博客:https://blog.csdn.net/u012993936,谢谢。

猜你喜欢

转载自blog.csdn.net/u012993936/article/details/81191925