上位机串口通信

背景

上位机与下位机通过RS232协议通信,现场没有硬件环境,通过软件模拟通信过程。

软件工具

Virtual Serial Port Driver (VSPD) :模拟串口驱动工具
如下图:添加了COM3-COM4和COM5-COM6,添加时成对添加,该对串口间进行通信,COM3与COM4连接通信,COM5与COM6通信,
若想自定义连接线,在Custom pinout中修改,具体可参考帮助文件。



Serial Port Utlity:串口调试助手
如下图:设置波特率,数据位,校验位,停止位即可



程序流程

建立CSerialPort类对象,打开串口,发送/接收数据,关闭串口

CSerialPort类

#pragma once

#include <windows.h>

/*
SerialPort.h
封装了串口通讯的WindowsAPI,支持异步操作串口
*/
class CSerialPort
{
public:
	CSerialPort(void);
	~CSerialPort(void);

	bool Open(int nPort = 2, int nBaud = 115200);

	bool Close(void);

	// 从串口读数据
	int ReadData(void *, int);

	// 向串口写数据
	bool WriteCommByte(unsigned char*, int); 

	// 查询缓冲区是否有未读取的数据
	int ReadDataWaiting(void);

	inline bool IsOpen(void) { return m_bOpened; }
protected:
	// 内部实现,向串口写数据
	bool WriteCommByte(unsigned char);
	
	HANDLE m_hIDComDev;
	OVERLAPPED m_OverLappedRead;
	OVERLAPPED m_OverLappedWrite;

	bool m_bOpened;
};


 

 
  
#include "SerialPort.h"

#define BUFFER_INPUT_RECOMMENT 10240
#define BUFFER_OUTPUT_RECOMMENT 10240

// 不使用读超时
#define TIMEOUT_READ_INTERVAL         0xFFFFFFFF

// 读超时
#define TIMEOUT_READ_TOTAL_MULTIPLIER 0
#define TIMEOUT_READ_TOTAL_CONSTANT   0

// 写超时为秒
#define TIMEOUT_WRITE_TOTAL_MULTIPLIER 0
#define TIMEOUT_WRITE_TOTAL_CONSTANT   5000

// 异步读取/写入操作时等待事件的超时时间
#define TIMEOUT_READCOMM_EVENT 4000
#define TIMEOUT_WRITECOMM_EVENT 2000

// 一些通讯协议使用的宏
#define FC_DTRDSR	0x01
#define FC_RTSCTS	0x02
#define FC_XONXOFF	0x04
#define ASSII_BEL	0x07
#define ASSII_BS	0x08
#define ASSII_LF	0x0A
#define ASSII_CR	0x0D
#define ASSII_XON	0x11
#define ASSII_XOFF	0x13

CSerialPort::CSerialPort(void) : m_hIDComDev(NULL), m_bOpened(false)
{
	memset(&m_OverLappedRead, 0x00, sizeof(OVERLAPPED));
	memset(&m_OverLappedWrite, 0x00, sizeof(OVERLAPPED));
}


CSerialPort::~CSerialPort(void)
{
	Close();
}

