1. 关键字
- 正向校验(Normal)、反向校验(Reversed)[1]
- MSB、LSB
2. 多项式
\[G(X) = X^{32} + X^{26} + X^{23} + X^{22} + X^{16} + X^{12} + X^{11} + X^{10} + X^{8} + X^{7} + X^{5} + X^{4} + X^{2} + X +1\]正向:0x04C1_1DB7
(实际是 0x1_04C1_1DB7
最前面的 1 通常不写出来)。
反向:0xEDB8_8320
(0x04C1_1DB7
反转)
3. 基本算法
(1) 正向校验
- 预置 1 个 32 位的变量 CRC,存放校验值,首先赋初值 0xffffffff;
- 查看规则判断第一个数据是否需要反转,若需要,则按位反转,若不需要,直接进入第 3 步;
- 把第 1 个数据按照步骤 2 处理后,与 32 位的变量 CRC 的高 8 位相异或,把结果放于变量 CRC,低 24 位数据不变;
- 检查左移后的移出位;
- 如果移出位为 0,左移一位;如果移出位为 1,变量 CRC 左移一位后与多项式
0x04C11DB7
进行异或; - 重复步骤 4 和 5,直到左移 8 次(和数据长度一致),这样整个 8 位数据全部进行了处理;
- 重复步骤 2 到步骤 6,进行通讯信息帧下一个数据(字节)的处理;
- 将该通讯信息帧所有字节按上述步骤计算完成后,将得到的 32 位变量 CRC,查看规则是否需要反转;
- 最后,与结果异或值异或,得到的变量 CRC 即为 CRC 校验值。
(2) 反向校验
- 预置 1 个 32 位的变量 CRC,存放校验值,首先赋初值 0xffffffff;
- 查看规则判断第一个数据是否需要反转,若需要,则按位反转,若不需要,直接进入第3步;
- 把第 1 个数据按照步骤 2处理后,与 32 位的变量 CRC 的低 8 位相异或,把结果放于变量 CRC,高 24 位数据不变;
- 检查右移后的移出位;
- 如果移出位为 0,右移一位;如果移出位为 1,变量 CRC 右移一位后与多项式
0xEDB88320
进行异或; - 重复步骤 4 和 5,直到右移 8 次(和数据长度一致),这样整个 8 位数据全部进行了处理;
- 重复步骤 2 到步骤 6,进行通讯信息帧下一个数据(字节)的处理;
- 将该通讯信息帧所有字节按上述步骤计算完成后,将得到的 32 位变量 CRC,查看规则是否需要反转;
- 最后,与结果异或值异或,得到的变量 CRC 即为 CRC 校验值。
(3) 正向校验与反向校验的关系
正向校验{输入数据反转、输出数据反转}的结果与反向校验{输入数据不反转、输出数据不反转}的校验结果相同。
4. 示例程序
本示例程序使用 Rust 编写,正向校验时,第 2 步和第 8 步都需要反转。反向校验时,第 2 步和第 8 步不需要反转。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
const CRC_SEED: u32 = 0xffffffff;
const CRC_POLYNOMIAL_NORMAL: u32 = 0x04c11db7;
const CRC_POLYNOMIAL_REVERSED: u32 = 0xedb88320;
struct Crc32Context {
crc: u32,
// Reversed or Normal
reversed: bool,
// 输入数据反转
refin: bool,
// 输出数据反转
refout: bool,
}
impl Crc32Context {
fn new(reversed: bool, refin: bool, refout: bool) -> Self {
Crc32Context{
crc: CRC_SEED,
reversed: reversed,
refin: refin,
refout: refout,
}
}
fn step_normal(&mut self, byte: u8) {
if self.refin {
self.crc ^= (byte.reverse_bits() as u32) << 24;
} else {
self.crc ^= (byte as u32) << 24;
}
for _ in 0..8 {
if self.crc & 0x80000000 != 0 {
self.crc = (self.crc << 1) ^ CRC_POLYNOMIAL_NORMAL;
} else {
self.crc <<= 1;
}
}
}
fn step_reversed(&mut self, byte: u8) {
self.crc ^= byte as u32;
for _ in 0..8 {
if self.crc & 1 == 1 {
self.crc = (self.crc >> 1) ^ CRC_POLYNOMIAL_REVERSED;
} else {
self.crc >>= 1;
}
}
}
fn step(&mut self, byte: u8) {
if self.reversed {
self.step_reversed(byte);
} else {
self.step_normal(byte);
}
}
fn finalize(&mut self) -> u32 {
if self.refout {
self.crc.reverse_bits()
} else {
self.crc
}
}
}
fn main() {
let data: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
//let mut checksum = Crc32Context::new(true, false, false);
let mut checksum = Crc32Context::new(false, true, true);
for i in 0..data.len() {
checksum.step(data[i]);
}
println!("checksum: 0x{:x}", checksum.finalize()); // 0x77559760
}
参考
[1] https://www.cnblogs.com/masonzhang/p/10261855.html
[2] https://blog.csdn.net/weixin_42109012/article/details/103467566
[3] CRC(循环冗余校验)在线计算