Skip to content

CDNET 协议简介及示范 ‐ L0 简化版

dukelec edited this page Nov 5, 2024 · 4 revisions

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

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

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

信息字符串查询

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

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 02} { } \color{#5dade2}{01} { } \color{#af7ac5}{00} { } \color{#58d68d}{b5 b8}}$

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

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

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

包头 bit6 为 0 时,表示当前数据包是命令(或上报),包头 bit[5:0] 存放目标端口号(或理解为命令号、功能号)。
包头 bit6 为 1 时,表示当前数据包是一个回复,包头 bit[5:0] 存放回复的状态(bit[5:4] 通常用来指示是否有全局报错,譬如设备温度过高等,而 bit[3:0] 指示当前命令是否执行出错)。

以上命令示范, $\texttt{\color{#5dade2}{01}}$ 表示当前为一个命令,端口号(或命令号)是 1.

1 号端口(或者说命令)对应的正是 信息字符串查询 的功能。

CDNET 协议对其内部装载的数据格式不做要求,但是为了统一和方便使用,我们对第一字节的用户数据也做了定义,只是推荐,不是强制:

对于请求和报告(主动上报),第一个字节的定义如下:
(报告的 not_reply 通常要置 1.)

位段 描述
[7] 固定为 0
[6] not_reply(不要回复)
[5:0] 子命令

对于最上面的例子,用户数据 $\texttt{\color{#af7ac5}{00}}$ 表示当前为一个命令请求,子命令号为 0 表示最基础的返回 信息字符串 功能。

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

$\texttt{RX:}$
$\texttt{\color{#58d68d}{fe 00 3d} { } \color{#5dade2}{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}{f8 0a}}$

返回 "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 的原地址和目标地址发生了调换。 包头 $\texttt{\color{#5dade2}{40}}$ 表示当前是一个回复包,错误状态为 0 表示没有错误。

本章节 信息字符串查询 的命令也不是必须要实现的,用户也可以选择把版本等信息放在下一小节的寄存器列表中,以供主机查询。只是用字符串更灵活方便,建议实现。

寄存器读写

和 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 类型的变量的完整命令如下(也可以一次性读写多个寄存器):

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 05} { } \color{#5dade2}{05} { } \color{#af7ac5}{00 { } 24 01 { } 04} { } \color{#58d68d}{d3 c9}}$

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

回复数据包则为:

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

其中,CDBUS 地址有交换,CDNET 端口也有交换。
$\texttt{\color{#5dade2}{40}}$ 表示当前为回复包,且没有错误发生。
$\texttt{\color{#af7ac5}{04 03 02 01}}$ 表示返回的寄存器的数据为 0x01020304.

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

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 08} { } \color{#5dade2}{05} { } \color{#af7ac5}{20 { } 24 01 { } 04 03 02 01} { } \color{#58d68d}{ee 5b}}$

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

回复包为:

$\texttt{RX:}$ $\texttt{\color{#58d68d}{fe 00 01} { } \color{#5dade2}{40} { } \color{#58d68d}{31 ac}}$

$\texttt{\color{#5dade2}{40}}$ 表示当前为回复包,且没有错误发生。
无数据需要返回。

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

cdbus_gui

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

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 05} { } \color{#5dade2}{05} { } \color{#af7ac5}{20 { } b8 00 { } 01} { } \color{#58d68d}{d9 b4}}$
$\texttt{RX:}$ $\texttt{\color{#58d68d}{fe 00 01} { } \color{#5dade2}{40} { } \color{#58d68d}{31 ac}}$

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

$\texttt{TX:}$ $\texttt{\color{#58d68d}{00 fe 08} { } \color{#5dade2}{05} { } \color{#af7ac5}{20 { } a8 00 { } 04 03 02 01} { } \color{#58d68d}{cc 97}}$
$\texttt{RX:}$ $\texttt{\color{#58d68d}{fe 00 01} { } \color{#5dade2}{40} { } \color{#58d68d}{31 ac}}$

寄存器快速读写

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

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

快速读写的目标寄存器地址可以通过代码写死,或者通过上一小节的寄存器读写功能来提前配置(譬如上图 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.

IAP 固件升级

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,完整数据包示范:

$\texttt{TX:}$
$\texttt{\color{#58d68d}{fe 00 37} { } \color{#5dade2}{09} { } \color{#af7ac5}{40} { } \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}{b8 48}}$

CDNET 用户数据首字节 $\texttt{\color{#af7ac5}{40}}$ 表示这是一个不需要对方回复的报告包(not_reply 为 1)。

上报 "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:   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 数据包:

$\texttt{TX:}$
$\texttt{\color{#58d68d}{fe 00 15} { } \color{#5dade2}{0a} { } \color{#af7ac5}{40} { } \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}{36 8c}}$

$\texttt{TX:}$
$\texttt{\color{#58d68d}{fe 00 15} { } \color{#5dade2}{0a} { } \color{#af7ac5}{40} { } \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}{6f a7}}$

示范中,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:   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