CRC校验详解
前言:CRC校验是技能大赛比较重要的一种数据校验方式,也是考察的重点。但是很多初次接触的人总是会感觉无从下手。鉴于此,总结一下我对CRC的认识和运用。
- 什么是CRC校验?
- CRC的校验原理
- 模2除法
- 二进制系数多项式
- 具体步骤
- 代码实现
什么是CRC校验?
CRC校验实用程序库在数据存储和数据通讯领域,为了保证数据的正确,就不得不采用检错的手段。在诸多检错手段中,CRC是最著名的一种。CRC的全称是循环冗余校验,其特点是:检错能力强,开销小,易于用编码器及检测电路实现。从其检错能力来看,它所不能发现的错误的几率仅为0.0047%以下。能够对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
CRC的校验原理
循环冗余校验同其他差错检测方式一样,通过在要传输的k比特数据D后添加(n-k)比特冗余位(又称帧检验序列,
FrameCheckSequence,FCS)
F
r
a
m
e
C
h
e
c
k
S
e
q
u
e
n
c
e
,
F
C
S
)
F形成n比特的传输帧T,再将其发送出去。
特别的,循环冗余校验提供一个预先设定的
(n−k+1)
(
n
−
k
+
1
)
比特整数P,并且要求添加的
(n−k)
(
n
−
k
)
比特F满足:
TmodP==0……(1)
T
m
o
d
P
==
0
…
…
(
1
)
T=2n−kD+F……(2)
T
=
2
n
−
k
D
+
F
…
…
(
2
)
基于上述要求,实际应用时,发送方和接收方按以下方式通信:
- 发送方和接收方在通信前,约定好预设整数P。
- 发送方在发送前通过(1)和(2)式确定并填充F,然后发送。
- 接收方收到数据,进行 result=TmodP r e s u l t = T m o d P 运算,当且仅当result = 0时接收方认为没有差错。
发送方在发送数据前需要确定填充的 (n−k) ( n − k ) 比特F,以下提供了两种等价的方式来确定F。
模二运算
模二运算采用无进位的二进制加法,恰好为异或(XOR)操作。
由于我们最终的目的是(1)式,根据(2)式,我们有
(2n−kD+F)/P=2n−kD/P+F/P……(3)
(
2
n
−
k
D
+
F
)
/
P
=
2
n
−
k
D
/
P
+
F
/
P
…
…
(
3
)
现在,我们令
2n−kD/P=Q+R/P……(4)
2
n
−
k
D
/
P
=
Q
+
R
/
P
…
…
(
4
)
于是,我们有
(2n−kD+F)/P=Q+R/P+F/P……(5)
(
2
n
−
k
D
+
F
)
/
P
=
Q
+
R
/
P
+
F
/
P
…
…
(
5
)
由于采用无进位的二进制加法(等价于XOR操作),因此当我们令 F = R 时,即T = 2n-kD + R,有
(2n−kD+F)/P=Q+R/P+F/P=Q……(6)
(
2
n
−
k
D
+
F
)
/
P
=
Q
+
R
/
P
+
F
/
P
=
Q
…
…
(
6
)
此时便有(1)式成立。
因此利用模二加法我们知,我们需要添加的帧检验序列F为:
F=2n−kDmodP……(7)
F
=
2
n
−
k
D
m
o
d
P
…
…
(
7
)
二进制系数多项式
该种方法,我们试图对任意的二进制数都构造与其对应的一个二进制系数多项式,构造如下:
对于任意k位二进制数D =dk-1…d2d1d0,其对应的多项式为
D(X)=∑di∗Xi,i∊[0,k)……(8)
D
(
X
)
=
∑
d
i
∗
X
i
,
i
∊
[
0
,
k
)
…
…
(
8
)
例如,D = 110101,则
D(X)=X5+X4+X2+1。
D
(
X
)
=
X
5
+
X
4
+
X
2
+
1
。
运算过程依然是模二的,则此时的CRC过程可描述如下:
Xn−kD(X)/P(X)=Q(X)+R(X)/P(X)……(9)
X
n
−
k
D
(
X
)
/
P
(
X
)
=
Q
(
X
)
+
R
(
X
)
/
P
(
X
)
…
…
(
9
)
T(X)=Xn−kD(X)+R(X)……(10)
T
(
X
)
=
X
n
−
k
D
(
X
)
+
R
(
X
)
…
…
(
10
)
即,此时的F(X)满足:
F(X)=Xn−kD(X)modP(X)……(11)
F
(
X
)
=
X
n
−
k
D
(
X
)
m
o
d
P
(
X
)
…
…
(
11
)
具体步骤
- 选择合适的除数
- 看选定除数的二进制位数,然后再要发送的数据帧上面加上这个位数-1位的0,然后用新生成的帧以模2除法的方式除上面的除数,得到的余数就是该帧的CRC校验码。注意,余数的位数一定只比除数位数少一位,也就是CRC校验码位数比除数位数少一位,如果前面位是0也不能省略。
- 将计算出来的CRC校验码附加在原数据帧后面,构建成一个新的数据帧进行发送;最后接收端在以模2除法方式除以前面选择的除数,如果没有余数,则说明数据帧在传输的过程中没有出错。
CRC校验码计算示例:
C
R
C
校
验
码
计
算
示
例
:
现假设选择的CRC生成多项式为G(X) = X4 + X3 + 1,要求出二进制序列10110011的CRC校验码。下面是具体的计算过程:
①将多项式转化为二进制序列,由G(X) = X4 + X3 + 1可知二进制一种有五位,第4位、第三位和第零位分别为1,则序列为11001
②多项式的位数位5,则在数据帧的后面加上5-1位0,数据帧变为101100110000,然后使用模2除法除以除数11001,得到余数。
③将计算出来的CRC校验码添加在原始帧的后面,真正的数据帧为101100110100,再把这个数据帧发送到接收端。
④接收端收到数据帧后,用上面选定的除数,用模2除法除去,验证余数是否为0,如果为0,则说明数据帧没有出错。
代码实现
以下是CRC的核心代码,仅供研究CRC的实现过程。
///C# CRC16 核心代码
public byte[] CRC16_C(byte[] data)
{
byte maxValue1 = byte.MaxValue;
byte maxValue2 = byte.MaxValue;
byte num1 = 1;
byte num2 = 160;
foreach (byte num3 in data)
{
maxValue1 ^= num3;
for (int index = 0; index <= 7; ++index)
{
byte num4 = maxValue2;
byte num5 = maxValue1;
maxValue2 >>= 1;
maxValue1 >>= 1;
if (((int)num4 & 1) == 1)
maxValue1 |= (byte)128;
if (((int)num5 & 1) == 1)
{
maxValue2 ^= num2;
maxValue1 ^= num1;
}
}
}
return new byte[2] { maxValue1, maxValue2 };
}
仅靠上述代码是无法将CRC运用到实践中去的。因为实际使用中总会遇见一些特殊的情况,这是有实际情景决定的。我写了一个CRC计算工具,一下仅供学习参考。
/// <summary>
//通过点击事件获取我们需要的CRC16 高低位
/// </summary>
private void Btn_GetCRC_Click(object sender, RoutedEventArgs e)
{
try
{
txt_CRC.Text = "";
if (txt_OldDate.Text.Length <= 0)
return;
foreach (byte num in this.CRC16_C(strToToHexByte(txt_OldDate.Text)))
txt_CRC.Text += num.ToString("X02");
'''strToToHexByte是一个字符传转字节的方法,由于步骤繁琐就不献丑了。'''
}
catch
{
MessageBox.Show("输入十六进制字符格式不对!");
txt_OldDate.Focus();
}
}
通过上述代码段基本上可以解决问题了。如有需要,可以点击下载完整的Demo.程序界面原型以及效果(如图)。
支持各类数据转换,附带LRC校验。