• 对于 CRC 校验的 学习笔记


    参考资料

    CRC校验原理及实现 - 知乎 (zhihu.com) <-- 这个讲的特别好,我的博客主要是抄他的,最后加了一点代码库的分析。

    [CRC校验]手算与直观演示_哔哩哔哩_bilibili <-- 这个视频非常直观

    【FPGA】CRC校验算法从数学原理到代码实现

    CRC 参数模型

    同样的 CRC 多项式,调用不同的 CRC 计算函数,得到的结果却不一样,这就涉及到 CRC 的参数模型了。计算一个正确的 CRC 值,需要知道 CRC 的参数模型。

    一个完整的 CRC 参数模型应该包含以下信息:WIDTH,POLY,INIT,REFIN,REFOUT,XOROUT。

    • NAME:参数模型名称。
    • WIDTH:宽度,即生成的 CRC 数据位宽,如 CRC-8,生成的 CRC 为8位
    • POLY:十六进制多项式,省略最高位1,如 x8 + x2 + x + 1,二进制为1 0000 0111,省略最高位1,转换为十六进制为 0x07。
    • INIT:CRC初始值,和WIDTH位宽一致。
    • REFIN:true 或 false,在进行计算之前,原始数据是否翻转,如原始数据:0x34 = 0011 0100,如果 REFIN 为 true,进行翻转之后为0010 1100 = 0x2c
    • REFOUT:true 或 false,运算完成之后,得到的 CRC 值是否进行翻转,如计算得到的 CRC 值:0x97 = 1001 0111,如果 REFOUT 为 true,进行翻转之后为 11101001 = 0xE9。
    • XOROUT:计算结果与此参数进行异或运算后得到最终的 CRC 值,和 WIDTH 位宽一致。

    通常如果只给了一个多项式,其他的没有说明则:INIT=0x00,REFIN=false,REFOUT=false,XOROUT=0x00。

    常用的21个标准CRC参数模型:

    21个常见的CRC校验表

    CRC校验在电子通信领域非常常用,可以说有通信存在的地方,就有CRC校验:

    • 美信(MAXIM)的芯片DS2401/DS18B20,都是使用的CRC-8/MAXIM模型
    • SD卡或MMC使用的是CRC-7/MMC模型
    • Modbus通信使用的是CRC-16/MODBUS参数模型
    • USB协议中使用的CRC-5/USB和CRC-16/USB模型
    • STM32自带的硬件CRC计算模块使用的是CRC-32模型

    至于多项式的选择,初始值和异或值的选择,输入输出是否翻转,这就涉及到一定的编码和数学知识了。感兴趣的朋友,可以了解一下每个CRC模型各个参数的来源。至于每种参数模型的检错能力、重复率,需要专业的数学计算了,不在本文讨论的范畴内。

    对于 发送 和 接收 两方的约定

    1. 共同的约定
      • 要传输的数据的原始数据:比如8‘b10101001
      • 生成多项式
    2. 发送方要做的事(编码)
      • 准备要传输的原始数据
      • 根据 原始数据 和 生成多项式 生成 原始数据对应的 CRC
      • 生成新数据({原始数据,CRC})
    3. 接收方要做的事(解码)
      • 接收发送方发送过来的新数据
      • 根据新数据和约定好的生成多项式做校验
      • 根据校验结果接收原始数据

    CRC 计算

    问:原始数据:0x34,使用CRC-8/MAXIN参数模型,求CRC值?

    答:根据CRC参数模型表,得到CRC-8/MAXIN的参数如下:

    POLY = 0x31 = 0011 0001(最高位1已经省略)
    INIT = 0x00
    XOROUT = 0x00
    REFIN = TRUE
    REFOUT = TRUE
    
    • 1
    • 2
    • 3
    • 4
    • 5

    有了上面的参数,这样计算条件才算完整,下面来实际计算:

    0.原始数据 = 0x34 = 0011 0100,多项式 = 0x31 = 1 0011 0001
    1.INIT = 00,原始数据高8位和初始值进行异或运算保持不变。
    2.REFIN为TRUE,需要先对原始数据进行翻转:0011 0100 > 0010 1100
    3.原始数据左移8位,即后面补8个0:0010 1100 0000 0000
    4.把处理之后的数据和多项式进行模2除法,求得余数:
    原始数据:0010 1100 0000 0000 = 10 1100 0000 0000
    多项式:1 0011 0001
    模2除法取余数低8位:1111 1011
    5.与XOROUT进行异或,1111 1011 xor 0000 0000 = 1111 1011 
    6.因为REFOUT为TRUE,对结果进行翻转得到最终的CRC-8值:1101 1111 = 0xDF
    7.数据+CRC:0011 0100 1101 1111 = 34DF,相当于原始数据左移8位+余数。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    模2除法求余数:

    crc计算

    验证手算结果:

    计算

    可以看出是一致的,当你手算的结果和工具计算结果不一致时,可以看看INIT,XOROUT,REFINT,REFOUT这些参数是否一致,有1个参数不对,计算出的 CRC 结果都不一样。

    CRC 校验

    按照上面 CRC 计算的结果,最终的数据帧:0011 0100 1101 1111 = 34DF,前8位0011 0100是原始数据,后8位1101 1111 是 CRC 结果。

    接收端的校验有两种方式,

    1. 一种是和 CRC 计算一样,在本地把接收到的数据和 CRC 分离,然后在本地对数据进行 CRC 运算,得到的 CRC 值和接收到的 CRC 进行比较,如果一致,说明数据接收正确,如果不一致,说明数据有错误。
    2. 另一种方法是把整个数据帧进行 CRC 运算,因为是数据帧相当于把原始数据左移8位,然后加上余数,如果直接对整个数据帧进行 CRC 运算(除以多项式),那么余数应该为0,如果不为0说明数据出错。

    校验

    而且,不同位出错,余数也不同,可以证明,余数与出错位数的对应关系只与CRC参数模型有关,而与原始数据无关。

    CRC 计算的 C 语言实现

    无论是用C还是其他语言,实现方法网上很多,这里我找了一个基于C语言的CRC计算库,里面包含了常用的21个CRC参数模型计算函数,可以直接使用,只有crcLib.ccrcLib.h两个文件。

    GitHub地址:https://github.com/whik/crc-lib-c

    使用方法非常简单:

    #include 
    #include 
    #include "crcLib.h"
    
    int main()
    {
        uint8_t LENGTH = 10;
        uint8_t data[LENGTH];
        uint8_t crc;
    
        for(int i = 0; i < LENGTH; i++)
        {
            data[i] = i*5;
            printf("%02x ", data[i]);
        }
        printf("\n");
    
        crc = crc8_maxim(data, LENGTH);
    
        printf("CRC-8/MAXIM:%02x\n", crc);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    计算结果:

    img

    CRC 库代码分析

    库中crc函数基本相似,一通百通。

    /******************************************************************************
     * Name:    CRC-7/MMC           x7+x3+1
     * Poly:    0x09
     * Init:    0x00
     * Refin:   False
     * Refout:  False
     * Xorout:  0x00
     * Use:     MultiMediaCard,SD,ect.
     *****************************************************************************/
    uint8_t crc7_mmc(uint8_t *data, uint16_t length)
    {
        uint8_t i;
        // 这里的初值为 Init = 0x00
        uint8_t crc = 0;        // Initial value
        while(length--)
        {
            // 第一次进来,crc = 0,异或上任意值,crc = 任意值
            // 后续数据进行异或,因为前一次数据的后7位补0,相当于异或上这些0,就是把数据加到上次的数据末尾
            // 最后一个数据加在末尾后,在下面的for循环,把后面的7个零补回来了。
            crc ^= *data++;        // crc ^= *data; data++;
    
            for ( i = 0; i < 8; i++ )
            {
                // 判断这个高位是否是 1,最高位为 1 时才可异或上多项式
                if ( crc & 0x80 )
                    // 因为最高位为一,异或上注定为0,直接省去计算
                    crc = (crc << 1) ^ 0x12;        // 0x12 = 0x09<<(8-7)
                else
                    crc <<= 1;
            }
        }
        // 因为是 7位宽的,而在循环中多向左移动了一位,再移动回来即可。
        return crc >> 1;
    }
    
    • 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

    CRC 计算工具

    下面这几款工具都可以自定义CRC算法模型,而且都有标准CRC模型可供选择。如果自己用C语言或者Verilog实现校验算法时,非常适合作为标准答案进行验证。

    格西CRC计算器:

    img

    总结

    CRC校验并不能100%的检查出数据的错误,非常低的概率会出现CRC校验正确但数据中有错误位的情况。这和CRC的位数,多项式的选择等等有很大的关系,所以在实际使用中尽量选择标准CRC参数模型,这些多项式参数都是经过理论计算得出的,可以提高CRC的检错能力。CRC校验可以检错,也可以纠正单一比特的错误,你知道纠错的原理吗?

  • 相关阅读:
    乒乓球发球技巧
    【Pingtunnel工具教程】利用ICMP隧道技术进行ICMP封装穿透防火墙
    LeetCode_704_二分查找
    OpenCV笔记--人脸识别算法Eigenfaces和Fisherfaces
    内存ECC高级纠错算法有哪些?
    数据恢复篇:如何在 Android 手机上恢复未保存/删除的 Word 文档
    vue3.0组合式api使用总结
    数字文档管理对您的业务是否具有成本效益?
    23种设计模式之迭代器模式
    【函数式编程实战】(四)流-Stream API原理解析
  • 原文地址:https://blog.csdn.net/bebebug/article/details/130817661