ZigBee协议栈探究—-(二)数据的接收与发送
1.回顾
上一章讲解了ZigBee协议栈中函数的运行走向,了解了ZigBee的任务最终在ProgressEvent
这个函数中执行,那么在协议栈中这个函数在不改动的情况下究竟执行了什么?我们又应该如何改动它呢?我们又如何进行数据的发送与接收呢?
2.从ProcessEvent函数开始讨论数据接收处理
首先来看看Smart_home_ProcessEvent()
函数究竟做了什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
UINT16 Smart_home_ProcessEvent( uint8 task_id, UINT16 events ) { (void)task_id; if ( events & SYS_EVENT_MSG ) { afIncomingMSGPacket_t *MSGpkt;
while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( Smart_home_TaskID )) ) { switch ( MSGpkt->hdr.event ) { case ZDO_CB_MSG: Smart_home_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: Smart_home_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break;
case AF_INCOMING_MSG_CMD: Smart_home_ProcessMSGCmd( MSGpkt ); break;
default: break; }
osal_msg_deallocate( (uint8 *)MSGpkt ); }
return ( events ^ SYS_EVENT_MSG ); }
if ( events & Smart_home_SEND_EVT ) { Smart_home_Send(); return ( events ^ Smart_home_SEND_EVT ); }
if ( events & Smart_home_RESP_EVT ) { Smart_home_Resp(); return ( events ^ Smart_home_RESP_EVT ); }
return ( 0 ); }
|
在这个函数中最引人瞩目的应该就是MSGpkt
以及一大堆的宏调用了。让我们来逐一分析一下。首先就是MSGpkt
,很明显这个函数的类型是afIncomingMSGPacket_t
的指针,我们来看一下这个结构体的构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| typedef struct { osal_event_hdr_t hdr; uint16 groupId; uint16 clusterId; afAddrType_t srcAddr;
uint16 macDestAddr; uint8 endPoint; uint8 wasBroadcast; uint8 LinkQuality; uint8 correlation; int8 rssi; uint8 SecurityUse; uint32 timestamp; uint8 nwkSeqNum; afMSGCommandFormat_t cmd; } afIncomingMSGPacket_t;
|
通过阅读注释,很明显能明白这是数据帧报文段的定义。也就是说MSGpkt
是一个初始化的报文段指针。那么这个报文段是由谁给出来的呢?再来看下面这一句
1
| while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( Smart_home_TaskID ))
|
对于osal_msg_receive( Smart_home_TaskID )
这一部分,这里很容易从函数名看出指的是接收到的数据。这里暂且不深入探究,我更好奇的是:在while中按case进行划分时,这些case都是什么?又都可以设置为什么呢?我们随便挑几个来看一看。
可以看出,指引case的条件是MSGpkt->hdr.event
,在上面可以找到,hdr的类型是osal_event_hdr_t
,这个数据结构定义是这样的
1 2 3 4 5
| typedef struct { uint8 event; uint8 status; } osal_event_hdr_t;
|
也就是说,这个结构体中只有uint8 的event和status两个。再回头来看这些case。通过观察我们发现,这几个case并不处于同一个.h文件,而是分散到几个.h文件中。但都具有一个比较共同的标识 :Global Generic System Message
,在下面我尽我所能的整理了一下可能出现的case,以供参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #define KEY_CHANGE 0xC0
#define SPI_INCOMING_ZTOOL_PORT 0x21 #define SPI_INCOMING_ZAPP_DATA 0x22 #define MT_SYS_APP_MSG 0x23 #define MT_SYS_APP_RSP_MSG 0x24 #define MT_SYS_OTA_MSG 0x25
#define AF_DATA_CONFIRM_CMD 0xFD #define AF_INCOMING_MSG_CMD 0x1A #define AF_INCOMING_KVP_CMD 0x1B #define AF_INCOMING_GRP_KVP_CMD 0x1C
#define ZDO_NEW_DSTADDR 0xD0 #define ZDO_STATE_CHANGE 0xD1 #define ZDO_MATCH_DESC_RSP_SENT 0xD2 #define ZDO_CB_MSG 0xD3 #define ZDO_NETWORK_REPORT 0xD4 #define ZDO_NETWORK_UPDATE 0xD5 #define ZDO_ADDR_CHANGE_IND 0xD6
#define NM_CHANNEL_INTERFERE 0x31 #define NM_ED_SCAN_CONFIRM 0x32 #define SAPS_CHANNEL_CHANGE 0x33 #define ZCL_INCOMING_MSG 0x34 #define ZCL_KEY_ESTABLISH_IND 0x35 #define ZCL_OTA_CALLBACK_IND 0x36
|
在了解了case都可以用什么表示之后,我们再来看看对应情况的应对函数。在示例中总共涉及了三个函数
1 2 3 4 5 6 7 8 9 10 11
| case ZDO_CB_MSG: Smart_home_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: Smart_home_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break;
case AF_INCOMING_MSG_CMD: Smart_home_ProcessMSGCmd( MSGpkt ); break;
|
这几个函数都是在Smart_home.c
中定义的,都是对应情况下的希望对应解决方法,其中Smart_home_HandleKeys
是需要用户自行实现填充的按键函数。
那么现在我们已经通过代码得知,我们所有的任务只要通过switch语句来处理网络包传输中表示事件的各种event,就可以成功的利用自写函数来控制设备进行反应。也就是说,我们已经掌握了数据接收处理的方法,那么就产生了一个更有趣的话题:数据的发送究竟是怎么完成的呢?又是通过什么样的代码来实现的呢?
3.数据的发送
可以肯定的是,数据的发送也是ZigBee提供好的,只需我们直接调用就好。那这个函数在哪里呢?
经过查找,我在AF.c中找到了这个函数,下面就来看看这个名为afStatus_t AF_DataRequest
的函数
这个函数比较长,所以从函数形参开始一点一点看,我会直接将函数说明标识在形参中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| uint8 AF_DataRequestDiscoverRoute = TRUE;
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP, uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
uint8 options, uint8 radius )
|
这个函数看起来很像计算机网络中的网络层发送函数,在这里我们比较关心的是两个数据类型:afAddrType_t
以及endPointDesc_t
。这两个代表的是目的地址与源地址,下面就来看看这两个地址都是什么样的数据结构。先来看afAddrType_t
1 2 3 4 5 6 7 8 9 10 11 12
| typedef struct { union { uint16 shortAddr; ZLongAddr_t extAddr; } addr; afAddrMode_t addrMode; uint8 endPoint; uint16 panId; } afAddrType_t;
|
在这里,addrMode
是模式选择选项,是一个枚举,定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| typedef enum { afAddrNotPresent = AddrNotPresent,
afAddr16Bit = Addr16Bit,
afAddrGroup = AddrGroup,
afAddrBroadcast = AddrBroadcast
} afAddrMode_t;
enum { AddrNotPresent = 0, AddrGroup = 1, Addr16Bit = 2, Addr64Bit = 3,
AddrBroadcast = 15 };
|
在afAddrMode_t
中要特殊注意一下Addr16Bit这个变量,16Bit指的是16位的网络地址,ZigBee还有一个64位的MAC地址,由IEEE进行分配和维护。
16位的网络地址是设备加入网络后由协调器或者路由器分配的,可以在Tool目录下的f8wConfig.cfg
中更改
然后再来看endPointDesc_t
1 2 3 4 5 6 7 8
| typedef struct { uint8 endPoint; uint8 *task_id; SimpleDescriptionFormat_t *simpleDesc; afNetworkLatencyReq_t latencyReq; } endPointDesc_t;
|
其中的SimpleDescriptionFormat_t
、afNetworkLatencyReq_t
定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| typedef struct { byte EndPoint; uint16 AppProfId; uint16 AppDeviceId; byte AppDevVer:4; byte Reserved:4; byte AppNumInClusters; cId_t *pAppInClusterList; byte AppNumOutClusters; cId_t *pAppOutClusterList; } SimpleDescriptionFormat_t;
typedef enum { noLatencyReqs, fastBeacons, slowBeacons } afNetworkLatencyReq_t;
|
从这里可以看出来,只要设置好这几个参数之后就可以直接进行数据发送了。,具体的程序可以参见
GenericApp_SendTheMessage
,GenericApp_Init
也就是说,通过简单的设置,我们就明白了数据收发的操作。但数据发送时又是如何绑定的呢?这个绑定又是如何实现的呢?请听下回分解