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,謝謝。