4 字节型算法
比特型算法逐位进行运算,效率比较低,不适用于高速通信的场合。数字通信系统(各种通信标准)一般是对一帧数据进行CRC校验,而字节是帧的基本单位。最常用的是一种按字节查表的快速算法。该算法基于这样一个事实:计算本字节后的CRC码,等于上一字节余式CRC码的低8位左移8位,加上上一字节CRC右移8位和本字节之和后所求得的CRC码。如果我们把8位二进制序列数的CRC(共256个)全部计算出来,放在一个表里 ,编码时只要从表中查找对应的值进行处理即可。
CRC-ITU的计算算法如下:
a.寄存器组初始化为全"1"(0xFFFF)。
b.寄存器组向右移动一个字节。
c.刚移出的那个字节与数据字节进行异或运算,得出一个指向值表的索引。
d.索引所指的表值与寄存器组做异或运算。
f.数据指针加1,如果数据没有全部处理完,则重复步骤b。
g.寄存器组取反,得到CRC,附加在数据之后。
CRC-ITU的验证算法如下:
a.寄存器组初始化为全"1"(0xFFFF)。
b.寄存器组向右移动一个字节。
c.刚移出的那个字节与数据字节进行异或运算,得出一个指向值表的索引。
d.索引所指的表值与寄存器组做异或运算。
e.数据指针加1,如果数据没有全部处理完,则重复步骤b (数据包括CRC的两个字节)。
f.寄存器组的值是否等于“Magic Value”(0xF0B8),若相等则通过,否则失败。
下面是通用的CRC-ITU查找表以及计算和验证CRC的C语言程序:
// CRC-ITU查找表
const u16 crctab16[] =
{
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
};
// 计算给定长度数据的16位CRC。
u16 GetCrc16(const byte* pData, int nLength)
{
u16 fcs = 0xffff; // 初始化
while(nLength>0)
{
fcs = (fcs >> 8) ^ crctab16[(fcs ^ *pData) & 0xff];
nLength--;
pData++;
}
return ~fcs; // 取反
}
// 检查给定长度数据的16位CRC是否正确。
bool IsCrc16Good(const byte* pData, int nLength)
{
u16 fcs = 0xffff; // 初始化
while(nLength>0)
{
fcs = (fcs >> 8) ^ crctab16[(fcs ^ *pData) & 0xff];
nLength--;
pData++;
}
return (fcs == 0xf0b8); // 0xf0b8是CRC-ITU的"Magic Value"
}
使用字节型算法,前面出现的PPP帧FCS计算和验证过程,可用下面的程序片断实现:
byte ppp[13] = {0xFF, 0x03, 0xC0, 0x21, 0x04, 0x03, 0x00, 0x07, 0x0D, 0x03, 0x06, 0x00, 0x00};
u16 result;
// 计算CRC
result = GetCrc16(ppp, 11);
// 填写FCS,先低后高
ppp[11] = result & 0xff;
ppp[12] = (result >> 8) & 0xff;
// 验证FCS
if(IsCrc16Good(ppp, 13))
{
... ...
}
该例中数据长度为11,说明CRC计算并不要求数据2字节或4字节对齐。
至于查找表的生成算法,以及CRC-32等其它CRC的算法,可参考RFC 1661, RFC 3309等文档。需要注意的是,虽然CRC算法的本质是一样的,但不同的协议、标准所规定的初始化、移位次序、验证方法等可能有所差别。
结语
CRC是现代通信领域的重要技术之一。掌握CRC的算法与实现方法,在通信系统的设计、通信协议的分析以及软件保护等诸多方面,能发挥很大的作用。如在作者曾经设计的一个多串口数据传输系统中,每串口速率为460kbps,不加校验时误码率大于10-6,加上简单的奇偶校验后性能改善不很明显,利用CRC进行检错重传,误码率降低至10-15以下,满足了实际应用的要求。
参考文献
1. Simpson, W., Editor, "The Point-to-Point Protocol (PPP)", STD 51, RFC 1661, 1994
2. J. Stone, "Stream Control Transmission Protocol (SCTP) Checksum Change", RFC 3309, 2002
3. J. Satran, "Internet Protocol Small Computer System Interface (iSCSI) Cyclic Redundancy Check (CRC)/Checksum Considerations", RFC 3385, 2002
4. International Standardization,"High-level data link control (HDLC) procedures", ISO/IEC 3309, 1992
5. ITU-T V.41, "Code-independent error-control system", 1989
6. 郭梯云等,《数据传输(修订本)》, 人民邮电出版社, 1998
|