Zigbee通信机制

当子设备(终端节点或路由器)加入协调器的网络后,它们之间便可以相互通信了。Zigbee设备通信的实质是Zigbee设备端口与另一个Zigbee设备端口之间的通信。只要知道目标设备的网络地址和端口号就可以调用数据发送函数

“AF_DataRequest()” 发送数据给目标设备的端口,再由该端口绑定的任务来处理该数据。如下图所示。
无线传输与接收 - 图1
Zigbee设备提供给用户使用的端口为1~240号端口。若用户要使用某端口收发数据,则需要使用afRegister()函数在设备上注册该端口,注册完成后便可以使用该端口收发数据。

接下来我们分析一下这个端口注册函数“afStatus_t afRegister( endPointDesc_t *epDesc )”如何使用。
先看一下afRegister()函数的形参端口描述符“epDesc”,它包含了该端口的所有信息。

  1. typedef struct
  2. {
  3. uint8 endPoint; // 端口号
  4. uint8 *task_id; // 该端口绑定的任务的任务ID,发送至此端口的数据都由此任务处理
  5. SimpleDescriptionFormat_t *simpleDesc; // 简单描述符:存储该端口更多的信息
  6. afNetworkLatencyReq_t latencyReq; // 固定为noLatencyReqs
  7. } endPointDesc_t;

下面是简单描述符所包含的信息:

  1. typedefstruct
  2. {
  3. byte EndPoint; // 端口号
  4. uint16 AppProfId; // 应用规范ID
  5. uint16 AppDeviceId; // 特定规范ID的设备类型
  6. byte AppDevVer:4; // 特定规范ID的设备的版本
  7. byte Reserved:4; // AF_V1_SUPPORTusesforAppFlags:4.
  8. byte AppNumInClusters; // 输入簇ID的个数
  9. cId_t *pAppInClusterList; // 输入簇ID的列表
  10. byte AppNumOutClusters; // 输出簇ID的个数
  11. cId_t *pAppOutClusterList; // 输出簇ID的列表
  12. }SimpleDescriptionFormat_t;

————————————————————————- Gateway.c —————————————————————————

  1. #include "OSAL.h"
  2. #include "AF.h"
  3. #include "ZDApp.h"
  4. #include "ZDObject.h"
  5. #include "ZDProfile.h"
  6. #include "GenericApp.h"
  7. #include "DebugTrace.h"
  8. #if !defined( WIN32 ) || defined( ZBIT )
  9. #include "OnBoard.h"
  10. #endif
  11. #include "Gateway.h"
  12. /*********************************************************************
  13. * MACROS
  14. */
  15. /*********************************************************************
  16. * CONSTANTS
  17. */
  18. /*********************************************************************
  19. * TYPEDEFS
  20. */
  21. /*********************************************************************
  22. * GLOBAL VARIABLES
  23. */
  24. // 任务ID
  25. byte g_gateway_taskid = 0;
  26. /* 新添加代码 START */
  27. // 定义端口描述符
  28. endPointDesc_t g_gateway_epdesc = {0};
  29. // 定义简单描述符
  30. const cId_t Gateway_InClusterList[] =
  31. {
  32. TRANSMISSION_CLUSTERID
  33. };
  34. #define GATEWAY_MAX_INCLUSTERS ( sizeof( Gateway_InClusterList )/
  35. sizeof( Gateway_InClusterList[0] ))
  36. const cId_t Gateway_OutClusterList[] =
  37. {
  38. TRANSMISSION_CLUSTERID
  39. };
  40. #define GATEWAY_MAX_OUTCLUSTERS ( sizeof( Gateway_OutClusterList )/
  41. sizeof( Gateway_OutClusterList[0] ))
  42. const SimpleDescriptionFormat_t g_gateway_simpledesc =
  43. {
  44. GATEWAY_ENDPOINT, // int Endpoint;
  45. GATEWAY_PROFID, // uint16 AppProfId[2];
  46. GATEWAY_DEVICEID, // uint16 AppDeviceId[2];
  47. GATEWAY_DEVICE_VERSION, // int AppDevVer:4;
  48. GATEWAY_FLAGS, // int AppFlags:4;
  49. GATEWAY_MAX_INCLUSTERS, // byte AppNumInClusters;
  50. (cId_t *)Gateway_InClusterList, // byte *pAppInClusterList;
  51. GATEWAY_MAX_OUTCLUSTERS, // byte AppNumInClusters;
  52. (cId_t *)Gateway_OutClusterList // byte *pAppInClusterList;
  53. };
  54. /* 新添加代码 END */
  55. /*********************************************************************
  56. * GLOBAL FUNCTIONS
  57. */
  58. /*********************************************************************
  59. * LOCAL VARIABLES
  60. */
  61. /*********************************************************************
  62. * LOCAL FUNCTIONS
  63. */
  64. static void Init_IndicatorLight(void);
  65. /*********************************************************************
  66. * EXTERN VARIABLES
  67. */
  68. /*********************************************************************
  69. * EXTERN FUNCTIONS
  70. */
  71. void Gateway_Init( uint8 task_id )
  72. {
  73. g_gateway_taskid = task_id;
  74. /* 新添加代码 START */
  75. // 填充端口描述符
  76. g_gateway_epdesc.endPoint = GATEWAY_ENDPOINT;
  77. g_gateway_epdesc.task_id = &g_gateway_taskid;
  78. g_gateway_epdesc.simpleDesc = (SimpleDescriptionFormat_t *)&g_gateway_simpledesc;
  79. g_gateway_epdesc.latencyReq = noLatencyReqs;
  80. // 注册该端口
  81. afRegister(&g_gateway_epdesc);
  82. /* 新添加代码 END */
  83. // 初始化LED灯
  84. Init_IndicatorLight();
  85. // 通知g_gateway_taskid任务有LED灯闪烁事件发生
  86. osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
  87. }
  88. uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
  89. {
  90. if ( events & EVENT_FLASH_LED )
  91. {
  92. // 置反LED灯
  93. if(P1_0==1)
  94. {
  95. P1_0 = 0;
  96. }
  97. else
  98. {
  99. P1_0 = 1;
  100. }
  101. // 定时500毫秒后通知g_gateway_taskid任务有LED灯闪烁事件发生
  102. osal_start_timerEx(g_gateway_taskid, EVENT_FLASH_LED, 500);
  103. return (events ^ EVENT_FLASH_LED);
  104. }
  105. return 0;
  106. }
  107. static void Init_IndicatorLight(void)
  108. {
  109. // P1_0,LED1,低电平亮,高电平灭
  110. P1SEL &= ~(1<<0);
  111. P1DIR |= (1<<0); // IO口方向输出
  112. P1_0 = 1; // LED灯灭
  113. }

