计算校验和的算法。

xiaoligang 2004-10-29 01:16:58
是不是自定义的通讯协议想怎么计算校验和都是可以的,是不是有些固定的计算方法?
那位大虾可以给我详细介绍几种?
谢谢。
...全文
626 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
AthlonxpX86 2004-10-29
  • 打赏
  • 举报
回复

6CA6
7C87
4CE4
5CC5
2C22
3C03
0C60
1C41
EDAE
FD8F
CDEC
DDCD
AD2A
BD0B
8D68
9D49


7E97
6EB6
5ED5
4EF4
3E13
2E32
1E51
0E70
FF9F
EFBE
DFDD
CFFC
BF1B
AF3A
9F59
8F78


9188
81A9
B1CA
A1EB
D10C
C12D
F14E
E16F
1080
00A1
30C2
20E3
5004
4025
7046
6067


83B9
9398
A3FB
B3DA
C33D
D31C
E37F
F35E
02B1
1290
22F3
32D2
4235
5214
6277
7256


B5EA
A5CB
95A8
8589
F56E
E54F
D52C
C50D
34E2
24C3
14A0
0481
7466
6447
5424
4405


A7DB
B7FA
8799
97B8
E75F
F77E
C71D
D73C
26D3
36F2
0691
16B0
6657
7676
4615
5634


D94C
C96D
F90E
E92F
99C8
89E9
B98A
A9AB
5844
4865
7806
6827
18C0
08E1
3882
28A3


CB7D
DB5C
EB3F
FB1E
8BF9
9BD8
ABBB
BB9A
4A75
5A54
6A37
7A16
0AF1
1AD0
2AB3
3A92


FD2E
ED0F
DD6C
CD4D
BDAA
AD8B
9DE8
8DC9
7C26
6C07
5C64
4C45
3CA2
2C83
1CE0
0CC1


EF1F
FF3E
CF5D
DF7C
AF9B
BFBA
8FD9
9FF8
6E17
7E36
4E55
5E74
2E93
3EB2
0ED1
1EF0



 
5 适用于PIC单片机的算法

表1所示的余式表虽然只占用512个字节的程序存储空间,但对于PIC单片机来说还是太大了,需要再进行压缩。思路是这样的:

将Ta00=[a 0 0 ]分解成Te00=[e 0 0 ]和Tf00=[f 0 0 ],并使字节e的上半字节内容与a的上半字节相同但下半字节为零,同时使字节f的下半字节内容与a的下半字节相同但上半字节为零,然后用Te00和Tf00生成余式表来代替Ta00余式表。由于Te00和Tf00中只有半个字节不为零,所以,每个余式表只需16个单元(32个字节),两个余式表总共只占用64个字节,这样就可满足PIC单片机的要求了。

由上述思路可知,e=a∧0F0H ,f=a∧0FH ,因此可得,Ta00=Te00Å Tf00,同时,还可以证明它们余式的关系为Ra00=Re00Å Rf00,也就是说,如果设Ra00=[ ha00 la00 ]、Re00=[ he00 le00 ]和Rf00=[ hf00 lf00 ],则ha00=he00Å hf00 ,la00=le00Å lf00。这样,三字节序列[a 0 0 ] 的计算可由图3所示的方法来进行,其中,[e 0 0 ]和[f 0 0 ]余式表见表2和表3。


 

表2 [ e 0 0 ] 余式表

00
10
20
30
40
50
60
70
80
90
A0
B0
C0
D0
E0
F0

0000
1231
2462
3653
48C4
5AF5
6CA6
7E97
9188
83B9
B5EA
A7DB
D94C
CB7D
FD2E
EF1F


表3 [ f 0 0 ] 余式表

00
01
02
03
04
05
06
07
08
09
0A
0B
0C
0D
0E
0F

