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 的代码:

csharp
using 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 最终异或值。
  1. 初始化 CRC 值:使用 InitialValue 进行初始化。
  2. 数据处理
    • 对每个字节,首先将其左移 8 位并与当前 CRC 值进行异或操作。
    • 对 CRC 值进行 8 次迭代处理,每次检查 CRC 的最高位是否为 1。
    • 如果最高位为 1,CRC 值左移并与多项式进行异或;否则,只进行左移。
  3. 最终处理:将 CRC 值与 FinalXORValue 进行异或,得到最终结果。

4. 串口通讯中使用 CRC

以下是如何在串口通讯中使用 CRC 来验证数据的完整性:

a. 发送数据

  1. 计算 CRC

    csharp
    byte[] data = new byte[] { /* 数据 */ }; ushort crc = CRC16CCITT.CalculateCRC16(data);
  2. 附加 CRC:将 CRC 值附加到数据的末尾(通常为 2 个字节)。

b. 接收数据

  1. 接收数据:从串口读取数据。
  2. 提取 CRC:从接收到的数据中提取 CRC 值。
  3. 计算 CRC:对接收到的数据(去除 CRC)进行 CRC 计算。
  4. 验证:将计算得到的 CRC 值与接收到的 CRC 值进行比较,验证数据的完整性。

c. 示例代码

csharp
using 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。这样可以确保数据在传输过程中的准确性和完整性。