#pragma once
#include <windows.h>
#include <process.h>
typedef UINT lpCallBack (LPVOID lpParam);
class BaseComm
{
public:
BaseComm(void);
virtual ~BaseComm(void);
// 打开串口
// 输入: pPort - 串口名称或设备路径,可用"COM1"
// nBaudRate - 波特率
// nParity - 奇偶校验
// nByteSize - 数据字节宽度
// nStopBits - 停止位
virtual bool OpenComm(wchar_t* pPort = L"COM3", int nBaudRate=9600, int nByteSize=8, int nStopBits=ONESTOPBIT, int nParity=NOPARITY);
// 关闭串口
bool CloseComm();
// 创建监听线程
bool CreateCommEventThread();
// 退出监听线程
void ExitCommEventThread();
protected:
// 清除收发缓冲区
void ClearCommBuff();
// 获得串口句柄
HANDLE GetHANDLE_Comm() { return hComm; }
// 写串口
// 输入: pData - 待写的数据缓冲区指针
// nLength - 待写的数据长度
// 返回: 实际写入的数据长度
bool WriteComm(void* pData, int nLength);
// 读串口
// 输入: pData - 待读的数据缓冲区指针
// nLength - 待读的最大数据长度
// 返回: 实际读出的数据长度
bool ReadComm(void* pData, int nLength);
//生成新的线程(in 线程函数指针, in 参数指针,out 线程句柄地址,out 线程ID)
bool CreateNewThread(lpCallBack *pfnThrearoc,LPVOID pParam,
HANDLE &hNewThread,DWORD &dwThreadId);
private:
// 串口设备句柄
HANDLE hComm;
// 串口空闲信号(用于发送和接收操作互斥)
HANDLE hCommFree;
// 串口接收信号
HANDLE hCommRecv;
// 串口接收信号
HANDLE hCommSend;
// 串口监听线程开关
bool IsRunThread;
// 线程句柄
HANDLE hCommEventThread;
// 线程ID
DWORD dwCommEventThreadID;
// 串口事件函数
friend UINT CommEventThread(LPVOID pParam);
};
//-------------------------------------------------------------------------------------
#include "BaseComm.h"
//将C的运行期函数参数转化为C++的
typedef unsigned (__stdcall *PTHREAD_START)(void *);
#define chBEGINTHREADEX(psa,cbStack,pfnStartAddr,/
pvParam,fdwCreate,pdwThreadID)( /
(HANDLE) _beginthreadex( /
(void*)(psa),(unsigned)(cbStack), /
(PTHREAD_START)(pfnStartAddr), /
(void*)(pvParam), /
(unsigned)(fdwCreate), /
(unsigned*)(pdwThreadID) ) )
UINT CommEventThread(LPVOID pParam)
{
if( NULL == pParam)
return 0;
BaseComm* pBC = (BaseComm*)pParam;
// 串口事件
OVERLAPPED tOverLaped;
tOverLaped.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
while(pBC->IsRunThread)
{
DWORD dwEvtMask, dwResult;
WaitCommEvent(pBC->hComm,&dwEvtMask, &tOverLaped);
dwResult = WaitForSingleObject(tOverLaped.hEvent,100);
if( WAIT_OBJECT_0 == dwResult)
{
if( dwEvtMask&EV_RXCHAR == EV_RXCHAR)
{
//printf("收到接收数据信号/n");
SetEvent(pBC->hCommRecv);
}
if( dwEvtMask&EV_TXEMPTY == EV_TXEMPTY)
{
//printf("发送完毕信号/n");
SetEvent(pBC->hCommSend);
}
}
}
return 0;
}
BaseComm::BaseComm(void)
{
hCommFree = CreateEvent(NULL,FALSE,TRUE,NULL);
hCommRecv = CreateEvent(NULL,FALSE,FALSE,NULL);
hCommSend = CreateEvent(NULL,FALSE,FALSE,NULL);
IsRunThread = false;
}
BaseComm::~BaseComm(void)
{
}
// 打开串口
// 输入: pPort - 串口名称或设备路径,可用"COM1"
// nBaudRate - 波特率
// nParity - 奇偶校验
// nByteSize - 数据字节宽度
// nStopBits - 停止位
bool BaseComm::OpenComm(wchar_t* pPort, int nBaudRate, int nByteSize, int nStopBits, int nParity)
{
//接入串口
hComm = CreateFile(pPort, // 串口名称或设备路径
GENERIC_READ | GENERIC_WRITE, // 读写方式
0, // 共享方式:独占
0, // 默认的安全描述符
OPEN_EXISTING, // 打开一个存在的串口
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //异步方式打开
0); // 不需参照模板文件
if( INVALID_HANDLE_VALUE == hComm)
{
return false; // 打开串口失败
}
Sleep(500);
//清空串口错误标志并记录当前通信状态
DWORD res;
COMSTAT rst;
ClearCommError(hComm,&res,&rst);
// 设置输入输出缓冲区大小
SetupComm(hComm, 1024, 1024);
//串口参数处理
DCB dcb; // 串口控制块
if( !GetCommState(hComm, &dcb))
{// 读取DCB
return false; // 打开串口失败
};
dcb.BaudRate = nBaudRate;
dcb.ByteSize = nByteSize;
dcb.Parity = nParity;
dcb.StopBits = nStopBits;
dcb.fParity = FALSE; // 禁止奇偶校验
dcb.fBinary = TRUE; // 禁止流量控制
dcb.fDtrControl = 0;
dcb.fRtsControl = 0;
dcb.fOutX = 0;
dcb.fInX = 0;
dcb.fTXContinueOnXoff = 0;
if( !SetCommState(hComm, &dcb)) // 设置串口DCB
{
CloseComm();
return false;
}
Sleep(200);
if( !SetCommMask(hComm, EV_RXCHAR|EV_TXEMPTY))
{// 串口事件: 接收到一个字符或发送区缓冲为空
CloseComm();
return false;
}
//超时处理
COMMTIMEOUTS CommTimeOuts;
GetCommTimeouts(hComm, &CommTimeOuts);
CommTimeOuts.ReadIntervalTimeout = 100; // 接收字符间最大时间间隔
CommTimeOuts.ReadTotalTimeoutMultiplier = 1; // 读操作时每字符的时间: 1 ms (n个字符总共为n ms)
CommTimeOuts.ReadTotalTimeoutConstant = 500; // 读数据总超时常量
CommTimeOuts.WriteTotalTimeoutMultiplier = 1; // 写操作时每字符的时间: 1 ms (n个字符总共为n ms)
CommTimeOuts.WriteTotalTimeoutConstant = 100; // 基本的(额外的)写超时时间: 100 ms
if( !SetCommTimeouts(hComm, &CommTimeOuts))
{
CloseComm();
return false;
}
ClearCommBuff();
return true;
}
bool BaseComm::CloseComm()
{
if( INVALID_HANDLE_VALUE != hComm)
{
SetCommMask(hComm, 0); // 串口事件
ClearCommBuff();
CloseHandle(hComm);
hComm = INVALID_HANDLE_VALUE;
return true;
}
return false;
}
void BaseComm::ClearCommBuff()
{
if( INVALID_HANDLE_VALUE != hComm)
{
PurgeComm(hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ); // 清除收发缓冲
}
}
// 创建监听线程
bool BaseComm::CreateCommEventThread()
{
if( IsRunThread)
return false;
IsRunThread = true;
if( !CreateNewThread(CommEventThread,this,hCommEventThread,dwCommEventThreadID))
{
IsRunThread = false;
Sleep(300);
}
return true;
}
void BaseComm::ExitCommEventThread()
{
IsRunThread = false;
}
bool BaseComm::CreateNewThread(lpCallBack *pfnThrearoc,LPVOID pParam,
HANDLE &hNewThread,DWORD &dwThreadId)
{
//创建新线程
hNewThread = chBEGINTHREADEX(NULL,0,pfnThrearoc,pParam,0,&dwThreadId);
if( INVALID_HANDLE_VALUE == hNewThread)
{
return false;
}
return true;
}
// 写串口
// 输入: pData - 待写的数据缓冲区指针
// nLength - 待写的数据长度
// 返回: 实际写入的数据长度
bool BaseComm::WriteComm(void* pData, int nLength)
{
Sleep(500);
DWORD res;
res = WaitForSingleObject(hCommFree,INFINITE);
if( WAIT_OBJECT_0 == res)
{
//printf("写操作!/n");
COMSTAT rst;
ClearCommError(hComm,&res,&rst);
OVERLAPPED wOverLaped;
wOverLaped.InternalHigh = 0;
wOverLaped.Offset = 0;
wOverLaped.OffsetHigh = 0;
wOverLaped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
DWORD factdata = 0;
if( WriteFile(hComm,pData,nLength,&factdata,&wOverLaped))
{
SetEvent(hCommFree);
return true;
}
else
{
res = WaitForSingleObject(wOverLaped.hEvent,INFINITE);
if( WAIT_OBJECT_0 == res)
{
if( wOverLaped.InternalHigh+factdata >= nLength)
{
SetEvent(hCommFree);
return true;
}
}
}
}
SetEvent(hCommFree);
return false;
}
// 读串口
// 输入: pData - 待读的数据缓冲区指针
// nLength - 待读的最大数据长度
// 返回: 实际读出的数据长度
bool BaseComm::ReadComm(void* pData, int nLength)
{
DWORD res;
res = WaitForSingleObject(hCommRecv,INFINITE);
if( WAIT_OBJECT_0 != res)
return false;
res = WaitForSingleObject(hCommFree,INFINITE);
if( WAIT_OBJECT_0 == res)
{
COMSTAT rst;
do
{
ClearCommError(hComm,&res,&rst);
}while( rst.cbInQue <nLength);
DWORD factdata = 0;
OVERLAPPED rOverLaped;
rOverLaped.InternalHigh = 0;
rOverLaped.Offset = 0;
rOverLaped.OffsetHigh = 0;
rOverLaped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
if( ReadFile(hComm,pData,nLength,&factdata,&rOverLaped))
{
SetEvent(hCommFree);
return true;
}
else
{
res = WaitForSingleObject(rOverLaped.hEvent,INFINITE);
SetEvent(hCommFree);
return true;
}
}
SetEvent(hCommFree);
return false;
}