由于我有个朋友在改造设备,需要用到S7-200中的ModBus通信,其中需要CRC校验码。本人查找了很多资料,终于理解了CRC校验码的原理及在优快云中找到一个有关CRC校验的一个源程序,通过理解及分析,最终写出了这个基于MFC框架下的"串口助手+CRC校验(可用于PLC中ModBus通信)”,已通过串口测试,能正常通信。
代码写得很粗糙,感觉有很多漏洞,由于时间关系没进一步完善,勉强能用啦!
以下为代码的讲解,用的编程环境为VS2012中MFC编程框架写的界面,界面也弄得很粗糙
下面为crc生成代码:
void Ccrc校验V20Dlg::OnBnClickedButton1()//生成crc校验码
{
UpdateData(true);
BYTE LinNumber = 0;
int CstrMount = 0;
CString CStrZ;
CString CStrH,CStrL;
int mountCstr = m_EditCrcYuan.GetLength();//获取数组长度
std::vector<BYTE> Bian(mountCstr/2);
for (int i = 0; i < mountCstr/2; i++)
{
CStrZ=m_EditCrcYuan.Mid(i*2,2);//分割字符串
HexToDec(CStrZ,LinNumber);//转16进制
Bian[i]=LinNumber;
}
BYTE CRC_L = 0xff,CRC_H = 0xff;//crc初始值
BYTE SH = 0x0,SL = 0x0;//临时变量
for (int mount = 0; mount < mountCstr/2; mount++)
{
CRC_L = (BYTE)CRC_L ^ Bian[mount];
for (int i = 0; i < 8; i++)
{
SH = CRC_H;SL = CRC_L;
CRC_H = (BYTE)(CRC_H >> 1);//高位右移一位
CRC_H = (BYTE)(CRC_H & 0x7f);
CRC_L = (BYTE)(CRC_L >> 1);//低位右移一位
CRC_L = (BYTE)(CRC_L & 0x7f);
if ((SH & 0x01) == 0x01)//高低字节连接,高位原本最后一个为1,则低位第一个应为1
{
CRC_L = (BYTE)(CRC_L | 0x80);
}
if ((SL & 0x01) == 1)//如果最低位为1,则与多项式进行异或
{
CRC_H = (BYTE)(CRC_H ^ 0xA0);
CRC_L = (BYTE)(CRC_L ^ 0x01);
}
}
}
CStrH.Format(_T("%2x"),CRC_H);
CStrL.Format(_T("%2x"),CRC_L);
CStrH.Replace(_T(" "),_T("0"));
CStrL.Replace(_T(" "),_T("0"));
m_EditCrcEnd = m_EditCrcYuan + CStrL + CStrH ;//校验结果
UpdateData(false);
return;
}
由于在PLC通信中,通常转换为16进制才发送,可按以下函数进行转换,函数可在网上找到,其作用是将文本框接受到的字符串转为16进制字符,如a转换为0x0a
BOOL HexToDec(LPCTSTR shex,BYTE& idec )
{
int i,mid;
int len = lstrlen( shex );
if(len>8) return FALSE;
mid = 0;idec = 0;
for(i=0;i<len;i++ )
{
if(shex[i]>='0'&&shex[i]<='9' ) mid = shex[i]-'0';
else if( shex[i]>='a'&&shex[i]<='f') mid = shex[i] -'a' +10;
else if( shex[i]>='A'&&shex[i]<='F') mid = shex[i] -'A' +10;
else return FALSE;
mid <<= ((len-i-1)<<2);
idec |= mid;
}
return TRUE;
}
在串口通信中16进制发送,可通过上面的函数转换为16进制,然后定义一个向量数组,然后发送向量数组
void Ccrc校验V20Dlg::OnBnClickedButton2()//串口发送
{
UpdateData(TRUE); //读取编辑框内容
m_EditReceive = _T("");
m_EditSend = m_EditCrcEnd;
UpdateData(false);//数据更新
CString CStrZ,CstrLin;
BYTE LinNumber = 0;
int mountCstr = m_EditCrcEnd.GetLength();//获取数组长度
std::vector<BYTE> Bian(mountCstr/2);
CByteArray Array;//存储获取plc数据的指令
Array.RemoveAll();
Array.SetSize(mountCstr/2);
for (int i = 0; i < mountCstr/2; i++)
{
CStrZ=m_EditCrcEnd.Mid(i*2,2);//分割字符串
HexToDec(CStrZ,LinNumber);//转16进制
Array.SetAt(i,LinNumber);
}
m_mscomm.put_Output(COleVariant(Array)); //发送数据
return;
}
下面为crc在线校验网站,我在于PLC通信时发现高低字节要互换,可能是高低字节发送的顺序不同的原因
https://www.lammertbies.nl/comm/info/crc-calculation.html
下面为本文源程序下载地址
http://download.youkuaiyun.com/detail/alfive/9791914