Canmetry
打开工作台
返回博客

CAN DBC 中的 Intel 与 Motorola 字节序:起始位到底怎么数

Intel 还是 Motorola?用同一组字节演示两种字节序取到的位有何不同,讲清 DBC 里的起始位和 @0/@1,以及为什么工具显示的起始位会和 DBC 文件对不上。

.dbcbyte orderCAN

Intel 字节序(DBC 里写作 @1)把多字节信号的低字节放在前面,DBC 起始位指的是信号的最低位(LSB);Motorola 字节序(@0)把高字节放在前面,起始位指的是最高位(MSB)。所以同一帧数据、同一个起始位,按两种字节序取到的位不一样,算出的数也不一样。解码结果对不上时,第一个要查的就是字节序。

同一组字节,两个结果

拿一帧 8 字节数据 12 34 00 00 00 00 00 00,定义一个 16 位无符号信号,系数 1,在 CAN 信号计算器 里分别按两种字节序解码:

字节序DBC 起始位占用的位原始值
Intel(@1)0字节 0 第 0 位(LSB)到字节 1 第 7 位(MSB)0x3412(13330)
Motorola(@0)7字节 0 第 7 位(MSB)到字节 1 第 0 位(LSB)0x1234(4660)

两种定义占用的是同样的 16 个位,区别在于把哪个字节当作高字节:

payload    12 34 00 00 00 00 00 00

Intel      高字节 = 字节 1,低字节 = 字节 0   →  0x3412 = 13330
Motorola   高字节 = 字节 0,低字节 = 字节 1   →  0x1234 = 4660

起始位也不同,因为两种字节序是从信号的不同一端开始数的。

CAN 信号计算器位布局:Intel 信号,起始位 0,长度 16,载荷 12 34,结果 13330,L 在字节 0 第 0 位,M 在字节 1 第 7 位
Intel,起始位 0:L(最低位)在字节 0 第 0 位,M(最高位)在字节 1 第 7 位,结果 13330。
CAN 信号计算器位布局:Motorola 信号,起始位 7,长度 16,载荷 12 34,结果 4660,M 在字节 0 第 7 位,L 在字节 1 第 0 位
Motorola,起始位 7:M 在字节 0 第 7 位,L 在字节 1 第 0 位。位相同,结果是 4660。

DBC 里的 @0 和 @1

DBC 文件里每个信号是一行 SG_:

SG_ Speed : 7|16@0+ (1,0) [0|65535] "" Vector__XXX

从左往右看:

  • 7|16:起始位和位长度。
  • @0:字节序。@0 是 Motorola(大端),@1 是 Intel(小端)。
  • + 表示无符号,- 表示有符号(补码)。
  • (1,0):系数和偏移,用法见下方公式。
  • [0|65535] 是最小值和最大值,"" 是单位,最后一个字段是接收节点。
物理值=原始值×系数+偏移\text{物理值} = \text{原始值} \times \text{系数} + \text{偏移}

字节序是按信号设置的,不是按报文或整个文件。同一条报文里可以同时有 Intel 信号和 Motorola 信号,所以要看你正在解码的那个信号自己的标志。

起始位是怎么数的

DBC 对两种字节序使用同一套位编号:bit 0 是字节 0 的最低位,bit 7 是字节 0 的最高位,bit 8 是字节 1 的最低位,依此类推。区别在于信号相对于这个编号往哪个方向走。

Intel:起始位是 LSB

信号从起始位开始往上长,到了某个字节的第 7 位,就接着从下一个字节的第 0 位继续。起始位为 0 的 16 位 Intel 信号先占满字节 0,再占满字节 1,数值的高位部分在字节 1。

Motorola:起始位是 MSB

信号从起始位开始,往同一字节的第 0 位方向走;走到第 0 位后,跳到下一个字节的第 7 位继续往下。在位网格上画出来是一条锯齿线,所以常被叫作锯齿(sawtooth)编号。

信号不在字节边界结束时,这个走法最直观。计算器自带的示例是一个 12 位 Motorola 信号,起始位 7,载荷相同。它占用整个字节 0 和字节 1 的高 4 位:

Motorola,起始位 7,长度 12,系数 0.1

