-
CRC16(循环冗余校验)简介
- CRC16 是一种错误检测码,在 Modbus 通信协议中用于检查数据传输过程中的错误。它的基本原理是基于多项式除法,通过对数据进行特定的多项式运算,生成一个 16 位的校验和。接收方使用相同的算法对收到的数据(包括校验和)进行计算,如果计算结果与收到的校验和一致,则认为数据传输正确;否则,数据可能在传输过程中出现了错误。
-
Modbus CRC16 算法步骤
-
初始化:
- 通常将一个 16 位的 CRC 寄存器初始化为 0xFFFF。这个寄存器将用于存储计算过程中的中间结果和最终的 CRC16 值。
-
数据处理:
- 对于要计算 CRC16 的每个字节数据,首先将该字节与 CRC 寄存器的低 8 位进行异或操作。然后,对 CRC 寄存器进行一次循环移位操作,即把最高位(MSB)移出并移到最低位(LSB)的位置。如果移出的最高位为 1,则将 CRC 寄存器与多项式 0xA001 进行异或操作;如果移出的最高位为 0,则不进行此异或操作。
- 这个过程会对每个字节数据重复进行,直到所有的数据字节都处理完毕。
-
结果获取:
- 当所有数据字节处理完后,CRC 寄存器中存储的值就是计算得到的 CRC16 校验和。不过需要注意的是,在 Modbus 协议中,CRC16 的字节顺序通常是低字节在前,高字节在后,所以可能需要对计算得到的 CRC16 值进行字节顺序的调整。
-
示例代码(C 语言)实现 Modbus CRC16 计算
- 以下是一个简单的 C 语言函数,用于计算 Modbus CRC16:
unsigned short modbusCRC16(unsigned char *data, unsigned short length) {
unsigned short crc = 0xFFFF;
unsigned char i;
for (unsigned short j = 0; j < length; j++) {
crc ^= (unsigned short)data[j];
for (i = 0; i < 8; i++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc = crc >> 1;
}
}
}
return crc;
}
uint16_t CRC_Check(uint8_t *CRC_Ptr,uint8_t LEN)
{
uint16_t CRC_Value = 0;
uint8_t i = 0;
uint8_t j = 0;
CRC_Value = 0xffff;
for(i=0;i<LEN;i++) //LEN为数组长度
{
CRC_Value ^= *(CRC_Ptr+i);
for(j=0;j<8;j++)
{
if(CRC_Value & 0x00001)
CRC_Value = (CRC_Value >> 1) ^ 0xA001;
else
CRC_Value = (CRC_Value >> 1);
}
}
CRC_Value = ((CRC_Value >> 8) + (CRC_Value << 8)); //交换高低字节
return CRC_Value;
}
- 在这个代码中:
- 首先将
crc初始化为 0xFFFF。 - 外层循环
for (unsigned short j = 0; j < length; j++)用于遍历要计算 CRC16 的所有数据字节。在每次循环中,先将当前字节与crc进行异或操作。 - 内层循环
for (i = 0; i < 8; i++)用于对crc寄存器进行 8 次循环移位和可能的异或操作,这是根据 CRC16 算法的步骤来执行的。 - 最后,返回计算得到的 CRC16 值。
-
在 Modbus 通信中的应用场景
-
数据帧校验:
- 在 Modbus 通信中,当主设备发送一个数据帧给从设备或者从设备返回数据帧给主设备时,数据帧的最后两个字节通常是 CRC16 校验和。例如,一个 Modbus RTU 数据帧包括地址域、功能码、数据域等部分,最后跟着 CRC16 校验和。接收方在收到数据帧后,会根据前面的数据部分重新计算 CRC16 值,然后与收到的校验和进行比较,以此来判断数据是否正确传输。
-
确保数据完整性和准确性:
- 由于工业环境等因素可能导致通信干扰,CRC16 能够有效地检测出数据传输过程中的错误,如比特翻转、字节丢失等情况,从而确保 Modbus 通信中数据的完整性和准确性。
参考Modbus-crc16校验原理和方法(含测试代码以及演算过程)_crc16modbus-CSDN博客