今儿看了几篇硕士论文,又看到了有人在用MSComm控件编的串口。当时我就不太懂这玩意儿是怎么用的,其实是因为在MFC(visual studio 2013环境)里怎么找也找不到这个控件。。。好吧,偷懒不成只好自己编一个串口类来实现功能了。
参考了很多文献和CSDN上的前辈的程序,最后编了一个小玩意儿,虽然不大,但是设置、收发、等待和其他一些功能也是完备了。
直接上代码吧,以供后来人参考,回报社会。
(p.s. 1. 串口使用非重叠模式(在代码中标识为异步模式,两者是否严格等价有待考证),执行收发程序时不必等待操作完成;2. 串口名实际命名为\\\\.\\COMX,X是串口号,这样可以创建10以上的串口)
头文件
#ifndef MYSERIALPORT_H_
#define MYSERIALPORT_H_
#include "stdafx.h"
#include "MyMutex.h"
class CSerialPort
{
private:
// 串口句柄
HANDLE m_hComm;
// 串口名
CString m_comName;
// 串口波特率
DWORD m_Baud;
// 串口读写缓冲区
DWORD m_dwInQueue;
DWORD m_dwOutQueue;
// 串口超时
DWORD m_ReadIntervalTimeout;
DWORD m_ReadTotalTimeoutMultiplier;
DWORD m_ReadTotalTimeoutConstant;
DWORD m_WriteTotalTimeoutMultiplier;
DWORD m_WriteTotalTimeoutConstant;
// 返回错误码
DWORD m_dwError;
public:
// 串口读相关变量
COMSTAT m_ComStat;
OVERLAPPED m_ovRead;
BOOL m_bWaitRead;
DWORD m_BytesRead;
BOOL m_IsReadFault;// 判断是否读出错,出错TRUE,没错FALSE
DWORD m_ReadError;
// 串口写相关变量
OVERLAPPED m_ovWrite;
BOOL m_bWaitWrite;
DWORD m_BytesSent;
BOOL m_IsWriteFault;// 判断是否写出错,出错TRUE,没错FALSE
DWORD m_WriteError;
// 标志操作是否成功
BOOL IsCreate;
BOOL IsClose;
BOOL IsSetBaud;
BOOL IsSetQueue;
BOOL IsSetTimeOut;
private:
// 用于设置串口属性,设置为内联函数
BOOL BaudExeu();
BOOL QueueExeu();
BOOL TimeOutExeu();
public:
// 构造函数及默认构造函数
CSerialPort(CString Name = _T("COM1"));
// 复制构造函数
CSerialPort(CSerialPort & Obj_port);
// 重载等号
CSerialPort & operator = (const CString Name);
// 析构函数
virtual ~CSerialPort();
// 转换函数,隐式
operator HANDLE() { return HANDLE(m_hComm); }
// 初始化串口属性的基本操作
BOOL CreatePort();
void Baud_Config(DWORD Baud = 57600);
void QueueSize_Config(DWORD inQueueSize = 1024, DWORD outQueueSize = 1024);
void TimeOut_Config(DWORD RT = 0, DWORD RM = 0, DWORD RC = 0, DWORD WM = 0, DWORD WC = 0);
// 获取句柄
HANDLE GetSerialHANDLE();
// close handle
BOOL ReleaseHandle();
// 获取错误码
DWORD GetErrorCode();
// 配置串口读写的操作
void InitParamofRW(BOOL IfWaitRead = TRUE, BOOL IfWaitWrite = TRUE);
// 串口读写函数
BOOL RecvData(char* bufferRecv);
BOOL SendData(char* m_szWriteBuffer, UINT num = NULL, BOOL Default = TRUE);
// 判断读写超时
BOOL StillOverlappedRead(DWORD timeout = INFINITE);
BOOL StillOverlappedWrite(DWORD timeout = INFINITE);
};
#endif
#include "stdafx.h"
#include "MySerialPort.h"
CSerialPort::CSerialPort(CString Name)
{
m_hComm = NULL;
m_comName = Name;
m_Baud = 57600;
m_dwInQueue = 1024;
m_dwOutQueue = 1024;
m_ReadIntervalTimeout = 0;
m_ReadTotalTimeoutMultiplier = 0;
m_ReadTotalTimeoutConstant = 0;
m_WriteTotalTimeoutMultiplier = 0;
m_WriteTotalTimeoutConstant = 0;
IsCreate = FALSE;
IsSetBaud = FALSE;
IsSetQueue = FALSE;
IsSetTimeOut = FALSE;
m_dwError = 0;
}
CSerialPort::CSerialPort(CSerialPort & Obj_port)
{
}
CSerialPort & CSerialPort::operator = (const CString Name)
{
m_hComm = NULL;
//m_comName.Format(Name.GetString());
m_comName = Name;
m_Baud = 57600;
m_dwInQueue = 1024;
m_dwOutQueue = 1024;
m_ReadIntervalTimeout = 0;
m_ReadTotalTimeoutMultiplier = 0;
m_ReadTotalTimeoutConstant = 0;
m_WriteTotalTimeoutMultiplier = 0;
m_WriteTotalTimeoutConstant = 0;
IsCreate = FALSE;
IsClose = FALSE;
IsSetBaud = FALSE;
IsSetQueue = FALSE;
IsSetTimeOut = FALSE;
m_dwError = 0;
return *this;
}
CSerialPort::~CSerialPort()
{
if (m_hComm != NULL)
{
CloseHandle(m_hComm);
m_hComm = NULL;
}
}
BOOL CSerialPort::CreatePort()
{
CString ipszPortName = _T("");
ipszPortName.Format(_T("\\\\.\\%s"), m_comName);
m_hComm = CreateFile(ipszPortName, // COM串口
GENERIC_READ | GENERIC_WRITE,
0, // 独占方式
NULL,
OPEN_EXISTING, // 打开而不是创建
FILE_FLAG_OVERLAPPED, // 重叠方式
NULL);
if (m_hComm == INVALID_HANDLE_VALUE) // if create failed
{
m_dwError = GetLastError();
CloseHandle(m_hComm);
m_hComm = NULL;
IsCreate = FALSE;
return FALSE;
}
IsClose = FALSE;
// set specific serial port event
SetCommMask(m_hComm,
EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RXCHAR | EV_TXEMPTY);
IsSetBaud = BaudExeu();
IsSetQueue = QueueExeu();
IsSetTimeOut = TimeOutExeu();
InitParamofRW(FALSE, TRUE);
IsCreate = TRUE;
return TRUE;
}
inline BOOL CSerialPort::BaudExeu()
{
DCB dcb;
memset(&dcb, 0, sizeof(dcb));
if (!GetCommState(m_hComm, &dcb))//获取当前 DCB 配置
{
m_dwError = GetLastError();
return FALSE;
}
//set DCB to configure the serialport
dcb.DCBlength = sizeof(dcb);
//Serial Port Config
dcb.BaudRate = m_Baud;
dcb.Parity = NOPARITY;
dcb.fParity = 0;
dcb.StopBits = ONESTOPBIT;
dcb.ByteSize = 8;
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = 0;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fOutX = 0;
dcb.fInX = 0;
//misc parameters
dcb.fErrorChar = 0;
dcb.fBinary = 1;
dcb.fNull = 0;
dcb.fAbortOnError = 0;
dcb.wReserved = 0;
dcb.XonLim = 2;
dcb.XoffLim = 4;
dcb.XonChar = 0x13;
dcb.XoffChar = 0x19;
dcb.EvtChar = 0;
//set DCB
if (!SetCommState(m_hComm, &dcb))
{
m_dwError = GetLastError();
return FALSE;
}
return TRUE;
}
inline BOOL CSerialPort::QueueExeu()
{
// in and out buffer size, byte
if (!SetupComm(m_hComm, m_dwInQueue, m_dwOutQueue))
{
m_dwError = GetLastError();
return FALSE;
}
return TRUE;
}
inline BOOL CSerialPort::TimeOutExeu()
{
COMMTIMEOUTS TimeOuts;
TimeOuts.ReadIntervalTimeout = m_ReadIntervalTimeout;
TimeOuts.ReadTotalTimeoutMultiplier = m_ReadTotalTimeoutMultiplier;
TimeOuts.ReadTotalTimeoutConstant = m_ReadTotalTimeoutConstant;
TimeOuts.WriteTotalTimeoutMultiplier = m_WriteTotalTimeoutMultiplier;
TimeOuts.WriteTotalTimeoutConstant = m_WriteTotalTimeoutConstant;
if (!SetCommTimeouts(m_hComm, &TimeOuts)) // 设定超时
{
m_dwError = GetLastError();
return FALSE;
}
return TRUE;
}
void CSerialPort::Baud_Config(DWORD Baud)
{
m_Baud = Baud;
}
void CSerialPort::QueueSize_Config(DWORD inQueueSize, DWORD outQueueSize)
{
m_dwInQueue = inQueueSize;
m_dwOutQueue = outQueueSize;
}
void CSerialPort::TimeOut_Config(DWORD RT, DWORD RM, DWORD RC, DWORD WM, DWORD WC)
{
m_ReadIntervalTimeout = RT;
m_ReadTotalTimeoutMultiplier = RM;
m_ReadTotalTimeoutConstant = RC;
m_WriteTotalTimeoutMultiplier = WM;
m_WriteTotalTimeoutConstant = WC;
}
HANDLE CSerialPort::GetSerialHANDLE()
{
return m_hComm;
}
BOOL CSerialPort::ReleaseHandle()
{
//if (m_hComm == NULL)
//{
// return FALSE;
//}
PurgeComm(m_hComm,
PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_RXABORT);
if (!CloseHandle(m_hComm))
{
m_dwError = GetLastError();
return FALSE;
}
m_dwError = GetLastError();
m_hComm = NULL;
IsCreate = FALSE;
IsClose = TRUE;
return TRUE;
}
DWORD CSerialPort::GetErrorCode()
{
return m_dwError;
}
void CSerialPort::InitParamofRW(BOOL IfWaitRead, BOOL IfWaitWrite)
{
m_BytesRead = 0;
m_IsReadFault = FALSE;
m_ReadError = 0;
m_bWaitRead = IfWaitRead;// overlapped read
// read event
memset(&m_ovRead, 0, sizeof(OVERLAPPED));
// 2nd param, auto reset; 3rd param, init status no signal
// when call function ReadFile or WriteFile,
m_ovRead.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_BytesSent = 0;
m_IsWriteFault = FALSE;
m_WriteError = 0;
m_bWaitWrite = IfWaitWrite;// overlapped write
// write event
memset(&m_ovWrite, 0, sizeof(OVERLAPPED));
// 2nd param, auto reset; 3rd param, init status no signal
// when call function ReadFile or WriteFile,
m_ovWrite.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
// clear in & out buffer in serial port
PurgeComm(m_hComm,
PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_RXABORT);
}
BOOL CSerialPort::RecvData(char* bufferRecv)
{
// 下面这个不对,因为要在外部通过m_ComStat位来确定是不是有数
/*if (!ClearCommError(m_hComm, &m_ReadError, &m_ComStat))
{
m_ReadError = GetLastError();
}*/
if (!ReadFile(m_hComm,//Handle to COMM port
bufferRecv,//RXBuffer Pointer
m_ComStat.cbInQue, //Read m_ComStat.cbInQue bytes
&m_BytesRead,//Stores number of bytes read
&m_ovRead))//pointer to the m_ov structure
{
m_ReadError = GetLastError();
switch (m_ReadError)
{
case ERROR_IO_PENDING:
//case ERROR_IO_INCOMPLETE:
m_IsReadFault = FALSE;
break;
default:
m_IsReadFault = TRUE;
break;
}
return FALSE;
}
return TRUE;
}
BOOL CSerialPort::StillOverlappedRead(DWORD timeout)
{
WaitForSingleObject(m_hComm, timeout);
// 非零表示异步操作结束,零表示异步操作仍在执行
if (0 != GetOverlappedResult(m_hComm, //Handle to COMM port
&m_ovRead,// Overlapped structure
&m_BytesRead, // Stores number of bytes read
FALSE)) //Wait flag
{
return FALSE;
}
return TRUE;
}
BOOL CSerialPort::SendData(char* m_szWriteBuffer, UINT num, BOOL Default)
{
// Clear buffer
PurgeComm(m_hComm, PURGE_TXCLEAR | PURGE_TXABORT);
m_ovWrite.Offset = 0;
m_ovWrite.OffsetHigh = 0;
DWORD m_nToSend = 0;
if (Default)
m_nToSend = strlen(m_szWriteBuffer);
else
m_nToSend = num;
if (!WriteFile(m_hComm, // Handle to COMM Port
m_szWriteBuffer, // Pointer to message buffer in calling finction
m_nToSend, // Length of message to send
&m_BytesSent, //Where to store the number of bytes sent
&m_ovWrite))//Overlapped structure
{
m_WriteError = GetLastError();
switch (m_WriteError)
{
case ERROR_IO_PENDING:
//case ERROR_IO_INCOMPLETE:
m_IsWriteFault = FALSE;
break;
default:
m_IsWriteFault = TRUE;
break;
}
return FALSE;
}
return TRUE;
}
BOOL CSerialPort::StillOverlappedWrite(DWORD timeout)
{
WaitForSingleObject(m_hComm, timeout);
// 非零表示异步操作结束,零表示异步操作仍在执行
if (0 != GetOverlappedResult(m_hComm, //Handle to COMM port
&m_ovWrite,// Overlapped structure
&m_BytesSent, //Stores number of bytes sent
FALSE)) //Waitflag
{
return FALSE;
}
return TRUE;
}