WIN32控制台下的串口通信程序

Winodws平台下,文件、通信设备、命名管道、邮件槽、磁盘、控制台等都是以文件的形式存在,它们的创建于打开操作都是利用CreateFile()函数。在MSDN中CreateFile()的声明方式为:

HANDLE WINAPI CreateFile(
  _In_     LPCTSTR               lpFileName,  //文件名“COM1”,"COM2"等
  _In_     DWORD                 dwDesiredAccess,  //访问模式,读、写
  _In_     DWORD                 dwShareMode,  //共享模式,常为0表示独占方式
  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,   //安全属性通常为NULL
  _In_     DWORD                 dwCreationDisposition,   //创建或打开方式
  _In_     DWORD                 dwFlagsAndAttributes,  //文件属性或标志
  _In_opt_ HANDLE                hTemplateFile  //临时文件,又或者模板,通常为NULL
);
以下Win32控制台程序演示CreateFile()函数的调用方式,

#include <iostream>
#include <cstdlib>
#include <Windows.h>

using namespace std;

bool OpenPort();  //打开串口
bool OpenPort()
{
	HANDLE hComm;
	hComm = CreateFile(L"COM3",   //串口编号
		GENERIC_READ | GENERIC_WRITE,  //允许读写
		0,   //通讯设备必须以独占方式打开
		NULL,
		OPEN_EXISTING,   //通讯设备已存在
		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,   //重叠方式
		NULL);   //通讯设备不能用模板打开
	if (hComm == INVALID_HANDLE_VALUE)
	{
		CloseHandle(hComm);
		return FALSE;
	}
	else
		return TRUE;

}
int main()
{
	bool open;
	open = OpenPort();
	if (open)
		cout << "Open serial port successfully!" << endl;
	else
		cout << "Open serial port unsuccessfully!" << endl;
	return EXIT_SUCCESS;
}
我的主机只有一个串口COM1,使用虚拟串口软件额外创建了两个窗口COM2和COM3,修改COM口均能测试成功,如下所示:



在Windows串口通信中还会用到设备控制、超时控制、通信错误、通信状态、通信事件等操作,以下演示了对设备控制DCB和超时控制COMMTIMEOUTS的设置

#include <iostream>
#include <cstdlib>
#include <Windows.h>

bool OpenPort();  //打开串口
bool SetupDCB(int rate_arg); //设置DCB设备控制块
bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier,
	DWORD ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant);  //超时设置
HANDLE hComm;
bool OpenPort()
{
	//HANDLE hComm;
	hComm = CreateFile(L"COM1",  //指定串口
		GENERIC_READ | GENERIC_WRITE,  //允许读写
		0,  //以独占方式打开
		0,  //无安全属性
		OPEN_EXISTING,  //通讯设备已存在
		0,  //同步I/O
		0);   //不指定模式
	if (hComm == INVALID_HANDLE_VALUE)
	{
		CloseHandle(hComm);
		return FALSE;
	}
	else
		return TRUE;
}

bool SetupDCB(int rate_arg)
{
	DCB dcb;
	memset(&dcb, 0,sizeof(dcb));
	if (!GetCommState(hComm, &dcb))  //获取当前DCB配置
		return FALSE;
	/* ---------- 串口设置 ------- */
	dcb.DCBlength = sizeof(dcb);  //DCB块大小
	dcb.BaudRate = rate_arg;  //波特率
	dcb.Parity = NOPARITY;    //奇偶校验0-4:分别表示不校验、奇校验,偶校验、标号、空格
	dcb.fParity = 0;   //不允许奇偶校验
	dcb.StopBits = ONESTOPBIT;   //停止位
	dcb.ByteSize = 8;   //数据位,以字节表示4-8
	dcb.fOutxCtsFlow = 0;   //CTS输出流控制
	dcb.fOutxDsrFlow = 0;   //DSR输出流控制
	dcb.fDtrControl = DTR_CONTROL_DISABLE;  //DTR流控制类型
	dcb.fDsrSensitivity = 0;   //对DSR信号线不敏感
	dcb.fRtsControl = RTS_CONTROL_DISABLE;  //RTS流控制
	dcb.fOutX = 0;   //XON/XOFF输出流控制
	dcb.fInX = 0;   //XON/XOFF输入流控制
	/* ---------- 容错机制 ------- */
	dcb.fErrorChar = 0;   //允许错误替换
	dcb.fBinary = 1;   //二进制模式,不检测EOF
	dcb.fNull = 0;   //允许剥离,去掉NULL字符
	dcb.fAbortOnError = 0;   //有错误时终止读写操作
	dcb.wReserved = 0;   //
	dcb.XonLim = 2;   //XON发送字符之前缓冲区中允许接收的最小字节数
	dcb.XoffLim = 4;   //XON发送字符之前缓冲区中允许的最小可用字节数
	dcb.XonChar = 0x13;   //发送和接受XON字符
	dcb.XoffChar = 0x19;   //发送和接受XOFF字符
	dcb.EvtChar = 0;   //接收到的事件字符
	if (!SetCommState(hComm, &dcb))
		return FALSE;
	else
		return TRUE;
}

bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier,
	DWORD ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant)
{
	COMMTIMEOUTS time;
	time.ReadIntervalTimeout = ReadInterval;   //读时间超时
	time.ReadTotalTimeoutConstant = ReadTotalConstant;  //读时间常量
	time.ReadTotalTimeoutMultiplier = ReadTotalMultiplier;  //读时间系数
	time.WriteTotalTimeoutConstant = WriteTotalConstant;  //写时间常量
	time.WriteTotalTimeoutMultiplier = WriteTotalMultiplier;  //写时间系数
	if (!SetCommTimeouts(hComm, &time))
		return FALSE;
	else
		return TRUE;
}
int main()
{
	if (OpenPort())
		std::cout << "Open port success" << std::endl;
	if (SetupDCB(9600))
		std::cout << "Set DCB success" << std::endl;
	if (SetupTimeout(0, 0, 0, 0, 0))
		std::cout << "Set timeout success" << std::endl;
	SetCommMask(hComm, EV_RXCHAR);  //当有字符在inbuf中时产生这个事件
	PurgeComm(hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

	return EXIT_SUCCESS;
}

在COM1口的测试结果如下,


串口的读写操作:

本例实现串口的读写,采用虚拟串口虚拟一对串口COM2和COM3,COM2用于接收,COM3用于发送。完整代码如下:

#include <iostream>
#include <cstdlib>
#include <windows.h>

HANDLE hComm;
OVERLAPPED OverLapped;
COMSTAT Comstat;
DWORD dwCommEvents;

bool OpenPort();  //打开串口
bool SetupDCB(int rate_arg);  //设置DCB
bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier, DWORD
	ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant);   //设置超时
void ReciveChar();   //接收字符
bool WriteChar(char* szWriteBuffer, DWORD dwSend);  //发送字符

bool OpenPort()
{
	hComm = CreateFile(L"COM2",
		GENERIC_READ | GENERIC_WRITE,
		0,
		0,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,
		0);
	if (hComm == INVALID_HANDLE_VALUE)
		return FALSE;
	else
		return true;
}

bool SetupDCB(int rate_arg)
{
	DCB dcb;
	memset(&dcb, 0, sizeof(dcb));
	if (!GetCommState(hComm, &dcb))//获取当前DCB配置
	{
		return FALSE;
	}
	dcb.DCBlength = sizeof(dcb);
	/* ---------- Serial Port Config ------- */
	dcb.BaudRate = rate_arg;
	dcb.Parity = NOPARITY;
	dcb.fParity = 0;
	dcb.StopBits = ONESTOPBIT;
	dcb.ByteSize = 8;
	dcb.fOutxCtsFlow = 0;
	dcb.fOutxDsrFlow = 0;
	dcb.fDtrControl = DTR_CONTROL_DISABLE;
	dcb.fDsrSensitivity = 0;
	dcb.fRtsControl = RTS_CONTROL_DISABLE;
	dcb.fOutX = 0;
	dcb.fInX = 0;
	dcb.fErrorChar = 0;
	dcb.fBinary = 1;
	dcb.fNull = 0;
	dcb.fAbortOnError = 0;
	dcb.wReserved = 0;
	dcb.XonLim = 2;
	dcb.XoffLim = 4;
	dcb.XonChar = 0x13;
	dcb.XoffChar = 0x19;
	dcb.EvtChar = 0;
	if (!SetCommState(hComm, &dcb))
	{
		return false;
	}
	else
		return true;
}

