(76条消息) MFC+Modbus-Tcp协议实现温湿度传感器采集-CSDN博客
(76条消息) MFC串口通信的两种方法及注意事项_lintec mfc通讯方式_iwilldoitx的博客-CSDN博客
(76条消息) MFC 串口的读写数据_mfc中读写串口用法_SunSachin的博客-CSDN博客
(75条消息) VC_MFC串口通信编程详解_mfc 串口_贝壳紫铃的博客-CSDN博客
(76条消息) 串口通信程序中十六进制格式发送和接收实现_路和前方的博客-CSDN博客
(76条消息) MFC edit编辑框无法实时更新数据的问题解决_mfc设置编辑框实时显示数据_柠檬有点酸的博客-CSDN博客
(76条消息) 关于Modbus数据16进制转10进制问题_modbus数据转换_ASWaterbenben的博客-CSDN博客
串口通信流程
(1)打开串口
Win32 系统把文件的概念进行了扩展.无论是文件、通信设备、命名管道磁盘、还是控制台,都是用 API 函数 CreateFile来打开或创建的。
HANDLE CreateFile( LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
/*
• lpFileName:将要打开的串口逻辑名,如“COM1”;
• dwDesiredAccess: 指定串口访问的类型,可以是读取、写入或二者并列;一般设为:GENERIC_READ | GENERIC_WRITE
• dwShareMode:指定共享属性,由于串口不能共享,该参数必须置为 0//独占方式;
• lpSecurityAttributes:引用安全性属性结构,缺省值为 NULL;
• dwCreationDistribution:创建标志,对串口操作该参数必须置为 OPEN_EXISTING// 打开而不是创建;
• dwFlagsAndAttributes:属性描述,用于指定该串口是否进行异步操作,该值 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,重叠方式; 0,表示同步 I/O ;
• hTemplateFile:对串口而言该参数必须置为 NULL;
*/
(2)配置串口
在打开通讯设备句柄后,常需要对串口进行一些初始化配置工作。这需要通过一个DCB 结构来进行。DCB 结构包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。
①用 CreateFile打开串口后,调用 GetCommState函数来获取串口的初始配置。
②修改串口的配置----修改 DCB 结构。
③再调用 SetCommState 函数设置串口。
④设置 I/O 缓冲区的大小和超时。Windows 用 I/O 缓冲区来暂存串口输入和输出的数据。如果通信的速率较高,则应该设置较大 的缓冲区。调用 SetupComm 函数可以设置串行口的输入和输出缓冲区的大小。
⑤ 超时问题:超时的作用是在指定的时间内没有读入或发送指定数量的字符,ReadFile 或 WriteFile 的操作仍然会结束。要查询当前的超时设置应调用 GetCommTimeouts 函数,调用 SetCommTimeouts 可以用某一个 COMMTIMEOUTS 结构的内容 来设置超时.
typedef struct _DCB{
//波特率,指定通信设备的传输速率.这个成员可以是实际波特率值或者下面的常量值之一:
DWORD BaudRate;
//CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200,
//CBR_38400,
//CBR_56000,CBR_57600,CBR_115200,CBR_128000,CBR_256000,CBR_14400
DWORD fParity;// 指定奇偶校验使能.若此成员为 1,允许奇偶校验检查
BYTE ByteSize;// 通信字节位数,4—8
BYTE Parity;//指定奇偶校验方法.此成员可以有下列值:
//EVENPARITY 偶校验 NOPARITY 无校验
//MARKPARITY 标记校验 ODDPARITY 奇校验
BYTE StopBits;// 指定停止位的位数.此成员可以有下列值:
//ONESTOPBIT 1 位停止位 TWOSTOPBITS 2 位停止位
//ONE5STOPBITS 1.5 位停止位
} DCB;
//在 winbase.h 文件中定义了以上用到的常量.如下所示:
#define NOPARITY 0
#define ODDPARITY 1
#define EVENPARITY 2
#define ONESTOPBIT 0
#define ONE5STOPBITS 1
#define TWOSTOPBITS 2
#define CBR_110 110
#define CBR_300 300
#define CBR_600 600
#define CBR_1200 1200
#define CBR_2400 2400
#define CBR_4800 4800
#define CBR_9600 9600
#define CBR_14400 14400
#define CBR_19200 19200
#define CBR_38400 38400
#define CBR_56000 56000
#define CBR_57600 57600
#define CBR_115200 115200
#define CBR_128000 128000
#define CBR_256000 256000
BOOL OpenComm(CString strComm)方法:
if (NULL == m_hComm)
{//若为null,才能打开串口相当于创建文件
m_hComm = CreateFile((TCHAR*)(LPCTSTR)strComm,//COM口
GENERIC_READ | GENERIC_WRITE, //允许读和写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
0, //同步方式
NULL);
if (INVALID_HANDLE_VALUE == m_hComm)//无效句柄类型
{
int nError = GetLastError();//返回错误类型
m_hComm = NULL;
AfxMessageBox(_T("串口打开失败"));
return FALSE;
}else
{
AfxMessageBox(_T("串口打开成功"));
BOOL bRet = FALSE;
//输入缓冲区和输出缓冲区的大小都是100
bRet = ::SetupComm(m_hComm,100,100);
if(!bRet) AfxMessageBox(_T("设置串口输入输出缓冲区失败!"));
//设定读超时
COMMTIMEOUTS TimeOuts;
TimeOuts.ReadIntervalTimeout=MAXDWORD;
TimeOuts.ReadTotalTimeoutMultiplier=0;
TimeOuts.ReadTotalTimeoutConstant=0;
//设定写超时
TimeOuts.WriteTotalTimeoutMultiplier=100;
TimeOuts.WriteTotalTimeoutConstant=500;
bRet = SetCommTimeouts(m_hComm,&TimeOuts);
if(!bRet) AfxMessageBox(_T("设置读写操作超时失败!"));
//配置串口属性
DCB dcb;
//获取串口的初始配置
bRet = ::GetCommState(m_hComm, &dcb);
if (!bRet)//不成功,关闭句柄
{
if (m_hComm)
{
CloseHandle(m_hComm);
m_hComm = NULL;
AfxMessageBox(_T("关闭句柄1"));
}
return FALSE;
}else
{
//修改串口的配置
dcb.BaudRate=9600; //波特率
dcb.ByteSize=8; //每个字节有8位
dcb.Parity=NOPARITY; //奇偶校验位
dcb.StopBits=0; //1个停止位
//设置串口
bRet = ::SetCommState(m_hComm, &dcb);
if (!bRet)
{
AfxMessageBox(_T("配置串口属性失败!"));
if (m_hComm)
{
CloseHandle(m_hComm);
m_hComm = NULL;
AfxMessageBox(_T("关闭句柄2"));
}
return FALSE;
}
}
//在读写串口之前,还要用 PurgeComm()函数清空缓冲区
bRet = ::PurgeComm(m_hComm, PURGE_RXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
if (!bRet) AfxMessageBox(_T("无法清除串口的错误状态!"));
return TRUE;
}
(3)读写串口
int ReadData(HANDLE handler, char* buffer)
{
char readBuffer[512];//读取的内容,读取到的是16进制ASCII码形式
memset(readBuffer, 0, 512);
DWORD wCount= 512;//读取的字节数
BOOL bReadStat;
while(1)
{
Sleep(200);
bReadStat = ReadFile(handler, readBuffer, wCount, &wCount, NULL);
if(wCount!=0) break;
}
if(!bReadStat)
{
AfxMessageBox("读串口失败!");
return -1;
}
//ASCII转换成十六进制字符串
CString str;
CString str_HEX;
for (int i=0;i<9;i++)
{
str.Format("%02X ", readBuffer[i]);
str_HEX = str_HEX +str;
}
strcpy(buffer, readBuffer);
PurgeComm(handler, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
return 0;
}
int WriteData(HANDLE handler, char* buffer)//buffer:写入串口的内容
{
unsigned long dwBytesWrite;
COMSTAT ComStat;
DWORD dwErrorFlags;
BOOL bWriteStat;
ClearCommError(handler, &dwErrorFlags, &ComStat);
dwBytesWrite = strlen(buffer);
bWriteStat=WriteFile(handler, buffer, dwBytesWrite, &dwBytesWrite, NULL);
if(!bWriteStat)
{
AfxMessageBox("写串口失败!");
return -1;
}
return 0;
}
(4)关闭串口
void SerialPort::CloseComm()
{
if (m_hComm)
{
CloseHandle(m_hComm);
m_hComm = NULL;
AfxMessageBox(_T("成功关闭串口"));
}
}
const unsigned char m_auchCRCHi[] =
{
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
const unsigned char m_auchCRCLo[] =
{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
///计算CRC_ModBus 校验码
unsigned short SerialPort::CRC_Modbus(unsigned char *puchMsg, unsigned short usDataLen)
{
unsigned char uchCRCHi = 0xFF;
unsigned char uchCRCLo = 0xFF;
unsigned short uIndex;
while (usDataLen--)
{
uIndex = uchCRCHi^*puchMsg++;
uchCRCHi = uchCRCLo^m_auchCRCHi[uIndex];
uchCRCLo = m_auchCRCLo[uIndex];
}
return(uchCRCHi << 8 | uchCRCLo);
}