C++ 计算 Modbus RTU CRC 16
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19347685/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Calculating Modbus RTU CRC 16
提问by Momergil
I'm implementing a software where I read and write data in Modbus RTU protocolo via serial. For that, I need to calculate the two CRC byte at the end of the string of bytes, but I'm being incapable of doing this.
我正在实现一个软件,在该软件中我通过串行读取和写入 Modbus RTU 协议中的数据。为此,我需要计算字节串末尾的两个 CRC 字节,但我无法这样做。
Searching throughout the web, I found two functions that seems to calculate the CRC correctly:
在整个网络上搜索,我发现了两个似乎可以正确计算 CRC 的函数:
WORD CRC16 (const BYTE *nData, WORD wLength)
{
static const WORD wCRCTable[] = {
0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };
BYTE nTemp;
WORD wCRCWord = 0xFFFF;
while (wLength--)
{
nTemp = *nData++ ^ wCRCWord;
wCRCWord >>= 8;
wCRCWord ^= wCRCTable[nTemp];
}
return wCRCWord;
} // End: CRC16
And
和
uint CRC16_2(QByteArray buf, int len)
{
uint crc = 0xFFFF;
for (int pos = 0; pos < len; pos++)
{
crc ^= (uint)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
crc >>= 1; // Just shift right
}
}
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
return crc;
}
The problem is that I'm supposed to get two hex bytes as CRC numbers while this functions returns a integer value. For example, for "01" (1 byte), I was supposed to get a "7E80" while I get "21695", and I'm being unable to do some sort of conversion from this to that hex data.
问题是我应该得到两个十六进制字节作为 CRC 数字,而这个函数返回一个整数值。例如,对于“01”(1 个字节),我应该得到“7E80”而我得到“21695”,但我无法从这个到那个十六进制数据进行某种转换。
My question, therefore, is: how do I go from the integer result to the double hex result needed? I tried a couple of options, with no success.
因此,我的问题是:如何从整数结果到所需的双十六进制结果?我尝试了几个选项,但没有成功。
Note: I'm using Qt, so if one could find a solution implementing QByteArray or another Qt friendly code, I'll be glad. Either way a solution not using Qt, C or C++ is useless :P
注意:我正在使用 Qt,所以如果有人能找到实现 QByteArray 或其他 Qt 友好代码的解决方案,我会很高兴。无论哪种方式,不使用 Qt、C 或 C++ 的解决方案都是无用的:P
采纳答案by Reinstate Monica
According to MODBUS over serial line specification and implementation guide V1.02, the CRC is sent little-endian (low byte first).
根据MODBUS over serial line规范和实现指南V1.02,CRC是little-endian(低字节在前)发送的。
I have no idea, though, how you came up with needing any hexadecimal bytes for the CRC. MODBUS RTU is a binary protocol, and the CRC is sent as two bytes, not as four hexadecimal digits!
但是,我不知道您是如何想出需要任何十六进制字节来进行 CRC 的。MODBUS RTU 是一个二进制协议,CRC 是作为两个字节发送的,而不是四个十六进制数字!
Here's how you'd do it, using the CRC16 function you provided.
以下是使用您提供的 CRC16 函数执行此操作的方法。
QByteArray makeRTUFrame(int slave, int function, const QByteArray & data) {
Q_ASSERT(data.size() <= 252);
QByteArray frame;
QDataStream ds(&frame, QIODevice::WriteOnly);
ds.setByteOrder(QDataStream::LittleEndian);
ds << quint8(slave) << quint8(function);
ds.writeRawData(data.constData(), data.size());
int const crc = CRC16((BYTE*)frame.constData(), frame.size());
ds << quint16(crc);
return frame;
}
回答by Adam Lam
unsigned int CRC16_2(unsigned char *buf, int len)
{
unsigned int crc = 0xFFFF;
for (int pos = 0; pos < len; pos++)
{
crc ^= (unsigned int)buf[pos]; // XOR byte into least sig. byte of crc
for (int i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else // Else LSB is not set
crc >>= 1; // Just shift right
}
}
return crc;
}
im kind of a noob myself, butttt-
我自己就是个菜鸟,butttt-
i used the code u provided and tested it myself, and as u said it didnt work right, but then i realized it was passing hex chars, so i just changed uint to char and it checks out for me at least.
我使用了你提供的代码并自己测试了它,正如你所说它没有正常工作,但后来我意识到它正在传递十六进制字符,所以我只是将 uint 更改为 char 并且它至少检查了我。
i even calculated a sample by hand to double check.
我什至手工计算了一个样本来仔细检查。
回答by Michal Fiala
I tried using the first example of code you posted in here (the one using table) and I found out, that there is a mistake in using index. To make the code running correctly, you have to access to the table in the area limited by its size.
我尝试使用您在此处发布的第一个代码示例(使用 table 的代码示例),但我发现使用 index.js 时出错。为了使代码正确运行,您必须访问受其大小限制的区域内的表。
wCRCWord ^= wCRCTable[(nTemp & 0xFF)];
So the whole code, that returns correct value of CRC16 for MODBUS is listed below. The number returned has already swaped Lo and Hi byte.
因此,下面列出了为 MODBUS 返回正确的 CRC16 值的整个代码。返回的数字已经交换了 Lo 和 Hi 字节。
WORD CRC16 (const BYTE *nData, WORD wLength)
{
static const WORD wCRCTable[] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 };
BYTE nTemp;
WORD wCRCWord = 0xFFFF;
while (wLength--)
{
nTemp = *nData++ ^ wCRCWord;
wCRCWord >>= 8;
wCRCWord ^= wCRCTable[(nTemp & 0xFF)];
}
return wCRCWord;
} // End: CRC16
回答by Devidas
Here are my two cents. First thing you cant return two values to a function so
这是我的两分钱。首先你不能将两个值返回给一个函数,所以
- Append two values to the array(remember to remove const)
- Return a struct containing these two values.
Proccess return value as follow
WORD n = CRC16(nData,wLength); WORD x = (0xFF00&n)>>8,y=0x00FF&n; printf("0x%04x\n", n); // to check original value printf("0x%02x\t0x%02x\n",x,y); // to check separated values
- 将两个值附加到数组中(记得删除 const)
- 返回一个包含这两个值的结构。
处理返回值如下
WORD n = CRC16(nData,wLength); WORD x = (0xFF00&n)>>8,y=0x00FF&n; printf("0x%04x\n", n); // to check original value printf("0x%02x\t0x%02x\n",x,y); // to check separated values
Try this and let me know.
试试这个,让我知道。