// 打开串口
bool CSerialPort::Open(int nPort, int nBaud)
{
	if (m_bOpened)
	{
		return true;
	}

	char szPort[50] = { 0 };
	char szComParams[50] = { 0 };
	DCB dcb;
	sprintf_s(szPort, 50, "COM%d", nPort);

	// API:建立文件,Windows中将串口设备当做文件对待
	m_hIDComDev = CreateFile(
							szPort,
							GENERIC_READ | GENERIC_WRITE,
							0,
							NULL,
							OPEN_EXISTING,
							FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // 异步读写
							NULL);

	if (m_hIDComDev == NULL)
	{
		return false;
	}

	memset(&m_OverLappedRead, 0x00, sizeof(OVERLAPPED));
	memset(&m_OverLappedWrite, 0x00, sizeof(OVERLAPPED));

	// 设置超时
	COMMTIMEOUTS CommTimeOuts;
	CommTimeOuts.ReadIntervalTimeout = TIMEOUT_READ_INTERVAL;
	CommTimeOuts.ReadTotalTimeoutMultiplier = TIMEOUT_READ_TOTAL_MULTIPLIER;
	CommTimeOuts.ReadTotalTimeoutConstant = TIMEOUT_READ_TOTAL_CONSTANT;
	CommTimeOuts.WriteTotalTimeoutMultiplier = TIMEOUT_WRITE_TOTAL_MULTIPLIER;
	CommTimeOuts.WriteTotalTimeoutConstant = TIMEOUT_WRITE_TOTAL_CONSTANT;
	
	SetCommTimeouts(m_hIDComDev, &CommTimeOuts);

	sprintf_s(szComParams, 50, "COM%d:%d, n, 8, 1", nPort, nBaud);

	// 设置异步读取/写入监视事件
	m_OverLappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	
	m_OverLappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	// 读取/设置串口设备参数
	dcb.DCBlength = sizeof(DCB);
	GetCommState(m_hIDComDev, &dcb);
	dcb.BaudRate = nBaud;
	dcb.ByteSize = 8;

	unsigned char ucSet;
	ucSet = (unsigned char)((FC_RTSCTS & FC_DTRDSR) != 0);
	ucSet = (unsigned char)((FC_RTSCTS & FC_RTSCTS) != 0);
	ucSet = (unsigned char)((FC_RTSCTS & FC_XONXOFF) != 0);

	if (!SetCommState(m_hIDComDev, &dcb) 
		|| !SetupComm(m_hIDComDev, BUFFER_INPUT_RECOMMENT, BUFFER_OUTPUT_RECOMMENT) 
		|| m_OverLappedRead.hEvent == NULL
		|| m_OverLappedWrite.hEvent == NULL)
	{
		DWORD dwError = GetLastError();

		if (m_OverLappedRead.hEvent != NULL)
		{
			CloseHandle(m_OverLappedRead.hEvent);
		}
		if(m_OverLappedWrite.hEvent != NULL)
		{
			CloseHandle(m_OverLappedWrite.hEvent);
		}
		CloseHandle(m_hIDComDev);
		return false;
	}

	m_bOpened = true;
	return m_bOpened;
}

// 关闭串口
bool CSerialPort::Close(void)
{
	if (!m_bOpened || m_hIDComDev == NULL)
	{
		return true;
	}

	if (m_OverLappedRead.hEvent != NULL)
	{
		CloseHandle(m_OverLappedRead.hEvent);
	}
	if(m_OverLappedWrite.hEvent != NULL)
	{
		CloseHandle(m_OverLappedWrite.hEvent);
	}
	CloseHandle(m_hIDComDev);

	m_bOpened = false;
	m_hIDComDev = NULL;

	return true;
}

// 向串口写数据,类内部使用
bool CSerialPort::WriteCommByte(unsigned char ucData)
{
	BOOL bWriteStat;
	DWORD dwBytesWritten;

	bWriteStat = WriteFile(m_hIDComDev, (unsigned char*)&ucData, 1, &dwBytesWritten, &m_OverLappedWrite);

	// 查询异步写入是否完成,未完成则挂起等待
	if (!bWriteStat && (GetLastError() == ERROR_IO_PENDING))
	{
		if (WaitForSingleObject(m_OverLappedWrite.hEvent, TIMEOUT_WRITECOMM_EVENT))
		{
			dwBytesWritten = 0;
		}
		else
		{
			GetOverlappedResult(m_hIDComDev, &m_OverLappedWrite, &dwBytesWritten, false);
			m_OverLappedWrite.Offset += dwBytesWritten;
		}
	}

	return true;
}

// 向串口写数据
bool CSerialPort::WriteCommByte(unsigned char * pData, int nLen)
{
	BOOL bWriteStat;
	DWORD dwBytesWritten;

	CLogFile::WriteLog(pData, nLen);

	bWriteStat = WriteFile(m_hIDComDev, pData, nLen, &dwBytesWritten, &m_OverLappedWrite);

	// 查询异步写入是否完成,未完成则挂起等待
	if (!bWriteStat && (GetLastError() == ERROR_IO_PENDING))
	{
		if (WaitForSingleObject(m_OverLappedWrite.hEvent, TIMEOUT_WRITECOMM_EVENT))
		{
			dwBytesWritten = 0;
		}
		else
		{
			GetOverlappedResult(m_hIDComDev, &m_OverLappedWrite, &dwBytesWritten, false);
			m_OverLappedWrite.Offset += dwBytesWritten;
		}
	}

	return true;
}