———————————————————————- Gateway.h ——————————————————————————

  1. #ifndef __GATEWAY_H
  2. #define __GATEWAY_H
  3. #ifdef __cplusplus
  4. extern "C"
  5. {
  6. #endif
  7. /*********************************************************************
  8. * MACROS
  9. */
  10. // 自定义事件
  11. #define EVENT_FLASH_LED 0x0001
  12. /* 新添加代码 START */
  13. // 简单描述符信息
  14. #define GATEWAY_ENDPOINT 8
  15. #define GATEWAY_PROFID 0x0F04
  16. #define GATEWAY_DEVICEID 0x0001
  17. #define GATEWAY_DEVICE_VERSION 0
  18. #define GATEWAY_FLAGS 0
  19. #define TRANSMISSION_CLUSTERID 0x01
  20. /* 新添加代码 END */
  21. /*********************************************************************
  22. * CONSTANTS
  23. */
  24. /*********************************************************************
  25. * TYPEDEFS
  26. */
  27. /*********************************************************************
  28. * VARIABLES
  29. */
  30. /*********************************************************************
  31. * FUNCTIONS
  32. */
  33. void Gateway_Init( uint8 task_id );
  34. uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events );
  35. #ifdef __cplusplus
  36. }
  37. #endif
  38. #endif

至此我们就已经在Zigbee设备上成功注册了端口8作为我们的数据收发端口

数据发送

上面我们已经注册了“端口8”作为我们数据的收发端口。那么接下来我们再来分析一下这个全协议栈唯一的数据发送函数“AF_DataRequest()”。

  1. afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
  2. uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
  3. 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 —————————————————————————

  1. ……
  2. // 定义周期发送数据事件
  3. #define EVENT_PERIOD_SEND_DATA 0x0002
  4. // Cluster
  5. #define TRANSMISSION_CLUSTERID 0x0001
  6. ……

