项目中所用到的DI数字开关模块通信方式为串口通信,内置数据校验方式为Modbus CRC16。
这种校验方式第一次遇到,于是百度了一下,找到这篇文章,按照步骤,自己写了下代码,对比了下文中代码,基本一样。
#include
#include
using namespace std;
int main()
{
unsigned short CRC = 0xffff;//(1)CRC寄存器初值0xffff
unsigned char data[6] = { 0x01,0x03,0x61,0x00,0x00,0x02 };//待校验的数据
for (int i = 0; i < 6; i++)//(5)重复步骤2~4
{
CRC = CRC^data[i];//(2)数据与CRC异或
for (int j = 0; j < 8; j++)//(4)重复8次步骤3
{
//(3)检测低位是否为1。方法:与1相与,低位为1则结果为1,低位为0则结果为0
if (CRC & 1)//如果低位为1,则先右移一位,再与A001H相异或
{
CRC >>= 1;
CRC ^= 0xA001;
}
else//低位为0,则右移一位
CRC >>= 1;
}
}
cout << CRC << endl;
printf("%X", CRC);
return 0;
}
之后,将该校验方法进行了封装:
#include
#include
#include
using namespace std;
unsigned int modbusCRC16(const vector<unsigned char> &data)
{
unsigned short CRC = 0xffff;//(1)CRC寄存器初值0xffff
int dataSize = data.size();
for (int i = 0; i < dataSize; i++)//(5)重复步骤2~4
{
CRC = CRC^data[i];//(2)数据与CRC异或
for (int j = 0; j < 8; j++)//(4)重复8次步骤3
{
//(3)检测低位是否为1。方法:与1相与,低位为1则结果为1,低位为0则结果为0
if (CRC & 1)//如果低位为1,则先右移一位,再与A001H相异或
{
CRC >>= 1;
CRC ^= 0xA001;
}
else//低位为0,则右移一位
CRC >>= 1;
}
}
return CRC;
}
int main()
{
vector<unsigned char> vec{ 0x01,0x02,0x02,0x03,0xFF };
unsigned int CRC = modbusCRC16(vec);
printf("%X", CRC);
return 0;
}
对上位机接收到的01 02 02 03 FF
数据进行校验
结果如下:
这里,DI模块先发送低字节,再发送高字节,所以收到的是F9 08。windows下计算机为小端模式,低字节在前,可将08 F9转换为大端,再与F9 08进行比较。
关于小端转大端,Qt中可直接使用自带库函数qFromBigEndian(),需包含头文件#include
其他可参照C/C++ —— 小端转大端函数的使用