// 查询接受缓冲区内是否有数据(只查询,不读取)
int CSerialPort::ReadDataWaiting(void)
{
	if (!m_bOpened || m_hIDComDev == NULL)
	{
		return 0;
	}

	DWORD dwErrorFlags;
	COMSTAT ComStat;
	ClearCommError(m_hIDComDev, &dwErrorFlags, &ComStat);
	return (int)ComStat.cbInQue;
}

// 读取来自串口的数据
int CSerialPort::ReadData(void* buffer, int limit)
{
	if (!m_bOpened || m_hIDComDev == NULL)
	{
		return 0;
	}
	BOOL bReadStatus;
	DWORD dwBytesRead, dwErrorFlags;
	COMSTAT ComStat;

	// 读取之前必须清楚错误信息
	ClearCommError(m_hIDComDev, &dwErrorFlags, &ComStat);

	if (!ComStat.cbInQue)
	{
		return 0;
	}

	dwBytesRead = (DWORD) ComStat.cbInQue;
	
	if (limit < (int)dwBytesRead)
	{
		dwBytesRead = (DWORD)limit;
	}

	bReadStatus = ReadFile(m_hIDComDev, buffer, dwBytesRead, &dwBytesRead, &m_OverLappedRead);

	// 查询异步读取是否完成,未完成则挂起等待
	if (!bReadStatus)
	{
		if (GetLastError() == ERROR_IO_PENDING)
		{
			WaitForSingleObject(m_OverLappedRead.hEvent, TIMEOUT_READCOMM_EVENT);
			return (int)dwBytesRead;
		}
		return 0;
	}

	DealReceiveData((unsigned char*)buffer, nCount);

	return (int)nCount;
}


void CSerialPort::DealReceiveData(unsigned char* szData, int nLen)
{
<span style="white-space:pre">	</span>// 处理数据
}

使用示例

#include "SerialPort.h"
#include <Windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	CSerialPort SerialPort;
	SerialPort.Open(6);
	unsigned char szBuffer[MAX_PATH] = { 0 };
	while (1)
	{
		int nLen = SerialPort.ReadData(szBuffer, MAX_PATH);
		if (nLen)
		{
			for (int i = 0; i < nLen; i++)
			{
				printf("0x%02x ", szBuffer[i]);
			}
			printf("\n", szBuffer);
			unsigned char* p = szBuffer;
			do 
			{
				unsigned short usCmdType = 0;
				unsigned short usLen     = 0;
				
				memcpy(&usCmdType, p + 7, 2);
				memcpy(&usLen, p + 4, 2);

				printf("%04x\n", usCmdType);
				switch(usCmdType)
				{
				case 0x000A:
					{
						unsigned char szData[] = { 0x00, 0x00, 0x0c, 0x00, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3};
						SerialPort.WriteCommByte(szData, 12)<span style="font-family:Arial, Helvetica, sans-serif;">;</span>
					}
					break;
				case 0x0002:
					{
						unsigned char szData[] = { 0x00, 0x00, 0x0c, 0x00, 0x0d, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb};
						SerialPort.WriteCommByte(szData, 12);
					}
					break;

				}
				p += usLen + 4;
				nLen -= usLen + 4;
			} while (nLen);

			memset(szBuffer, 0x00, MAX_PATH);
		}
	}
	SerialPort.Close();
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD threadID;
	HANDLE hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID); // 创建线程
	WaitForSingleObject(hThread,INFINITE);
	CloseHandle(hThread); // 关闭内核对象
	return 0;
}
该示例是之前一个项目的Demo程序,但是已经包含串口发送和接收数据了。




猜你喜欢

转载自blog.csdn.net/bai2010bingbing/article/details/50546463