精华内容
下载资源
问答
  • CRC的全称为Cyclic Redundancy Check,中文名称为差错控制理论是...利用CRC进行检错的过程可简单描述为:发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的r位监督 码(CRC码),附原始信息后边...

    5d1201e9a455f446206af216ecb02ab8.png

    CRC的全称为Cyclic Redundancy Check,中文名称为

    差错控制理论是在代数理论基础上建立起来的。这里我们着于介绍CRC的算法与实现,对原理只能捎带说明一下。若需要进一步了解线性码、分组码、循环码、纠错编码等方面的原理,可以阅读有关资料。

    利用CRC进行检错的过程可简单描述为:在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的r位监督 码(CRC码),附在原始信息后边,构成一个新的二进制码序列数共kr位,然后发送出去。在接收端,根据信息码和CRC码之间所遵循的规则进行检验,以 确定传送中是否出错。这个规则,在差错控制理论中称为“生成多项式”。

    1 代数学的一般性算法

    在代数编码理论中,将一个码组表示为一个多项式,码组中各码元当作多项式的系数。例如 1100101 表示为

    1·x61·x50·x40·x31·x20·x1,即 x6x5x21。

    设编码前的原始信息多项式为P(x),P(x)的最高幂次加1等于k;生成多项式为G(x),G(x)的最高幂次等于r;CRC多项式为R(x);编码后的带CRC的信息多项式为T(x)。

    发送方编码方法:将P(x)乘以xr(即对应的二进制码序列左移r位),再除以G(x),所得余式即为R(x)。用公式表示为

    T(x)=xrP(x)R(x)

    接收方解码方法:将T(x)除以G(x),如果余数为0,则说明传输中无错误发生,否则说明传输有误。

    举例来说,设信息码为1100,生成多项式为1011,即P(x)=x3x2,G(x)=x3x1,计算CRC的过程为

    即 R(x)=x。注意到G(x)最高幂次r=3,得出CRC为010。

    如果用竖式除法,计算过程为

    因此,T(x)=(x6x5)(x)=x6x5x, 即 1100000010=1100010

    如果传输无误,

    无余式。回头看一下上面的竖式除法,如果被除数是1100010,显然在商第三个1时,就能除尽。

    上述推算过程,有助于我们理解CRC的概念。但直接编程来实现上面的算法,不仅繁琐,效率也不高。实际上在工程中不会直接这样去计算和验证CRC。

    下表中列出了一些见于标准的CRC资料:

    ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCS

    ZIP, RAR, IEEE 802 LAN/FDDI, IEEE 1394, PPP-FCS

    4.CRC算法的实现

    ---------------

    要用程序实现CRC算法,考虑对第2节的长除法做一下变换,依然是M = 11100110,G = 1011,

    其系数r为3。

    11001100

    ------------------------

    1011 )11100110000

    1011.......

    ----.......

    1010......

    1011......

    ----......

    1110...

    1011...

    ------...

    1010..

    1011..

    -------

    100

    本文来自电脑杂谈,转载请注明本文网址:

    http://www.pc-fly.com/a/tongxinshuyu/article-38614-1.html

    展开全文
  • 循环冗余校验在一些传输协议,发送端并不指出消息长度,而是采用结束标志,考虑以下几种差错:1)消息之前,增加1个或多个0字节;2)消息以1个或多个连续的0字节开始,丢掉1个或多个0;3)消息(包括校验码)之后,...

    接收端对收到的len 2字节执行do_crc,如果没有差错发生则结果应为0。循环冗余校验

    在一些传输协议中,发送端并不指出消息长度,而是采用结束标志,考虑以下几种差错:

    1)在消息之前,增加1个或多个0字节;

    2)消息以1个或多个连续的0字节开始,丢掉1个或多个0;

    3)在消息(包括校验码)之后,增加1个或多个0字节;

    4)消息(包括校验码)以1个或多个连续的0字节结尾,丢掉1个或多个0;

    显然,这几种差错都检测不出来,其原因就是如果寄存器为0,处理0消息字节(或位),寄存器不变。为了解决前2个问题,只需寄存器的初非0即可,对do_crc作以下改进:

    unsigned short do_crc(unsigned short reg_init, unsigned char *message, unsigned int len)

    {

    unsigned short crc_reg = reg_init;

    while (len--)

    crc_reg = (crc_reg >> 8) ^ crc16_ccitt_table[(crc_reg ^ *message) & 0xff];

    return crc_reg;

    }

    在CRC16-CCITT标准中reg_init = 0xffff,为了解决后2个问题,在CRC16-CCITT标准中将计算出的校验码与0xffff进行异或,即:

    unsigned short code = do_crc(0xffff, message, len);

    code ^= 0xffff;

    message[len] = code & 0x00ff;

    message[len 1] = (code >> 8) & 0x00ff;

    显然,现在接收端对收到的所有字节执行do_crc,如果没有差错发生则结果应为某一常GOOD_CRC。其满足以下关系:

    unsigned char p[]= {0xff, 0xff};

    GOOD_CRC = do_crc(0, p, 2);

    其结果为GOOD_CRC = 0xf0b8。

    在同一程序中验证如下(放在main函数中可试验):

    unsigned char p[]= {0xa0,0xb0,0xff, 0xff};

    unsigned short crc;

    crc= do_crc(0xffff, p, 2); //计算前两位的CRC码

    crc^=0xffff; //对其取反

    p[2]=crc&0x00ff; //将计算的CRC码加到信息序列后面

    p[3]=crc>>8&0x00ff;

    printf("p[2]=%x,p3=%x\n",p[2],p[3]);

    crc=do_crc(0xffff,p,4); //对信息码+CRC码共同计算得出CRC=0xf0b8

    printf("crc is %x\n",crc);

    假设发送的信息是p[0],p[1];低位先发,对其计算的CRC加到信息码后面

    然后对信息码+CRC码共同计算CRC,此时应该是常数0xf0b8。不管信息码如何变化,内容和长度都可变,只要把计算的CRC码加进去一起计算CRC,就应该是得该常数GOOD_CRC。

    参考文献

    --------

    [1] Ross N. Williams,"A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS",Version 3,

    ,August 1993

    [2] Simpson, W., Editor, "PPP in HDLC Framing",RFC 1549, December 1993

    [3] P. E. Boudreau,W. C. Bergman and D. R. lrvin,"Performance of a cyclic redundancy check and its interaction with a data scrambler",IBM J. RES. DEVELOP.,VOL.38 NO.6,November 1994

    本文来自电脑杂谈,转载请注明本文网址:

    http://www.pc-fly.com/a/tongxinshuyu/article-38614-6.html

    展开全文
  • 循环冗余校验(CRC)算法入门引导

    万次阅读 多人点赞 2012-08-19 12:42:34
    CRC校验(循环冗余校验)是数据通讯最常采用的校验方式。嵌入式软件开发,经常要用到CRC 算法对各种数据进行校验。因此,掌握基本的CRC算法应是嵌入式程序员的基本技能。可是,我认识的嵌入式程序员中能真正...

    写给嵌入式程序员的循环冗余校验(CRC)算法入门引导

    前言

    CRC校验(循环冗余校验)是数据通讯中最常采用的校验方式。在嵌入式软件开发中,经常要用到CRC 算法对各种数据进行校验。因此,掌握基本的CRC算法应是嵌入式程序员的基本技能。可是,我认识的嵌入式程序员中能真正掌握CRC算法的人却很少,平常在项目中见到的CRC的代码多数都是那种效率非常低下的实现方式。

    其实,在网上有一篇介绍CRC 算法的非常好的文章,作者是Ross Williams,题目叫:“A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS”。我常将这篇文章推荐给向我询问CRC算法的朋友,但不少朋友向我抱怨原文太长了,而且是英文的。希望我能写篇短点的文章,因此就有了本文。不过,我的水平比不了Ross Williams,我的文章肯定也没Ross Williams的写的好。因此,阅读英文没有障碍的朋友还是去读Ross Williams的原文吧。

    本文的读者群设定为软件开发人员,尤其是从事嵌入式软件开发的程序员,而不是专业从事数学或通讯领域研究的学者(我也没有这个水平写的这么高深)。因此,本文的目标是介绍CRC算法的基本原理和实现方式,用到的数学尽量控制在高中生可以理解的深度。

    另外,鉴于大多数嵌入式程序员都是半路出家转行过来的,不少人只会C语言。因此,文中的示例代码全部采用C语言来实现。作为一篇入门短文,文中给出的代码更注重于示范性,尽可能的保持易读性。因此,文中的代码并不追求最高效的实现,但对于一般的应用却也足够快速了。

    从奇偶校验说起

    所谓通讯过程的校验是指在通讯数据后加上一些附加信息,通过这些附加信息来判断接收到的数据是否和发送出的数据相同。比如说RS232串行通讯可以设置奇偶校验位,所谓奇偶校验就是在发送的每一个字节后都加上一位,使得每个字节中1的个数为奇数个或偶数个。比如我们要发送的字节是0x1a,二进制表示为0001 1010。

    采用奇校验,则在数据后补上个0,数据变为0001 1010 0,数据中1的个数为奇数个(3个)

    采用偶校验,则在数据后补上个1,数据变为0001 1010 1,数据中1的个数为偶数个(4个)

    接收方通过计算数据中1个数是否满足奇偶性来确定数据是否有错。

    奇偶校验的缺点也很明显,首先,它对错误的检测概率大约只有50%。也就是只有一半的错误它能够检测出来。另外,每传输一个字节都要附加一位校验位,对传输效率的影响很大。因此,在高速数据通讯中很少采用奇偶校验。奇偶校验优点也很明显,它很简单,因此可以用硬件来实现,这样可以减少软件的负担。因此,奇偶校验也被广泛的应用着。

    奇偶校验就先介绍到这来,之所以从奇偶校验说起,是因为这种校验方式最简单,而且后面将会知道奇偶校验其实就是CRC 校验的一种(CRC-1)。

    累加和校验

    另一种常见的校验方式是累加和校验。所谓累加和校验实现方式有很多种,最常用的一种是在一次通讯数据包的最后加入一个字节的校验数据。这个字节内容为前面数据包中全部数据的忽略进位的按字节累加和。比如下面的例子:

    我们要传输的信息为: 6、23、4

    加上校验和后的数据包:6、23、4、33

    这里 33 为前三个字节的校验和。接收方收到全部数据后对前三个数据进行同样的累加计算,如果累加和与最后一个字节相同的话就认为传输的数据没有错误。

    累加和校验由于实现起来非常简单,也被广泛的采用。但是这种校验方式的检错能力也比较一般,对于单字节的校验和大概有1/256 的概率将原本是错误的通讯数据误判为正确数据。之所以这里介绍这种校验,是因为CRC校验在传输数据的形式上与累加和校验是相同的,都可以表示为:通讯数据 校验字节(也可能是多个字节)

    初识 CRC 算法

    CRC 算法的基本思想是将传输的数据当做一个位数很长的数。将这个数除以另一个数。得到的余数作为校验数据附加到原数据后面。还以上面例子中的数据为例:

    6、23、4 可以看做一个2进制数: 0000011000010111 00000010

    假如被除数选9,二进制表示为:1001

    则除法运算可以表示为:

    可以看到,最后的余数为1。如果我们将这个余数作为校验和的话,传输的数据则是:6、23、4、1

    CRC 算法和这个过程有点类似,不过采用的不是上面例子中的通常的这种除法。在CRC算法中,将二进制数据流作为多项式的系数,然后进行的是多项式的乘除法。还是举个例子吧。

    比如说我们有两个二进制数,分别为:1101 和1011。

    1101 与如下的多项式相联系:1x3+1x2+0x1+1x0=x3+x2+x0

    1011与如下的多项式相联系:1x3+0x2+1x1+1x0=x3+x1+x0

    两个多项式的乘法:(x3+x2+x0)(x3+x1+x0)=x6+x5+x4+x3+x3+x3+x2+x1+x0

    得到结果后,合并同类项时采用模2运算。也就是说乘除法采用正常的多项式乘除法,而加减法都采用模2运算。所谓模2运算就是结果除以2后取余数。比如3 mod 2 = 1。因此,上面最终得到的多项式为:x6+x5+x4+x3+x2+x1+x0,对应的二进制数:111111

    加减法采用模2运算后其实就成了一种运算了,就是我们通常所说的异或运算:

    0+0=0

    0+1=1

    1+0=1

    1+1=0

    0-0=0

    1-0=1

    0-1=1

    1-1=0

    上面说了半天多项式,其实就算是不引入多项式乘除法的概念也可以说明这些运算的特殊之处。只不过几乎所有讲解 CRC 算法的文献中都会提到多项式,因此这里也简单的写了一点基本的概念。不过总用这种多项式表示也很罗嗦,下面的讲解中将尽量采用更简洁的写法。

    除法运算与上面给出的乘法概念类似,还是遇到加减的地方都用异或运算来代替。下面是一个例子:

    要传输的数据为:1101011011

    除数设为:10011

    在计算前先将原始数据后面填上4个0:11010110110000,之所以要补0,后面再做解释。

    从这个例子可以看出,采用了模2的加减法后,不需要考虑借位的问题,所以除法变简单了。最后得到的余数就是CRC 校验字。为了进行CRC运算,也就是这种特殊的除法运算,必须要指定个被除数,在CRC算法中,这个被除数有一个专有名称叫做“生成多项式”。生成多项式的选取是个很有难度的问题,如果选的不好,那么检出错误的概率就会低很多。好在这个问题已经被专家们研究了很长一段时间了,对于我们这些使用者来说,只要把现成的成果拿来用就行了。

    最常用的几种生成多项式如下:

    CRC8=X8+X5+X4+X0

    CRC-CCITT=X16+X12+X5+X0

    CRC16=X16+X15+X2+X0

    CRC12=X12+X11+X3+X2+X0

    CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+X0

    有一点要特别注意,文献中提到的生成多项式经常会说到多项式的位宽(Width,简记为W),这个位宽不是多项式对应的二进制数的位数,而是位数减1。比如CRC8中用到的位宽为8的生成多项式,其实对应得二进制数有九位:100110001。另外一点,多项式表示和二进制表示都很繁琐,交流起来不方便,因此,文献中多用16进制简写法来表示,因为生成多项式的最高位肯定为1,最高位的位置由位宽可知,故在简记式中,将最高的1统一去掉了,如CRC32的生成多项式简记为04C11DB7实际上表示的是104C11DB7。当然,这样简记除了方便外,在编程计算时也有它的用处。

    对于上面的例子,位宽为4(W=4),按照CRC算法的要求,计算前要在原始数据后填上W个0,也就是4个0。

    位宽W=1的生成多项式(CRC1)有两种,分别是X1和X1+X0,读者可以自己证明10 对应的就是奇偶校验中的奇校验,而11对应则是偶校验。因此,写到这里我们知道了奇偶校验其实就是CRC校验的一种特例,这也是我要以奇偶校验作为开篇介绍的原因了。

    CRC算法的编程实现

    说了这么多总算到了核心部分了。从前面的介绍我们知道CRC校验核心就是实现无借位的除法运算。下面还是通过一个例子来说明如何实现CRC校验。

    假设我们的生成多项式为:100110001(简记为0x31),也就是CRC-8

    则计算步骤如下:

    (1)      将CRC寄存器(8-bits,比生成多项式少1bit)赋初值0

    (2)      在待传输信息流后面加入8个0

    (3)      While (数据未处理完)

    (4)      Begin

    (5)          If (CRC寄存器首位是1)

    (6)              reg = reg XOR 0x31

    (7)          CRC寄存器左移一位,读入一个新的数据于CRC寄存器的0 bit的位置。

    (8)      End

    (9)      CRC寄存器就是我们所要求的余数。

     

    实际上,真正的CRC 计算通常与上面描述的还有些出入。这是因为这种最基本的CRC除法有个很明显的缺陷,就是数据流的开头添加一些0并不影响最后校验字的结果。这个问题很让人恼火啊,因此真正应用的CRC 算法基本都在原始的CRC算法的基础上做了些小的改动。

    所谓的改动,也就是增加了两个概念,第一个是“余数初始值”,第二个是“结果异或值”。

    所谓的“余数初始值”就是在计算CRC值的开始,给CRC寄存器一个初始值。“结果异或值”是在其余计算完成后将CRC寄存器的值在与这个值进行一下异或操作作为最后的校验值。

    常见的三种CRC 标准用到个各个参数如下表。

     

    CCITT

    CRC16

    CRC32

    校验和位宽W

    16

    16

    32

    生成多项式

    x16+x12+x5+1

    x16+x15+x2+1

    x32+x26+x23+x22+x16+

    x12+x11+x10+x8+x7+x5+

    x4+x2+x1+1

    除数(多项式)

    0x1021

    0x8005

    0x04C11DB7

    余数初始值

    0xFFFF

    0x0000

    0xFFFFFFFF

    结果异或值

    0x0000

    0x0000

    0xFFFFFFFF

     

    加入这些变形后,常见的算法描述形式就成了这个样子了:

    (1)      设置CRC寄存器,并给其赋值为“余数初始值”。

    (2)      将数据的第一个8-bit字符与CRC寄存器进行异或,并把结果存入CRC寄存器。

    (3)      CRC寄存器向右移一位,MSB补零,移出并检查LSB。

    (4)      如果LSB为0,重复第三步;若LSB为1,CRC寄存器与0x31相异或。

    (5)      重复第3与第4步直到8次移位全部完成。此时一个8-bit数据处理完毕。

    (6)      重复第2至第5步直到所有数据全部处理完成。

    (7)      最终CRC寄存器的内容与“结果异或值”进行或非操作后即为CRC值。

     

    示例性的C代码如下所示,因为效率很低,项目中如对计算时间有要求应该避免采用这样的代码。不过这个代码已经比网上常见的计算代码要好了,因为这个代码有一个crc的参数,可以将上次计算的crc结果传入函数中作为这次计算的初始值,这对大数据块的CRC计算是很有用的,不需要一次将所有数据读入内存,而是读一部分算一次,全读完后就计算完了。这对内存受限系统还是很有用的。

    #define POLY        0x1021
    /**
     * Calculating CRC-16 in 'C'
     * @para addr, start of data
     * @para num, length of data
     * @para crc, incoming CRC
     */
    uint16_t crc16(unsigned char *addr, int num, uint16_t crc)
    {
        int i;
        for (; num > 0; num--)              /* Step through bytes in memory */
        {
            crc = crc ^ (*addr++ << 8);     /* Fetch byte from memory, XOR into CRC top byte*/
            for (i = 0; i < 8; i++)             /* Prepare to rotate 8 bits */
            {
                if (crc & 0x8000)            /* b15 is set... */
                    crc = (crc << 1) ^ POLY;    /* rotate and XOR with polynomic */
                else                          /* b15 is clear... */
                    crc <<= 1;                  /* just rotate */
            }                             /* Loop for 8 bits */
            crc &= 0xFFFF;                  /* Ensure CRC remains 16-bit value */
        }                               /* Loop until num=0 */
        return(crc);                    /* Return updated CRC */
    }
    

    上面的代码是我从http://mdfs.net/Info/Comp/Comms/CRC16.htm找到的,不过原始代码有错误,我做了些小的修改。

    下面对这个函数给出个例子片段代码:

        unsigned char data1[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
        unsigned char data2[] = {'5', '6', '7', '8', '9'};
        unsigned short c1, c2;
        c1 = crc16(data1, 9, 0xffff);
        c2 = crc16(data1, 4, 0xffff);
        c2 = crc16(data2, 5, c2);
        printf("%04x\n", c1);
        printf("%04x\n", c2);
    

    读者可以验算,c1、c2 的结果都为 29b1。上面代码中crc 的初始值之所以为0xffff,是因为CCITT标准要求的除数初始值就是0xffff。

    上面的算法对数据流逐位进行计算,效率很低。实际上仔细分析CRC计算的数学性质后我们可以多位多位计算,最常用的是一种按字节查表的快速算法。该算法基于这样一个事实:计算本字节后的CRC码,等于上一字节余式CRC码的低8位左移8位,加上上一字节CRC右移 8位和本字节之和后所求得的CRC码。如果我们把8位二进制序列数的CRC(共256个)全部计算出来,放在一个表里,编码时只要从表中查找对应的值进行处理即可。

    按照这个方法,可以有如下的代码(这个代码也不是我写的,是我在Micbael Barr的书“Programming Embedded Systems in C and C++” 中找到的,同样,我做了点小小的改动。):

    /*
    crc.h
    */
    
    #ifndef CRC_H_INCLUDED
    #define CRC_H_INCLUDED
    
    /*
    * The CRC parameters. Currently configured for CCITT.
    * Simply modify these to switch to another CRC Standard.
    */
    /*
    #define POLYNOMIAL          0x8005
    #define INITIAL_REMAINDER   0x0000
    #define FINAL_XOR_VALUE     0x0000
    */
    #define POLYNOMIAL          0x1021
    #define INITIAL_REMAINDER   0xFFFF
    #define FINAL_XOR_VALUE     0x0000
    
    /*
    #define POLYNOMIAL          0x1021
    #define POLYNOMIAL          0xA001
    #define INITIAL_REMAINDER   0xFFFF
    #define FINAL_XOR_VALUE     0x0000
    */
    
    /*
    * The width of the CRC calculation and result.
    * Modify the typedef for an 8 or 32-bit CRC standard.
    */
    typedef unsigned short width_t;
    #define WIDTH (8 * sizeof(width_t))
    #define TOPBIT (1 << (WIDTH - 1))
    
    /**
     * Initialize the CRC lookup table.
     * This table is used by crcCompute() to make CRC computation faster.
     */
    void crcInit(void);
    
    /**
     * Compute the CRC checksum of a binary message block.
     * @para message, 用来计算的数据
     * @para nBytes, 数据的长度
     * @note This function expects that crcInit() has been called
     *       first to initialize the CRC lookup table.
     */
    width_t crcCompute(unsigned char * message, unsigned int nBytes);
    
    #endif // CRC_H_INCLUDED
    

     

    /*
     *crc.c
     */
    
    #include "crc.h"
    /*
    * An array containing the pre-computed intermediate result for each
    * possible byte of input. This is used to speed up the computation.
    */
    static width_t crcTable[256];
    
    /**
     * Initialize the CRC lookup table.
     * This table is used by crcCompute() to make CRC computation faster.
     */
    void crcInit(void)
    {
        width_t remainder;
        width_t dividend;
        int bit;
        /* Perform binary long division, a bit at a time. */
        for(dividend = 0; dividend < 256; dividend++)
        {
            /* Initialize the remainder.  */
            remainder = dividend << (WIDTH - 8);
            /* Shift and XOR with the polynomial.   */
            for(bit = 0; bit < 8; bit++)
            {
                /* Try to divide the current data bit.  */
                if(remainder & TOPBIT)
                {
                    remainder = (remainder << 1) ^ POLYNOMIAL;
                }
                else
                {
                    remainder = remainder << 1;
                }
            }
            /* Save the result in the table. */
            crcTable[dividend] = remainder;
        }
    } /* crcInit() */
    
    /**
     * Compute the CRC checksum of a binary message block.
     * @para message, 用来计算的数据
     * @para nBytes, 数据的长度
     * @note This function expects that crcInit() has been called
     *       first to initialize the CRC lookup table.
     */
    width_t crcCompute(unsigned char * message, unsigned int nBytes)
    {
        unsigned int offset;
        unsigned char byte;
        width_t remainder = INITIAL_REMAINDER;
        /* Divide the message by the polynomial, a byte at a time. */
        for( offset = 0; offset < nBytes; offset++)
        {
            byte = (remainder >> (WIDTH - 8)) ^ message[offset];
            remainder = crcTable[byte] ^ (remainder << 8);
        }
        /* The final remainder is the CRC result. */
        return (remainder ^ FINAL_XOR_VALUE);
    } /* crcCompute() */
    

     

    上面代码中crcInit() 函数用来计算crcTable,因此在调用 crcCompute 前必须先调用 crcInit()。不过,对于嵌入式系统,RAM是很紧张的,最好将 crcTable 提前算好,作为常量数据存到程序存储区而不占用RAM空间。CRC 计算实际上还有很多内容可以介绍,不过对于一般的程序员来说,知道这些也就差不多了。余下的部分以后有时间了我再写篇文章来介绍吧。

     

    最后,给出个 C++ 代码,实现了 CRC8、CRC16 和 CRC32 的计算。收集了常见的各种 CRC 系数。 代码可以从这里下载:https://code.csdn.net/liyuanbhu/crc_compute/tree/master

    #ifndef CRCCOMPUTE_H
    #define CRCCOMPUTE_H
    
    #include <stdint.h>
    
    template <typename TYPE> class CRC
    {
    public:
        CRC();
        CRC(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value);
        void build(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value);
        /**
         * Compute the CRC checksum of a binary message block.
         * @para message, 用来计算的数据
         * @para nBytes, 数据的长度
         */
        TYPE crcCompute(char * message, unsigned int nBytes);
        TYPE crcCompute(char * message, unsigned int nBytes, bool reinit);
    protected:
        TYPE m_polynomial;
        TYPE m_initial_remainder;
        TYPE m_final_xor_value;
        TYPE m_remainder;
        TYPE crcTable[256];
        int m_width;
        int m_topbit;
        /**
         * Initialize the CRC lookup table.
         * This table is used by crcCompute() to make CRC computation faster.
         */
        void crcInit(void);
    };
    
    template <typename TYPE>
    CRC<TYPE>::CRC()
    {
        m_width = 8 * sizeof(TYPE);
        m_topbit = 1 << (m_width - 1);
    }
    
    template <typename TYPE>
    CRC<TYPE>::CRC(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value)
    {
        m_width = 8 * sizeof(TYPE);
        m_topbit = 1 << (m_width - 1);
        m_polynomial = polynomial;
        m_initial_remainder = init_remainder;
        m_final_xor_value = final_xor_value;
    
        crcInit();
    }
    
    template <typename TYPE>
    void CRC<TYPE>::build(TYPE polynomial, TYPE init_remainder, TYPE final_xor_value)
    {
        m_polynomial = polynomial;
        m_initial_remainder = init_remainder;
        m_final_xor_value = final_xor_value;
    
        crcInit();
    }
    
    template <typename TYPE>
    TYPE CRC<TYPE>::crcCompute(char * message, unsigned int nBytes)
    {
        unsigned int offset;
        unsigned char byte;
        TYPE remainder = m_initial_remainder;
        /* Divide the message by the polynomial, a byte at a time. */
        for( offset = 0; offset < nBytes; offset++)
        {
            byte = (remainder >> (m_width - 8)) ^ message[offset];
            remainder = crcTable[byte] ^ (remainder << 8);
        }
        /* The final remainder is the CRC result. */
        return (remainder ^ m_final_xor_value);
    }
    
    template <typename TYPE>
    TYPE CRC<TYPE>::crcCompute(char * message, unsigned int nBytes, bool reinit)
    {
        unsigned int offset;
        unsigned char byte;
        if(reinit)
        {
            m_remainder = m_initial_remainder;
        }
        /* Divide the message by the polynomial, a byte at a time. */
        for( offset = 0; offset < nBytes; offset++)
        {
            byte = (m_remainder >> (m_width - 8)) ^ message[offset];
            m_remainder = crcTable[byte] ^ (m_remainder << 8);
        }
        /* The final remainder is the CRC result. */
        return (m_remainder ^ m_final_xor_value);
    }
    
    class CRC8 : public CRC<uint8_t>
    {
    public:
        enum CRC8_TYPE {eCRC8, eAUTOSAR, eCDMA2000, eDARC, eDVB_S2, eEBU, eAES, eGSM_A, eGSM_B, eI_CODE,
                        eITU, eLTE, eMAXIM, eOPENSAFETY, eROHC, eSAE_J1850, eWCDMA};
        CRC8(CRC8_TYPE type);
        CRC8(uint8_t polynomial, uint8_t init_remainder, uint8_t final_xor_value)
            :CRC<uint8_t>(polynomial, init_remainder, final_xor_value){}
    };
    
    class CRC16 : public CRC<uint16_t>
    {
    public:
        enum CRC16_TYPE {eCCITT, eKERMIT, eCCITT_FALSE, eIBM, eARC, eLHA, eSPI_FUJITSU,
                         eBUYPASS, eVERIFONE, eUMTS, eCDMA2000, eCMS, eDDS_110, eDECT_R,
                         eDECT_X, eDNP, eEN_13757, eGENIBUS, eEPC, eDARC, eI_CODE, eGSM,
                         eLJ1200, eMAXIM, eMCRF4XX, eOPENSAFETY_A, eOPENSAFETY_B, ePROFIBUS,
                         eIEC_61158_2, eRIELLO, eT10_DIF, eTELEDISK, eTMS37157, eUSB,
                         eCRC_A, eMODBUS, eX_25, eCRC_B, eISO_HDLC, eIBM_SDLC, eXMODEM,
                         eZMODEM, eACORN, eLTE};
        CRC16(CRC16_TYPE type);
        CRC16(uint16_t polynomial, uint16_t init_remainder, uint16_t final_xor_value)
            :CRC<uint16_t>(polynomial, init_remainder, final_xor_value){}
    };
    
    class CRC32 : public CRC<uint32_t>
    {
    public:
        enum CRC32_TYPE {eADCCP, ePKZIP, eCRC32, eAAL5, eDECT_B, eB_CRC32, eBZIP2, eAUTOSAR,
                         eCRC32C, eCRC32D, eMPEG2, ePOSIX, eCKSUM, eCRC32Q, eJAMCRC, eXFER};
        CRC32(CRC32_TYPE type);
    };
    
    
    #endif // CRCCOMPUTE_H
    

    #include "crcCompute.h"
    
    template <typename TYPE>
    void CRC<TYPE>::crcInit(void)
    {
        TYPE remainder;
        TYPE dividend;
        int bit;
        /* Perform binary long division, a bit at a time. */
        for(dividend = 0; dividend < 256; dividend++)
        {
            /* Initialize the remainder.  */
            remainder = dividend << (m_width - 8);
            /* Shift and XOR with the polynomial.   */
            for(bit = 0; bit < 8; bit++)
            {
                /* Try to divide the current data bit.  */
                if(remainder & m_topbit)
                {
                    remainder = (remainder << 1) ^ m_polynomial;
                }
                else
                {
                    remainder = remainder << 1;
                }
            }
            /* Save the result in the table. */
            crcTable[dividend] = remainder;
        }
    }
    
    CRC8::CRC8(CRC8_TYPE type)
    {
        switch (type)
        {
        case eCRC8:
            m_polynomial = 0x07; //http://reveng.sourceforge.net/crc-catalogue/all.htm
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        case eAUTOSAR:
            m_polynomial = 0x2f;
            m_initial_remainder = 0xff;
            m_final_xor_value = 0xff;
            break;
        case eCDMA2000:
            m_polynomial = 0x9b;
            m_initial_remainder = 0xFF;
            m_final_xor_value = 0x00;
            break;
        case eDARC:
            m_polynomial = 0x39;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        case eDVB_S2:
            m_polynomial = 0xd5;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        case eEBU:
        case eAES:
            m_polynomial = 0x1d;
            m_initial_remainder = 0xFF;
            m_final_xor_value = 0x00;
            break;
        case eGSM_A:
            m_polynomial = 0x1d;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        case eGSM_B:
            m_polynomial = 0x49;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0xFF;
            break;
        case eI_CODE:
            m_polynomial = 0x1d;
            m_initial_remainder = 0xFD;
            m_final_xor_value = 0x00;
            break;
        case eITU:
            m_polynomial = 0x07;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x55;
            break;
        case eLTE:
            m_polynomial = 0x9b;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        case eMAXIM:
            m_polynomial = 0x31;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        case eOPENSAFETY:
            m_polynomial = 0x2f;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        case eROHC:
            m_polynomial = 0x07;
            m_initial_remainder = 0xff;
            m_final_xor_value = 0x00;
            break;
        case eSAE_J1850:
            m_polynomial = 0x1d;
            m_initial_remainder = 0xff;
            m_final_xor_value = 0xff;
            break;
        case eWCDMA:
            m_polynomial = 0x9b;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        default:
            m_polynomial = 0x07;
            m_initial_remainder = 0x00;
            m_final_xor_value = 0x00;
            break;
        }
        crcInit();
    
    }
    
    CRC16::CRC16(CRC16_TYPE type)
    {
        switch (type)
        {
        case eCCITT_FALSE:
        case eMCRF4XX:
            m_polynomial = 0x1021;
            m_initial_remainder = 0xFFFF;
            m_final_xor_value = 0x0000;
            break;
        case eIBM:
        case eARC:
        case eLHA:
        case eBUYPASS:
        case eVERIFONE:
        case eUMTS:
            m_polynomial = 0x8005;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case eSPI_FUJITSU:
            m_polynomial = 0x1021;
            m_initial_remainder = 0x1d0f;
            m_final_xor_value = 0x0000;
            break;
        case eCCITT:
        case eKERMIT:
        case eXMODEM:
        case eZMODEM:
        case eACORN:
        case eLTE:
            m_polynomial = 0x1021;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case eCDMA2000:
            m_polynomial = 0xc867;
            m_initial_remainder = 0xffff;
            m_final_xor_value = 0x0000;
            break;
        case eCMS:
        case eMODBUS:
            m_polynomial = 0x8005;
            m_initial_remainder = 0xffff;
            m_final_xor_value = 0x0000;
            break;
        case eDDS_110:
            m_polynomial = 0x8005;
            m_initial_remainder = 0x800d;
            m_final_xor_value = 0x0000;
            break;
        case eDECT_R:
            m_polynomial = 0x0589;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0001;
            break;
        case eDECT_X:
            m_polynomial = 0x0589;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case eDNP:
        case eEN_13757:
            m_polynomial = 0x3d65;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0xffff;
            break;
        case eGENIBUS:
        case eEPC:
        case eDARC:
        case eI_CODE:
        case eX_25:
        case eCRC_B:
        case eISO_HDLC:
        case eIBM_SDLC:
            m_polynomial = 0x1021;
            m_initial_remainder = 0xffff;
            m_final_xor_value = 0xffff;
            break;
        case eGSM:
            m_polynomial = 0x1021;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0xffff;
            break;
        case eLJ1200:
            m_polynomial = 0x6f63;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case eMAXIM:
            m_polynomial = 0x8005;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0xffff;
            break;
        case eOPENSAFETY_A:
            m_polynomial = 0x5935;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case eOPENSAFETY_B:
            m_polynomial = 0x755b;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case ePROFIBUS:
        case eIEC_61158_2:
            m_polynomial = 0x1dcf;
            m_initial_remainder = 0xffff;
            m_final_xor_value = 0xffff;
            break;
        case eRIELLO:
            m_polynomial = 0x1021;
            m_initial_remainder = 0xb2aa;
            m_final_xor_value = 0x0000;
            break;
        case eT10_DIF:
            m_polynomial = 0x8bb7;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case eTELEDISK:
            m_polynomial = 0xa097;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        case eTMS37157:
            m_polynomial = 0x1021;
            m_initial_remainder = 0x89ec;
            m_final_xor_value = 0x0000;
            break;
        case eUSB:
            m_polynomial = 0x8005;
            m_initial_remainder = 0xffff;
            m_final_xor_value = 0xffff;
            break;
        case eCRC_A:
            m_polynomial = 0x1021;
            m_initial_remainder = 0xc6c6;
            m_final_xor_value = 0x0000;
            break;
        default:
            m_polynomial = 0x8005;
            m_initial_remainder = 0x0000;
            m_final_xor_value = 0x0000;
            break;
        }
        crcInit();
    }
    
    
    CRC32::CRC32(CRC32_TYPE type)
    {
        switch (type)
        {
        case eADCCP:
        case ePKZIP:
        case eCRC32:
        case eBZIP2:
        case eAAL5:
        case eDECT_B:
        case eB_CRC32:
            m_polynomial = 0x04c11db7;
            m_initial_remainder = 0xFFFFFFFF;
            m_final_xor_value = 0xFFFFFFFF;
            break;
        case eAUTOSAR:
            m_polynomial = 0xf4acfb13;
            m_initial_remainder = 0xFFFFFFFF;
            m_final_xor_value = 0xFFFFFFFF;
            break;
        case eCRC32C:
            m_polynomial = 0x1edc6f41;
            m_initial_remainder = 0xFFFFFFFF;
            m_final_xor_value = 0xFFFFFFFF;
            break;
        case eCRC32D:
            m_polynomial = 0xa833982b;
            m_initial_remainder = 0xFFFFFFFF;
            m_final_xor_value = 0xFFFFFFFF;
            break;
        case eMPEG2:
        case eJAMCRC:
            m_polynomial = 0x04c11db7;
            m_initial_remainder = 0xFFFFFFFF;
            m_final_xor_value = 0x00000000;
            break;
        case ePOSIX:
        case eCKSUM:
            m_polynomial = 0x04c11db7;
            m_initial_remainder = 0x00000000;
            m_final_xor_value = 0xFFFFFFFF;
            break;
        case eCRC32Q:
            m_polynomial = 0x814141ab;
            m_initial_remainder = 0x00000000;
            m_final_xor_value = 0x00000000;
            break;
        case eXFER:
            m_polynomial = 0x000000af;
            m_initial_remainder = 0x00000000;
            m_final_xor_value = 0x00000000;
            break;
        default:
            m_polynomial = 0x04C11DB7;
            m_initial_remainder = 0xFFFFFFFF;
            m_final_xor_value = 0xFFFFFFFF;
            break;
        }
        crcInit();
    }
    
    

    #include <iostream>
    #include <stdio.h>
    #include "crcCompute.h"
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
    
        CRC16 crc16(CRC16::eCCITT_FALSE);
        char data1[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
        char data2[] = {'5', '6', '7', '8', '9'};
        unsigned short c1, c2;
        c1 = crc16.crcCompute(data1, 9);
        c2 = crc16.crcCompute(data1, 4, true);
        c2 = crc16.crcCompute(data2, 5, false);
    
    
        printf("%04x\n", c1);
        printf("%04x\n", c2);
    
        return 0;
    }
    


     

     

    展开全文
  • 前言本文主要给大家介绍了关于Java实现循环冗余校验(CRC32)的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。CRC32简介CRC校验实用程序库 数据存储和数据通讯领域,为了保证数据的...

    前言

    本文主要给大家介绍了关于Java实现循环冗余校验(CRC32)的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

    CRC32简介

    CRC校验实用程序库 在数据存储和数据通讯领域,为了保证数据的正确,就不得不采用检错的手段。在诸多检错手段中,CRC是最著名的一种。CRC的全称是循环冗余校验。

    CRC32检错能力极强,开销小,易于用编码器及检测电路实现。从其检错能力来看,它所不能发现的错误的几率仅为0.0047%以下。从性能上和开销上考虑,均远远优于奇偶校验及算术和校验等方式。因而,在数据存储和数据通讯领域,CRC无处不在:著名的通讯协议X.25的FCS(帧检错序列)采用的是CRC-CCITT,ARJ、LHA等压缩工具软件采用的是CRC32,磁盘驱动器的读写采用了CRC16,通用的图像存储格式GIF、TIFF等也都用CRC作为检错手段。

    CRC实现

    package com.jianggujin.codec;

    import java.io.IOException;

    import java.io.InputStream;

    import java.util.zip.CRC32;

    /**

    * CRC32

    *

    * @author jianggujin

    *

    */

    public class HQCRC32

    {

    private static HQCRC32 crc32 = new HQCRC32();

    public static HQCRC32 getInstance()

    {

    return crc32;

    }

    private HQCRC32()

    {

    }

    private static final int STREAM_BUFFER_LENGTH = 1024;

    public long encrypt(byte[] data)

    {

    CRC32 crc32 = new CRC32();

    crc32.update(data);

    return crc32.getValue();

    }

    public long encrypt(InputStream data) throws IOException

    {

    final byte[] buffer = new byte[STREAM_BUFFER_LENGTH];

    int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);

    CRC32 crc32 = new CRC32();

    while (read > -1)

    {

    crc32.update(buffer, 0, read);

    read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);

    }

    return crc32.getValue();

    }

    }

    测试代码:

    import org.junit.Test;

    import com.jianggujin.codec.HQCRC32;

    public class CRC32Test

    {

    HQCRC32 crc32 = HQCRC32.getInstance();

    @Test

    public void encode()

    {

    byte[] data = "jianggujin".getBytes();

    long result = crc32.encrypt(data);

    System.err.println(result);

    }

    }

    测试结果:

    724585211

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对聚米学院的支持。

    展开全文
  • 本文介绍了循环冗余校验(CRC)MCS-51单片机的实现方法。
  • 执行循环冗余校验 示例说明如何对数值的位执行循环冗余校验(CRC)。CRC 用于检测数字系统数据传输的错误。发送数据时,会对数据附加一个短校验值。该校验值通过用数据的位进行多项式除法获得。当接收到数据时...
  • 本文介绍了循环冗余码CRC码校验的原理和计算方法$ 分析两种查表冗余校验快速算法$ 提出新型分段查表法$ 良好地解决以单片机为核心的湿度测控系统无线数据传输差错控制的实时性和小存储量的要求
  • CRC 循环冗余校验

    2021-01-18 16:44:58
    写点基础,CRC校验(循环冗余校验 Cyclic Redundancy Check)有时候玩RFID的时候也许会用到,有些校验位使用简单的异或或相加,有些校验位使用CRC校验,实际上CRC也是使用和异或差不多的方式(模2除法),只是多了一个...
  • CRC校验实用程序库数据存储和数据通讯领域,为了保证数据的正确,就不得不采用检错的手段,下面这篇文章主要给大家介绍了关于Java中循环冗余校验(CRC32)实现的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧...
  • 工业环境,电子系统通常工作极端的温度条件下,或处 于电子噪声环境,或是其它恶劣条件,而系统这种条件下能 否正常工作至关重要。举例来说,如果发送给控制机器臂位置 的DAC 的数据遭到破坏,机器臂就会按...
  • ProgramProgramDevelopmentDevelopmentofofC CalculatalculatingingCRC-16CRC-16CyclicCyclicRedundancyRedundancyCheckCheckCodeCode【内容摘要】CRC-16 是检测数据发送过程发生错误的常用校验方法,本文通过从...
  • CRC循环冗余校验

    千次阅读 2017-06-19 16:31:57
    CRC循环冗余校验码是一种差错检测方式,数据传输时,给数据后方加上一定长度的冗余码对其进行校验。是数据通信领域最常用的一种查错校验,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是...
  • 循环冗余校验码:是数据通信领域最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。循 环冗余检查是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附帧的后面,接收设备也...
  • CRC校验实用程序库数据存储和数据通讯领域,为了保证数据的正确,就不得不采用检错的手段,下面这篇文章主要给大家介绍了关于Java中循环冗余校验(CRC32)实现的相关资料,需要的朋友可以参考借鉴,下面来一起看看...
  • 串行传送(磁盘、通讯),广泛采用循环冗余校验码(CRC)。CRC也是给信息码加上几位校验码,以增加整个编码系统的码距和查错纠错能力。  CRC的理论很复杂,一般书上只介绍已有生成多项式后计算校验码的方法。...
  • CRC循环冗余校验码代码实现

    千次阅读 2018-08-13 12:36:44
    目录   一、CRC循环冗余校验简介 二、CRC循环冗余校验基本原理 三、CRC循环冗余校验代码实现 四、程序执行结果 ...一、CRC循环冗余校验简介 ...目前数据链路层广泛使用的是循环冗余校验CRC...
  • 循环冗余校验

    千次阅读 2017-06-10 16:55:04
    CRC即循环冗余校验码(Cyclic Redundancy Check ):是数据通信领域最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,...
  • 工业环境,电子系统通常工作极端的温度条件下,或处 于电子噪声环境,或是其它恶劣条件,而系统这种条件下能 否正常工作至关重要。举例来说,如果发送给控制机器臂位置 的DAC 的数据遭到破坏,机器臂就会按...
  • 计算机数据通信,由于干扰等各种内外因素,数据出现差错不可避免,数据通信需要对数据进行差错检测。实现差错检测的基本原理是:发送方发送数据的基础上生产某些编码...此我们主要介绍循环冗余校验编码。...
  • 第 2卷 第 3期 华北科技学院学报 2005年 9月 循环冗余校验算法分析和实现 杜杏菁 ,刘春梅 (华北科技学院计算机系 ,北京东燕郊 101601) 摘 要 :网络传输报文时 ,噪声干扰或传输中断等因素往往使接收端收到的报文...
  • 摘要:本文介绍了循环冗余码(CRC 码)校验的原理和计算方法,分析两种查表冗余校验 快速算法,提出新型分段查表法,良好地解决以单片机为核心的湿度测控系统无线数据传输 差错控制的实时性和小存储量的要求。
  • CAN总线中循环冗余校验码的原理及其电路实现 [日期:2004-12-8] 来源:电子技术应用 作者:李书瑞 李 明 石龙海 [字体:大 小]   摘要:CAN网络传输摄文时,噪声干扰或传输中断等因素往往使接收端...
  • CRC循环冗余校验计算

    千次阅读 2017-05-21 09:44:51
    循环冗余校验(Cyclic Redundancy Check, CRC)是一种主要用来检测或校验数据传输过程出现的错误,是一种常用且强大的检错方法,数据块同步传输方式中都使用CRC检验。 特点:信息字段和校验字段的长度是任意的! ...
  • CRC循环冗余校验

    2019-04-20 16:31:35
    CRC循环冗余校验 基本原理: CRC检验原理: 一个p位二进制数据序列之后附加一个r位二进制检验码(序列),构成一个总长为n=p+r位的二进制序列; 附加数据序列之后的这个检验码与数据序列的内容之间存在着...
  • python 实现CRC(循环冗余校验

    万次阅读 2020-09-01 19:45:07
    循环冗余校验码(CRC),简称循环码,是一种常用的、具有检错、纠错能力的校验码,早期的通信运用广泛。循环冗余校验码常用于外存储器和计算机同步通信的数据校验。奇偶校验码和海明校验码都是采用奇偶检测为...
  • 循环冗余校验CRC(Cyclic Redun2 dancy Check) 是由分组线性码分支而来,其主要应用是二元码组。循环冗余校验CRC 编码简单且误判概 率很低,通信系统得到了广泛的应用。文中详细介绍了循环冗余校验CRC 的差错控制...
  • 循环冗余校验CRC算法

    万次阅读 2018-05-13 11:25:24
    本文参考自: ... 博主的文章写的太好了,我...CRC校验(循环冗余校验)是数据通讯最常采用的校验方式。嵌入式软件开发,经常要用到CRC 算法对各种数据进行校验。因此,掌握基本的CRC算法应是嵌入式程序员的基...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 889
精华内容 355
关键字:

在循环冗余校验中