文章目录

  • Win10 串口通信 —— 同步/异步
  • 简介
  • 实现
  • 1.主函数
  • 2.串口模块
  • 源码

Win10 串口通信 —— 同步/异步

简介

之前接到的一个小项目,好像不能算。win10下的串口通信,不需要界面,排除了Qt,MFC只剩C++ 底层了,调用WindowsApi来实现。

系统环境:Win10

IDE: VS2017

编译器:MSVC2017/C++11

实现

主要分为接收线程和主线程调度模块,由于没有共用数据体,就没有做线程锁。底层串口模块主要调用 上述git上的源码。WzSerialPort,并做了一些简单修改,实现了异步串口通信。

1.主函数

  • 入口说明 - 提示
void showHelp()
{
	std::cout << " portname(串口名): 在Windows下是\"COM1\"\"COM2\"" << std::endl;
	std::cout << " baudrate(波特率): 9600、19200、38400、43000、56000、57600、115200 " << std::endl;
	std::cout << " parity(校验位): 0为无校验,1为奇校验,2为偶校验,3为标记校验" << std::endl;
	std::cout << " databit(数据位): 4-8(windows),通常为8位" << std::endl;
	std::cout << " stopbit(停止位): 1为1位停止位,2为2位停止位,3为1.5位停止位" << std::endl;
}
  • 入口说明-main函数
bool isHelp = false;
	std::cout << "请输入是否查看帮助(0:不查看 1:查看):";
	std::cin >> isHelp;
	if (isHelp) showHelp();

	std::string comName;
	std::cout << "请输入串口名称: ";
	std::cin >> comName;

	int bata = 0;
	std::cout << "请输入波特率:";
	std::cin >> bata;
	int checkBit = 0;

	std::cout << "请输入校验位:";
	std::cin >> checkBit;

	std::cout << "数据位默认为:8,停止位默认为:0" << std::endl;
	std::cout << "ComName:" << comName << " BaudRate:" << bata << " Parity:" << checkBit << std::endl;
	std::cout << "" << std::endl;
	std::cout << "---------------------" << std::endl;
  • 入口函数-串口初始化
WzSerialPort w;
	serialPortInit(w,comName,bata, checkBit);
bool serialPortInit(WzSerialPort &w, std::string  comName, int bata, int checkBit)
{
	bool ret = false;
	if (w.open(comName.c_str(), bata, checkBit, 8, 1, 0))
	{
		ret = true;
		std::cout << "SerialPort Init OK!" << std::endl;
	}
	else
		std::cout << "SerialPort Init Fail!" << std::endl;
	return ret;
}
  • 接收线程 - 因为只是单纯的数据显示,并没有做数据解析
void receiveDemo(WzSerialPort w)
{
	char buf[1024];
	bool runFlag = true;
	while (runFlag)
	{
		if (closeFlag) runFlag = false;
		memset(buf, 0, 1024);
		if(w.receive(buf, 1024))
			std::cout << "接收数据为: " << buf << std::endl;
		Sleep(200);
	}
}
std::thread recv(receiveDemo, w); //线程启动
recv.detach(); //线程分离
  • 主线程-发送
while (true)
	{
		int type = 0;
		std::cin >>  type;
		if (type == 15)
		{
			closeFlag = true;
			w.close();
			break;
		}
		sendThread(w,type);
	}
	std::cout << "程序退出成功" << std::endl;
void sendThread(WzSerialPort w,int type)
{
	protocol pro;
	getCmdData(pro, type);
	if (sendDemo(w, &pro, sizeof(pro)))
		std::cout << "数据发送完成!发送类型: " << type << " len:" << sizeof(pro) << std::endl;
	else
		std::cout << "数据发送失败!" << std::endl;
	std::cout << "请输入发送类型:" << std::endl;
}
bool sendDemo(WzSerialPort w, const void* buf,int len)
{	
	if (w.send(buf, len) == 0)
	{
		std::cout << "send data fail" << std::endl;
		return false;
	}
	return true;
}
  • 数据结构体、字转换、数据拼装
bool closeFlag = false;
#pragma pack(1) //设置为一字节对齐
typedef struct 
{
	unsigned short header; //数据头
	unsigned char devNo; //设备节点
	unsigned char len; //数据长度
	unsigned char cmd; //命令
	unsigned short data; //数据内容
}protocol,*pProtocol;

