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; // 应用规范IDuint16 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;
————————————————————————- 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*/// 任务IDbyte 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 __cplusplusextern "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作为我们的数据收发端口
数据发送
上面我们已经注册了“端口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, // 数据传输Clustersizeof(data), // 要发送数据的大小data, // 要发送的数据&g_transid, // 此条发送命令的发送IDAF_DISCV_ROUTE, // 后面分析,默认AF_DISCV_ROUTEAF_DEFAULT_RADIUS); // 后面分析,默认AF_DEFAULT_RADIUS// 每隔1秒发送一次字符串数据“Enddevice”给协调器osal_start_timerEx(g_gateway_taskid, EVENT_PERIOD_SEND_DATA, 1000);return (events ^ EVENT_PERIOD_SEND_DATA);}……}
数据接收
协调器设备要接收来自终端设备的数据。
协调器编程
———————————————————————- 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 memoryosal_msg_deallocate( (uint8 *)MSGpkt );// NextMSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( g_gateway_taskid );}// return unprocessed eventsreturn (events ^ SYS_EVENT_MSG);}……}
数据处理
协调器设备接收到终端设备的无线数据后,置反一次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秒钟闪烁一次了。

