记得之前公司的七轴机械臂内部使用 RS485 通讯,每个关节都是一个 RS485 节点,当时有个命令可以用来修改设备地址,同时目标地址为 255 的命令为广播命令,所有节点都会响应, 然后,我不小心发送了一个广播改地址的命令,结果所有关节地址都相同了,也就没办法再次通过命令单独修改各关节的地址,最终要把每个关节拆开重新配置,差点把手臂毁掉,因为关节拆开后,编码器的校準也失效了,所谓牵一发动全身,悔的肠子都青了,那以后,我都会为设备加上自动分配地址的功能。
传统很多设备都用硬件拨码开关来设置地址,然而很多产品譬如机械臂关节空间很小,没有地方放置拨码开关,而且很多产品增加拨码开关后外壳处理会十分麻烦,没有软件配置灵活。
为实现地址自动分配,首先要来选择基本的 RS485 通讯协议,多个节点通讯时,建议每条通讯命令都包含源地址和目的地址,这样做不易出错,也比较简洁和统一。
建议使用 CDBUS 格式,你可能没有听过这个名字,但你可能曾经或正在使用相似的协议,它的组成包含 3 个部分: - 3 个字节的头:「源地址,目标地址,用户数据长度」 - 0~255 字节的用户数据(因为数据长度用 1 个字节表示) - 2 个字节的 CRC 校验,涵盖整个数据包,校验算法同 ModBus.
数据包与数据包之间要有一定的空闲时间,来隔开不同的数据包,详细请参见 CDBUS 的协议定义: https://github.com/dukelec/cdbus_ip (CDBUS 最大的好处是支持硬件控制器增强,主动避让冲突,支持并发读写、多主机、对等通讯、数据主动上报等。)
譬如地址 0x00 为主机,0x01 为 1 号从机,那么主机发送两个字节数据 0x10 0x11 给 1 号从机的完整数据为:
[00 01 02 10 11 49 f0]
然后 1 号从机回覆单个 0x10 给主机:
[01 00 01 10 04 b8]
注:CDBUS 中地址 255 为广播地址;总线用户数据长度通常限制在 253 字节,方便用于硬件控制器及串口转发数据;CDBUS 硬件控制器只有在地址不冲突的情况下可以主动避让数据冲突。
然而 CDBUS 只是最底层的协议,接下来我们要定义上述用户数据的格式,最简单常用的方式就是首字节为命令号,然后后面跟可选命令参数;
回覆数据第一个字节通常为状态,然后是返回的数据。
这种方式完善之后也有一个名字,叫 CDNET, 它除了上面说的最基本的形式之外,还支持类似电脑的网络端口形式、支持多网络、确保数据完整性、大数据拆包等功能, 有兴趣可以详见:https://github.com/dukelec/cdnet
而这篇文章只涉及 CDNET 最基本的部分,举个例子,譬如命令 0x01 的定义是查询设备信息:
[00 01 01 01 91 b4]
返回设备信息的字符串:
[01 00 0f 40 4d 3a 20 ... 34 crc_l crc_h]
- 40 表示当前数据包为回覆(区分请求,详见 CDNET 定义);
- 4d 到 34 对应的字符串为 "M: c1; S: 1234"(M 后跟设备型号,S 后面跟设备序列号,也就是唯一码),用字符串比较方便,可一次返回所有信息,也方便扩充,譬如增加版本号,设备端实现也很简便。
接下来开始进入主题,我们需要定义两个命令,一个用来查询设备信息,一个用来设置设备地址,
查询设备信息的 0x01 命令上面已经说了一半,也就是没有跟参数的情况下,直接返回设备信息,
它还可以跟以下 4 个参数来配合地址自动分配:
[max_time, mac_start, mac_end, "filter string"]
- max_time 是两个字节,单位毫秒的时间长度,设备生成一个不大于此数的随机数,等待相应随机时间才能回覆;
- mac_start 和 mac_end 是需要当前设备地址介于这两个数之间时,才能回覆;
- "filter string" 是需要当前设备信息包含此字串的情况下,才能回覆(方便用于查询特定设备当前的地址)。
跟参数与不跟参数都是返回相同格式的设备信息字符串。
另一个命令是设置地址,命令号为 0x03, 它有 3 个参数,第 3 个为可选:
[0x00, new_mac, "filter string"]
- 0x00 是代表 mac 地址设置,因为 CDNET 可以跨网,所以还有其它值代表设置网络号等;
- new_mac 为设置的新地址;
- "filter string" 同样是需要当前设备信息包含此字串的情况下,才能执行命令,否则忽略且不返回。
返回空数据表示成功(40 表示当前数据包为回覆这个还是要的;返回之后才改变地址)。
具体的地址自动分配流程是这样的:
扫描过程一般要多扫描几次,确保不会因为冲突等因素漏掉一些回覆,通常要连续扫描 3 次得到相同结果才行。
以上命令的具体定义可以参见上面的 CDNET 连接,里面有包含;
而具体的实现代码可以参考 CDBUS Bridge 的固件:https://github.com/dukelec/cdbus_bridge
(相关代码在 fw/usr/common_services.c
路径)
有很多朋友问到唯一码相关的问题,所以再补充一下:
通常唯一码的获取方法是取 CPU ID 信息,每个片子都不同。
如果片子没有 CPU ID 唯一码,也可以取周边设备,譬如蓝牙、WiFi 等 MAC 地址。
如果这些都没有,可以在代码里面放一个特定的数据,烧录工具每次烧录时动态搜寻并替换 BIN 文件中对应的数据,这样程序运行的时候取到的数据就是不同的了。也可以用烧录工具单独写 FLASH、写 EEPROM、写 OTP 区域等等。(通常是烧录工具配合条码扫描枪来录入唯一码。)
还有一个不需要唯一码的做法,就是每次开机随机生成一个序例码,譬如 6 或 8 个字节,那么重复的可能性也是很小的了。就算重复我们还有随机时间回复的机制,所以多扫描几次也能确保检测出来,万一真遇到重复的情况,发一个重启命令让部分或全部设备重新生成序例码即可。
要知道,唯一码不仅仅只是用来做地址分配,同时可以实现产品售后管理等需求,也可以用来做一些防抄保护,也让你的产品看起来更专业。
This work is licensed under a Creative Commons Attribution 4.0 International License.
#1, Monday, September 09, 2019, WoodBridge <yu.~@m~.com> wrote:
你好,最近在机器人上面使用485通信,看到您介绍的cdbus感觉非常适用,上gihub看了下,还是有些疑问,请问有没有什么方式可以交流下呢?有没有技术讨论组或帖子什么的?
#2, Saturday, September 14, 2019, Duke <du~@duk~.com> wrote:
@WoodBridge 目前國內論壇有一些討論帖,不過你有疑問可以直接發郵件給我,email 地址參考 git 代碼提交信息。
Please visit the original link: /rs485-auto-addressing-zh