文章目录
- Win10 串口通信 —— 同步/异步
- 简介
- 实现
- 1.主函数
- 2.串口模块
- 源码
Win10 串口通信 —— 同步/异步
简介
之前接到的一个小项目,好像不能算。win10下的串口通信,不需要界面,排除了Qt,MFC只剩C 底层了,调用WindowsApi来实现。翻了翻网上资料大致写出来了。
- CSDN地址:通信相关
- Git地址:https://github.com/ayowin/WZSerialPort
- 串口调试工具:https://gitee.com/fengmeitech/Micro-Lab
- 虚拟串口工具:https://blog.csdn.net/qq_34202873/article/details/88391265
系统环境: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);
代码语言:javascript复制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);
}
}
代码语言:javascript复制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;
代码语言:javascript复制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;
}
代码语言:javascript复制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;
}
}
```
源码
等待上传中,上传成功做修改。