CRC16是指什么意思

CRC16是指什么意思

CRC16(Cyclic Redundancy Check,循环冗余校验)是一种用于检测数据传输或存储过程中是否出现错误的校验算法。它通过计算数据的校验码(通常为16位,即两个字节)来实现。在通信协议(如Modbus)中,CRC16校验被广泛用于确保数据的完整性。

CRC16的基本原理

多项式除法:CRC16使用一个特定的生成多项式(例如Modbus中使用的是0x8005)对数据进行除法运算(在GF(2)域,即模2除法),得到的余数就是校验码。初始值:计算前通常有一个初始值(如0xFFFF)。结果异或值:计算完成后,有时会将结果与某个值(如0x0000)进行异或。输出反转:有些CRC16算法要求将最终结果按位反转。

Modbus RTU中的CRC16

在Modbus RTU协议中,CRC16的计算遵循以下规范:

多项式(Polynomial):0x8005(二进制:10000000000000101)初始值(Initial Value):0xFFFF输入反转(Input Reflected):True(按字节反转,即每个字节的位序颠倒)输出反转(Output Reflected):True(整个16位结果位序颠倒)结果异或值(XOR Out):0x0000

CRC16计算步骤(Modbus)

以计算数据01 03 00 00 00 01的CRC16为例:

初始化CRC寄存器为0xFFFF。依次处理每一个数据字节(包括地址和功能码等):

a. 将当前数据字节与CRC寄存器的低8位进行异或。

b. 对异或后的结果进行8次循环移位操作(每次移一位),在移位过程中根据移出的位决定是否与多项式0xA001(0x8005按位反转后的形式)进行异或。处理完所有字节后,得到的CRC寄存器中的值就是CRC16校验码(注意,Modbus要求将最终结果按小端序放置,即低字节在前,高字节在后)。

C#实现Modbus CRC16

以下是C#中计算Modbus CRC16的典型实现:

public static byte[] ComputeModbusCrc16(byte[] data)

{

ushort crc = 0xFFFF; // 初始值

foreach (byte b in data)

{

crc ^= b; // 与当前字节异或

for (int i = 0; i < 8; i++)

{

bool lsb = (crc & 0x0001) != 0; // 检查最低位是否为1

crc >>= 1; // 右移1位

if (lsb)

crc ^= 0xA001; // 如果移出位为1,则与多项式0xA001异或

}

}

// 将CRC拆分为两个字节(小端序:低字节在前,高字节在后)

return new byte[] { (byte)(crc & 0xFF), (byte)((crc >> 8) & 0xFF) };

}

示例

计算数据帧 01 03 00 00 00 01 的CRC16:

数据:01 03 00 00 00 01计算CRC:使用上述函数,得到两个字节:0x84 和 0x0A完整帧:01 03 00 00 00 01 84 0A

在Modbus通信中的作用

发送方:在数据帧末尾附加计算出的CRC16(低字节在前)。接收方:对接收到的数据(不包括最后的CRC部分)重新计算CRC16,然后与接收到的CRC16比较。如果一致,则数据正确;否则,认为传输错误,丢弃该帧。

注意事项

字节顺序:Modbus RTU中,CRC16的结果以低字节在前(小端序)的方式附加在帧尾。性能优化:实际应用中,通常使用预计算的CRC表(查表法)来提高计算速度,尤其当数据量较大时。

查表法优化

下面是使用查表法优化后的CRC16计算(在Modbus通信中更高效):

public static class ModbusCrc16

{

private static readonly ushort[] CrcTable = new ushort[256];

static ModbusCrc16()

{

// 初始化CRC表

for (ushort i = 0; i < 256; i++)

{

ushort value = 0;

ushort temp = (ushort)(i << 8);

for (byte j = 0; j < 8; j++)

{

if (((value ^ temp) & 0x8000) != 0)

value = (ushort)((value << 1) ^ 0x8005);

else

value <<= 1;

temp <<= 1;

}

CrcTable[i] = value;

}

}

public static byte[] Compute(byte[] data)

{

ushort crc = 0xFFFF;

foreach (byte b in data)

{

byte index = (byte)(crc ^ b);

crc = (ushort)((crc >> 8) ^ CrcTable[index]);

}

return new byte[] { (byte)crc, (byte)(crc >> 8) };

}

}

使用查表法可以大幅提高CRC16的计算效率,特别适合在嵌入式系统或高频通信中使用。

总结

CRC16是Modbus RTU协议中用于错误检测的关键机制,确保数据传输的可靠性。理解其原理和实现对于开发工业通信应用至关重要。

CRC16(循环冗余校验)详解

CRC16(Cyclic Redundancy Check,循环冗余校验)是一种广泛用于数据通信和存储中检测传输错误的校验算法。它通过计算数据的校验值(Checksum)来验证数据的完整性。

CRC16的核心原理

多项式除法:

将数据视为一个大型二进制数

用预定义的多项式(如Modbus使用0x8005)除这个数

