windows下实现同时收发的串口通信类设计

背景

最近有一个串口通信的需求,在一个串口实现消息帧的解析和帧指令的发送。

本来串口从硬件上就是一个全双工的设计,但在程序实现上需要小心谨慎,因为可能会出现收发冲突的情况,甚至有时候虽然收发不冲突,但是收到错误的数据。这两个坑在笔者设计串口通信程序时都踩过。

本文就介绍一种可以同时实现收发的串口通信类,并且预留了消息解析和指令发送的接口。

具体实现细节

基本思路

基本思路是在串口类中的打开串口方法中开启两个线程,分别用于串口读写。

m_hListenThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)this->ListenThread, this, 0, NULL);

m_hCommandThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)this->CommandThread, this, 0, NULL);

SetThreadPriority(m_hListenThread, THREAD_PRIORITY_ABOVE_NORMAL);

并且把协议解析的优先级设置到较高的水平。

串口打开

	//打开串口,设置波特率等参数
	char* strCOM = "COM2";

	hSerial = CreateFile(strCOM,
		GENERIC_READ | GENERIC_WRITE,
		0,
		0,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,
		0);
	if (hSerial == INVALID_HANDLE_VALUE)
	{
		if (GetLastError() == ERROR_FILE_NOT_FOUND)
		{
			printf("Error 1\n");
			return false;
		}
		printf("Error 2\n");
		return false;
	}

	DCB dcbSerialParams = { 0 };
	DCB dcbSerial = { 0 };
	dcbSerial.DCBlength = sizeof(dcbSerialParams);
	if (!GetCommState(hSerial, &dcbSerialParams))
	{
		printf("Error 3\n");
		return false;
	}
	dcbSerialParams.BaudRate = 38400;
	dcbSerialParams.ByteSize = 8;
	dcbSerialParams.StopBits = ONESTOPBIT;
	dcbSerialParams.Parity = NOPARITY;
	if (!SetCommState(hSerial, &dcbSerialParams))
	{
		printf("Error 4\n");
		return false;
	}

	COMMTIMEOUTS timeouts = { 0 };
	timeouts.ReadIntervalTimeout = 1;
	timeouts.ReadTotalTimeoutConstant = 0;
	timeouts.ReadTotalTimeoutMultiplier = 1;
	timeouts.WriteTotalTimeoutConstant = 0;
	timeouts.WriteTotalTimeoutMultiplier = 5;
	if (!SetCommTimeouts(hSerial, &timeouts))
	{
		printf("Error 5\n");
		return false;
	}

串口发送线程

串口发送线程,只要newCommand被置位,就发送消息

UINT WINAPI CSerialPort::CommandThread(void* pParam)
{
	CSerialPort *pSerialPort = reinterpret_cast<CSerialPort*>(pParam);
	cout << "发送串口命令线程启动成功" << endl;
	while (!pSerialPort->s_bExit)
	{
		if (pSerialPort->newCommand)
		{
			cout << "发送一次指令" << endl;
			u8 tqk_data[12] = { 0x00, 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
			//根据协议赋值
			pSerialPort->WriteBuffer(tqk_data, 12);  //WriteBuffer函数实现在后面
			pSerialPort->newCommand = false;
		}
		Sleep(5);
	}
	return true;
}

WriteBuffer函数实现

bool CSerialPort::WriteBuffer(u8* lpBuf, DWORD dwToWrite)
{
	//int_T nInputPorts = ssGetNumInputPorts(S);
	//HANDLE* ptrHandle = (HANDLE*)ssGetDWork(S, nInputPorts);
	//HANDLE hSerial = ptrHandle[0];
	//HANDLE hThreadRead = ptrHandle[1];
	//HANDLE hThreadWrite = ptrHandle[2];
	OVERLAPPED osWrite = { 0 };
	DWORD dwWritten;
	DWORD dwRes;
	bool fRes;

	// Create this write operation's OVERLAPPED structure's hEvent.
	osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (osWrite.hEvent == NULL)
		// error creating overlapped event handle
		return FALSE;

	// Issue write.
	if (!WriteFile(hSerial, lpBuf, dwToWrite, &dwWritten, &osWrite))
	{
		if (GetLastError() != ERROR_IO_PENDING)
		{
			// WriteFile failed, but isn't delayed. Report error and abort.
			fRes = FALSE;
			printf("Serial port Writing error!\n");
		}
		else
			// Write is pending.
			dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
		switch (dwRes)
		{
			// OVERLAPPED structure's event has been signaled.
		case WAIT_OBJECT_0:
			if (!GetOverlappedResult(hSerial, &osWrite, &dwWritten, FALSE))
				fRes = FALSE;
			else
				if (dwToWrite > dwWritten)
					printf("Serial port Writing timeout!\n");
			// Write operation completed successfully.
			fRes = TRUE;
			break;
		default:
			// An error has occurred in WaitForSingleObject.
			// This usually indicates a problem with the
			// OVERLAPPED structure's event handle.
			fRes = FALSE;
			break;
		}
	}
	else
	{
		// WriteFile completed immediately.
		fRes = TRUE;
	}

	CloseHandle(osWrite.hEvent);
	return fRes;
}

串口接收线程

Data_Receive_Pre是协议解析函数,每次传输一个字节进去。
因为线程函数必须是类的静态成员,所以需要把类自身指针传进去,在协议解析函数中给类中的属性赋值。
Data_Receive_Pre函数需要根据自身协议实现,可以参考mavlink和匿名的协议解析。

UINT WINAPI CSerialPort::ListenThread(void* pParam)
{
	CSerialPort *pSerialPort = reinterpret_cast<CSerialPort*>(pParam);
	cout << "串口监听线程启动成功" << endl;
	char szBuff[2] = { 0 };
	DWORD dwBytesRead = 0;
	DWORD dwRes;
	BOOL fWaitingOnRead = FALSE;
	OVERLAPPED osReader = { 0 };

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

	while (!pSerialPort->s_bExit)
	{
		if (!fWaitingOnRead)
		{
			// Issue read operation.
			if (!ReadFile(pSerialPort->hSerial, szBuff, 1, &dwBytesRead, &osReader))
			{
				if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
					printf("Serial port Reading error!\n");
				else
				{
					fWaitingOnRead = TRUE;
					// printf("Serial port is waiting for data!\n");
				}
			}
			else
			{
				//cout << "0" << (int)szBuff[0] << endl;
				Data_Receive_Pre(szBuff[0], pSerialPort);
			}
		}
		else
		{
			dwRes = WaitForSingleObject(osReader.hEvent, INFINITE);
			switch (dwRes)
			{
				// Read completed.
			case WAIT_OBJECT_0:
				if (!GetOverlappedResult(pSerialPort->hSerial, &osReader, &dwBytesRead, FALSE))
					// Error in communications; report it.
					printf("Serial port Reading error after Overlapped!\n ");
				else
				{
					if (dwBytesRead>0)
					{
						Data_Receive_Pre(szBuff[0], pSerialPort);
					}
					//  Reset flag so that another opertion can be issued.
					fWaitingOnRead = FALSE;
					// printf("%d Byte Data received after Overlapped!\n", (int)dwBytesRead);
				}
				break;
			case WAIT_TIMEOUT:
				printf("STILL Serial port is waiting for data!\n ");
				break;

			default:
				break;
			}
		}
	}
	return 0;
}

以上就能做到串口收发同时。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仟人斩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值