windows串口编程之重叠方式

一、前言
最近在学习windows编程中的串口编程,使用的软件平台是vs2019,串口通信方式采用的是重叠方式,
即读写数据操作在单独的线程中进行,发出读写要求的主线程可以继续运行,当读写数据成功后,写
数据线程可以通过某种方式通知主线程,实现两个线程协调工作。
二、相关函数介绍
1、线程创建函数CreateThread( )
HANDLE CreateThread(
		LPSECURITY_ATTRIBUTES lpThreadAttributes,//线程安全属性,NULL表示该线程不能被继承
		DWORD dwStackSize,//初始化栈大小,0表示按默认值或调用线程配置
		LPTHREAD_START_ROUTINE lpStartAddress,//执行线程函数名称
		LPVOID lpParameter,//向新线程传递的参数
		DWORD dwCreationFlags,//创建标志,0表示创建后立即执行
		LPDWORD lpThreadId //指向接收线程的ID
	);
2、事件创建函数CreateEvent( )
HANDLE CreateEvent(
		LPSECURITY_ATTRIBUTES lpEventAttributes,//事件安全属性,NULL表示该事件句柄不能用于继承
		BOOL bManualReset,//TRUE表示该事件需要人工复位
		BOOL bInitialState,//事件对象的初始状态,TRUE表示有信号,反之无信号
		LPCTSTR pName//指向事件对象的名称,NULL表示创建一个无名事件对象
	);
3、WaitForSingleObject( )–获取后台读写命令返回结果函数
DWORD WaitForSingleObject(
		HANDLE hHandle,
		DWORD dwMilliseconds
	);
4、GetOverlappedResult( )–同上,但可以返回读操作实际读取的字节数量
BOOL GetOverlappedResult(
		HANDLE hFile,   //指向串口句柄
		LPOVERLAPPED lpOverlapped, //执行读函数中使用的OVERLAPPED结构变量
		LPDWORD lpNumberOfBytesTransferred, //存放实际读出字节数量变量的地址
		//设为TRUE,表示直到读操作完成该函数才返回,若为FALSE,表示若读操作未完成是函数将
		//返回FALSE,此时说明使用GetLastError()函数可以获取错误信息
		BOOL bWait);
三、代码实现
1、定义相关全局变量
HANDLE hComm; //串口句柄
HANDLE hCom = INVALID_HANDLE_VALUE;
DWORD ThreadProcWrite(LPVOID pParam);//写线程函数
DWORD ThreadProcRead(LPVOID pParam);//读线程函数
OVERLAPPED Wol = { 0 };//写操作OVERLAPPED结构变量
OVERLAPPED Rol = { 0 };//读操作OVERLAPPED结构变量
HANDLE hThreadWrite;//写线程句柄
HANDLE hThreadRead;//读线程句柄
DWORD dwThreadID;
DWORD dwParam;
2、创建和配置串口
	int hdinit() {
	//HWND hWnd;
	hCom = CreateFile(
		"//.//COM6", //将要打开的串口逻辑名
		GENERIC_READ | GENERIC_WRITE, //允许读和写
		0,   //指定共享属性,由于串口不能共享,该参数必须置为0,独占方式
		NULL,  //引用安全性属性结构,缺省值为NULL
		OPEN_EXISTING,   //创建标志,对串口操作该参数必须置为OPEN_EXISTING
		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //属性描述,此处重叠方式
		NULL); //对串口而言该参数必须置为NULL);
	if (hCom == INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();//读取错误信息
		printf("打开串口失败error:%d\n", error);
		return 0;
	}
	else
	{
		printf("打开串口成功!\n");
		DCB dcb;
		GetCommState(hCom, &dcb); //首先获取串口的配置
		dcb.BaudRate = 9600; //配置波特率
		dcb.fParity = 0; //校验不是能
		dcb.ByteSize = 8;  //数据位为8
		dcb.Parity = 0;  //无奇偶校验
		dcb.StopBits = 1; //停止位为1
		SetCommState(hCom, &dcb);  //设置上述配置信息
		SetupComm(hCom, 4200, 4200);//配置缓冲区,分别表示设置输入输出缓冲区的大小
		COMMTIMEOUTS timeout;
		GetCommTimeouts(hCom, &timeout);
		timeout.ReadTotalTimeoutConstant = 1000;//这里只设置了读时间常量
		SetCommTimeouts(hCom, &timeout);
		PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);
		printf("串口配置成功!\n");
		return 1;
	}
	//hWnd = GetSafeHwnd();
}

注意:这里串口创建的是重叠方式