0000
1021
2042
3063
4084
50A5
60C6
70E7
8108
9129
A14A
B16B
C18C
D1AD
E1CE
F1EF



 
显然,对于PIC单片机来说,三字节序列[ a b c ]的计算需要综合图2和图3 所示的两种办法来进行,下面就给出一个这样的PIC子程序,它的使用与上面所述的51系列单片机子程序基本相同,即,在第一次被调用之前,先将m1、 m2和m3分别存入通用寄存器BYTEa、BYTEb和BYTEc中;从第二次调用时开始,每次在调用之前只需先将参与本次运算的字节存入BYTEc即可(第二次是m4,第三次是m5,...,第i次是mi+2,...)。每次子程序返回时,计算结果将存放在BYTEa和BYTEb中,最后一次调用返回后,BYTEa和BYTEb分别存放的就是最终结果h和l。子程序中,除BYTEa、BYTEb和BYTEc外,ADDR、RESULTh和RESULTl也是通用寄存器。

 

 图3 三字节序列[ a 0 0 ]的计算办法

START MOVLW DATAe

MOVWF ADDR ;将[e 0 0]余式表首地址DATAe存入ADDR
SWAPF BYTEa,0
ANDLW 0FH ;求e和e指定的[e 0 0] 余式高字节的相对地址
ADDWF ADDR,1 ;取其绝对地址,存入ADDR

MOVF ADDR,0 ;把这一绝对地址再存入W

CALL TABLE ;查表,返回时he00放在W中
MOVWF RESULTh ;把he00存入RESULTh
MOVLW 16
ADDWF ADDR,0 ;求e指定的[e 0 0]余式低字节的绝对地址
CALL TABLE ;查表,返回时le00放在W中
MOVWF RESULTl ;把le00存入RESULTl
MOVLW DATAf
MOVWF ADDR ;将[f 0 0]余式表首地址DATAf存入ADDR
MOVF BYTEa,0
ANDLW 0FH ;求f和f指定的[f 0 0]余式高字节的相对地址
ADDWF ADDR,1 ;取其绝对地址,存入ADDR
MOVF ADDR,0 ;把这一绝对地址再存入W
CALL TABLE ;查表,返回时hf00放在W中
XORWF RESULTh,0 ;he00与hf00异或,得ha00,存入W
XORWF BYTEb,0 ;ha00与b异或,得habc,存入W
MOVF BYTEa ;habc存入BYTEa
MOVLW 16
ADDWF ADDR,0 ;求f指定的[f 0 0]余式低字节的绝对地址
CALL TABLE ;查表,返回时lf00放在W中
XORWF RESULTl,0 ;le00与lf00异或,得la00,存入W
XORWF BYTEc,0 ;la00与c异或,得labc,存入W
MOVF BYTEb ;labc存入BYTEb
RETLW 0

参 考 文 献

1 常晓明,潘卫华,王建东. CRC校验及其软件实现. 电子技术应用, 1995(6)



AthlonxpX86 2004-10-29
  • 打赏
  • 举报
回复
摘 要 提供两个实用的、能够在单片机上通过软件来实现的CRC快速算法,其中一个适用于51系列等单片机,另一个适用于PIC单片机,这两种算法十分简单快捷。

关键词 CRC 算法 单片机

1 引言

CRC(循环冗余码)检验技术广泛应用于测控及通信领域。在很多情况下,CRC计算是靠专用的硬件来实现的,但是对于小型低成本的单片机系统来说,若要在没有这些硬件的支持下实现CRC检验,首先要解决的就是如何通过软件高效快速地完成CRC计算的问题,也就是CRC算法的问题。

这里将提供两种算法,它们稍有不同,一种适用于程序空间大一些的51系列等单片机,另一种适用于程序空间的使用条件十分苛刻的PIC单片机。这些算法按字节进行计算,仅使用查表和简单的异或运算等操作,所以,计算过程相当简捷,而计算速度却很快。

下面先简述一下CRC原理,然后再以CRC-CCITT标准生成多项式为例对算法进行说明,并给出一个51系列单片机子程序和一个PIC单片机子程序。

2 CRC原理