//大小端转换函数
unsigned short BLEndianUshort(unsigned short value)
{
	return ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
}
//因为数据格式为固定,这里边就写死了,根据实际需要来做修改。
void getCmdData(protocol &pro, int type)
{
	if (type > 14 || type < 0)
	{
		std::cout << "输入类型错误!请重新输入!" << std::endl;
	}
	pro.header = BLEndianUshort(0XAA96);
	pro.devNo = 0X00;
	pro.len = 0X03;
	pro.cmd = 0X01;
	pro.data = BLEndianUshort(data[type]);
}

2.串口模块

只做一些简单说明,为什么上述博文中说明异步通信为什么没有实现,把错误地方给贴出来修改。

利用WindowsAPI实现,C++实现,在windows系统,移植或者适用匹配度很高。

修改部分,最先测试同步通信一直没有问题,异步通信没有实现。

  • 修改部分 - 头文件说明 – 同步异步 之前为 1异步 0 同步 --实际代码中 1为同步,0为异步,默认同步
// 打开串口,成功返回true,失败返回false
	// portname(串口名): 在Windows下是"COM1""COM2"等,在Linux下是"/dev/ttyS1"等
	// baudrate(波特率): 9600、19200、38400、43000、56000、57600、115200 
	// parity(校验位): 0为无校验,1为奇校验,2为偶校验,3为标记校验(仅适用于windows)
	// databit(数据位): 4-8(windows),5-8(linux),通常为8位
	// stopbit(停止位): 1为1位停止位,2为2位停止位,3为1.5位停止位
	// synchronizeflag(同步、异步,仅适用与windows): 0为异步,1为同步
	bool open(const char* portname, int baudrate, char parity, char databit, char stopbit, char synchronizeflag=1);
  • 修改部分-发送部分–异步发送一直为失败。

WaitForSingleObject(m_osWrite.hEvent, 1000); 原先为并没有对等待写入事件成功失败处理,主要是成功,成功为发送成功,但返回值还为0.所以在主线程做发送判断时会一直提示失败,此处做修改,实现异步通信

int WzSerialPort::send(const void *buf,int len)
{
	HANDLE hCom = *(HANDLE*)pHandle;

	if (this->synchronizeflag)
	{
		// 同步方式
		DWORD dwBytesWrite = len; //成功写入的数据字节数
		BOOL bWriteStat = WriteFile(hCom, //串口句柄
									buf, //数据首地址
									dwBytesWrite, //要发送的数据字节数
									&dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
									NULL); //NULL为同步发送,OVERLAPPED*为异步发送
		if (!bWriteStat)
		{
			return 0;
		}
		return dwBytesWrite;
	}
	else
	{
		//异步方式
		DWORD dwBytesWrite = len; //成功写入的数据字节数
		DWORD dwErrorFlags; //错误标志
		COMSTAT comStat; //通讯状态
		OVERLAPPED m_osWrite; //异步输入输出结构体

		//创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
		memset(&m_osWrite, 0, sizeof(m_osWrite));
		m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, L"WriteEvent");

		ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
		BOOL bWriteStat = WriteFile(hCom, //串口句柄
			buf, //数据首地址
			dwBytesWrite, //要发送的数据字节数
			&dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
			&m_osWrite); //NULL为同步发送,OVERLAPPED*为异步发送
		if (!bWriteStat)
		{
			if (GetLastError() == ERROR_IO_PENDING) //如果串口正在写入
			{
                //WaitForSingleObject(m_osWrite.hEvent, 1000); 原先为并没有对等待写入事件成功失败处理,主要是成功,成功为发送成功,但返回值还为0.所以在主线程做发送判断时会一直提示失败,此处做修改,实现异步通信
				if (WaitForSingleObject(m_osWrite.hEvent, 1000) == WAIT_OBJECT_0) //等待写入事件1秒钟
				{
					bWriteStat = true;	//写入事件完成,修改写入状态
					dwBytesWrite = len; //修改写入字节长度为实际字节长度
				}
			}
			else
			{
				ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
				CloseHandle(m_osWrite.hEvent); //关闭并释放hEvent内存
				return 0;
			}
		}
		return dwBytesWrite;
	}
}

```

源码

等待上传中,上传成功做修改。