CRC算法与实现[2]

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 bhw98 的 blog

    *  生成多项式的最高幂次项系数是固定的1,故在简记式中,将最高的1统一去掉了,如04c11db7实际上是104c11db7。
    ** 前称crc-ccitt。itu的前身是ccitt。

2 硬件电路的实现方法

多项式除法,可用除法电路来实现。除法电路的主体由一组移位寄存器和模2加法器(异或单元)组成。以crc-itu为例,它由16级移位寄存器和3个加法器组成,见下图(编码/解码共用)。编码、解码前将各寄存器初始化为"1",信息位随着时钟移入。当信息位全部输入后,从寄存器组输出crc结果。

crc-itu


3 比特型算法

上面的crc-itu除法电路,完全可以用软件来模拟。定义一个寄存器组,初始化为全"1"。依照电路图,每输入一个信息位,相当于一个时钟脉冲到来,从高到低依次移位。移位前信息位与bit0相加产生临时位,其中bit15移入临时位,bit10、bit3还要加上临时位。当全部信息位输入完成后,从寄存器组取出它们的值,这就是crc码。

typedef unsigned char bit;
typedef unsigned char byte;
typedef unsigned short u16;
    
typedef union {
    u16 val;
    struct {
        u16 bit0 : 1;
        u16 bit1 : 1;
        u16 bit2 : 1;
        u16 bit3 : 1;
        u16 bit4 : 1;
        u16 bit5 : 1;
        u16 bit6 : 1;
        u16 bit7 : 1;
        u16 bit8 : 1;
        u16 bit9 : 1;
        u16 bit10 : 1;
        u16 bit11 : 1;
        u16 bit12 : 1;
        u16 bit13 : 1;
        u16 bit14 : 1;
        u16 bit15 : 1;
    } bits;
} crcregs;
    
// 寄存器组
crcregs regs;
    
// 初始化crc寄存器组:移位寄存器置为全"1"
void crcinitregisters()
{
    regs.val = 0xffff;
}
    
// crc输入一个bit
void crcinputbit(bit in)
{
    bit a;
    
    a = regs.bits.bit0 ^ in;
    
    regs.bits.bit0 = regs.bits.bit1;
    regs.bits.bit1 = regs.bits.bit2;
    regs.bits.bit2 = regs.bits.bit3;
    regs.bits.bit3 = regs.bits.bit4 ^ a;
    regs.bits.bit4 = regs.bits.bit5;
    regs.bits.bit5 = regs.bits.bit6;
    regs.bits.bit6 = regs.bits.bit7;
    regs.bits.bit7 = regs.bits.bit8;
    regs.bits.bit8 = regs.bits.bit9;
    regs.bits.bit9 = regs.bits.bit10;
    regs.bits.bit10 = regs.bits.bit11 ^ a;
    regs.bits.bit11 = regs.bits.bit12;
    regs.bits.bit12 = regs.bits.bit13;
    regs.bits.bit13 = regs.bits.bit14;
    regs.bits.bit14 = regs.bits.bit15;
    regs.bits.bit15 = a;
}
    
// 输出crc码(寄存器组的值)
u16 crcgetregisters()
{
    return regs.val;
}

crcinputbit中一步一步的移位/异或操作,可以进行简化:

void crcinputbit(bit in)
{
    bit a;
    a = regs.bits.bit0 ^ in;
    regs.val >>= 1;
    if(a) regs.val ^= 0x8408;
}

细心的话,可以发现0x8408和0x1021(crc-itu的简记式)之间的关系。由于我们是从低到高输出比特流的,将0x1021左右反转就得到0x8408。将生成多项式写成 g(x)=1+x5+x12+x16,是不是更好看一点?

下面是一个典型的ppp帧。最后两个字节称为fcs(frame check sequence),是前面11个字节的crc。

ff 03 c0 21 04 03 00 07 0d 03 06 d0 3a

我们来计算这个ppp帧的crc,并验证它。

    byte ppp[13] = {0xff, 0x03, 0xc0, 0x21, 0x04, 0x03, 0x00, 0x07, 0x0d, 0x03, 0x06, 0x00, 0x00};
    int i,j;
    u16 result;
    
    /////////// 以下计算fcs
    
    // 初始化
    crcinitregisters();
    
    // 逐位输入,每个字节低位在先,不包括两个fcs字节
    for(i = 0; i < 11; i++)
    {
        for(j = 0; j < 8; j++)
        {
            crcinputbit((ppp[i] >> j) & 1);
        }
    }
    
    // 得到crc:将寄存器组的值求反
    result = ~crcgetregisters();
    
    // 填写fcs,先低后高
    ppp[11] = result & 0xff;
    ppp[12] = (result >> 8) & 0xff;
    
    /////////// 以下验证fcs
    
    // 初始化
    crcinitregisters();
    
    // 逐位输入,每个字节低位在先,包括两个fcs字节
    for(i = 0; i < 13; i++)
    {
        for(j = 0; j < 8; j++)
        {
            crcinputbit((ppp[i] >> j) & 1);
        }
    }
    
    // 得到验证结果
    result = crcgetregisters();

可以看到,计算出的crc等于0x3ad0,与原来的fcs相同。验证结果等于0。初始化为全"1",以及将寄存器组的值求反得到crc,都是crc-itu的要求。事实上,不管初始化为全"1"还是全"0",计算crc取反还是不取反,得到的验证结果都是0。


4 字节型算法

比特型算法逐位进行运算,效率比较低,不适用于高速通信的场合。数字通信系统(各种通信标准)一般是对一帧数据进行crc校验,而字节是帧的基本单位。最常用的是一种按字节查表的快速算法。该算法基于这样一个事实:计算本字节后的crc码,等于上一字节余式crc码的低8位左移8位,加上上一字节crc右移8位和本字节之和后所求得的crc码。如果我们把8位二进制序列数的crc(共256个)全部计算出来,放在一个表里 ,编码时只要从表中查找对应的值进行处理即可。

本文关键:CRC, FCS, 生成多项式, 检错重传
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top