CRC(循环冗余校验)是一种非常流行的错误检测码,广泛应用于数据存储(如硬盘、光盘)和数字通信(如以太网、蓝牙、Wi-Fi)中。CRC-8 特指生成一个 8 比特(即 1 字节)校验和的 CRC 算法。

一、核心思想

        CRC 的本质是 二进制多项式除法

  1. 发送方和接收方预先约定一个固定的 生成多项式

  2. 发送方将待发送的数据视为一个很长的二进制数,并用它除以生成多项式。

  3. 将计算得到的 余数(即 CRC 校验码)附加在原始数据的末尾一起发送出去。

  4. 接收方对收到的完整数据(原始数据 + CRC 码)进行同样的除法计算。

    • 如果余数为 0,则认为数据在传输过程中极大概率没有出错。

    • 如果余数 不为 0,则断定数据中肯定存在错误。

二、关键要素:生成多项式

        CRC-8 的性能(即能检测出哪些错误)完全由其生成多项式决定。生成多项式通常用十六进制或二进制表示。

        一些常见的 CRC-8 标准:

标准名称 生成多项式(十六进制) 生成多项式(二进制) 多项式表示 应用场景
CRC-8 0x07 1000 0111 x8+x2+x1+1x8+x2+x1+1 通用,如 i²C 总线
CRC-8/MAXIM (Dallas 1-Wire) 0x31 0011 0001 x8+x5+x4+1x8+x5+x4+1 DS18B20 温度传感器等
CRC-8/CCITT 0x07 1000 0111 x8+x2+x1+1x8+x2+x1+1 与通用 CRC-8 相同
CRC-8/SAE J1850 0x1D 0001 1101 x8+x4+x3+x2+1x8+x4+x3+x2+1 汽车网络

注意:最高位的 1(对应 x8x8)通常被省略,因为它总是存在。所以 0x07 实际代表 9 比特的 1 0000 0111

三、计算步骤(模二除法)

        我们以一个简单的例子来说明计算过程。

  • 待发送数据: 1101 0011 (1 字节)

  • 生成多项式: 采用 CRC-8/MAXIM, 0x31 (二进制为 0011 0001,实际除数为 9 位的 1 0011 0001)

步骤 1:在数据末尾补 0
        在数据的末尾补上 8 个 0(因为 CRC-8 的校验码长度是 8 位)。
原始数据: 1101 0011
补 0 后: 1101 0011 0000 0000

步骤 2:进行模二除法(异或运算)

  1. 将除数 (1 0011 0001) 的最高位与补 0 后数据的最高位对齐。

  2. 进行 按位异或 操作。

  3. 将结果下移一位,再重复步骤 2,直到除数移动到最后一位。

下图清晰地展示了这个计算过程:

最终得到的余数 1010 1101(0xAD)就是我们的 CRC-8 校验码

步骤 3:组成发送帧
        将 CRC 校验码附加到原始数据之后,形成最终发送的数据帧:1101 0011 1010 1101

四、验证过程

接收方收到数据帧 1101 0011 1010 1101 后,使用 同样的生成多项式 进行模二除法。

  • 关键区别:这次不再在末尾补 0。

  • 直接用整个数据帧 1101 0011 1010 1101 除以生成多项式 1 0011 0001

如果传输没有错误,计算的最终余数将为 0

五、实例解析

uint8_t uav_crc8(uint8_t *ptr, size_t len) 
{
    uint8_t i;
    uint8_t crc = 0;
    while (len-- != 0)
    {
        for (i = 0x80; i != 0; i /= 2)
        {
            if ((crc & 0x80) != 0)
            {
                crc *= 2;
                crc ^= 0x2A;
            } else
            {
                crc *= 2;
            }
            if ((*ptr & i) != 0)
                crc ^= 0x2A;
        }
        ptr++;
    }
    return (crc);
}

        这段代码定义了一个名为 uav_crc8 的函数,用于计算给定数据的 CRC-8 校验值。具体来说,代码的含义如下:

函数定义:

uint8_t uav_crc8(uint8_t *ptr, size_t len) 

        uint8_t uav_crc8:函数返回一个8位的无符号整数,即 CRC-8 校验值。

        uint8_t *ptr:指向要计算 CRC-8 校验值的数据的指针。

        size_t len:数据的长度(以字节为单位)。

初始化变量:

    uint8_t i;
    uint8_t crc = 0;

        uint8_t i:用于在循环中作为计数器。

        uint8_t crc = 0:初始化 CRC 值为0。

主循环:

while (len-- != 0)

        这个循环会遍历数据中的每一个字节,直到 len 变为0。

每个字节的处理:

for (i = 0x80; i != 0; i /= 2)

        这个循环会遍历当前字节的每一位,从最高位(0x80)到最低位(0x01)。

CRC 计算逻辑:

if ((crc & 0x80) != 0)
{
    crc *= 2;
    crc ^= 0x2A;
} else
{
    crc *= 2;
}

        如果当前 CRC 值的最高位(0x80)为1,则将 CRC 值左移一位(相当于乘以2),然后与多项式 0x2A 进行异或操作。

        如果当前 CRC 值的最高位为0,则仅将 CRC 值左移一位。

数据字节的处理:

if ((*ptr & i) != 0)
    crc ^= 0x2A;

        如果当前字节的最高位(0x80)为1,则将 CRC 值与多项式 0x2A 进行异或操作。然后,指针 ptr 指向下一个字节,继续处理。

返回 CRC 值:

    return (crc);

        最后,返回计算得到的 CRC-8 校验值。

        总结: 这个函数通过遍历数据的每一个字节及其每一位,使用特定的多项式 0x2A 计算出 CRC-8 校验值。

六、技术细节与实现

        在实际应用中(如单片机、通信芯片),CRC 通常通过硬件移位寄存器或高效的软件查表法实现,而不是上面展示的手动计算。

  • 初始值: 有些 CRC-8 标准在计算前会将一个寄存器初始化为 0x00 或 0xFF。

  • 输入/输出反转: 有些标准会要求将每个字节的比特位顺序反转(MSB first vs LSB first),或者对最终结果进行按位取反。

  • 查表法: 为了提高速度,可以预先计算好所有 256 个可能字节的 CRC 值,存入一个查找表。计算时只需将数据字节与当前的 CRC 值进行查表和异或操作,极大提升了效率。

七、总结

特性 描述
目的 检测数据传输或存储过程中的错误
原理 二进制多项式除法(模二运算)
校验码长度 8 比特(1 字节)
核心参数 生成多项式(决定检错能力)
结果 余数作为校验码,附加在数据后
验证 接收方计算余数,若为 0 则通过

        CRC-8 是一种简单、高效且强大的轻量级错误检测机制,特别适合在数据量不大但要求可靠性的嵌入式通信场景中使用。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