CDNET 协议简介及示范 - dukelec/cdnet GitHub Wiki

不同的设备,譬如步进电机、无刷电机、振动盘控制器,根据用途不同,使用的协议也有所不同,但是基础格式是相同的。

不同设备都推荐支持的两个基础协议是:设备信息查询 和 寄存器读写。

下面先围绕这两者介绍一下 CDNET 协议细节。

本文仅示范 Level 0 协议,Level 0 协议可以覆盖绝大多数应用。

信息字符串查询

CDNET 协议是 CDBUS 协议的一种上层协议,CDNET 数据包做为 CDBUS 的数据存放,譬如下面是一条字符串信息查询的完整命令:

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 02} { } \color{#5dade2}{40 01} { } \color{#58d68d}{44 28}}$

其中,开头 3 字节和结尾 2 字节为 CDBUS 的包头包尾。
开头 3 字节 $\texttt{\color{#58d68d}{00 fe 02}}$ 分别为:原地址、目标地址、数据长度(主机地址默认为 00,设备地址默认为 fe,地址都可以配置,广播地址 ff 除外)。
包尾 $\texttt{\color{#58d68d}{44 28}}$ 为整包的 crc 校验值,校验算法为 modbus crc。

$\texttt{\color{#5dade2}{40 01}}$ 为 CDNET 的包头。
此包 CDNET 的用户数据为空。

下面重点先介绍一下 CDNET 的包头,Level 0 简化版通信包头固定为 2 字节(首字节 bit7 固定为 0 表示 Level 0 简化版通信)。

包头第一字节是原端口号,第二字节是目标端口号(第二字节的 bit7 也保留为 0,所以两个端口号都 < 128)

以上命令示范, $\texttt{\color{#5dade2}{40 01}}$ 表示从 0x40 端口发送一个数据包到 1 号端口。

1 号端口(或者说命令)对应的正是 信息字符串查询 的功能。
原端口 0x40 可以是任意值,可看做是命令序列号,建议使用 ≥ 0x40 的端口。

接下来,设备回复给主机的完整数据包为:

$\texttt{RX:}$
$\texttt{\color{#58d68d}{fe 00 3e} { } \color{#5dade2}{01 40} { } \color{#af7ac5}{4d 3a 20 63 64 73 74 65 70}}$
$\texttt{\color{#af7ac5}{3b 20 53 3a 20 36 66 30 30 38 39 30 30 30 33 35}}$
$\texttt{\color{#af7ac5}{30 35 32 33 32 35 30 33 33 33 34 32 30 3b 20 53}}$
$\texttt{\color{#af7ac5}{57 3a 20 76 35 2e 31 2d 31 35 2d 67 62 37 30 36}}$
$\texttt{\color{#af7ac5}{32 35 38} { } \color{#58d68d}{e3 b6}}$

返回 "M: cdstep; S: 6f0089000350523250333420; SW: v5.1-15-gb706258" 这样一个字符串,字符串的十六进制数据为 $\texttt{\color{#af7ac5}{4d 3a 20 63 64 ... 35 38}}$.
(字串内容由厂商自己定义,这里 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 [0x00, data]
read_dft:   0x01, offset_16, len_8   | return [0x00, data]
write:      0x20, offset_16 + [data] | return [0x00] on success

其中有 3 个子命令,分别为读寄存器当前值、读寄存器默认值、写寄存器。(可选:write 子命令最高位置 1 的话不回复。)

譬如,读 0x0124 所在的 uint32_t 类型的变量的完整命令如下(也可以一次性读写多个寄存器):

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 06} { } \color{#5dade2}{40 05} { } \color{#af7ac5}{00 { } 24 01 { } 04} { } \color{#58d68d}{60 a0}}$

其中:
$\texttt{\color{#5dade2}{40}}$ 为原端口或命令序列号。
$\texttt{\color{#5dade2}{05}}$ 为目标端口或主命令号。
$\texttt{\color{#af7ac5}{00}}$ 为子命令 read.
$\texttt{\color{#af7ac5}{24 01}}$ 为目标寄存器地址 0x0124.
$\texttt{\color{#af7ac5}{04}}$ 为读取 4 个字节长度.
$\texttt{\color{#58d68d}{60 a0}}$ 为 crc 校验。

回复数据包则为:

$\texttt{RX:}$ $\texttt{\color{#58d68d}{fe 00 07} { } \color{#5dade2}{05 40} { } \color{#af7ac5}{00} { } \color{#af7ac5}{04 03 02 01} { } \color{#58d68d}{3f 87}}$

其中,CDBUS 地址有交换,CDNET 端口也有交换。
$\texttt{\color{#af7ac5}{00}}$ 表示没有错误发生。(通常高位用来指示是否有全局报错,譬如设备温度过高等,而低 4 位指示当前命令是否执行出错)
$\texttt{\color{#af7ac5}{04 03 02 01}}$ 表示返回的寄存器的数据为 0x01020304.

写入 0x0124 寄存器,写入数据为 0x01020304 的命令示范:

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 09} { } \color{#5dade2}{40 05} { } \color{#af7ac5}{20 { } 24 01 { } 04 03 02 01} { } \color{#58d68d}{59 4c}}$

其中 $\texttt{\color{#af7ac5}{20}}$ 为子命令 write.
寄存器地址 0x0124 后面直接跟需要写入的数据,数据有多少就跟多少。

回复包为:

$\texttt{RX:}$ $\texttt{\color{#58d68d}{fe 00 03} { } \color{#5dade2}{05 40} { } \color{#af7ac5}{00} { } \color{#58d68d}{34 40}}$

$\texttt{\color{#af7ac5}{00}}$ 表示没有错误发生。
无数据需要返回。

不同设备的寄存器列表定义不同,不过开头基本相同,为设备地址设置、波特率、复位、保存配置等比较通用的操作。 具体设备可以查看具体文档,或者通过上位机软件查看寄存器定义,譬如步进电机的寄存器列表如下:

p2

譬如使步进电机运转,先执行一次电机上电(state 寄存器写 1):

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 06} { } \color{#5dade2}{40 05} { } \color{#af7ac5}{20 { } 08 01 { } 01} { } \color{#58d68d}{6a aa}}$
$\texttt{RX:}$ $\texttt{\color{#58d68d}{fe 00 03} { } \color{#5dade2}{05 40} { } \color{#af7ac5}{00} { } \color{#58d68d}{34 40}}$

再往 tc_pos 写入目标位置即可(tc 是梯形加减速曲线的缩写),譬如写入 0x01020304:

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 09} { } \color{#5dade2}{40 05} { } \color{#af7ac5}{20 { } bc 00 { } 04 03 02 01} { } \color{#58d68d}{78 94}}$
$\texttt{RX:}$ $\texttt{\color{#58d68d}{fe 00 03} { } \color{#5dade2}{05 40} { } \color{#af7ac5}{00} { } \color{#58d68d}{34 40}}$

寄存器快速读写

对于电机控制等应用,我们需要频繁设置目标位置、速度、加速度等参数,不想每次命令都带寄存器地址,所以可以提前定义好快速读写的目标寄存器地址,然后只需要发送位置、速度等数据即可。

对于不需要高效率快速交换数据的其它应用,无需实现此功能。

快速读写的目标寄存器地址可以通过代码写死,或者通过上一小节的寄存器读写功能来提前配置(譬如上图 qxchg 相关配置,可以配置同时读写不连续的寄存器,qxchg 为 quick exchange 的缩写)。

寄存器快速读写 的端口号(或命令号)是 6,CDNET 用户数据区的定义如下:

write:        [data_w]                        | return [data_r]
write_multi:  [data_w0, data_w1, data_w2 ...] | return [data_r]

其中,write 为写入,内容为要写入的数据(可仅写部分寄存器),返回提前定义好的要读取的数据,譬如返回错误状态寄存器、编码器位置、母线电压等数据(如果命令本身出错返回空包)。
write_multi 为多轴数据合并写入,每个设备从预先定义好的偏移和长度取出自己的数据,取出的数据和 write 的定义相同,返回数据定义也相同。

可选支持:

  • 原端口号 bit4 为 0 时,数据包为 write 命令;为 1 时,数据包为 write_multi 命令。
  • 原端口号 bit3 为 1 时,命令不回复。

IAP 固件升级

Flash 操作的 端口号(或命令号)是 8,CDNET 用户数据区的定义如下:

erase:   0x2f, addr_32, len_32  | return [0x00] on success
write:   0x20, addr_32 + [data] | return [0x00] on success
read:    0x00, addr_32, len_8   | return [0x00, data]
cal crc: 0x10, addr_32, len_32  | return [0x00, crc_16] # modbus crc

有 4 个子命令,分别是擦除、写入、读取、和校验。(可选:子命令最高位置 1 的话不回复。)

其中校验接口可以不实现,可以通过读出全部数据比对来做校验,只是效率低一些。

这里操作的地址是 flash 真实地址,用户可以在 bootloader 固件中,通过此命令更新 app 固件,或者是在 app 固件中更新 bootloader.
除了固件升级,此命令还可以用来读写 flash 中的配置表等数据。

此命涉及的 flash 数据默认是没有加密的,如果需要加密,协议可以不变,或者增加加密版的子命令。

调试打印

设备装好外壳通常不方便使用 串口打印 或者 jtag 调试,此时可以通过用户串口进行打印调试,用户程序需要不被此信息干扰,或者关闭此功能。
(也可以在用户主控和目标设备之间加一个过滤器,打印消息可以过滤并转发到另一台调试设备。)

打印调试上报的目标端口号为 9,完整数据包示范:

$\texttt{TX:}$
$\texttt{\color{#58d68d}{fe 00 37} { } \color{#5dade2}{40 09} { } \color{#af7ac5}{44 3a 20 61 64 63 20 63 61}}$
$\texttt{\color{#af7ac5}{6c 69 3a 20 61 62 20 31 36 36 35 20 31 35 36 38}}$
$\texttt{\color{#af7ac5}{2c 20 63 61 20 31 36 30 38 20 31 36 36 34 2c 20}}$
$\texttt{\color{#af7ac5}{63 62 20 31 36 30 38 20 31 35 36 38} { } \color{#58d68d}{00 d2}}$

9 号端口无回复包。

上报 "D: adc cali: ab 1665 1568, ca 1608 1664, cb 1608 1568" 这样一个字符串,字符串的十六进制数据为 $\texttt{\color{#af7ac5}{44 3a 20 61 ... 36 38}}$.

波形调试

除了串口打印,我们往往还要显示波形数据,以调试电机三环等参数。

波形调试 的目标端口号为 0xa, CDNET 用户数据区的定义如下:

report type 0:   x, d1,d2,d3, d1,d2,d3 ...
report type 1:   x,d1,d2,d3, x,d1,d2,d3 ...

0xa 端口也无回复包,原端口号低 4 位存放波形号 index,譬如 0 号波形是电流环数据,1 号波形是速度环相关的数据,对应上位机不同的波形窗口。

x 是时间标号,对应波形图的 x 时间轴数据,譬如电流环周期 20KHz,每个电流环周期 x 加 1.

report type 0 用于固定周期的波形,譬如下面的示范,由于周期固定,所以一个命令包只需要在开头存放一个 x 数据,对应第一组数据的 x 值,后续数据按照固定间隔值推算 x 值。
report type 1 用于非固定周期的上报,每组数据都要在开头跟一个单独的 x 值。

当前数据为 type 0 还是 1,以及每组数据的定义等,都是在上位机进行配置和指定,命令本身不包含这些信息。
上报数据来源可以在 mcu 代码中写死,或者通过寄存器读写来配置。

完整数据包示范,连续上报两个 type 0 数据包:

$\texttt{TX:}$
$\texttt{\color{#58d68d}{fe 00 15} { } \color{#5dade2}{40 0a} { } \color{#af7ac5}{02 00 00 00 { } { } 04 03 02 01 { } 24}}$
$\texttt{\color{#af7ac5}{05 03 02 01 { } 22 { } 06 03 02 01 { } 21} { } \color{#58d68d}{44 86}}$

$\texttt{TX:}$
$\texttt{\color{#58d68d}{fe 00 15} { } \color{#5dade2}{40 0a} { } \color{#af7ac5}{08 00 00 00 { } { } 07 03 02 01 { } 20}}$
$\texttt{\color{#af7ac5}{09 03 02 01 { } 1f { } 0a 03 02 01 { } 1e} { } \color{#58d68d}{1d ad}}$

示范中,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 定义。

CDCAM 图片上报格式

图片上报 的目标端口号为 0x10, CDNET 用户数据区的定义如下:
(0x10 及之后的端口通常为不同应用的特定协议,小于 0x10 的端口为较为通用的协议。)

report:   [data]

原端口号的格式:

  • [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

⚠️ **GitHub.com Fallback** ⚠️