• 7天快速入门Zigbee:无线传输与接收
  • 2025-05-29 11:22:56
  • 7天快速入门Zigbee:无线传输与接收

    点击左上角的“关注”,定期更新Zigbee最新资讯,总有你想要的信息!

    目录

    概述解析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 ,谢谢。

    --- End ---

    你可能还想看:

    > Zigbee进阶:功能模块 > 免费的Zigbee抓包神器!比Ubiqua还好用! 文章都看完了,随手点个赞吧~ ↓↓↓ ↓↓↓