CRC-8 算法详细解析
CRC(循环冗余校验)是一种非常流行的错误检测码,广泛应用于数据存储(如硬盘、光盘)和数字通信(如以太网、蓝牙、Wi-Fi)中。CRC-8 特指生成一个 8 比特(即 1 字节)校验和的 CRC 算法。
一、核心思想
CRC 的本质是 二进制多项式除法。
-
发送方和接收方预先约定一个固定的 生成多项式。
-
发送方将待发送的数据视为一个很长的二进制数,并用它除以生成多项式。
-
将计算得到的 余数(即 CRC 校验码)附加在原始数据的末尾一起发送出去。
-
接收方对收到的完整数据(原始数据 + 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 0011 0001) 的最高位与补 0 后数据的最高位对齐。 -
进行 按位异或 操作。
-
将结果下移一位,再重复步骤 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 是一种简单、高效且强大的轻量级错误检测机制,特别适合在数据量不大但要求可靠性的嵌入式通信场景中使用。
更多推荐


所有评论(0)