得到的余数就是CRC校验码

位操作实现:

通过移位和异或(XOR)操作高效计算

不需要实际的除法运算

Modbus RTU中的CRC16规范

特性 值 说明

多项式 0x8005 x¹⁶ + x¹⁵ + x² + 1

初始值 0xFFFF 计算开始时的种子值

输入反转 是 每个字节的位顺序反转

输出反转 是 最终结果的位顺序反转

结果异或 0x0000 最终结果不需要额外异或

校验码长度 16位 2字节

CRC16计算步骤(以Modbus为例)

初始化

ushort crc = 0xFFFF; // 初始值

处理每个字节

foreach (byte b in data)

{

crc ^= b; // 当前字节与CRC异或

for (int i = 0; i < 8; i++) // 处理每个bit

{

bool lsb = (crc & 0x0001) != 0; // 检查最低位

crc >>= 1; // 右移一位

if (lsb) // 如果移出的位是1

crc ^= 0xA001; // 与多项式异或 (0x8005的反转)

}

}

最终处理`

// 输出反转 (大端序转小端序)

byte crcLow = (byte)crc;

byte crcHigh = (byte)(crc >> 8);

CRC16在Modbus帧中的位置

完整的Modbus RTU帧结构:

[地址][功能码][数据][CRC低字节][CRC高字节]

示例帧(读取寄存器):

01 03 00 00 00 01 84 0A

└─┬┘ └─────┬────┘ └──┬──┘

│ │ └── CRC16校验码 (0x0A84)

│ └────────── 数据部分

└────────────────── 从站地址和功能码

为什么需要CRC16

错误检测能力:

检测所有单比特错误

检测所有双比特错误

检测奇数个错误

检测大多数突发错误(最长16位)

计算效率高:

适合嵌入式系统

硬件实现简单

广泛支持:

工业标准(Modbus, CAN, USB等)

文件校验(ZIP, RAR等)

CRC16算法实现(C#)

public static byte[] CalculateModbusCRC16(byte[] data)

{

ushort crc = 0xFFFF; // 初始值

foreach (byte b in data)

{

crc ^= b; // 异或当前字节

for (int i = 0; i < 8; i++) // 处理每个bit

{

bool lsb = (crc & 0x0001) != 0;

crc >>= 1; // 右移一位

if (lsb)

crc ^= 0xA001; // 0xA001是0x8005的位反转

}

}

// 返回小端序 [低字节, 高字节]

return new byte[] { (byte)(crc & 0xFF), (byte)(crc >> 8) };

}

CRC16验证过程

接收方验证数据的完整性:

接收完整数据帧(包括CRC)

计算接收数据的CRC(不包括最后2字节)

将计算结果与接收的CRC比较

匹配则数据正确,不匹配则数据有误

public bool VerifyCRC(byte[] frame)

{

// 分离数据和CRC

byte[] data = frame.Take(frame.Length - 2).ToArray();

byte[] receivedCrc = frame.Skip(frame.Length - 2).Take(2).ToArray();

// 计算数据的CRC

byte[] calculatedCrc = CalculateModbusCRC16(data);

// 比较CRC

return receivedCrc.SequenceEqual(calculatedCrc);

}

常见CRC16变体

名称 多项式 初始值 应用领域

Modbus 0x8005 0xFFFF 工业控制

CCITT 0x1021 0xFFFF XMODEM, Bluetooth

USB 0x8005 0xFFFF USB数据包

CRC-16-IBM 0x8005 0x0000 SDLC协议

实际通信示例

主站请求读取寄存器:

// 请求帧: 01 03 00 00 00 01

byte[] request = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x01 };

byte[] crc = CalculateModbusCRC16(request); // 返回 [0x84, 0x0A]

// 完整发送帧: 01 03 00 00 00 01 84 0A

byte[] fullFrame = request.Concat(crc).ToArray();

_serialPort.Write(fullFrame, 0, fullFrame.Length);

从站响应:

// 接收响应: 01 03 02 01 42 39 E5

byte[] response = ReadSerialData(); // 实现读取串口数据

// 验证CRC

if (!VerifyCRC(response))

throw new Exception("CRC校验失败");

// 提取有效数据: [0x01, 0x42]

byte[] validData = response.Skip(3).Take(response[2]).ToArray();

C16的重要性

数据可靠性:确保工业环境中数据传输的准确性

安全性:防止错误数据导致设备误操作

协议完整性:Modbus RTU标准要求

错误定位:帮助识别通信线路问题

理解CRC16的原理和实现对于开发可靠的工业通信系统至关重要,特别是在Modbus RTU这类没有内置错误纠正机制的协议中,CRC16是确保数据完整性的第一道防线。

相关推荐

全网都在问世界杯直播去哪看?这么挑就不会错
哪些网页游戏平台人多 排行榜推荐前十名
历届世界杯吉祥物回顾与创意足球队口号精彩汇聚