封装WIN32 串口API (基于消息机制)

时间:2024-05-20 08:51:24

封装WIN32 串口API (基于消息机制)

接上一篇文章,放出我对Win32 串口API 的封装代码

(BBA是我个人限定名) 完整工程已上传到我的资源

#include <Windows.h>

#include <thread>  

using namespace std;
#define BBA_SERIAL_EVENT 10002
class BBA_Serial
{

public:
BBA_Serial();
~BBA_Serial();
int OpenSerial(const WCHAR *SerialPort, int BaudRate, int ByteSize,
int Parity, int StopBits);
int CloseSerial();


int Read(char *RX_Buffer, int len);
int Write(char *TX_Buffer, int len);
int OpenListenThread(HWND hWnd);
int GetListenThreadState();
bool SetEventDone();
bool EventClear();
protected:

private:
HANDLE hCom; //串口句柄
thread m_threadEvent; //监听事件线程对象
bool ListThreadState; //监听事件线程关闭
//HANDLE threadHandle; //监听线程句柄
HWND hWnd; //事件通知的窗口句柄
DWORD dwMask; //记录事件类型
OVERLAPPED lpOverlapped; //事件类型
OVERLAPPED EventOverlapped; //异步通信事件记录
void EventListenThread(void* lpParam); //事件监听线程

};





#include "stdafx.h"

#include "BBA_Serial.h"
BBA_Serial::BBA_Serial()
{

}

BBA_Serial::~BBA_Serial()
{

}

int BBA_Serial::OpenSerial(const WCHAR *SerialPort, int BaudRate, int ByteSize,
int Parity, int StopBits)
{
hCom = CreateFile(SerialPort, //COM口
GENERIC_READ | GENERIC_WRITE, //允许读和写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //重叠方式
NULL);


if (hCom == INVALID_HANDLE_VALUE)
{
//std::cout << "打开COM失败!\n";
return FALSE;
}
SetupComm(hCom, 1024, 1024);//输入缓冲区和输出缓冲区的大小都是1024


COMMTIMEOUTS TimeOuts;//设定读超时
TimeOuts.ReadIntervalTimeout = 1000; //读间隔超时
TimeOuts.ReadTotalTimeoutMultiplier = 500; //读时间系数
TimeOuts.ReadTotalTimeoutConstant = 5000; //读时间常量
TimeOuts.WriteTotalTimeoutMultiplier = 500; //写时间系数
TimeOuts.WriteTotalTimeoutConstant = 2000; //写时间常量
SetCommTimeouts(hCom, &TimeOuts);//设置超时
//总超时的计算公式是:总超时=时间系数×要求读 / 写的字符数+时间常量
//例如,要读入10个字符,那么读操作的总超时的计算公式为:
//读总超时=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant


DCB dcb;
GetCommState(hCom, &dcb);
dcb.BaudRate = BaudRate;//波特率为9600
dcb.ByteSize = ByteSize;//每个字节有8位
dcb.Parity = Parity;//无奇偶校验位
dcb.StopBits = StopBits;//一个停止位//TWOSTOPBITS;//两个停止位
SetCommState(hCom, &dcb);


//在读写串口之前,还要用PurgeComm()函数清空缓冲区,该函数原型:
PurgeComm(hCom, //串口句柄
PURGE_TXCLEAR |
PURGE_RXCLEAR);// 需要完成的操作 );
//参数dwFlags指定要完成的操作,可以是下列值的组合:
//PURGE_TXABORT 中断所有写操作并立即返回,即使写操作还没有完成。
//PURGE_RXABORT 中断所有读操作并立即返回,即使读操作还没有完成。
//PURGE_TXCLEAR 清除输出缓冲区
//PURGE_RXCLEAR 清除输入缓冲区
return 0;
}


int BBA_Serial::CloseSerial()
{
ListThreadState = 0;
//SetEvent(hCom);
CloseHandle(hCom);
m_threadEvent.~thread();
return 0;
}


int BBA_Serial::Read(char *RX_Buffer, int len)
{
DWORD dwBytesRead;


COMSTAT ComStat; // 指向通讯状态缓冲区


DWORD dwErrorFlags; // 接收错误码变量


OVERLAPPED m_osRead; //当前操作的状态,例如串口是否正在传输


memset(&m_osRead, 0, sizeof(OVERLAPPED));


m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);


//PurgeComm(hCom,PURGE_TXCLEAR |PURGE_RXCLEAR); //清空缓冲区
//读写前先清除串口的错误
ClearCommError(hCom, &dwErrorFlags, &ComStat);


