目录
1 CRC 算法的工程应用
CRC(循环冗余校验,Cyclic Redundancy Check)算法是一种广泛应用于通信和存储系统的错误检测方法。CRC通过计算一个固定长度的校验码来检测数据传输过程中可能发生的错误。下面是一些CRC算法在工程中的典型应用:
1.1. 通信系统:
• 网络通信:在TCP/IP协议栈中,CRC用于数据包的错误检测,确保数据包完整无误地到达目的地。
• 串行通信:在各种串行总线标准中(如SPI、I²C、CAN、USB等),CRC被用于验证数据帧的完整性。
• 无线通信:在无线通信协议中,CRC用于检测传输过程中的数据损坏,例如在Wi-Fi、蓝牙、Zigbee等协议中。
1.2. 存储系统:
• 硬盘和固态硬盘:在磁盘和SSD等存储介质中,CRC用于检测和纠正读取过程中可能出现的错误。
• RAID技术:在RAID阵列中,CRC可以帮助检测和修复数据块的一致性问题。
1.3. 嵌入式系统:
• 微控制器通信:在嵌入式系统中,CRC用于确保传感器数据、控制信号等的准确传输。
• 数据记录:在需要高可靠性的数据记录系统中,CRC用于验证存储数据的准确性。
1.4. 协议和标准:
• Modbus:CRC-16通常用于Modbus协议中,确保数据帧的完整性和一致性。
• Ethernet:CRC-32用于以太网帧的错误检测。
1.5. 软件加密保护:
• 在软件加密保护中,CRC可用于生成注册码或者作为壳程序的一部分,确保软件的完整性和防止非法修改。
1.6. DSP数据通信:
• 在数字信号处理器(DSP)之间进行数据交换时,CRC用于保证数据传输的准确性。
1.7. 其他应用:
• 文件系统:在文件系统中,CRC可以用来检测文件的损坏。
• 内存校验:在一些高端服务器和工作站中,CRC用于检测和纠正内存错误。
• 汽车电子:在汽车电子控制系统中,CRC用于确保数据的准确传输,尤其是在安全相关的系统中。
总结:CRC算法之所以受到青睐,是因为它的错误检测能力较强,同时计算相对简单且易于实现。不同应用场景会根据具体需求选择不同的CRC多项式和位数,比如CRC-8、CRC-16、CRC-32等。这些CRC变体提供了不同程度的错误检测能力,可以根据实际需要进行选择。
2 CRC16的代码推导过程及源码
2.1 从多项式到C源代码
CRC(循环冗余校验)是一种用于检测数据传输过程中错误的有效方法。它通过计算一个固定长度的校验码来检测数据中的错误。CRC校验码通常是在数据发送端计算并附加到数据后面,然后在接收端再次计算并进行比较,以确定数据是否在传输过程中发生了改变。
CRC校验算法的基本原理:
(1)多项式表示:
• CRC校验码的计算基于二进制的模2除法,通常使用一个固定的多项式作为除数。
• 数据比特流也被表示为一个多项式。
(2)计算步骤:
• 将数据比特流表示为一个多项式。
• 用这个多项式除以一个固定的生成多项式。
• 余数(模2除法的余数)就是CRC校验码。
(3)CRC16 校验算法
CRC16校验算法是一种常用的CRC校验算法,它生成一个16位的校验码。CRC16有许多不同的变体,如CRC16-CCITT、CRC16-X25等,主要区别在于使用的生成多项式不同。
假设CRC16-CCITT使用多项式 x^16 + x^12 + x^5 + 1,表示为十六进制为 0x1021。
2.2 C语言源码
下面是一个基于CRC16-CCITT的C语言实现示例:
uint16_t crc16_ccitt(const uint8_t *data, size_t length) {
uint16_t crc = 0xFFFF; // 初始值
uint16_t poly = 0x1021; // CRC16-CCITT多项式
while (length--) {
crc ^= (*data << 8); // 取一个字节并移位
data++;
for (int i = 8; i != 0; i--) {
if (crc & 0x8000) {
crc = (crc << 1) ^ poly;
} else {
crc <<= 1;
}
}
}
return crc;
}
int main() {
const uint8_t testData[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF};
uint16_t crc = crc16_ccitt(testData, sizeof(testData));
printf("CRC16-CCITT: 0x%04X\n", crc);
return 0;
}
2.3 c shap源码
using System.Security.Cryptography;
using System.Text;
using System;
public class Crc16Ccitt
{
public static ushort CalculateCrc16Ccitt(byte[] data)
{
ushort crc = 0xFFFF; // 初始值
ushort poly = 0x1021; // CRC16-CCITT多项式
foreach (byte b in data)
{
crc ^= (ushort)(b << 8); // 取一个字节并移位
for (int i = 8; i != 0; i--)
{
if ((crc & 0x8000) != 0)
{
crc = (ushort)((crc << 1) ^ poly);
}
else
{
crc <<= 1;
}
}
}
return crc;
}
public static void Main()
{
byte[] testData = new byte[] { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF };
ushort crc = CalculateCrc16Ccitt(testData);
Console.WriteLine("CRC16-CCITT: 0x{0:X4}", crc);
}
}
2.4 pascal源码
program Crc16Ccitt;
uses
SysUtils;
function CalculateCrc16Ccitt(const Data: TBytes): Word;
var
i, j: Integer;
crc: Word;
poly: Word;
begin
crc := $FFFF; // 初始值
poly := $1021; // CRC16-CCITT多项式
for i := Low(Data) to High(Data) do
begin
crc := crc xor (Data[i] shl 8); // 取一个字节并移位
for j := 1 to 8 do
begin
if (crc and $8000) <> 0 then
begin
crc := (crc shl 1) xor poly;
end
else
begin
crc := crc shl 1;
end;
end;
end;
Result := crc;
end;
var
testData: TBytes;
crc: Word;
begin
testData := TBytes([18, 52, 86, 120, 154, 188, 222, 255]);
crc := CalculateCrc16Ccitt(testData);
Writeln('CRC16-CCITT: ', IntToHex(crc, 4));
end.
【创作不易,欢迎转载,转载请注明出处】
如果大家对相关文章感兴趣,可以关注公众号“嵌入式毛哥”,持续分享嵌入式干货,不限于疑难故障分析解决、算法共享、开发设计思想等,志在帮助更多人在嵌入式行业发光发热。