bool SetupTimeout(DWORD ReadInterval, DWORD ReadTotalMultiplier, DWORD
	ReadTotalConstant, DWORD WriteTotalMultiplier, DWORD WriteTotalConstant)
{
	COMMTIMEOUTS timeouts;
	timeouts.ReadIntervalTimeout = ReadInterval;
	timeouts.ReadTotalTimeoutConstant = ReadTotalConstant;
	timeouts.ReadTotalTimeoutMultiplier = ReadTotalMultiplier;
	timeouts.WriteTotalTimeoutConstant = WriteTotalConstant;
	timeouts.WriteTotalTimeoutMultiplier = WriteTotalMultiplier;
	if (!SetCommTimeouts(hComm, &timeouts))
	{
		return false;
	}
	else
		return true;
}

void ReciveChar()
{
	bool bRead = TRUE;
	bool bResult = TRUE;
	DWORD dwError = 0;
	DWORD BytesRead = 0;
	char RXBuff;
	for (;;)
	{
		bResult = ClearCommError(hComm, &dwError, &Comstat);
		if (Comstat.cbInQue == 0)
			continue;
		if (bRead)
		{
			bResult = ReadFile(hComm,  //通信设备(此处为串口)句柄,由CreateFile()返回值得到
				&RXBuff,  //指向接收缓冲区
				1,  //指明要从串口中读取的字节数
				&BytesRead,   //
				&OverLapped);  //OVERLAPPED结构
			std::cout << RXBuff << std::endl;
			if (!bResult)
			{
				switch (dwError == GetLastError())
				{
				case ERROR_IO_PENDING:
					bRead = FALSE;
					break;
				default:
					break;
				}
			}
		}
		else
		{
			bRead = TRUE;
		}
	}
	if (!bRead)
	{
		bRead = TRUE;
		bResult = GetOverlappedResult(hComm,
			&OverLapped,
			&BytesRead,
			TRUE);
	}
}

bool WriteChar(char* szWriteBuffer, DWORD dwSend)
{
	bool bWrite = TRUE;
	bool bResult = TRUE;
	DWORD BytesSent = 0;
	HANDLE hWriteEvent=NULL;
	ResetEvent(hWriteEvent);
	if (bWrite)
	{
		OverLapped.Offset = 0;
		OverLapped.OffsetHigh = 0;
		bResult = WriteFile(hComm,  //通信设备句柄,CreateFile()返回值得到
			szWriteBuffer,  //指向写入数据缓冲区
			dwSend,  //设置要写的字节数
			&BytesSent,  //
			&OverLapped);  //指向异步I/O数据
		if (!bResult)
		{
			DWORD dwError = GetLastError();
			switch (dwError)
			{
			case ERROR_IO_PENDING:
				BytesSent = 0;
				bWrite = FALSE;
				break;
			default:
				break;
			}
		}
	}
	if (!bWrite)
	{
		bWrite = TRUE;
		bResult = GetOverlappedResult(hComm,
			&OverLapped,
			&BytesSent,
			TRUE);
		if (!bResult)
		{
			std::cout << "GetOverlappedResults() in WriteFile()" << std::endl;
		}
	}
	if (BytesSent != dwSend)
	{
		std::cout << "WARNING: WriteFile() error.. Bytes Sent:" << BytesSent << "; Message Length: " << strlen((char*)szWriteBuffer) << std::endl;
	}
	return TRUE;
}
int main()
{
	if (OpenPort())
		std::cout << "Open port success" << std::endl;
	if (SetupDCB(9600))
		std::cout << "Set DCB success" << std::endl;
	if (SetupTimeout(0, 0, 0, 0, 0))
		std::cout << "Set timeout success" << std::endl;
	PurgeComm(hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
	WriteChar("Please send data:", 20);
	std::cout << "Received data:";
	ReciveChar();
	return EXIT_SUCCESS;
}
首先编译运行如下结果:

此时打开串口调试助手,设置串口号为COM3,波特率9600,数据位8,停止位1,以ASCII码形式发送,然后在发送栏写入要发送的字符如“Hello world!”,点击发送,COM2口就可以成功接收发送来的数据,并且COM3口成功接收到了COM2发来的数据请求。图如所示,




猜你喜欢

转载自blog.csdn.net/u013232740/article/details/49871113