#ifndef SERIAL_PORT_H #define SERIAL_PORT_H #include <afxwin.h> // MFC 核心组件和标准组件 /// <summary> /// 描述:EVC串口类库 /// 日期:2012/3/30 /// </summary> class CSerialPort : public CObject { public: CSerialPort() { m_hSerialPort = INVALID_HANDLE_VALUE; m_bWatchThreadLived = FALSE; } virtual ~CSerialPort() { Close(5000); } /// <summary>最大输入缓冲大小</summary> static CONST UINT32 s_MaxInQueue = 4096; /// <summary>最大输出缓冲大小</summary> static CONST UINT32 s_MaxOutQueue = 4096; /// <summary>获取系统错误消息</summary> /// <param name="psErrorMessage">输出的错误消息</param> static void GetLastError(CString& psErrorMessage) { if (DWORD dwLastError = ::GetLastError()) { LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwLastError, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPTSTR) &lpMsgBuf, 0, NULL); psErrorMessage.Format(_T("(%d): %s"), dwLastError, (LPCTSTR)lpMsgBuf); LocalFree( lpMsgBuf ); } } /// <summary>获取串口设备控制块</summary> /// <param name="lpDCB">输出的串口设备控制块</param> BOOL GetCommState(LPDCB lpDCB) { return ::GetCommState(m_hSerialPort, lpDCB); } /// <summary>设置串口设备控制块</summary> /// <param name="lpDCB">输入的串口设备控制块</param> BOOL SetCommState(LPDCB lpDCB) { return ::SetCommState(m_hSerialPort, lpDCB); } /// <summary>打开串口设备</summary> /// <param name="dwPort">端口号</param> /// <param name="dwBaudRate">波特率</param> /// <param name="byParity">奇偶校验</param> /// <param name="byByteSize">数据位</param> /// <param name="byStopBits">停止位</param> /// <example>CSerialPort::Open(1, 9600, 0, 8, 0)</example> /// <returns>是否打开成功</returns> virtual BOOL Open(DWORD dwPort, DWORD dwBaudRate = CBR_9600, BYTE byParity = NOPARITY, BYTE byByteSize = 8, BYTE byStopBits = ONESTOPBIT) { CString strPort; strPort.Format(_T("\\$device\\COM%d"), dwPort); return Open(strPort, dwBaudRate, byParity, byByteSize, byStopBits); } /// <summary>打开串口设备</summary> /// <param name="pszPort">端口名称</param> /// <param name="dwBaudRate">波特率</param> /// <param name="byParity">奇偶校验</param> /// <param name="byByteSize">数据位</param> /// <param name="byStopBits">停止位</param> /// <example>CSerialPort::Open(1, 9600, 0, 8, 0)</example> /// <returns>是否打开成功</returns> virtual BOOL Open(LPCTSTR pszPort, DWORD dwBaudRate = CBR_9600, BYTE byParity = NOPARITY, BYTE byByteSize = 8, BYTE byStopBits = ONESTOPBIT) { if (IsOpen()) return TRUE; // 防止重复创建串口句柄 m_hSerialPort = CreateFile(pszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); GetCommState(&m_tDeviceControlBlock); m_tDeviceControlBlock.BaudRate = dwBaudRate; m_tDeviceControlBlock.Parity = byParity; m_tDeviceControlBlock.ByteSize = byByteSize; m_tDeviceControlBlock.StopBits = byStopBits; m_tDeviceControlBlock.fParity = (byParity != NOPARITY); SetCommState(&m_tDeviceControlBlock); // 设置串口设备控制块 SetupComm(m_hSerialPort, s_MaxInQueue, s_MaxOutQueue); // 初始化串口参数(不能在此处设置断言),即设置输入输出缓冲区大小 GetCommTimeouts(m_hSerialPort, &m_tCommTimeOuts); m_tCommTimeOuts.ReadIntervalTimeout = 100;// 字符读取间隔 m_tCommTimeOuts.ReadTotalTimeoutMultiplier = 0; m_tCommTimeOuts.ReadTotalTimeoutConstant = 250;// 读取超时间隔 m_tCommTimeOuts.WriteTotalTimeoutMultiplier = 0; m_tCommTimeOuts.WriteTotalTimeoutConstant = 250;// 写入超时间隔 SetCommTimeouts(m_hSerialPort, &m_tCommTimeOuts); // 设置串口超时参数 PurgeComm(m_hSerialPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); // 清空串口缓冲区数据 m_bWatchThreadLived = TRUE; m_hWatchThread = CreateThread(NULL, 0, WatchThreadProc, this, 0, NULL); // 创建串口监视线程 return TRUE; } /// <summary>关闭串口设备</summary> /// <returns>是否关闭成功</returns> virtual void Close(DWORD dwTimeout = 5000) { if (IsOpen()) { m_bWatchThreadLived = FALSE; SetCommMask(m_hSerialPort, 0); // 停止监视串口接收事件 PurgeComm(m_hSerialPort, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_RXABORT); // 清空串口缓冲区数据 if (WaitForSingleObject(m_hWatchThread, dwTimeout) != WAIT_OBJECT_0) { TerminateThread(m_hWatchThread, WAIT_ABANDONED); } CloseHandle(m_hWatchThread); m_hWatchThread = NULL; CloseHandle(m_hSerialPort); m_hSerialPort = INVALID_HANDLE_VALUE; } } /// <summary>获取串口设备状态</summary> /// <returns>是否已打开</returns> BOOL IsOpen() { return (m_hSerialPort != INVALID_HANDLE_VALUE); } /// <summary>将指定数量的数据写入串口</summary> /// <param name="pbyBuffer">输入缓冲区指针</param> /// <param name="dwOffset">输入缓冲区位置</param> /// <param name="dwCount">输入缓冲区大小</param> /// <returns>是否写入成功</returns> BOOL Write(const BYTE* pbyBuffer, DWORD dwOffset = 0, DWORD dwCount = 1) { ASSERT(IsOpen()); DWORD dwCommError; if (ClearCommError(m_hSerialPort, &dwCommError, NULL) && dwCommError > 0) PurgeComm(m_hSerialPort, PURGE_TXABORT); DWORD dwBytesWritten; return WriteFile(m_hSerialPort, pbyBuffer + dwOffset, dwCount, &dwBytesWritten, NULL); } /// <summary>将指定数量的字符写入串口</summary> /// <param name="pszBuffer">输入缓冲区指针</param> /// <param name="dwOffset">输入缓冲区位置</param> /// <param name="dwCount">输入缓冲区大小</param> /// <returns>是否写入成功</returns> BOOL Write(LPCTSTR pszBuffer, DWORD dwOffset = 0, DWORD dwCount = 1) { return Write(pszBuffer, dwOffset * sizeof(TCHAR), dwCount * sizeof(TCHAR)); } /// <summary>从串口读取指定数量的数据</summary> /// <param name="pbyBuffer">输出缓冲区指针</param> /// <param name="dwOffset">输出缓冲区位置</param> /// <param name="dwCount">输出缓冲区大小</param> /// <returns>是否读取成功</returns> BOOL Read(LPBYTE pbyBuffer, DWORD dwOffset = 0, DWORD dwCount = 1) { ASSERT(IsOpen()); DWORD dwCommError; if (ClearCommError(m_hSerialPort, &dwCommError, NULL) && dwCommError > 0) PurgeComm(m_hSerialPort, PURGE_RXABORT); DWORD dwBytesRead; return ReadFile(m_hSerialPort, pbyBuffer + dwOffset, dwCount, &dwBytesRead, NULL); } /// <summary>从串口读取指定数量的字符</summary> /// <param name="pszBuffer">输出缓冲区指针</param> /// <param name="dwOffset">输出缓冲区位置</param> /// <param name="dwCount">输出缓冲区大小</param> /// <returns>是否读取成功</returns> BOOL Read(LPTSTR pszBuffer, DWORD dwOffset = 0, DWORD dwCount = 1) { return Read(pszBuffer, dwOffset * sizeof(TCHAR), dwCount * sizeof(TCHAR)); } /// <summary>监视串口数据收发线程处理函数</summary> /// <returns>线程退出代码</returns> static DWORD WINAPI WatchThreadProc(LPVOID lpParam) { CSerialPort* pSender = (CSerialPort*) lpParam; return pSender->WatchThreadProc(); } /// <summary>监视串口数据收发线程处理函数</summary> /// <returns>线程退出代码</returns> virtual DWORD WatchThreadProc() { ASSERT(IsOpen()); SetCommMask(m_hSerialPort, EV_RXCHAR); // 设置监视串口数据接收事件 DWORD dwCommEvent; // 串口事件掩码 DWORD dwCommError; COMSTAT tComStat; // 串口设备数据 while (m_bWatchThreadLived) { if (WaitCommEvent(m_hSerialPort, &dwCommEvent, NULL)) { if (dwCommEvent & EV_RXCHAR) { if (ClearCommError(m_hSerialPort, &dwCommError, &tComStat)) // 获取串口错误信息 { if (tComStat.cbInQue) { DataReceived(tComStat.cbInQue); } } } } } return 0; // 线程正常退出 } /// <summary>串口接收事件处理函数</summary> /// <param name="dwBytesSize">接收到的数据大小</param> virtual void DataReceived(int nBytesSize) { ; } private: /// <summary>串口设备句柄</summary> HANDLE m_hSerialPort; /// <summary>串口超时参数</summary> COMMTIMEOUTS m_tCommTimeOuts; /// <summary>串口设备控制块</summary> DCB m_tDeviceControlBlock; /// <summary>收发线程句柄</summary> HANDLE m_hWatchThread; /// <summary>收发线程是否活动</summary> BOOL m_bWatchThreadLived; }; #endif // SERIAL_PORT_H