C#实现串口通讯CRC算法
在 C# 中实现串口通讯的 CRC(循环冗余校验)算法是确保数据传输完整性的重要步骤。CRC 是一种用于检测数据传输错误的哈希函数。以下是如何在 C# 中实现 CRC 校验的详细步骤。
1. 选择 CRC 类型
CRC 有多种类型,最常见的有 CRC-16-CCITT、CRC-32 等。这里我们以 CRC-16-CCITT-FALSE 为例进行说明。
2. CRC-16-CCITT-FALSE 算法概述
CRC-16-CCITT-FALSE(也称为 CRC-16-CCITT 或 CRC-16-CCITT-TRUE)是 CRC-16 的一个变体,通常用于串口通讯。它的多项式是 0x1021
,初始值通常是 0xFFFF
,最终值通常为 0x0000
。
3. CRC-16-CCITT-FALSE 实现步骤
a. CRC 计算函数
以下是 C# 中实现 CRC-16-CCITT-FALSE 的代码:
csharpusing System;
public class CRC16CCITT
{
// CRC-16-CCITT-FALSE 常量
private const ushort Polynomial = 0x1021;
private const ushort InitialValue = 0xFFFF;
private const ushort FinalXORValue = 0x0000;
public static ushort CalculateCRC16(byte[] data)
{
ushort crc = InitialValue;
foreach (byte b in data)
{
crc ^= (ushort)(b << 8);
for (int i = 0; i < 8; i++)
{
if ((crc & 0x8000) != 0)
{
crc = (ushort)((crc << 1) ^ Polynomial);
}
else
{
crc <<= 1;
}
}
}
return (ushort)(crc ^ FinalXORValue);
}
}
b. 解释代码
Polynomial
:CRC-16-CCITT-FALSE 使用的多项式。InitialValue
:CRC 初始值。FinalXORValue
:CRC 最终异或值。
- 初始化 CRC 值:使用
InitialValue
进行初始化。 - 数据处理:
- 对每个字节,首先将其左移 8 位并与当前 CRC 值进行异或操作。
- 对 CRC 值进行 8 次迭代处理,每次检查 CRC 的最高位是否为 1。
- 如果最高位为 1,CRC 值左移并与多项式进行异或;否则,只进行左移。
- 最终处理:将 CRC 值与
FinalXORValue
进行异或,得到最终结果。
4. 串口通讯中使用 CRC
以下是如何在串口通讯中使用 CRC 来验证数据的完整性:
a. 发送数据
计算 CRC:
csharpbyte[] data = new byte[] { /* 数据 */ }; ushort crc = CRC16CCITT.CalculateCRC16(data);
附加 CRC:将 CRC 值附加到数据的末尾(通常为 2 个字节)。
b. 接收数据
- 接收数据:从串口读取数据。
- 提取 CRC:从接收到的数据中提取 CRC 值。
- 计算 CRC:对接收到的数据(去除 CRC)进行 CRC 计算。
- 验证:将计算得到的 CRC 值与接收到的 CRC 值进行比较,验证数据的完整性。
c. 示例代码
csharpusing System.IO.Ports;
public class SerialPortHandler
{
private SerialPort _serialPort;
public SerialPortHandler(string portName)
{
_serialPort = new SerialPort(portName);
_serialPort.BaudRate = 9600;
_serialPort.Parity = Parity.None;
_serialPort.StopBits = StopBits.One;
_serialPort.DataBits = 8;
_serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
_serialPort.Open();
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] receivedData = new byte[sp.BytesToRead];
sp.Read(receivedData, 0, receivedData.Length);
// 提取数据和 CRC
byte[] data = new byte[receivedData.Length - 2];
Array.Copy(receivedData, 0, data, 0, data.Length);
ushort receivedCRC = BitConverter.ToUInt16(receivedData, data.Length);
// 计算 CRC
ushort calculatedCRC = CRC16CCITT.CalculateCRC16(data);
if (receivedCRC == calculatedCRC)
{
// 数据有效
Console.WriteLine("Data received correctly.");
}
else
{
// 数据错误
Console.WriteLine("Data error detected.");
}
}
public void SendData(byte[] data)
{
ushort crc = CRC16CCITT.CalculateCRC16(data);
byte[] crcBytes = BitConverter.GetBytes(crc);
byte[] dataWithCRC = new byte[data.Length + crcBytes.Length];
Array.Copy(data, 0, dataWithCRC, 0, data.Length);
Array.Copy(crcBytes, 0, dataWithCRC, data.Length, crcBytes.Length);
_serialPort.Write(dataWithCRC, 0, dataWithCRC.Length);
}
}
总结
通过上述代码示例和说明,你可以在 C# 中实现 CRC-16-CCITT-FALSE 算法,并在串口通讯中使用它来验证数据的完整性。实现步骤包括 CRC 计算函数的编写、在串口通讯中附加和验证 CRC。这样可以确保数据在传输过程中的准确性和完整性。