Posts CRC-32学习笔记
Post
Cancel

CRC-32学习笔记

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_83200x04C1_1DB7 反转)

3. 基本算法

(1) 正向校验

  1. 预置 1 个 32 位的变量 CRC,存放校验值,首先赋初值 0xffffffff;
  2. 查看规则判断第一个数据是否需要反转,若需要,则按位反转,若不需要,直接进入第 3 步;
  3. 把第 1 个数据按照步骤 2 处理后,与 32 位的变量 CRC 的高 8 位相异或,把结果放于变量 CRC,低 24 位数据不变;
  4. 检查左移后的移出位;
  5. 如果移出位为 0,左移一位;如果移出位为 1,变量 CRC 左移一位后与多项式 0x04C11DB7 进行异或;
  6. 重复步骤 4 和 5,直到左移 8 次(和数据长度一致),这样整个 8 位数据全部进行了处理;
  7. 重复步骤 2 到步骤 6,进行通讯信息帧下一个数据(字节)的处理;
  8. 将该通讯信息帧所有字节按上述步骤计算完成后,将得到的 32 位变量 CRC,查看规则是否需要反转;
  9. 最后,与结果异或值异或,得到的变量 CRC 即为 CRC 校验值。

(2) 反向校验

  1. 预置 1 个 32 位的变量 CRC,存放校验值,首先赋初值 0xffffffff;
  2. 查看规则判断第一个数据是否需要反转,若需要,则按位反转,若不需要,直接进入第3步;
  3. 把第 1 个数据按照步骤 2处理后,与 32 位的变量 CRC 的低 8 位相异或,把结果放于变量 CRC,高 24 位数据不变;
  4. 检查右移后的移出位;
  5. 如果移出位为 0,右移一位;如果移出位为 1,变量 CRC 右移一位后与多项式 0xEDB88320 进行异或;
  6. 重复步骤 4 和 5,直到右移 8 次(和数据长度一致),这样整个 8 位数据全部进行了处理;
  7. 重复步骤 2 到步骤 6,进行通讯信息帧下一个数据(字节)的处理;
  8. 将该通讯信息帧所有字节按上述步骤计算完成后,将得到的 32 位变量 CRC,查看规则是否需要反转;
  9. 最后,与结果异或值异或,得到的变量 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(循环冗余校验)在线计算

This post is licensed under CC BY 4.0 by the author.

加快访问 Coursera 的速度

Rust 串口编程