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

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

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还好用! 文章都看完了,随手点个赞吧~ ↓↓↓ ↓↓↓

相关阅读

LOLkda多少算正常水平
BET3365手机下载

LOLkda多少算正常水平

⌚ 09-11 👁️ 7148
永泰绿色生态园到底是怎么样的一个地方?
BET3365手机下载

永泰绿色生态园到底是怎么样的一个地方?

⌚ 09-16 👁️ 2377
QQ音乐音响力11级要听多久
best365体育官网平台

QQ音乐音响力11级要听多久

⌚ 09-23 👁️ 6717