-
Notifications
You must be signed in to change notification settings - Fork 33
CDNET 协议简介及示范 ‐ L0 简化版
不同的设备,譬如步进电机、无刷电机、振动盘控制器,根据用途不同,使用的协议也有所不同,但是基础格式是相同的。
不同设备都支持的两个基础协议是:设备信息查询 和 寄存器读写。
下面先围绕这两者介绍一下 CDNET 协议细节。
CDNET 协议是 CDBUS 协议的一种上层协议,CDNET 数据包做为 CDBUS 的数据存放,譬如下面是一条字符串信息查询的完整命令:
其中,开头 3 字节和结尾 2 字节为 CDBUS 的包头包尾。
开头 3 字节
包尾
下面重点先介绍一下 CDNET 的包头,Level 0 简化版通信包头固定为 1 字节,其 bit7 固定为 0 表示 Level 0 简化版通信。
包头 bit6 为 0 时,表示当前数据包是命令(或上报),包头 bit[5:0] 存放目标端口号(或理解为命令号、功能号)。
包头 bit6 为 1 时,表示当前数据包是一个回复,包头 bit[5:0] 存放回复的状态(bit[5:4] 通常用来指示是否有全局报错,譬如设备温度过高等,而 bit[3:0] 指示当前命令是否执行出错)。
以上命令示范,
1 号端口(或者说命令)对应的正是 信息字符串查询
的功能。
CDNET 协议对其内部装载的数据格式不做要求,但是为了统一和方便使用,我们对第一字节的用户数据也做了定义,只是推荐,不是强制:
对于请求和报告(主动上报),第一个字节的定义如下:
(报告的 not_reply 通常要置 1.)
位段 | 描述 |
---|---|
[7] | 固定为 0 |
[6] | not_reply(不要回复) |
[5:0] | 子命令 |
对于最上面的例子,用户数据 信息字符串
功能。
接下来,设备回复给主机的完整数据包为:
返回 "M: cdstep; S: 6f0089000350523250333420; SW: v5.1-15-gb706258"
这样一个字符串,字符串的十六进制数据为
(字串内容由厂商自己定义,这里 M 表示 model 型号,S 是序列号,SW 是软件版本,是 git 自动生成的版本信息,tag v5.1 后的第 15 个提交,对应提交的 hash 值是 b706258. bootloader 模式下,cdstep 后面会增加 (bl) 以便上位机识别当前模式。)
其中,要留意 CDBUS 的原地址和目标地址发生了调换。
包头
本章节 信息字符串查询
的命令也不是必须要实现的,用户也可以选择把版本等信息放在下一小节的寄存器列表中,以供主机查询。只是用字符串更灵活方便,建议实现。
和 MODBUS 等常见协议一样,CDNET 也有类似的寄存器表,方便用户配置、控制,以及查询设备状态。
CDNET 的寄存器默认是真实的地址偏移,譬如 0x0124 地址有一个 uint32_t 类型的变量,那么 0x0124 ~ 0x0127(包含)都属于这个变量。
寄存器读写的端口号(或命令号)是 5,CDNET 用户数据区的定义如下:
read: 0x00, offset_16, len_8 | return [0x40, data]
read_dft: 0x01, offset_16, len_8 | return [0x40, data]
write: 0x20, offset_16 + [data] | return [0x40] on success
其中有 3 个子命令,分别为读寄存器当前值、读寄存器默认值、写寄存器。
譬如,读 0x0124 所在的 uint32_t 类型的变量的完整命令如下(也可以一次性读写多个寄存器):
其中:
回复数据包则为:
其中,CDBUS 地址有交换,CDNET 端口也有交换。
写入 0x0124 寄存器,写入数据为 0x01020304 的命令示范:
其中
寄存器地址 0x0124 后面直接跟需要写入的数据,数据有多少就跟多少。
回复包为:
无数据需要返回。
不同设备的寄存器列表定义不同,不过开头基本相同,为设备地址设置、波特率、复位、保存配置等比较通用的操作。 具体设备可以查看具体文档,或者通过上位机软件查看寄存器定义,譬如步进电机的寄存器列表如下:
譬如使步进电机运转,先执行一次电机上电(state 寄存器写 1):
再往 tc_pos 写入目标位置即可(tc 是梯形加减速曲线的缩写),譬如写入 0x01020304:
对于电机控制等应用,我们需要频繁设置目标位置、速度、加速度等参数,不想每次命令都带寄存器地址,所以可以提前定义好快速读写的目标寄存器地址,然后只需要发送位置、速度等数据即可。
对于不需要高效率快速交换数据的其它应用,无需实现此功能。
快速读写的目标寄存器地址可以通过代码写死,或者通过上一小节的寄存器读写功能来提前配置(譬如上图 qxchg 相关配置,可以配置同时读写不连续的寄存器,qxchg 为 quick exchange 的缩写)。
寄存器快速读写 的端口号(或命令号)是 6,CDNET 用户数据区的定义如下:
write: 0x20, [data_w] | return [0x40, data_r]
write_multi: 0x2f, [data_w0, data_w1, data_w2 ...] | return [0x40, data_r]
read: 0x00 | return [0x40, data_ro]
其中,write 为写入,子命令号后直接跟要写入的数据(可仅写部分寄存器),返回为提前定义好的要读取的数据,譬如返回错误状态寄存器、编码器位置、母线电压等数据。
write_multi 为多轴数据合并写入,每个设备从预先定义好的偏移和长度取出自己的数据,取出的数据和 write 的定义相同,返回数据定义也相同。
read 命令为读取 预先定义好的数据,read 命令返回的数据和 write 命令返回的定义可以不同,因为 write 模式和 read 模式通常关注的数据不同。譬如控制电机运转用 write, 示教时用 read.
Flash 操作的 端口号(或命令号)是 8,CDNET 用户数据区的定义如下:
erase: 0x2f, addr_32, len_32 | return [0x40] on success
write: 0x20, addr_32 + [data] | return [0x40] on success
read: 0x00, addr_32, len_8 | return [0x40, data]
cal crc: 0x10, addr_32, len_32 | return [0x40, crc_16] # modbus crc
有 4 个子命令,分别是擦除、写入、读取、和校验。
其中校验接口可以不实现,可以通过读出全部数据比对来做校验,只是效率低一些。
这里操作的地址是 flash 真实地址,用户可以在 bootloader 固件中,通过此命令更新 app 固件,或者是在 app 固件中更新 bootloader.
除了固件升级,此命令还可以用来读写 flash 中的配置表等数据。
此命涉及的 flash 数据默认是没有加密的,如果需要加密,协议可以不变,或者增加加密版的子命令,譬如所有子命令的 bit3 置 1.
设备装好外壳通常不方便使用 串口打印 或者 jtag 调试,此时可以通过用户串口进行打印调试,用户程序需要不被此信息干扰,或者关闭此功能。
(也可以在用户主控和目标设备之间加一个过滤器,打印消息可以过滤并转发到另一台调试设备。)
打印调试上报的目标端口号为 9,完整数据包示范:
CDNET 用户数据首字节
上报 "D: adc cali: ab 1665 1568, ca 1608 1664, cb 1608 1568"
这样一个字符串,字符串的十六进制数据为
除了串口打印,我们往往还要显示波形数据,以调试电机三环等参数。
波形调试 的目标端口号为 0xa, CDNET 用户数据区的定义如下:
report type 0: 0x40 | index, x, d1,d2,d3, d1,d2,d3 ...
report type 1: 0x40 | index, x,d1,d2,d3, x,d1,d2,d3 ...
其中 0x40 表示这是一个不需要对方回复的报告包,低 6 位子命令号存放的 index 为波形号,譬如 0 号波形是电流环数据,1 号波形是速度环相关的数据,对应上位机不同的波形窗口。
x 是时间标号,对应波形图的 x 时间轴数据,譬如电流环周期 20KHz,每个电流环周期 x 加 1.
report type 0 用于固定周期的波形,譬如下面的示范,由于周期固定,所以一个命令包只需要在开头存放一个 x 数据,对应第一组数据的 x 值,后续数据按照固定间隔值推算 x 值。
report type 1 用于非固定周期的上报,每组数据都要在开头跟一个单独的 x 值。
当前数据为 type 0 还是 1,以及每组数据的定义等,都是在上位机进行配置和指定,命令本身不包含这些信息。
上报数据来源可以在 mcu 代码中写死,或者通过寄存器读写来配置。
完整数据包示范,连续上报两个 type 0 数据包:
示范中,index 为 0,x 值是 uint32_t 型的数据,每组数据之间 x 值差值为 2,每组中有两个数据,第一个数据为 int32_t 型,譬如表示当前位置,第二个数据为 uint8_t 型,譬如表示某个电压值。
那么以上两个数据包,总共包含 6 组数据,列为表格如下:
(为了方便演示,命令中的数据组数才故意存放比较少。)
波形时间轴 x | 位置 | 电压 |
---|---|---|
0x00000002 | 0x01020304 | 0x24 |
0x00000004 | 0x01020305 | 0x22 |
0x00000006 | 0x01020306 | 0x21 |
0x00000008 | 0x01020307 | 0x20 |
0x0000000a | 0x01020309 | 0x1f |
0x0000000c | 0x0102030a | 0x1e |
对应 CDBUS_GUI 工具的 设备配置文件中的波形设置格式为:
"I2.iB - N, position, voltage"
其中 I 表示 x 的数据类型为 uint32_t, I 后面有数字表示 report type 为 0,数字 2 表示每组数据的 x 间隔为 2.
小数点后面为每组数据的定义,其中 i 表示第一个数据为 int32_t 型,B 表示第二个数据为 uint8_t 型。
最后的 N, position, voltage 分别为 x 轴的数据名称,以及每组中数据的名称。
数据类型的字母表示法参见 python struct 库文档中的 format characters 定义。
图片上报 的目标端口号为 0x10, CDNET 用户数据区的定义如下:
(0x10 及之后的端口通常为不同应用的特定协议,不同协议可以共用同一个端口号,小于 0x10 的端口为较为通用的协议。)
report: 0x40 | hdr, [data]
hdr 格式:
- [5:4]: FRAGMENT: 00: 出错, 01: 首包, 10: 中间包, 11: 尾包
- [3:0]: cnt, 对应第一个分包为 0,后续分包每次加 1.
主机按顺序,把接收到的一个首包、多个中间包、一个尾包的 data 拼接起来,即为一张 jpg 图片。
寄存器 capture
写 1 返回单张 jpg 图片,写 255 连续返回 jpg 图片,再次写 0 停止返回。
CDNET-协议中文版:https://github.com/dukelec/cdnet/wiki/CDNET-协议中文版
CDBUS 协议:https://cdbus.org