NXP ZigBee3.0 网关之 ZigBee 串口通信协议解析
前言
上一篇博文我们聊完了网关的硬件设计,本篇博文,我们再来聊一聊网关上 MCU 与 ZigBee 通信的协议。MCU 与 ZigBee 通过串口进行通信,通过约定的协议格式来互相收发数据,保证了数据的可识别性和完整性。
一、串口协议
NXP ZigBee 3.0 约定了串口数据为 01 开头 03 结尾的 16 进制数据,每一个数据串传递一个信息,数据最大长度为 256 Byte(默认),数据中若出现 00 ~ 0F 的数据,则将该数据与 10 异或,并在此数据前加上 02 作为标志,存放在发送数据中。实际在串口间传输的数据均为 01 开头,03 结尾,中间数据经过 10 异或后的数据串。 字节填充前,串口数据串组成格式固定,01 开头,前 16 个字节(2 Byte)为数据类型(Data Type),然后的 16 个字节(2 Byte)为数据长度(Data Length),再然后是 8 个字节(1 Byte)的CRC(CRC8),之后就是该数据串的真实数据,最后由 03 结尾。如下表。
表 1 字节填充前数据的格式 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | … | N+6 | N+7 | N+8 | 01 |
|
|
|
| 03 | Start | Data Type | Data Length | CRC | Data | Stop |
生成上述格式之后,NXP ZigBee 会将该数据进行转码,即添加 02 和 10 异或; 如下,有一串数据: 01 00 49 00 04 B2 FF FC FC 00 03 Data Type 为 0x0049,Data Length 为 0x0004,Data 为 0xFFFCFC00,CRC 为 B2; 转码后的数据为: 01 02 10 49 02 10 02 14 B2 FF FC FC 02 10 03 即将数据头 01 和 数据尾 03 中间小于 0x10 的数据 00 00 04 00 与 0x10 异或,并在其前面加上 02 作为标志。最后得到的转码后的数据为串口发送的真实数据。
这里需要注意的一点是 CRC 的生成。NXP ZigBee 所给例程中,默认使用的 CRC 算法是异或校验,即将所有值进行异或,这里的所有值包括 Data Type 、Data Length 、Data 三项。这是很简单的一个校验算法,减少了 ZigBee 的计算量,但是也带来了的问题,当前后两个数颠倒时,异或算法得出的值是一致的,这就出现了问题。解决办法是,我们将异或算法替换,NXP ZigBee 的例程代码中,提供了 CCITT-CRC(CRC8)的实现代码,在下面的章节我会详细讲如何在代码中替换 CRC 算法。
二、上位机 ZGUI
NXP ZigBee 3.0 的例程 JN-AN-1216-Zigbee-3-0-IoT-ControlBridge[1] 中提供了一个可以模拟 MCU 与 ZigBee 串口进行通信的上位机 ZGUI(ZigBee Gateway User Interface)。
[1] 该例程为 JN5169 例程,JN5189 例程为 JN-AN-1247
图 2-1 ZGUI 界面
上面我们说了,串口数据主要由 Data Type 、Data Length 、Data 组成,那面对纷繁的 Data Type ,初始开发人员如何快速上手呢?答案就是上位机 ZGUI。ZGUI 将 Data Type 可视化,不需要我们再去代码中或是协议文档中一个一个去寻找,直接填上数据就可以发送,并且在鼠标指向某一参数框时,它会弹出相应的参数大小,如下图,方便我们更准确的填写数据。
图 2-2 ZGUI 参数注释
在我们开发前期,网关板还未画好,MCU 串口数据代码还未写好,我们便可以使用 ZGUI 模拟 MCU 与 ZigBee 进行通信,从而对 ZigBee 的功能进行测试。ZGUI 下面有两个窗口,左边的窗口能够看到 ZigBee 与 ZGUI 通信的串口数据,右边能看到对 ZigBee 上报的数据的解析,如下图,十分方便。 图 2-3 ZGUI 窗口数据显示
ZGUI 是 NXP 的一个测试工具,非正式,所以有一些功能还不够完善,比如:在发送数据时,无论怎么选择地址模式,都是以短地址(网络地址)为源地址;但是,ZGUI 的 C# 源码已经包含在 JN-AN-1216-Zigbee-3-0-IoT-ControlBridge 中,所以有需求有能力的小伙伴,可以自己进行二次开发,包括上面说的 CRC,ZGUI 中默认使用的 CRC 算法也是异或算法,所以如果在 ZigBee 这边更换了 CRC 校验算法,默认的 ZGUI 就会无法使用。
三、代码解析
NXP ZigBee 的例程 JN-AN-1216-Zigbee-3-0-IoT-ControlBridge 在代码中已经给的足够的数据类型工我们使用,基本上不需要我们自己去定义新的数据类型,但是如果有特殊需要,我们也能够通过代码去添加我们自定义的数据,通过 ZigBee 串口发送到 MCU。下面就带大家看一下 NXP ZigBee 例程中的数据发送过程。
首先,我们先进到数据发送的函数中。 代码的入口在 App_start.C 中, vAppMain //主函数 -> APP_vMainLoop //主循环 -> APP_vHandleAppEvents //处理来自 App 的事件 -> APP_vProcessRxData //接收串口数据,看门狗复位 -> APP_vProcessIncomingSerialCommands //处理串口数据
图 3-1 APP_vProcessIncomingSerialCommands 函数
APP_vProcessIncomingSerialCommands 接收来自串口的数据,并在 bSL_ReadMessage 函数中将串口接收的数据打包成 Data Type 、Data Length 、Data,往下根据 Data Type 对不同的数据进行处理。上面提到了 ZGUI 无法发送带有长地址(IEEE Address / MAC Address)的数据串,其实在默认的例程代码中,也没有写解析除了短地址之外的代码,那如果我们在后续的开发中,希望 MCU 发送的串口数据是由长地址(具有唯一性)组成,那么就可以对下面的数据进行更改,如下图。
图 3-2 修改代码为长短地址均可解析
数据类型
我们继续往下看,就能看到函数中,对串口数据类型的判断,我们进到存放数据类型的 SerialLink.h 中,可以看到很多定义好的数据类型,基本已经涵盖大部分需求。
图 3-3 串口数据类型枚举
如果有需要,我们可以在该枚举类型中自己添加相应的数据类型,然后回到 APP_vProcessIncomingSerialCommands 组装数据,并用 vSL_WriteMessage 函数将数据发送
CRC 校验
上面我们提到默认的 CRC 异或算法可能会在校验过程中出现问题,所以我们需要更换 CRC 算法为 CCITT-CRC,这个算法的实现已经包含在代码中,我们只需要启用它就行。
图 3-3 vSL_WriteMessage 函数
CCITT-CRC 代码包含在 SerialLink.C 中,我们需要打开宏定义 CCITT_CRC,这里可以直接在函数中定义,也可以在该工程的 makefile 中使用 CFLAGS += -D 来定义。定义完之后,替换 vSL_WriteMessage 中的 u8SL_CalculateCRC 即可,但是在这里还是要在重复一句,CRC 校验算法改完之后,ZGUI 就会无法使用。
串口协议是 ZigBee 和 MCU 通信的基础,搞清楚了通信协议,在收到串口数据时,对照文档,就能搞清楚串口数据每一位的含义,附录是串口协议的文档。
参考资料
[1]: JN-AN-1216-ZigBee-3-0-IoT-ControlBridge-UserGuide |