Windows下的串口同通信及类的封装

本文详细介绍了Windows环境下使用C++进行串口通信的步骤,包括通过CreateFile打开串口、设置串口参数、监听接收数据的线程实现,以及如何封装成一个易复用的类。示例代码展示了如何使用封装后的类进行串口读写操作,简化了串口编程的复杂性。
摘要由CSDN通过智能技术生成

Windows下的串口同通信及类的封装

最近用到了串口编程,所以写篇随笔将其记录下来。

Windows下串口通信的基本流程

先来梳理一下在Windows下对串口读写数据操作的基本流程。在Windows下(linux也是)串口的读写是以文件的方式去操作的,所以使用WindowsAPI:CreateFile来打开串口,代码为:

	LPCTSTR Comname = _T("COM3");
	HANDLE hSerial;
	//API原型就不列了,可以去看文档,为了体流程这里使用方法。
	//要注意的是第一个参数Comname为要打开的串口名,第六参数FILE_FLAG_OVERLAPPED为使用异步方式打开串口。
	hSerial = CreateFile(Comname, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	if (hSerial == INVALID_HANDLE_VALUE)
	{
		printf("open fail \n");
		CloseHandle(hSerial);
		return -1;
	}

打开串口后我们对串口设置缓冲去的大小:

//第一个参数为打开的串口,后面分别为设备内部读和写缓冲区的大小。
	SetupComm(hSerial, 1024, 1024);

接下来开始设置串口的参数:

	DCB dcb;
	GetCommState(hSerial, &dcb);//获取当前的配置保存到dcb
	dcb.DCBlength = sizeof(DCB);
	dcb.BaudRate = CBR_115200;//设置串口波特率
	dcb.ByteSize = 8;//设置一个字节的长度
	dcb.StopBits = ONESTOPBIT;//设置停止位
	dcb.Parity = NOPARITY;//设置校验位
	SetCommState(hSerial, &dcb); //最后把存有配置信息的dcb设置给串口

然后对串口设置超时

	COMMTIMEOUTS timeout; //创建一个端口超时结构体
	timeout.ReadIntervalTimeout = MAXDWORD;
	timeout.ReadTotalTimeoutConstant = 0;
	timeout.ReadTotalTimeoutMultiplier = 0;
	timeout.WriteTotalTimeoutConstant = 500;
	timeout.WriteTotalTimeoutMultiplier = 5000;
	SetCommTimeouts(hSerial, &timeout);//设定好参数后将其设置给串口
	PurgeComm(hSerial, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);//最后对串口进行一下清空操作。

由于我们使用异步的方式打开的串口,所以需要一个OVERLAPPED结构,先创建它后面会用到

	OVERLAPPED ov_write, ov_read;
	memset(&ov_write, 0, sizeof(OVERLAPPED));//初始化
	memset(&ov_read, 0, sizeof(OVERLAPPED));
	ov_write.hEvent = CreateEvent(NULL, false, false, NULL);//创建事件
	ov_read.hEvent = CreateEvent(NULL, false, false, NULL);

然后为串口设置言关心的事件

SetCommMask(hSerial, EV_RXCHAR);//绑定串口接收到字符的事件

然后就是获取串口接收到的内容了,这里新开一个线程去后台监听串口是否收到数据,当有数据时将数据取出来,首先需要写一个读取串口的方法:

void readData(HANDLE serial, OVERLAPPED* ov_r)
{
	DWORD dwGetEventMask = 0;
	DWORD TrByte = 0;
	BOOL Status = 0;
	DWORD CError = 0;
	COMSTAT comstat;
	char readBuffer[1024] = { 0 };
	
	while (true)
	{
		Status = WaitCommEvent(serial, &dwGetEventMask, ov_r);
		if (Status == FALSE && GetLastError() == ERROR_IO_PENDING)
		{
			Status = GetOverlappedResult(serial, ov_r, &TrByte, TRUE);
		}
		ClearCommError(serial, &CError, &comstat);
		if (Status == TRUE && dwGetEventMask & EV_RXCHAR && comstat.cbInQue > 0)
		{
			DWORD readedSize = 0;
			memset(readBuffer, 0, sizeof(readBuffer));
			Status = ReadFile(serial, readBuffer, sizeof(readBuffer), &readedSize, ov_r);
			if (Status)
			{
				std::cout << "Read: " << readBuffer << std::endl;
			}

			PurgeComm(serial, PURGE_RXABORT | PURGE_RXCLEAR);
		}
	}
}

然后在刚才SetCommMask(hSerial, EV_RXCHAR);的后面启动线程去执行这个函数

std::thread readThread(&readData, hSerial, &ov_read);

至此便开始了对串口的监听,如果要向串口调用WindowsAPI函数WriteFile即可:

WriteFile(hSerial, writeBuffer, sizeof(writeBuffer), &writedSize, &ov_write);

C++封装

最后为了方便以后的复用,我使用C++对其进行了封装形成一个类,使用方法也简单了很多,以下为一个简单的示例:

//新建应用程序
#include "JSerialPortTool.h"

void showReadContant(char*indata);//槽函数,串口接收到数据后调用此函数,并把接收到的内容传给indata
int main()
{
	JSerialPortTool serialTool(_T("COM1")); //构造时传入串口名
	serialTool.SetComConf(CBR_115200, 8, 1, NOPARITY);//设置波特率、字节长度、停止位和校验位
	serialTool.SetTimeOut(MAXDWORD, 0, 0, 500, 5000);//设置超时参数

	serialTool.connectReadSlot(showReadContant);//绑定槽函数,在串口接收到数据后调用绑定的方法

	while (true)
	{
		char writebuff[1024] = { 0 };
		std::cin >> writebuff;
		serialTool.WriteData(writebuff);//调用WriteData成员函数向串口写数据
	}
	return 0;
}

void showReadContant(char*indata)
{
	std::cout << "get contance:" << indata << std::endl;
}

资源下载

CSDN下载
Gitee下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值