—————————————————————————— Gateway.c ———————————————————————-

  1. void Gateway_Init( uint8 task_id )
  2. {
  3. ……
  4. // 通知g_gateway_taskid任务有LED灯闪烁事件发生
  5. // osal_set_event(g_gateway_taskid, EVENT_FLASH_LED); // 关掉上一篇文章的LED灯闪烁功能
  6. // 开始定期发送数据给协调器
  7. osal_set_event(g_gateway_taskid, EVENT_PERIOD_SEND_DATA);
  8. }
  9. uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
  10. {
  11. ……
  12. // 处理周期性发送数据事件
  13. if( events & EVENT_PERIOD_SEND_DATA )
  14. {
  15. uint8 data[] = "Enddevice";
  16. // 目标地址为协调器
  17. afAddrType_t dstaddr = {0};
  18. dstaddr.addrMode = (afAddrMode_t)Addr16Bit; // 单播
  19. dstaddr.addr.shortAddr = 0x0000; // 目标地址为协调器
  20. dstaddr.endPoint = GATEWAY_ENDPOINT; // 目标端口为收发数据端口8
  21. // 发送数据
  22. AF_DataRequest(&dstaddr, // 目标设备地址和端口
  23. &g_gateway_epdesc, // 发送设备的端口描述符
  24. TRANSMISSION_CLUSTERID, // 数据传输Cluster
  25. sizeof(data), // 要发送数据的大小
  26. data, // 要发送的数据
  27. &g_transid, // 此条发送命令的发送ID
  28. AF_DISCV_ROUTE, // 后面分析,默认AF_DISCV_ROUTE
  29. AF_DEFAULT_RADIUS); // 后面分析,默认AF_DEFAULT_RADIUS
  30. // 每隔1秒发送一次字符串数据“Enddevice”给协调器
  31. osal_start_timerEx(g_gateway_taskid, EVENT_PERIOD_SEND_DATA, 1000);
  32. return (events ^ EVENT_PERIOD_SEND_DATA);
  33. }
  34. ……
  35. }

数据接收

协调器设备要接收来自终端设备的数据。

协调器编程
———————————————————————- Gateway.c ——————————————————————————

  1. uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
  2. {
  3. afIncomingMSGPacket_t *MSGpkt;
  4. if ( events & SYS_EVENT_MSG )
  5. {
  6. MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( g_gateway_taskid );
  7. while ( MSGpkt )
  8. {
  9. switch ( MSGpkt->hdr.event )
  10. {
  11. // 接收无线数据事件
  12. case AF_INCOMING_MSG_CMD:
  13. // 处理无线数据函数
  14. Gateway_MessageMSGCB(MSGpkt);
  15. break;
  16. default:
  17. break;
  18. }
  19. // Release the memory
  20. osal_msg_deallocate( (uint8 *)MSGpkt );
  21. // Next
  22. MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( g_gateway_taskid );
  23. }
  24. // return unprocessed events
  25. return (events ^ SYS_EVENT_MSG);
  26. }
  27. ……
  28. }

数据处理

协调器设备接收到终端设备的无线数据后,置反一次LED灯的开关状态。

协调器编程:
—————————————————————- Gateway.h ———————————————————————————

  1. ……
  2. // Cluster
  3. #define TRANSMISSION_CLUSTERID 0x0001
  4. ……

—————————————————————————— Gateway.c ————————————————————————

  1. // 声明无线数据处理函数
  2. static void Gateway_MessageMSGCB( afIncomingMSGPacket_t *pkt );
  3. void Gateway_Init( uint8 task_id )
  4. {
  5. ……
  6. // 通知g_gateway_taskid任务有LED灯闪烁事件发生
  7. // osal_set_event(g_gateway_taskid, EVENT_FLASH_LED); // 关掉上一篇文章的LED灯闪烁功能
  8. }
  9. uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
  10. {
  11. ……
  12. if ( events & EVENT_FLASH_LED )
  13. {
  14. ……
  15. // 定时500毫秒后通知g_gateway_taskid任务有LED灯闪烁事件发生
  16. // osal_start_timerEx(g_gateway_taskid, EVENT_FLASH_LED, 500); // 关闭自动闪烁LED功能
  17. return (events ^ EVENT_FLASH_LED);
  18. }
  19. ……
  20. }
  21. static void Gateway_MessageMSGCB( afIncomingMSGPacket_t *pkt )
  22. {
  23. switch ( pkt->clusterId )
  24. {
  25. case TRANSMISSION_CLUSTERID:
  26. {
  27. // 置反LED灯开关状态
  28. osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
  29. }
  30. break;
  31. }
  32. }

分别按下终端设备和协调器设备工程的编译按钮,0错误0警告,然后我们将程序分别烧录到两个CC2530设备中,等待设备自动组网后,协调器就可以收到终端设备每秒钟发送过来的数据,然后置反LED灯开关状态,我们就可以观察协调器上的LED灯1秒钟闪烁一次了。


qrcode_for_gh_e95b474fcf08_344.jpg