3、创建读操作线程
int threadReadinit(void) {
	hThreadRead = CreateThread(NULL,
		0,//初始化栈大小,0表示按默认值或调用线程配置
		(LPTHREAD_START_ROUTINE)ThreadProcRead,//读线程函数名称
		&dwParam,//向新线程传递的参数
		0,  //创建线程后,立即执行该线程
		&dwThreadID);//指向接收线程的ID
	if (hThreadRead == NULL) {
		AfxMessageBox(_T("读线程创建失败!"));
		return 0;
	}
	else {
		printf("创建线程函数中:读线程创建成功!\n");
		return 1;
	}
}
4、创建写操作线程
int threadWriteinit(void) {
	hThreadWrite = CreateThread(NULL,
		0,
		(LPTHREAD_START_ROUTINE)ThreadProcWrite,//读线程函数名称
		&dwParam,
		0,  //创建线程后,立即执行该线程
		&dwThreadID);
	if (hThreadWrite == NULL) {
		AfxMessageBox(_T("读线程创建失败!"));
		return 0;
	}
	else {
		AfxMessageBox(_T("写线程创建成功!"));
		return 1;
	}
}
5、读线程函数
DWORD ThreadProcRead(LPVOID pParam)//入口参数为任意指针
{
	BYTE myByte{ 20 };
	CString myStr;
	DWORD dwRes;
	DWORD dwRead;
	BOOL fRes;
	char myChar[20];
	Rol.hEvent = CreateEvent(NULL,//创建Rol的hEvent成员为无信号状态
		TRUE,
		FALSE,
		NULL);
	if (Rol.hEvent == NULL) {
		AfxMessageBox(_T("hEvent空"));
		return -1;
	}
	if (ReadFile(hCom,//串口句柄
		&myChar,//存放读取数据
		10,//要读取的字节数
		NULL,
		&Rol)) //指向创建hCom时的Rol指针
	{//如果ReadFile返回TRUE,表明操作已完成,可以处理读取的数据
		printf("ReadFile之后的成功读出!\n");
		//在这里加入存放已读数的代码,已读数据存放在myByte数组中
	}
	else//如果ReadFile返回FALSE,表明读操作为完成,此时使用WaitForSingleObject函数等待读操作的结果,根据不同的返回结果采取不同的处理
	{
		dwRes = WaitForSingleObject(Rol.hEvent,
			5000);//设置5秒的超时---这里是等待后台读操作的命令的结果
		switch (dwRes)
		{
		case WAIT_OBJECT_0: //返回成功,表示等待的对象已经被置为有信号状态;
			//---表示读操作完成,读操作完成需要验证GetOverlappedResult,如果返回了实际读取的字节数表示完成
			if (!GetOverlappedResult(hCom,
				&Rol,
				&dwRead,//实际读取的字节数
				TRUE))//TRUE表示直到操作完成该函数才返回 
			{
				//操作失败的代码
				DWORD error = GetLastError();//读取错误信息
				printf("操作失败:%d\n", error);
			}
			else {
				printf("%d\n", myChar[0]);
				printf("WAIT_OBJECT_0下的成功!\n");
				//操作成功完成,数据读出,并存放到myByte数组中
				//在这里加入处理数据的代码
			}
			break;
		case WAIT_TIMEOUT:
			//读操作失败,原因是读超时
			break;
		default:
			//这里可以加入默认情况下的处理代码
			break;
		}
	}
	//CloseHandle(Rol.hEvent);//这里的关闭线程会报异常,待查!
	//FindClose(Rol.hEvent);// file search handle 
	return 0;
}
6、写线程函数

DWORD ThreadProcWrite(LPVOID pParam) {

BYTE myByte[9];
CString myStr;
int i;
DWORD dwRes;
DWORD dwWrite;
BOOL fRes;
char myChar[10];
for (i = 0; i <= 9; i++) {
	myByte[i] = i;   //等待发送数据写入myBte数组中
}
Wol.Internal = 0;  //设置OVERLAPPED结构Wol
Wol.InternalHigh = 0;
Wol.Offset = 0;
Wol.OffsetHigh = 0;
Wol.hEvent = CreateEvent(NULL,//创建Wol的hEvent成员为无信号状态
	TRUE,
	FALSE,
	NULL);
if (Wol.hEvent == NULL) {
	AfxMessageBox(_T("hEvent为空!"));
	return -1;
}
if (WriteFile(hCom,//串口句柄
	&myByte,//存放待发送的数据
	3,//欲发送的字节数
	NULL,
	&Wol))//指向创建hCom时的Wol指针
{
	AfxMessageBox(_T("成功发送!"));
	//这里加入成功发送的代码
}
else {
	dwRes = WaitForSingleObject(Wol.hEvent, 500);
	switch (dwRes) {
	case WAIT_OBJECT_0:
		if (!GetOverlappedResult(hCom,
			&Wol,
			&dwWrite,
			TRUE)) {
			//操作失败,可以使用GetLastError()来获取错误信息
		}
		else {
			//发送数据成功,这里加入发送成功的处理代码
		}
		break;
	case WAIT_TIMEOUT:
		//发送失败,失败原因是发送超时
		break;
	default:
		break;
	}
}
CloseHandle(Wol.hEvent);
return 0;

}

6、main函数
int main()
{
	if (hdinit()) {
		LPVOID pParam;
		unsigned char* ret = (unsigned char*)malloc(1000);
		if (threadReadinit()) {
			printf("main读线程创建成功\n");
		}
		ThreadProcRead(ret);
	}
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值