所有1-Wire器件(包括iButton器件)在只读存储器(ROM)中包含一个8字节的唯一注册号。该注册号用作1-Wire总线上的唯一网络地址。为确保数据通信的完整性,每个注册号的一个字节为1-Wire CRC字节。本应用笔记解释了如何计算这种8位1-Wire CRC。它还继续解释用于验证保存在设备内存中的记录的 16 位 CRC。1-Wire CRC和CRC-16均在选定的1-Wire器件的硬件中生成,以验证数据。®®
介绍
Maxim iButton产品是一系列器件,它们都按照称为1-Wire协议的特定命令序列通过单线进行通信。每个器件的一个关键特性是在制造时写入每个部件的唯一 8 字节 ROM 代码。此 8 字节代码的组件如图 1 所示。最低有效字节包含标识 iButton 产品类型的系列代码。例如,DS1990A的家族代码为01十六进制,DS1922L的家族代码为41十六进制。由于相同或不同系列类型的多个器件可以同时驻留在同一条1-Wire总线上,因此主机必须确定如何正确访问其位于1-Wire总线上的每个器件。家庭代码提供了此信息。接下来的 6 个字节包含一个唯一的序列号,允许将同一系列代码中的多个设备相互区分。这个唯一的序列号可以看作是1-Wire总线上每个器件的“地址”。整个设备集合加上主机,形成了一种微型局域网或MicroLAN;它们都通过单根公共线路进行通信。每个器件的ROM代码中的最高有效字节包含一个循环冗余校验(CRC)值,该值基于该部分的前7个字节的数据。当主机系统开始与设备通信时,首先读取 8 字节 ROM,首先读取 LSB。如果主机计算的CRC与ROM数据第7字节中包含的CRC一致,则可以认为通信有效。如果不是这种情况,则发生了错误,应再次读取ROM代码。
图1.iButton系统配置使用1-Wire CRC。
一些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 位数据包具有奇数位数所需的奇偶校验位值。如果接收到的数据是111010001的,则假定该信息是正确的。但是,如果收到的数据是111010101,其中 7千左边的位被错误地接收,1 的总数不再奇数,并且检测到错误条件并采取适当的措施。这种类型的方案称为奇偶校验。同样,也可以选择1的总数始终等于偶数,因此称为偶数奇偶校验。但是,此方案仅限于检测奇数位错误。在上面的示例中,如果数据损坏并变得111011101,其中 6千和 7千左边的位是错误的,奇偶校验看起来是正确的;然而,无论使用偶数奇偶校验还是奇偶校验,都不会检测到错误。
描述
马克西姆1线CRC
在以最少的硬件量定位串行数据流中的错误时,最有效的错误检测方案是CRC。本文介绍了Maxim产品中使用的CRC函数的操作和性质,而不涉及证明语句和描述的数学细节。CRC属性背后的数学概念在参考文献中有详细的描述。CRC最容易理解的是考虑该功能,因为它实际上是内置在硬件中的,通常表示为带有反馈的移位寄存器排列,如图2所示。或者,CRC 有时被称为虚拟变量 X 中的多项式表达式,每个项都有二进制系数。系数直接对应于移位寄存器实现中显示的反馈路径。硬件描述的移位寄存器中的级数或多项式表达式中存在的最高阶系数表示计算的 CRC 值的大小。数字数据通信中常用的CRC代码包括CRC-16和CRC-CCITT,每个代码都计算16位CRC值。Maxim 1-Wire CRC幅度为8位,用于检查写入每个64-Wire产品的1位ROM代码。该ROM代码由写入最低有效字节的8位家族代码,写入接下来48个字节的唯一6位序列号以及基于ROM的前56位计算然后写入最高有效字节的CRC值组成。图2中由异或门表示的反馈路径的位置,或多项式表达式中系数的存在,决定了CRC的属性以及算法定位数据中某些类型错误的能力。对于1-Wire CRC,可检测的错误类型包括:
64 位数字内任意位置的任何奇数错误。
64 位数字内的任何位置的所有双位错误。
可以包含在 8 位“窗口”中的任何错误集群(1-8 位不正确)。
大多数较大的错误群集。
输入数据与图2中移位寄存器第八级的输出是非或的。移位寄存器在数学上可以视为分频电路。输入数据是除数,带有反馈的移位寄存器充当除数。结果商被丢弃,其余部分是该特定输入数据流的CRC值,该值在最后一个数据位移入后驻留在移位寄存器中。从移位寄存器实现中可以明显看出,最终结果(CRC值)以一种非常复杂的方式依赖于所呈现位的过去历史。因此,需要极其罕见的错误组合才能逃脱这种方法的检测。
图2.马克西姆 1 线 8 位 CRC。
示例2中的示例在显示每个数据位后计算CRC值。移位寄存器电路在计算开始时始终复位为0。计算从 64 位 ROM 的 LSB 开始,这是本例中的 02 十六进制系列代码。输入所有56个数据位(序列号+族码)后,移位寄存器中包含的值为A2十六进制,即该输入流的1-Wire CRC值。如果已经计算出的CRC值(在本例中为A2十六进制)现在用作接下来8位数据的移位寄存器的输入,则输入整个64位数据后移位寄存器中的最终结果应全部为0。对于1-Wire CRC算法,此属性始终成立。如果移位寄存器中出现的任何 8 位值也用作输入流中的下一个 8 位,则移位寄存器中出现的结果在 8千数据位已移入始终为 00 十六进制。这可以通过观察8千移位寄存器的级始终等于输入数据位,使得控制反馈的EXOR门的输出和移位寄存器第一级的下一个状态值始终等于逻辑0。这导致移位寄存器在每个数据位显示时简单地从左向右偏移 0,直到整个寄存器在 0 之后用 8 填充千位。Maxim 1-Wire 64位ROM的结构利用这一特性来简化用于读取ROM的器件的硬件设计。清除主机中的移位寄存器,然后读取64个ROM位,包括CRC值。如果发生了正确的读数,则移位寄存器再次全为0,这是一个容易检测到的情况。如果移位寄存器中保留非零值,则必须重复读取操作。
到目前为止,讨论主要围绕CRC工艺的硬件表示,但显然,与硬件方法并行的软件解决方案是计算1-Wire CRC值的另一种方法。示例 1 中给出了如何对过程进行编码的示例。请注意,具有常数18十六进制的A寄存器的XRL(异或)是由于在第四级和第五级之后1-Wire CRC中存在EXOR反馈门,如图2所示。另一种软件解决方案是简单地构建一个查找表,该查找表可以直接访问当前存储在CRC寄存器中的任何8位值和任何8位模式的新数据。对于CRC寄存器的当前值为00十六进制的简单情况,可以计算输入字节的256个不同位组合并将其存储在矩阵中,其中矩阵的索引等于输入字节的值(即索引为I = 0到255)。可以证明,如果CRC寄存器的当前值不是00十六进制,那么对于任何当前CRC值和任何输入字节,查找表值与简化情况相同,但索引到表中的计算采用以下形式:
新CRC = 表 [I] 表示 I = 0 至 255;
其中 I = (当前 CRC) EXOR (输入字节)
对于当前CRC寄存器值为00十六进制的情况,公式简化为简单情况。第二种方法可以减少计算时间,因为操作可以基于字节完成,而不是前面示例中面向位的命令。但是,存在内存容量权衡,因为查找表必须存储并消耗 256 字节,而第一个示例除了程序代码之外几乎没有存储。示例 3 显示了此类代码的示例。表 1 显示了使用查找表方法重复的前面的示例。1-Wire CRC的两个特性有助于调试用于计算CRC值的代码。对于硬件实现,已经提到了第一个属性。如果将CRC寄存器的当前值用作数据的下一个字节,则生成的CRC值始终为00十六进制(请参阅上面的解释)。可用于确认代码正确操作的第二个属性是输入 CRC 寄存器当前值的 1 补码。对于1-Wire CRC算法,得到的CRC值始终为35十六进制或53十进制。其原因可以通过观察输入1的补码数据时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.1-Wire CRC的计算示例
CRC价值 | 输入值 |
00000000 | 0 |
00000000 | 1 |
10001100 | 0 2 |
01000110 | 0___ |
00100011 | 0 |
10011101 | 0 |
11000010 | 0 0 |
01100001 | 0___ |
10111100 | 0 |
01011110 | 0 |
00101111 | 1 字节 |
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 字节 |
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 十六进制 = [00000001B81C (序列号) + 02(家庭代码)] 的 CRC 值] | |
10100010 | 0 |
01010001 | 1 |
00101000 | 0 2 |
00010100 | 0___ |
00001010 | 0 |
00000101 | 1 |
00000010 | 0 Å |
00000001 | 1___ |
00000000 = 00 十六进制 = A2 的 CRC 值 [(CRC) + 00000001B81C(序列号)+ 02(家庭代码)] |
例 3.1线CRC查找功能
Var CRC : Byte; Procedure Do_CRC(X: Byte); { This procedure calculates the cumulative Maxim 1-Wire CRC of all bytes passed to it. 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;
当前 CRC 值 (= 当前表索引) | 输入数据 | 新指数(= 当前 CRC 异或输入数据) | 表 (新索引) (= 新的 CRC 值) |
0000 0000 = 00 十六进制 | 0000 0010 = 02 十六进制 | (00 H xor 02 H) = 02 十六进制 = 2 十进制 | 表[2]= 1011 1100 = BC 十六进制 = 188 dec |
1011 1100 = BC 十六进制 | 0001 1100 = 1C 十六进制 | (BC H xor 1C H) = A0 十六进制 = 160 dec | 表[160]= 1010 1111 = AF 十六进制 = 175 dec |
1010 1111 = 自动对焦六角 | 1011 1000 = B8 十六进制 | (自动对焦 H xor B8 H) = 17 十六进制 = 23 十进制 | 表[23]= 0001 1110 = 1E 十六进制 = 30 dec |
0001 1110 = 1E 十六进制 | 0000 0001 = 01 十六进制 | (1E H xor 01 H) = 1 F 十六进制 = 31 dec | 表[31]= 1101 110 = 直流十六进制 = 220 dec |
1101 1100 = 直流十六进制 | 0000 0000 = 00 十六进制 | (直流高或 00 小时)= 直流十六进制 = 220 十进制 | 表[220]= 1111 0100 = F4 十六进制 = 244 dec |
11110100 = F4 十六进制 | 0000 0000 = 00 十六进制 | (F4 H xor 00 H) = F4 十六进制 = 244 dec | 表 [244]= 0001 0101 = 15 十六进制 = 21 十进制 |
0001 0101 = 15 十六进制 | 0000 0000 = 00 十六进制 | (15 H xor 00 H) = 15 十六进制 = 21 十进制 | 表[21]= 1010 0010 = A2 十六进制 = 162 dec |
1010 0010 = A2 十六进制 | 10100010 = A2 十六进制 | (A2 H xor A2 H) = 十六进制 = 0 十进制 | 表[0]=0000 0000 = 00 十六进制 = 0 十进制 |
CRC寄存器与1的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 | 最终 CRC 值 = 35 十六进制,53 十进制 |
注:X我* = X 的补码我 |
CRC-16 计算 iButton 设备中的 RAM 记录
如介绍中所述,除了在所有iButton设备中找到的唯一8字节ROM代码外,某些iButton设备还具有RAM。由于RAM中存储的数据量可能比8字节ROM码大,Maxim建议使用16位CRC值来保证数据的完整性,而不是用于ROM的8位1-Wire CRC。建议的特定CRC通常称为CRC-16。移位寄存器和多项式表示如图3所示。该图显示,对于 16 位 CRC,移位寄存器包含 16 个阶段,多项式表达式具有 16 阶项。如前所述,iButton设备不计算CRC值。主机必须生成该值,然后将 16 位 CRC 值追加到实际数据的末尾。由于iButton设备的“通信通道”(即两个金属接触面)的不确定性,数据传输可能会遇到通常分为三类的错误。首先,短暂的间歇性连接会导致数据中出现少量位错误,正常的CRC-<>函数旨在检测这些错误。第二种类型的错误发生在完全失去联系时,例如当iButton设备从阅读器上移开时。
这会导致数据的最后一部分被读取为逻辑 1,因为主机不会将任何与 iButton 设备的连接解释为所有 1。在大多数情况下,正常的CRC-16功能也可以检测到这种情况。第三种类型的错误是由读卡器短路引起的,这可能是由iButton设备未正确插入或在读卡器中明显倾斜引起的。读取器短路会导致主机将数据读取为全部 0。使用 CRC 时,这可能会导致问题,因为确定数据有效性的方法是读取数据加上存储的 CRC 值,并查看在主机上计算的结果 CRC 是否为 0000 十六进制(对于 16 位 CRC)。如果读取器短路,则数据加上与数据一起存储的CRC值将读取为全为0,并且发生了错误读取,但主机计算的CRC错误地指示了有效的读取。为避免这种情况,Maxim建议将计算出的CRC-16值(CRC-16*)的补码与写入RAM的数据一起存储。使用未互补的CRC-16值,从iButton器件检索数据的过程与1-Wire CRC外壳类似。也就是说,如果主机中的CRC寄存器初始化为0000十六进制,然后从iButton设备读取所有数据加上与数据一起存储的CRC-16值,则主机的计算结果应具有0000十六进制,作为最终结果。相反,如果CRC-16值的补码与iButton中的数据一起存储,则主机上的CRC寄存器初始化为0000十六进制,并读取实际数据加上存储的CRC-16*值。生成的 CRC 值应为 B001 十六进制才能有效读取。这极大地改善了系统的操作,因为它不能再被阅读器的短路所愚弄。CRC-16功能具有这些特性的原因可以用类似于1-Wire CRC外壳的方式显示出来(见图3和图5)。16 位 CRC 的操作在理论上与前面描述的 8 位版本相同,但 CRC 的属性发生了变化,因为 16 位值现在可用于错误检测。对于CRC-16功能,可检测的错误类型包括:
数据记录中任何位置的任何奇数错误。
数据记录中任意位置的所有双位错误。
可能包含在 16 位“窗口”内的任何错误簇(1-16 位不正确)。
大多数较大的错误群集。
图3.CRC-16 硬件描述和多项式。
CRC-16 功能的硬件实现从图 3 中给出的描述中很简单。示例 4 显示了一个软件解决方案,该解决方案类似于使用单位操作计算 CRC-16 值的硬件操作。和以前一样,可以通过使用查找表开发计算量较少的软件解决方案。8位1-Wire CRC查找表的基本概念也适用于CRC-16外壳。但是,需要对 8 位情况的过程进行轻微修改,因为如果像以前一样将 CRC-16 函数的整个 16 位结果映射到一个表中,则该表将有 2 个16或 65,536 个条目。示例 5 显示了另一种方法,其中计算 16 位 CRC 值并将其存储在两个 256 个条目表中,一个包含高位字节,另一个包含生成的 CRC 的低位字节。对于任何当前的 16 位 CRC 值(表示为当前高阶字节的Current_CRC16_Hi和当前低阶字节的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给出了一种有趣的中间方法。该代码使用图 16 所示的公式对整个当前 CRC 值和输入字节进行操作,从而为每个输入的字节生成一个 CRC-5 值。还显示了方程的推导,使用字母字符表示当前的 16 位 CRC 值,使用数字字符表示传入字节的位。八班后的结果得出所示方程。然后,这些方程可用于预先计算新CRC值的大部分。例如,请注意,数量ABCDEFGH01234567(定义为所有这些位的EXOR)是当前CRC的传入数据字节和低位字节的奇偶校验。与上述逐位方法和查找表方法相比,此方法减少了计算时间和内存空间。最后,提到了可用作测试用例的 CRC-16 函数的两个属性,以帮助调试任何先前方法的代码。第一个特性与1-Wire CRC外壳相同。如果CRC寄存器的当前16位内容也用作接下来的16位输入,则生成的CRC值始终为0000十六进制。CRC-16函数的第二个特性也类似于1-Wire CRC情况,如果CRC寄存器当前1位内容的16补码也用作下一个16位输入,则得到的CRC值始终为B0 01十六进制。这两个CRC-16特性的证明与1-Wire 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
图5.高速CRC-16计算方法。
审核比较:郭婷
全部0条评论
快来发表一下你的评论吧 !