• CRC校验


    一:CRC概念

    1.1、参考博客

    参考的教程如下:

    手算CRC及其实现

    CRC校验算法原理分析

    一文讲透CRC校验码-附赠C语言实例

    CRC校验(手算与直观演示)

    CRC(循环冗余校验)在线计算

    1.2、什么是CRC

    CRC(Cyclic Redundancy Checksum)是一种纠错技术,代表循环冗余校验和,可以认为在输入端根据一定的规则计算出来CRC checksum,编组到message信息中,发送到接收端。接收端根据相同的规则解码接收到的信息,如果接收到的CRC checksum与预期值相同,则说明得到的数据正确,否则异常。

    image-20220227151702796

    下面接收CRC checksum如何获取:

    这里需要知道几个组成部分或者说计算概念:多项式公式、多项式简记式(poly)、数据宽度、初始值(init)、结果异或值、输入值反转、输出值反转、参数模型。

    1.3、多项式公式与简记式

    对于CRC标准除数,一般使用多项式(或二项式)公式表示;

    这里以CRC8作为介绍校验算法过程,CRC16和CRC32同理。

    CRC8标准生成多项式

    CRC-8 x8+x5+x4+1 0x31(0x131)
    CRC-8 x8+x2+x1+1 0x07(0x107)
    CRC-8 x8+x6+x4+x3+x2+x1 0x5E(0x15E)
    注:由于多项式的最高为都为1,并且在代码的crc8计算中,最高位也是不使用的,
    所以在多项式记录时都去掉了最高位。

    实际计算也可以采用完整的多项式公式,但是这种会比较麻烦。所以我们实际代码使用的都是简记式;

    1.4、计算过程

    以CRC16为例计算:  

    1.根据CRC16的标准选择初始值(init)

    2.如果有输入反转,则将数据进行反转(如果没有输入反转,则跳过。默认的都是每一个字节按位反转)。

    2.将反转后数据的第一个字节与初始值高8位异或。

    3.判断最高位,若该位为 0 左移一位,若为 1 左移一位再与多项式异或。

    4.重复3直至8位全部移位计算结束。

    5.重复将所有输入数据操作完成以上步骤,所得16位数即16位CRC校验码。

    6.如果有输出反转,则将CRC校验码进行反转(如果没有输出反转,则跳过。默认的都是整个数据按位反转)。

    7.将反转后的CRC校验码与结果异或值得到最终的CRC校验码。

    输入反转举例

    以输入 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08为例说明输入反转的概念

    反转得到的值为:0x80 0x40 0xc0 0x20 0xa0 0x60 0xe0 0x10

    输出反转举例

    0x5ceeac03输出反转值为0xc035773a

    image-20220227154648140

    image-20220227154704443

    二:手算CRC

    手算的时候,最高项保留

    写程序的时候,最高项不保留(优化执行次数)

    image-20220227154842839

    三:代码分析

    3.1、计算一个字节

    //CRC-8
    //计算单个字节
    #include <iostream> //支持uint类型
    uint8_t cal_table_high_first(uint8_t value)
    {
        uint8_t crc;
        crc = value;
        /* 数据往左移了8位,需要计算8次 */
        for (int i =0 ; i< 8 ; i++)
        {
            //1000 0000 = 0x80
            //判断最高位是否为1
            if (crc & 0x80)
            {
              //如果为1,先左移一位然后再与多项式x31异或
              //0011 0001 = 0x31 x^8+x^5+x^4+1
              //左移一位是为了简化最高项x^8的计算
                 crc = (crc << 1) ^ 0x31;        }
            else
            {
              //如果为0,则不需要异或,整体数据左移一位
                 crc = (crc << 1);
            }
        }
        return crc;
    }
    

    3.2、计算多个字节

    //CRC-8
    //计算多个字节
    #include <iostream>  //支持uint类型
    uint8_t crc_high_first(uint8_t  *ptr, int len)
    {
        uint8_t crc=0x00; /* 计算的初始crc值 */
        while(len--)
        {
            //先把上一字节与下一字节异或
            crc ^= *ptr++;
            //下面的代码与计算一个字节的一致
            for (int i=0; i<8 ; i++)
            {
                if (crc & 0x80)
                    crc = (crc << 1) ^ 0x31;
                else
                    crc = (crc << 1);
            }
        }
        return (crc);
    }
    

    3.3、产生表

    //生成表
    //调用了上方的函数
    
    
    #include <iostream>  //支持uint类型
    #include <stdio.h>
    void  create_crc_table(void)
    {
    
            int i = 0x00;
    
    
            for (; i<=0xFF; i++)
            {
                if (0 == (i%16))
                    printf("\n");
    
    
                //把每个字节的CRC检验码的结果保存下来
                printf("0x%.2x, ", cal_table_high_first (i));
            }
    }
    

    3.4、查表方式生成CRC

    //查表计算CRC
    #include <iostream>  //支持uint类型
    uint8_t cal_crc_table(uint8_t *ptr, int len)
    {
        //初始化
        uint8_t crc = 0x00;
        while (len--)
        {
            crc = crc_table[crc ^ *ptr++];
            //等价与  crc = crc_table[  crc ^ (*ptr) ];
            //         ptr++;
        }
        return crc;
    }
    

    3.5、反转

    uint32_t ReflectedData(uint32_t data, REFLECTED_MODE mode)
    {
    	data = ((data & 0xffff0000) >> 16) | ((data & 0x0000ffff) << 16);
    	data = ((data & 0xff00ff00) >> 8) | ((data & 0x00ff00ff) << 8);
    	data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4);
    	data = ((data & 0xcccccccc) >> 2) | ((data & 0x33333333) << 2);
    	data = ((data & 0xaaaaaaaa) >> 1) | ((data & 0x55555555) << 1);
    
    	switch (mode)
    	{
    	case REF_32BIT:
    		return data;
    	case REF_16BIT:
    		return (data >> 16) & 0xffff;
    	case REF_8BIT:
    		return (data >> 24) & 0xff;
    	case REF_7BIT:
    		return (data >> 25) & 0x7f;
    	case REF_6BIT:
    		return (data >> 26) & 0x7f;
    	case REF_5BIT:
    		return (data >> 27) & 0x1f;
    	case REF_4BIT:
    		return (data >> 28) & 0x0f;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Saruman's Army
    Best Cow Line
    Lake Counting
    题目1417:变形金刚
    Ants
    mysql学习笔记--数据库事务
    mysql学习笔记--数据库视图
    mysql学习笔记--数据库多表查询
    mysql学习笔记--数据库单表查询
    mysql学习笔记--数据操作
  • 原文地址:https://www.cnblogs.com/agui125/p/15942563.html
Copyright © 2020-2023  润新知