CRC检验原理实际上就是在一个p位二进制数据序列之后附加一个r位二进制检验码(序列),从而构成一个总长为n=p+r位的二进制序列,例如,p位二进制数据序列D=[dp-1dp-2 ......d1d0],r位二进制检验码R=[rr-1 rr-2....r1 r0],所得到的这个n位二进制序列就是M=[dp-1dp-2 ......d1d0 rr-1 rr-2....r1 r0]; 附加在数据序列之后的这个检验码与数据序列的内容之间存在着某种特定的关系。如果因干扰等原因使数据序列中的某一位或某些位发生错误,这种特定关系就会被破坏,因此,通过检查这一关系, 就可以实现对数据正确性的检验。

校验码R是通过对数据序列D进行二进制除法取余式运算得到的,它被一个称为生成多项式的(r+1)位二进制序列G=[gr gr-1 .... g1 g0]来除,用多项式形式表示为

(1)

其中,xrD(x)表示将数据序列D左移r位(即在D的末尾再增加r个0位),Q(x)代表这一除法所得的商,R(x)就是所需的余式。这一运算关系还可以用式(2)来表达

(2)

其中,Re[ ]表示对括号内的式子进行取余式运算。

检验码的编码计算如上所述,而检验过程则是对M序列直接进行除法取余式运算,即

(3)

或表示为

(4)

所得到的余式R(x)若为零则表示数据正确,否则认为发生错误。

3 快速算法的基本思路

这里仅以CRC-CCITT标准生成多项式为例进行说明。CRC-CCITT是一个17位生成多项式G=[1 0001 0000 0010 0001],用多项式形式表示为G(x)=x16+x12+x5+1,由它产生的检验码R的二进制位数是16位(2字节)。

单片机的操作是以字节形式进行的,所以,算法应以字节为单位进行运算。这里将把用字节构成的二进制序列称为“字节序列”,显然,单片机的数据序列、检验码以及它俩组成的序列M都是字节序列,或者说是“多字节序列”。

实际上,这种算法所要解决的问题就是如何对多字节序列进行除法取余式运算的问题。

3.1 多字节序列运算规律

首先设一个由i个字节 m1、m2、......、mi-1、mi 构成的8×i位二进制序列,并用字节形式表示它为Mi =[ m1 m2 ...... mi-1 mi ],然后再截取Mi的前(i-1)个字节构成一个Mi-1序列,Mi-1=[ m1 m2 ...... mi-1 ],这两个序列之间的关系可以用多项式表示为Mi(x)=x 8Mi-1(x)+mi(x),其中,mi(x)是字节mi的二进制多项式表示形式,而x8Mi-1(x)表示将Mi-1序列左移一个字节。

对于序列Mi-1来说,

(5)

其中,二字节序列余式Ri-1=[hi-1 li-1]。

而对于Mi序列来说,可得

(6)

这一结果的前一项为一整数,所以它与余式无关,这样,余式只可能出现在后一项中。因此,对x8Ri-1(x)+mi(x)取余式运算就等价于对Mi(x)的取余式运算,用式(4)的形式表示为

(7)

x8Ri-1(x)+mi(x)代表一个由Ri-1和mi共同组成的三字节序列[ hi-1 li-1 mi],而且对这个三字节序列的取余式运算就等于对Mi序列的取余式运算,其结果就是Mi序列的余式Ri=[ hi li ]。同理可得,对于一个Mi+1序列(它比Mi序列多一个字节mi+1)来说,对三字节序列[ hi li mi+1]的运算就等价于对Mi+1序列的运算,其结果就是Mi+1序列的余式Ri+1=[ hi+1 li+1 ]。

显然,这反映出一种如图1所示的递推运算的规律。可见,每一次递推运算都是对一个三字节序列的计算,所以,如何简单快捷地对三字节序列进行计算是这种算法的又一个关键。

3.2 三字节序列计算

提到简单快捷,人们自然会想到采用查表的办法,例如事先把三字节序列的所有余式计算出来,置于一个称为“余式表”的表格中,供随时读取。不过,这样的表格太大,需要224个单元,也就是要占用225个字节的存储空间,这对单片机来说是绝对无法接受的,因此,需要想办法减少所占用的存储空间。



 图1 递推计算步骤

设一个三字节序列Tabc =[ a b c ] 、一个 Ta00=[ a 0 0 ]和一个二字节序列 Tbc=[ b c ]。可以用多项式形式表示它们之间的关系为 Tabc(x)=Ta00(x)+Tbc(x),因此,对Ta00来说,

