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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章