理解和运用MAXIM IBUTTON产品中的循环冗余校验(CRC)
摘要 : 全部1-Wire®器件,包括iButton®器件,都具有唯一的8字节注册码,储存在只读存储器(ROM)中。该注册码在1-Wire总线上用作唯一的网络地址。为确保数据通信的完整性,每个注册码的一个字节是一个DOW CRC字节。本篇应用笔记说明了8位DOW CRC的计算方法以及用于验证器件存储器记录的16位CRC的计算。DOW CRC和CRC-16还会在选择1-Wire器件验证数据的硬件中产生。
引言
Maxim的iButton系列产品是通过单线按照1-Wire协议传送特定命令序列,进行数据通信。该系列产品都有个很重要的特性,就是在出厂前每个器件都被写入了唯一的8字节ROM码。其ROM码组成如图1所示,最低有效字节为家族代码,代表iButton器件的类型,如:DS1990A的家族码为01,DS1991的家族码为02。由于在同一条1-Wire总线上可同时挂接多个相同系列或不同系列的1-Wire器件,因此主机必须能够决定如何正确地访问位于1-Wire总线上的各个器件,这一点尤为重要。家族码提供器件的类型,随后的6个字节是器件的唯一序列号,用以区分同一个系列的不同器件。该序列号可作为1-Wire总线上器件的“地址”,这样1-Wire总线上的所有器件连同主机就构成了一个微型局域网(MicroLAN),它们之间通过一条公共线来进行通信。1-Wire器件ROM码的最高有效字节是循环冗余校验(CRC)码,该值基于前面的7个字节数据。当系统主机开始与某个器件进行通信时,可以读取8个ROM字节,低位在前。如果主机计算出的CRC码与ROM数据本身所含的CRC码相同,则通信有效;反之,则表明有错误发生,需重新读取器件的ROM码。图1. 采用DOW CRC的iButton系统配置
有些iButton器件除了8字节ROM外,还具有高达8kB的RAM,主机可以通过适当的命令进行访问。即使iButton器件本身不带CRC硬件电路,如果主机具有为ROM码计算CRC值的能力,就可以采用CRC技术,开发一个访问器件RAM部分的子程序。数据按正常模式写入器件,主机将计算出的CRC结果附在数据后面,与数据一起保存。当从iButton器件读入数据时,则执行相反的过程。主机将计算出的CRC值与存储器中存储的CRC进行比较,如果相同,则认为从iButton接收的数据有效。为了充分利用CRC来验证1-Wire总线上进行的串行通信的有效性,用户有必要了解一下CRC的概念和工作原理。此外,无论是基于硬件实现还是软件实现,还需要掌握通过主机计算CRC的实用计算方法。
背景知识
有多种串行数据的检错方法,一种常用的方法是在被检测的数据包中包含一个附加位,用于指示是否出错。如:对于8位ASCII字符来说,可在其ASCII字符串后添加一位用于检错。假设数据为11010001,可以附加第9位,使数据中"1"的位数为奇数个。这样,应该附加1,数据包就变为:111010001,其中带下划线的字符为所要求的奇偶校验位,使全部9位数据中1的位数为奇数。如果收到的数据为:111010001,则认为接收到的信息有效;但是,若收到的数据为:111010101,即左边第七位接收错误,此时数据中"1"的个数就不再是奇数,则表明发生了错误,进而采取相应的措施,这种校验方法称作奇校验。与之类似,如果要求数据中1的个数总为偶数,则称为偶检验。但是这种检验方式有其局限性,它只能检查出数据中的奇数个错误。在上例中,如果收到的数据为:111011101,其中从左边数第6位和第7位都是错的,但此时奇偶检测结果却是正确的。对于这类错误,因此无论采用奇校验还是偶校验,都不能够检测出来。详述
Maxim 1-Wire器件的CRC
在串行数据流中发现错误的最有效检错方案是循环冗余校验(CRC),并且所要求的硬件最少。这里主要介绍Maxim器件的CRC校验的工作及特性,暂不涉及详细的数学定义和描述。包含在CRC特性之后的数学概念在参考资料中进行了详细介绍。由于CRC实际上是由硬件实现,因此很容易理解CRC功能,通常CRC表示为带反馈的移位寄存器,如图2所示。另一种方式,也可将CRC看成是变量X的多项式,每一项的系数为二进制数,这些系数与移位寄存器的反馈通道直接对应。硬件方案中的移位寄存器的阶数,或多项式中的最高幂次就是将要计算CRC的位数。通常数字通信中使用的CRC编码方式有CRC-16和CRC-CCITT,它们产生的CRC码都为16位。Maxim的1-Wire CRC (DOW CRC)的位数是8位,用于1-Wire器件的64位ROM码的检错,该ROM码包括:最低有效字部分的8位家族码、与最低有效字节紧挨着的6字节48位唯一序列号、位于最高有效字节是前56位ROM码所计算出的CRC校验码。图2中,异或门构成的反馈路径(多项式的系数)决定了CRC检错性能和错误定位性能。DOW CRC可检测到以下几种错误:- 任意64位数据中的奇数个错误。
- 所有64位数据中的双位错误。
- 包含在8位"window" (1至8位错误)中的任何字符串的错误。
- 绝大多数长字符串的错误。
图2. Maxim 1-Wire 8位CRC
例2所示是当所有数据都移入之后计算出的CRC码。在计算开始时移位寄存器全置为‘0’,计算由64位ROM的LSB位开始,本例中家族码为02。当56位数据(序列号加上家族码)都移入后,移位寄存器的的值变为A2,这就是输入数据流的DOW CRC码。如果把计算得到的CRC码(A2)的8个位接在数据流的后面继续移入移位寄存器,那么当64位数据全部输入后,移位寄存器的值就全部为0。在DOW CRC算法中,就是利用这样一个特性来检查是否发生了错误的。把任意的8位数据移入移位寄存器到得到的8位数字接在移入的数据的后面继续移入移位寄存器,那么第8位数据位移入后的移位寄存器的结果总为00,通过观察不难注意到,移位寄存器的第8阶的值总是和输入的数据位相等,这样就使得用来控制反馈的异或门的输出和移位寄存器第一阶的下一个状态总为逻辑0,这样一来,当数据移入时,从左到右移入的每一位都是0,直到第8位后移位寄存器的每一位都被置0为止。Maxim 1-Wire 64位ROM就是利用上述特性来简化64位ROM的硬件设计的。主机端的移位寄存器首先被清0,然后读取包括CRC码在内的64位ROM。如果读取正确,则移位寄存器为零,易于检测。如果移位寄存器的值不是全部为0,则(表示发生了错误)必须重新读取数据。
到现在为止,我们一直围绕CRC硬件实现方法进行讨论,当然,与硬件方法等价的软件解决方案,则是另一种计算DOW CRC码的实现方法。如何编写程序代码如例1示例所示。需注意的是,寄存器A与常量18的异或运算是为了实现DOW CRC中第4、5阶之后的的异或反馈门,如图2所示。另一种软件实现方案就是简单地构建一个查询表,可以根据CRC寄存器中的8位值和新的8位数据直接读取。对于CRC寄存器为00的这种简单情况,可以推出输入数据的256种不同的组合,并将它们保存距阵中,该距阵索引值等于输入数据(即索引值为:I = 0至255)。很明显,如果CRC寄存器的当前值不是00,那么对于任一个当前CRC码和输入字来说,其查询表的值与CRC寄存器为0的简单情况相同,但表的索引值计算方式应改为:
新CRC = 表[I],I = 0至255 ;
这里,I = (当前CRC) EXOR (输入字)。
当CRC寄存器的当前值为00时,该等式就和上述简单情况一样。由于第二种方案是以字节为基础进行操作,而不是上例中面向位的操作,因此可以节省计算时间。但该方法会占用存储器的空间,因为查询表是存储在存储器中的,占用256字节,而在第一种方案中除了程序代码外,不再需要占用任何存储空间。例3为第二种方案的程序代码示例,表1给出了上例中查询表实现方法。在生成CRC码时,DOW CRC的两个特性有助于其代码的调试,其中的第一个特性在硬件实现时已介绍过,即:如果下一个输入数据等于当前CRC寄存器值,则计算后的CRC码肯定为00 (见前面的说明)。其第二个特性也能够用于确认代码是否正确,出现在输入数据等于当前CRC寄存器的反码时。对于DOW CRC算法来说,计算出的CRC码将为35H,即十进制的53 。当输入数据为CRC寄存器反码时,通过观察CRC寄存器工作方式,就可以解释这种特性,如表2所示。
例1. 汇编语言程序
DO_CRC: PUSH ACC ;save accumulator PUSH B ;save the B register PUSH ACC ;save bits to be shifted MOV B,#8 ;set shift = 8 bits ; CRC_LOOP: XRL A,CRC ;calculate CRC RRC A ;move it to the carry MOV A,CRC ;get the last CRC value JNC ZERO ;skip if data = 0 XRL A,#18H ;update the CRC value ; ZERO: RRC A ;position the new CRC MOV CRC,A ;store the new CRC POP ACC ;get the remaining bits RR A ;position the next bit PUSH ACC ;save the remaining bits DJNZ B,CRC_LOOP ;repeat for eight bits POP ACC ;clean up the stack POP B ;restore the B register POP ACC ;restore the accumulator RET
例2. DOW CRC算法举例
CRC Value | Input Value |
00000000 | 0 |
00000000 | 1 |
10001100 | 0 2 |
01000110 | 0 |
00100011 | 0 |
10011101 | 0 |
11000010 | 0 0 |
01100001 | 0 |
10111100 | 0 |
01011110 | 0 |
00101111 | 1 C |
00010111 | 1 |
00001011 | 1 |
00000101 | 0 |
10001110 | 0 1 |
01000111 | 0 |
10101111 | 0 |
11011011 | 0 |
11100001 | 0 8 |
11111100 | 1 |
11110010 | 1 |
11110101 | 1 |
01111010 | 0 B |
00111101 | 1 |
00011110 | 1 |
10000011 | 0 |
11001101 | 0 1 |
11101010 | 0 |
01110101 | 0 |
10110110 | 0 |
01011011 | 0 0 |
10100001 | 0 |
11011100 | 0 |
01101110 | 0 |
00110111 | 0 0 |
10010111 | 0 |
11000111 | 0 |
11101111 | 0 |
11111011 | 0 0 |
11110001 | 0 |
11110100 | 0 |
01111010 | 0 |
00111101 | 0 0 |
10010010 | 0 |
01001001 | 0 |
10101000 | 0 |
01010100 | 0 0 |
00101010 | 0 |
00010101 | 0 |
10000110 | 0 |
01000111 | 0 0 |
10101101 | 0 |
11011010 | 0 |
01101101 | 0 |
10111010 | 0 0 |
01011101 | 0 |
10100010 = A2 Hex = CRC Value for [00000001B81C (Serial Number) + 02 (Family Code)] | |
10100010 | 0 |
01010001 | 1 |
00101000 | 0 2 |
00010100 | 0 |
00001010 | 0 |
00000101 | 1 |
00000010 | 0 A |
00000001 | 1 |
00000000 = 00 Hex = CRC Value for A2 [(CRC) + 00000001B81C (Serial Number) + 02 (Family Code)] |
例3. DOW CRC查询函数
Var CRC : Byte; Procedure Do_CRC(X: Byte); { This procedure calculates the cumulative Maxim 1-Wire CRC of all bytes passed to it.表1. 查表方式计算DOW CRC
The result accumulates in the global variable CRC. } Const Table : Array[0..255] of Byte = ( 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53); Begin CRC := Table[CRC xor X]; End;
Current CRC Value (= Current Table Index) | Input Data | New Index (= Current CRC xor Input Data) | Table (New Index) (= New CRC Value) |
0000 0000 = 00 Hex | 0000 0010 = 02 Hex | (00 H xor 02 H) = 02 Hex = 2 Dec | Table[2]= 1011 1100 = BC Hex = 188 Dec |
1011 1100 = BC Hex | 0001 1100 = 1C Hex | (BC H xor 1C H) = A0 Hex = 160 Dec | Table[160]= 1010 1111 = AF Hex = 175 Dec |
1010 1111 = AF Hex | 1011 1000 = B8 Hex | (AF H xor B8 H) = 17 Hex = 23 Dec | Table[23]= 0001 1110 = 1E Hex = 30 Dec |
0001 1110 = 1E Hex | 0000 0001 = 01 Hex | (1E H xor 01 H) = 1 F Hex = 31 Dec | Table[31]= 1101 110 = DC Hex = 220 Dec |
1101 1100 = DC Hex | 0000 0000 = 00 Hex | (DC H xor 00 H) = DC Hex = 220 Dec | Table[220]= 1111 0100 = F4 Hex = 244 Dec |
11110100 = F4 Hex | 0000 0000 = 00 Hex | (F4 H xor 00 H) = F4 Hex = 244 Dec | Table [244]= 0001 0101 = 15 Hex = 21 Dec |
0001 0101 = 15 Hex | 0000 0000 = 00 Hex | (15 H xor 00 H) = 15 Hex = 21 Dec | Table[21]= 1010 0010 = A2 Hex = 162 Dec |
1010 0010 = A2 Hex | 10100010 = A2 Hex | (A2 H xor A2 H) = Hex = 0 Dec | Table[0]=0000 0000 = 00 Hex = 0 Dec |
带有CRC寄存器1的补码CRC寄存器
表2. CRC寄存器值输入X0 | X1 | X2 | X3 | X4 | X5 | X6 | X7 | X7* |
1 | X0 | X1 | X2 | X3* | X4* | X5 | X6 | X6* |
1 | 1 | X0 | X1 | X2* | X3 | X4* | X5 | X5* |
1 | 1 | 1 | X0 | X1* | X2* | X3 | X4* | X4* |
0 | 1 | 1 | 1 | X0 | X1* | X2 | X3 | X3* |
1 | 0 | 1 | 1 | 0 | X0* | X1* | X2 | X2* |
1 | 1 | 0 | 1 | 0 | 1 | X0* | X1* | X1* |
0 | 1 | 1 | 0 | 1 | 0 | 1 | X0* | X0* |
0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | Final CRC Value = 35 Hex, 53 Decimal |
iButton RAM中CRC-16的算法
如前所述,一些iButton器件除了内部唯一的8字节ROM码,还有RAM存储器。与内部的8字节ROM码相比,RAM中存储的数据量要大得多,因此Maxim推荐使用16位CRC码而不是ROM所使用的8位DOW CRC,以确保数据的完整性。这种特殊的16位CRC通常称为CRC-16。图3给出了这种16位CRC的移位寄存器的硬件实现图和相应的多项表达式,图中的移位寄存器有16阶,其表达式也有16次幂项。如前所述,iButton器件自身并不产生CRC码,而是由主机生成16位CRC码并将其附加在实际数据之后。由于iButton的“通信通道”,如两个金属接触面,存在不确定性,数据传输可能会出现一些错误,分为三类:第一,短暂的连接中断引起的数据传输中小部分数据位出错,CRC-16可检测出这类错误;第二类错误是完全脱离接触引起的,如:当iButton器件快速移离读写头时。将导致后续部分的读入数据为逻辑1,这是因为未连接iButton器件时,主机将解释所有的接收数据为1。 CRC-16在大多数情况下可以检测出这类错误。第三类错误是由读写头短路引起的,这可能因为iButton器件没有正确插入或iButton器件在阅读器内大幅度翘起导致的。读写头短路会使主机把数据全读为0,此时采用CRC进行校验时,就会出现问题。由于确认数据是否有效的方法是判断主机在读取数据和附加的CRC之后,计算出的CRC码是否全为0000H (采用16位CRC)。当阅读器短路时,读到的数据和CRC值全部为0,此时读操作已经出错,但由主机计算出的CRC值将错误地指示该读操作是有效的。为了避免这种情况的发生,Maxim推荐将CRC-16反码(CRC-16*)随同数据一起写入RAM。当采用无补码的CRC-16时,iButton的数据验证过程与DOW CRC相似,即,主机把CRC寄存器初始化成0000H,然后从iButton读取全部数据和存储的CRC-16,则最终计算出来的结果应为0000H。如果采用CRC-16*,在iButton中保存的就是CRC-16的反码和数据。进行CRC校验时,主机同样把CRC寄存器初始化成0H,然后从iButton读取数据和CRC-16*,如果操作没有错误的话,最后的结果应该是B001H。这样大大地提高了系统可靠性,读写头短路造成的错误就不会被漏检。至于为什么CRC-16具有这些特性,可通过与之相似的DOW CRC的分析(见图3和图5)来解释。在理论上16位CRC的操作与此前介绍的8位CRC完全相同,只是由于采用的是16的CRC,性能有所改善。对于CRC-16函数,可以检测到以下几类错误:
- 任何数据记录中的奇数个错误。
- 任何数据记录中的双位错误。
- 包含在16位"window" (1至16错误)中的任何字符串的错误。
- 绝大多数长字符串的错误。
图3. CRC-16硬件实现及其多项式表示
图3给出了CRC-16函数的硬件实现图,例4则列出了与其硬件相对应的软件实现方案,采用位操作计算CRC-16值。之前很少有高效的查询表软件解决方案,8位DOW CRC查询表的基础概念也同样适用于CRC-16,只需对8位的程序稍加改动即可。但是,如果仍然采用DOW CRC实现方法的话,要把CRC-16全部16位结果全部放进一个查询表,则表中就会有216也就是65536个记录(占用的空间将会很大)。与DOW CRC不同的实现方法如例5所示,图中采用两个256位表来计算和存储16位CRC值,其中一个表包含CRC的高8位,另一个存放低8位。任何一个16位CRC都可以分为代表高8位的Current_CRC16_Hi和代表低8位的Current_CRC16_Lo两部分。对于任何一个输入字节,在高阶字节表中决定新的CRC值高阶字节(New_CRC16_Hi)的索引计算公式如下:
New_CRC16_Hi = CRC16_Tabhi[I], I = 0至255; 这里:I = (Current_CRC16_Lo) EXOR (输入字)
在低阶字节表中决定新的CRC值低阶字节(New_CRC16_Lo)的索引计算公式如下:
New_CRC16_Lo = (CRC16_Tablo[I]) EXOR (Current_ CRC16_Hi) I = 0至255;
这里:I = (Current_CRC16_Lo) EXOR (输入字)
图4所示为如何实现该方法的一个实例。
例4. 计算CRC-16的汇编语言程序
crc_lo data 20h ; lo byte of crc calculation (bit addressable) crc_hi data 21h ; hi part of crc calculation ;--------------------------------------------------------------------------- ; CRC16 subroutine. ; - accumulator is assumed to have byte to be crc'ed ; - two direct variables are used crc_hi and crc_lo ; - crc_hi and crc_lo contain the CRC16 result ;--------------------------------------------------------------------------- crc16: ; calculate crc with accumulator push b ; save value of b mov b, #08h ; number of bits to crc. crc_get_bit: rrc a ; get low order bit into carry push acc ; save a for later use jc crc_in_1 ;got a 1 input to crc mov c, crc_lo.0 ;xor with a 0 input bit is bit sjmp crc_cont ;continue crc_in_1: mov c, crc_lo.0 ;xor with a 1 input bit cpl c ;is not bit. crc_cont: jnc crc_shift ; if carry set, just shift cpl crc_hi.6 ;complement bit 15 of crc cpl crc_lo.1 ;complement bit 2 of crc crc_shift mov a, crc_hi ; carry is in appropriate setting rrc a ; rotate it mov crc_hi, a ; and save it mov a, crc_lo ; again, carry is okay rrc a ; rotate it mov crc_lo, a ; and save it pop acc ; get acc back djnz b, crc_get_bit ; go get the next bit pop b ; restore b ret end
例5. 利用查找表进行CRC-16计算的汇编程序
crc_lo data 40h ; any direct address is okay crc_hi data 41h tmp data 42h ;--------------------------------------------------------------------------- ; CRC16 subroutine. ; - accumulator is assumed to have byte to be crc'ed ; - three direct variables are used, tmp, crc_hi and crc_lo ; - crc_hi and crc_lo contain the CRC16 result ; - this CRC16 algorithm uses a table lookup ;--------------------------------------------------------------------------- crc16: xrl a, crc_lo ; create index into tables mov tmp, a ; save index push dph ; save dptr push dpl ; mov dptr, #crc16_tablo ; low part of table address movc a, @a+dptr ; get low byte xrl a, crc_hi ; mov crc_lo, a ; save of low result mov dptr, #crc16_tabhi ; high part of table address mov a, tmp ; index movc a, @a+dptr ; mov crc_hi, a ; save high result pop dpl ; restore pointer pop dph ; ret ; all done with calculation crc16_tablo: db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 000h, 0c1h, 081h, 040h, 001h, 0c0h, 080h, 041h db 001h, 0c0h, 080h, 041h, 000h, 0c1h, 081h, 040h crc16_tabhi: db 000h, 0c0h, 0c1h, 001h, 0c3h, 003h, 002h, 0c2h db 0c6h, 006h, 007h, 0c7h, 005h, 0c5h, 0c4h, 004h db 0cch, 00ch, 00dh, 0cdh, 00fh, 0cfh, 0ceh, 00eh db 00ah, 0cah, 0cbh, 00bh, 0c9h, 009h, 008h, 0c8h db 0d8h, 018h, 019h, 0d9h, 01bh, 0dbh, 0dah, 01ah db 01eh, 0deh, 0dfh, 01fh, 0ddh, 01dh, 01ch, 0dch db 014h, 0d4h, 0d5h, 015h, 0d7h, 017h, 016h, 0d6h db 0d2h, 012h, 013h, 0d3h, 011h, 0d1h, 0d0h, 010h db 0f0h, 030h, 031h, 0f1h, 033h, 0f3h, 0f2h, 032h db 036h, 0f6h, 0f7h, 037h, 0f5h, 035h, 034h, 0f4h db 03ch, 0fch, 0fdh, 03dh, 0ffh, 03fh, 03eh, 0feh db 0fah, 03ah, 03bh, 0fbh, 039h, 0f9h, 0f8h, 038h db 028h, 0e8h, 0e9h, 029h, 0ebh, 02bh, 02ah, 0eah db 0eeh, 02eh, 02fh, 0efh, 02dh, 0edh, 0ech, 02ch db 0e4h, 024h, 025h, 0e5h, 027h, 0e7h, 0e6h, 026h db 022h, 0e2h, 0e3h, 023h, 0e1h, 021h, 020h, 0e0h db 0a0h, 060h, 061h, 0a1h, 063h, 0a3h, 0a2h, 062h db 066h, 0a6h, 0a7h, 067h, 0a5h, 065h, 064h, 0a4h db 06ch, 0ach, 0adh, 06dh, 0afh, 06fh, 06eh, 0aeh db 0aah, 06ah, 06bh, 0abh, 069h, 0a9h, 0a8h, 068h db 078h, 0b8h, 0b9h, 079h, 0bbh, 07bh, 07ah, 0bah db 0beh, 07eh, 07fh, 0bfh, 07dh, 0bdh, 0bch, 07ch db 0b4h, 074h, 075h, 0b5h, 077h, 0b7h, 0b6h, 076h db 072h, 0b2h, 0b3h, 073h, 0b1h, 071h, 070h, 0b0h db 050h, 090h, 091h, 051h, 093h, 053h, 052h, 092h db 096h, 056h, 057h, 097h, 055h, 095h, 094h, 054h db 09ch, 05ch, 05dh, 09dh, 05fh, 09fh, 09eh, 05eh db 05ah, 09ah, 09bh, 05bh, 099h, 059h, 058h, 098h db 088h, 048h, 049h, 089h, 04bh, 08bh, 08ah, 04ah db 04eh, 08eh, 08fh, 04fh, 08dh, 04dh, 04ch, 08ch db 044h, 084h, 085h, 045h, 087h, 047h, 046h, 086h db 082h, 042h, 043h, 083h, 041h, 081h, 080h, 040h
图4. CRC-16计算和查表方法的比较
例6描述了一种令人感兴趣的的中间方案。该程序利用图5所示的公式,通过对当前整个CRC的值和输入字节进行运算来生成CRC-16。同时在图中还给出了该等式的推导过程,用字母字符表示当前16位CRC值,用数字字符表示输入字节的位。8次移位后就得到了所示的等式,这些等式随后可以预计算出大部分的新CRC值。注意:例如ABCDEFGH01234567 (定义为所有位异或的结果)数字量等于输入数据和当前CRC低字节的奇偶性。与前面的按位计算或查表方法相比较,这种方法可以减少计算时间,或减少存储空间。最后,应说明的是,前面介绍的CRC-16函数的两种特性在这里也用作调试准则。其第一个特性与DOW CRC的情况完全相同,即如果当前CRC寄存器的16位内容作为后续的16位输入数据,则得到的CRC也总为0000H。CRC功能的第二个特性与DOW CRC相似,如果把当前CRC寄存器的16位反码也作为后续的16位输入数据,则CRC结果总为B0 01H。这两个CRC-16特性的证明方法也与DOW CRC相似。
例6. 高速CRC-16算法的汇编语言程序
lo equ 40h ; low byte of CRC hi equ 41h ; high byte of CRC crc16: push acc ; save the accumulator. xrl a, lo mov lo, hi ; move the high byte of the CRC. mov hi, a ; save data xor low(crc) for later mov c, p jnc crc0 xrl lo, #01h ; add the parity to CRC bit 0 crc0: rrc a ; get the low bit in c jnc crc1 xrl lo, #40h ; need to fix bit 6 of the result crc1: mov c, acc.7 xrl a, hi ; compute the results for other bits. rrc a ; shift them into place mov hi, a ; and save them jnc crc2 xrl lo, #80h ; now clean up bit 7 crc2: pop acc ; restore everything and return ret
详细图片(GIF)
图5. 高速CRC-16的计算方法