//if ((DWORD)ComStat.cbInQue == 0) return FALSE;
len = min(len, (DWORD)ComStat.cbInQue);

if (!len) return FALSE;
//cout << dwBytesRead;
BOOL bReadStatus;


bReadStatus = ReadFile(hCom,
RX_Buffer,
len, //要读入的个数
&dwBytesRead //实际读入的个数
, &m_osRead);


//如果ReadFile函数返回FALSE,即读失败
if (!bReadStatus)
{
//GetLastError()函数返回ERROR_IO_PENDING,表明串口正在进行读操作
if (GetLastError() == ERROR_IO_PENDING)
{
WaitForSingleObject(m_osRead.hEvent, 2000);
//使用WaitForSingleObject函数等待,直到读操作完成或延时已达到2秒钟
//当串口读操作进行完毕后,m_osRead的hEvent事件会变为有信号
//PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
return len;
}
}
return len;
}
int BBA_Serial::Write(char *TX_Buffer, int len)
{
DWORD dwBytesWritten; //实际写入的数据个数
DWORD dwErrorFlags; //错误标志
COMSTAT ComStat; //串口状态


OVERLAPPED m_osWrite; //记录串口事件
memset(&m_osWrite, 0x00, sizeof(OVERLAPPED));
m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

BOOL bWriteStat; //写入结果


//PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR); //清空缓冲区
//读写前先清除串口的错误
ClearCommError(hCom, &dwErrorFlags, &ComStat);


if ((DWORD)ComStat.cbOutQue + len>1023) return -1; //输入缓冲区字节数

bWriteStat = WriteFile(hCom,
TX_Buffer,
len, //要写入的个数
&dwBytesWritten, //实际写入的个数
&m_osWrite);

if (!bWriteStat)
{

if (GetLastError() == ERROR_IO_PENDING)
{
WaitForSingleObject(m_osWrite.hEvent, 1000);
return 0;
}
return -1;
}
return 0;
}


int BBA_Serial::OpenListenThread(HWND hWnd)
{
//PostMessage(hWnd, 1, WPARAM wParam, LPARAM lParam);
this->hWnd = hWnd;
ListThreadState = 1;
m_threadEvent=thread(&BBA_Serial::EventListenThread, this, (void*)&hWnd);
//listenThreadID = listenThread.get_id();

m_threadEvent.detach();
return 0;
}


void BBA_Serial::EventListenThread(void* lpParam)
{
//DWORD error;
//COMSTAT comStat;
//DWORD rec;
memset(&lpOverlapped, 0, sizeof(OVERLAPPED));
lpOverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);


//memset(&EventOverlapped, 0, sizeof(OVERLAPPED));
//EventOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);


SetCommMask(hCom, EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY); //设置监控串口事件

while (ListThreadState)
{

//ResetEvent(lpOverlapped.hEvent);

WaitCommEvent(hCom, &dwMask, &lpOverlapped);
//rec=ClearCommError(hCom, &error, &comStat);
//if (!rec) continue;
WaitForSingleObject(lpOverlapped.hEvent, INFINITE);
PostMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
//WaitForSingleObject(EventOverlapped.hEvent, INFINITE); //等待接收事件完成处理
//EventClear();

//ClearCommError(hCom, &error, &comStat);

                //以下为单独处理一种类型消息(主要用于测试),由上面的消息路由已经足够实现消息控制串口读写和各种事件

//switch (dwMask)
//{
// case EV_DSR:
// SendMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
// break;
// case EV_ERR:
// SendMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
// break;
// case EV_RING:
// SendMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
// break;
// case EV_RLSD:
// SendMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
// break;
// case EV_RXCHAR:
// if (comStat.cbInQue == 0) break;
// PostMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
// WaitForSingleObject(EventOverlapped.hEvent, INFINITE); //等待接收事件完成处理
// EventClear();
// break;
// case EV_RXFLAG:
// SendMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
// break;
// case EV_TXEMPTY:
// SendMessage(hWnd, BBA_SERIAL_EVENT, NULL, (LPARAM)dwMask);
// break;
//}
}
}


bool BBA_Serial::SetEventDone()
{
SetEvent(EventOverlapped.hEvent);
return 0;
}


bool BBA_Serial::EventClear()
{
ResetEvent(EventOverlapped.hEvent);
return 0;
}
int BBA_Serial::GetListenThreadState()
{
return ListThreadState;

}


直接引入MFC或各种win32 窗口工程都能兼容