占用位   字节 0 的 7…0 位,接着字节 1 的 7…4 位
原始值   0x12(8 位)后接 0x3(4 位)  →  0x123 = 291
物理值   291 × 0.1 = 29.1
CAN 信号计算器位布局:12 位 Motorola 信号,起始位 7,载荷 12 34,占用字节 0 全部和字节 1 的第 7 到第 4 位,结果 29.1
起始位 7 的 12 位 Motorola 信号在字节 1 第 4 位结束。原始值 0x123 = 291,× 0.1 = 29.1。

为什么工具里显示的起始位和 DBC 对不上

手工填信号定义时,最容易错在这里。DBC 文件里,Motorola 信号的起始位记录的是 MSB 的位置,但有些工具对同一个信号显示的是另一个数。canmatrix 关于信号字节序的说明 里有一个例子:一个 32 位 Motorola 信号,DBC 文本里起始位是 7,CANdb++ 里显示为 24,也就是它 LSB 的位置。换到本文的 16 位信号,LSB 在 bit 8。

如果把工具里显示的这个数抄进按 DBC 约定计算的工具,得到的就是另一个信号。在计算器里填 Motorola、起始位 8、长度 16,信号会从字节 1 第 0 位开始,一直延伸到字节 2 和字节 3。这帧里这两个字节都是 0,于是结果是 0 而不是 4660,而且没有任何报错。

CAN 信号计算器位布局:Motorola 信号,起始位 8,长度 16,从字节 1 第 0 位开始延伸到字节 2 和字节 3,结果 0
同一个信号把起始位填成 8 而不是 7:它从字节 1 第 0 位开始,一直到字节 3,读出来是 0。

起始位错了不一定会得到明显离谱的数。如果相邻字节里放着别的信号,你会得到一个看起来合理但其实错误的值。数值不对时,先对比 DBC 文本里的起始位和工具里显示的起始位,再去动别的参数。MathWorks 的文档 也说明了大端信号在 AUTOSAR 和其工具箱之间起始位编号的类似差异。

字节序之外:有符号值

有无符号是另一个独立标志(+ 或 -),但出错的方式同样安静。有符号信号按补码存储。设信号长 nn 位,这些位按无符号读出的值为 uu:

原始值={u,u<2n−1u−2n,u≥2n−1\text{原始值} = \begin{cases} u, & u < 2^{n-1} \\ u - 2^{n}, & u \ge 2^{n-1} \end{cases}

以一个起始位 0、系数 0.1 的 12 位 Intel 信号为例。这里 n=12n = 12,uu 只要不小于 2048 就是负数:

Intel,起始位 0,长度 12,系数 0.1,载荷 9C FF

占用位   字节 1 的 3…0 位,接着字节 0 的 7…0 位  →  0xF9C = 3996
无符号   3996 × 0.1 = 399.6
有符号   最高位为 1,3996 − 4096 = −100   →  −100 × 0.1 = −10.0

本该出现负数的温度或扭矩突然变成一个很大的正数时,查完字节序再查符号位。

我的 DBC 会用哪种字节序?

没法预设。公开资料对哪种更常见说法不一:OpenXC 固件文档 说汽车网络里大端最常见,embdx 则认为现代 CAN 里 Intel 更常见。既然每个信号都带着标志,直接读 DBC 里的 @0 或 @1,不要猜。

如果手上根本没有 DBC,就找一个能主动改变的量,比如踏板位置或计数器,盯住它所在的报文。在 Trace 里按这个 ID 筛选,让数值沿一个方向慢慢变化:几乎每一步都在变的字节放的是低位部分,偶尔才变的字节放的是高位部分。如果高位部分在编号更小的字节里,这个信号多半是 Motorola。

检查整个 DBC

计算器一次只看一帧。要检查文件里的所有信号,可以用 DBC 查看器 打开,它的位布局会显示每个信号的起始位、字节序、系数和偏移。字节序写错,常常表现为两个信号占用了相同的位,或者信号超出了帧长度;DBC 检查器 会把这两类问题连同行号一起报出来。要解码整段录制而不是一帧,可以看没有 CANoe,怎么打开 BLF 文件。

把你自己的一帧数据粘贴进计算器,在 Intel 和 Motorola 之间切换,看哪种读法和你预期的数值对得上。

.blf.mf4.asc

打开一份日志,看见信号。

在浏览器里打开 BLF、MF4 或 ASC 文件,不会上传任何内容。

打开工作台

继续阅读