计算UART传输的CRC:LSB还是MSB?

Calculating CRC for UART transmission LSB or MSB?

提问人:Rasmus Palk 提问时间:11/17/2023 更新时间:11/17/2023 访问量:70

问:

我必须通过 UART 传输 8 个字节的数据,最后一个字节本身就是 CRC。

我理解计算 CRC,但我不确定是根据传输前的原始数据(每个字节都写入 LSB)还是基于通过 UART 传输的数据(即 MSB)来计算它。

我目前将数据分成 1 个字节的块,并带有打包的结构,这样我就更容易摆弄这些字节。

typedef struct __attribute__((packed)){
    uint8_t sync;
    uint8_t slave_address;
    uint8_t register_address;
    uint8_t pwmA;
    uint8_t dirA;
    uint8_t pwmB;
    uint8_t dirB;
    uint8_t crc;
} packet_t;

packet_t new_sig = {
        .sync = 0b10100000,
        .slave_address = 0b10000000,
        .register_address = 0b01000101,
        .pwmA = 0b0,
        .dirA = 0b0,
        .pwmB = 0b0,
        .dirB = 0b0,
        .crc = 0b0,
};

所有这些都是以LSB格式编写的。在这个例子中,Sync、Slave 和 Register 是常量,大部分摆弄发生在其余位置。Pwm A 和 B 可以是 0-255,目录 A 和 B 可以是 0 或 1。

一旦我摆弄完数据,我想计算 CRC。我使用这个函数:

void CalcCRC(unsigned char* datagram, unsigned char datagramLength) {
    int i,j;
    unsigned char* crc = datagram + (datagramLength-1); // CRC located in last byte of message
    unsigned char currentByte;
    *crc = 0;
    for (i=0; i<(datagramLength-1); i++) { // Execute for all bytes of a message
        currentByte = datagram[i]; // Retrieve a byte to be sent from Array
        for (j=0; j<8; j++) {
            if ((*crc >> 7) ^ (currentByte&0x01)) // update CRC based result of XOR operation
            {
                *crc = (*crc << 1) ^ 0x07;
            }
            else
            {
                *crc = (*crc << 1);
            }
            currentByte = currentByte >> 1;
        } // for CRC bit
    } // for message byte
    new_sig.crc = *crc;
}

我从电机驱动器的数据表(https://www.mouser.ee/datasheet/2/609/TMC7300_datasheet_rev1_08-3364772.pdf)中获得了该功能。

我的解决方案可能还有更多问题,但我认为我的问题是计算 CRC。我尝试将数据从 LSB 切换到 MSB 进行计算,但没有奏效。有什么建议吗?

C UART结 直肠癌

评论

2赞 Support Ukraine 11/17/2023
它必须使用与线路上相同的字节顺序(即网络顺序)进行计算。
0赞 Support Ukraine 11/17/2023
OT:LSB 和 MSB 并不是真正的格式。我的猜测是,你宁愿参考大端序还是小端序。或者当涉及到数据传输时,主机与网络顺序。
0赞 nielsen 11/17/2023
字节顺序通常只对多字节数据(例如 16 位整数)起作用,但所有数据都是单字节的。我希望它按列出的顺序传输:、、......由于您从数据表中获得了CRC计算,因此它可能是正确的(除了您添加的最后一行看起来很奇怪),并且问题出在其他地方。syncslave_address
0赞 Mark Adler 11/17/2023
你怎么知道你的解决方案是错误的?也许您的解决方案是正确的,但您确定数据包是坏的方法是不正确的。计算看起来不错。它从每个字节的最低有效开始计算 CRC-8,这就是它出线的方式。四字节值首先以最高有效字节存储这一事实很奇怪,但与计算 CRC 无关。它只与将所需的四字节值正确地放在该位置有关。
0赞 Mark Adler 11/17/2023
请注意,CRC 是根据消息的所有字节计算的,包括同步字节、地址字节和寄存器字节。

答:

0赞 chux - Reinstate Monica 11/17/2023 #1

首先是一些细节:

字节序通常是指字节顺序

示例:使用 BIG endian 和 2 字节时,最高有效字节存储在最低地址中,后跟最低有效字节存储在下一个较高地址中。LITTLE endian 正好相反。OP 的代码没有多字节数据,因此此问题不适用。int16_t

对于串行数据传输,也有一个字节顺序或字节序。使用 BIG endian 时,首先传输最高有效字节,然后是最低有效字节。OP 的代码没有多字节数据,因此此问题不直接适用。

在串行传输中,还有位字节序,哪个位先发送,最高有效位还是最少?通常,程序员看不到此顺序。UART 首先发送最低有效位。


OP 使用从最低寻址到最高寻址字节的结构字节。在算法中,字节从最低有效位到最高有效位使用。CalcCRC()

如果串行传输首先发送最低寻址字节,则 OP 会匹配该字节。如果串行传输首先发送最高寻址字节,则 OP 应更改为按该顺序计算 CRC。CalcCRC()CalcCRC()


为什么CRC计算要与传输顺序匹配?

OP 在 所指向的数据中存在所有字节后计算 CRC。更高性能的代码可以在发送/接收每个字节后计算 CRC。因此,订单必须匹配。CalcCRC()datagram

在硬件级别,一些 CRC 计算一次完成 1 位,因此传输不仅必须匹配字节顺序,还必须匹配位顺序。这通常不适用于 UART 串行传输。


鉴于 OP 和 UART 发送,我希望正确的计算顺序是大多数寻址字节的最低值。这并不意味着 BIG 或 LITTLE 端多字节类型,因为这是一个单独的问题。CalcCRC()

当然,重要的是发送方和接收方同意。

评论

0赞 Mark Adler 11/17/2023
“位端”??我从未听说过 endian 用来指代位。只有字节。串行传输的位顺序与字节序无关。对于CRC,CRC计算中的位顺序称为反射或不反射。
0赞 chux - Reinstate Monica 11/17/2023
@MarkAdler 虽然您可能没有听说过 Bit endian,但它确实存在并适用于首先传输的位(最小或最重要)。这对于一次执行 CRC 的硬件很重要。裁判
0赞 chux - Reinstate Monica 11/17/2023
@MarkAdler“反映或不反映”——>很有趣。
0赞 Mark Adler 11/17/2023
不,这个术语不存在。你只会混淆人们这么说,所以请不要试图把它引入白话。众所周知,endianess 是指字节。时期。
0赞 chux - Reinstate Monica 11/17/2023
@MarkAdler 它已经被其他人介绍过,甚至在发布的链接中也是如此。Endian 也用于 twenty-one vs. fourteen 等词中。Endian 是一个概念,它很好地解释了表达和布局数字字段的不同方式。就像 endian 这个词的起源一样,一方说鸡蛋应该以一种方式破裂,而另一方则说得不同,双方会因为这样一个微不足道的问题而发生冲突。这里没有必要进行圣战。
0赞 Mark Adler 11/17/2023 #2

您的同步字节似乎不正确。链接的文档将其显示为 .请注意位编号:0b00000101

显示同步位的数据报结构

评论

0赞 nielsen 11/17/2023
寄存器地址可能也有同样的问题,文档说最高有效位必须是 1,但它不在 OP 代码中。
0赞 Mark Adler 11/18/2023
@nielsen 也许吧。该位为 1 表示写入,0 表示读取。我不知道 OP 试图用他们的命令做什么。