(8)

而对Tabc来说,



其中,Qa00是整数,与余式无关;而Ra00和Tbc都是二字节序列,因而,它们的和(模2加法,即异或运算)仍然是二字节序列(二进制16位,小于生成多项式的17位),因此,它就是 Tabc的余式Rabc,即

(9)

这说明,可以把三字节序列Tabc=[ a b c ]的运算分解成两个步骤来进行,如图2所示。

1. 通过查余式表(表1),读取Ta00=[a 0 0 ]的余式Ra00=[ ha00 la00 ];

2. 将Ra00与[ b c ]进行异或运算,从而得到[ a b c ]的余式Rabc=[ habc labc ],即habc=ha00 Å b,labc=la00 Å c。

由于[a 0 0 ]中只有一个字节不为零,因此,[a 0 0 ]余式表仅需要256个单元,即占用512个字节。



图2 三字节序列[ a b c ]的计算办法

4 适用于51系列等单片机的算法

前面所述的办法可以直接用于51系列等单片机,因为512字节的余式表对它们的程序存储容量来说是完全不成问题的。

计算直接通过上述的递推过程来进行,每一次递推都是对一个三字节序列进行的计算:第一次是[ m1 m2 m3 ],结果是[ h3 l3 ];第二次是[ h3 l3 m4 ],结果是[ h4 l4 ];......,第i次是[ hi+1 li+1 mi+2 ],结果是[ hi+2 li+2 ];......;最后是[ hk+1 lk+1 mk+2 ],最终结果是[ h l ]。如果有k个数据字节,则递推k次。下面给出一个三字节序列计算子程序,供每一次递推运算时调用。注意,在第一次被调用之前,先将m1、 m2和m3分别存入R0、R1和R2中(子程序返回时,计算结果将存放在R0和R1中)。从第二次调用时开始,每次在调用之前只需先将参与本次运算的字节存入R2即可(第二次是m4,第三次是m5,...,第i次是mi+2,...)。当最后一次调用返回后,R0和R1分别存放的就是最终结果h和l 。

CRC MOV DPH, #table ; 指向余式表下半区
MOV DPL, R0 ; 指向对应单元
CLR A ;
MOVC A, @A+DPTR ; 读余式的高字节
XRL A, R1 ; 计算余式的高字节
MOV R0, A ; 存入R0
INC DPH ; 指向余式表上半区
CLR A ;
MOVC A, @A+DPTR ; 读余式的低字节
XRL A, R2 ; 计算余式的低字节
MOV R1, A ; 存入R1
RET

这一子程序只有12条指令,因此十分简捷,而且只占用16个机器周期,也就是说,相当于计算每一个字节只需16个机器周期即可完成,这将比传统的软件算法快十几倍。

 


表1 [ a 0 0 ] 余式表

a
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F


0000
1021
2042
3063
4084
50A5
60C6
70E7
8108
9129
A14A
B16B
C18C
D1AD
E1CE
F1EF


1231
0210
3273
2252
52B5
4294
72F7
62D6
9339
8318
B37B
A35A
D3BD
C39C
F3FF
E3DE


2462
3443
0420
1401
64E6
74C7
44A4
5485
A56A
B54B
8528
9509
E5EE
F5CF
C5AC
D58D


3653
2672
1611
0630
76D7
66F6
5695
46B4
B75B
A77A
9719
8738
F7DF
E7FE
D79D
C7BC


48C4
58E5
6886
78A7
0840
1861
2802
3823
C9CC
D9ED
E98E
F9AF
8948
9969
A90A
B92B


5AF5
4AD4
7AB7
6A96
1A71
0A50
3A33
2A12
DBFD
CBDC
FBBF
EB9E
9B79
8B58
BB3B
AB1A

oyljerry 2004-10-29
  • 打赏
  • 举报
回复
参考CRC
xiaoligang 2004-10-29
  • 打赏
  • 举报
回复
最好把报文头,和报文体的数据结构也给出,然后列举几个计算方法。谢谢。。

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