* 生成多项式的最高幂次项系数是固定的1,故在简记式中,将最高的1统一去掉了,如04c11db7实际上是104c11db7。
** 前称crc-ccitt。itu的前身是ccitt。2 硬件电路的实现方法
多项式除法,可用除法电路来实现。除法电路的主体由一组移位寄存器和模2加法器(异或单元)组成。以crc-itu为例,它由16级移位寄存器和3个加法器组成,见下图(编码/解码共用)。编码、解码前将各寄存器初始化为"1",信息位随着时钟移入。当信息位全部输入后,从寄存器组输出crc结果。

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个)全部计算出来,放在一个表里 ,编码时只要从表中查找对应的值进行处理即可。