news 2026/6/11 10:13:14

C++RS232串口通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++RS232串口通信

文章目录

    • 串口参数结构体
    • 全局变量
    • 打开串口
    • 检查超时函数
    • 重置超时计时函数
    • 发送指令并接收返回值
    • 关闭串口连接

串口参数结构体

structSP4A_COM_SETUP_STRUCT{intiCom;intiBaud;intiParity;intiDataBit;intiStopBit;int64_tlTimeout;//msint64_tlIntervalTimems=50;//msJSON_HELPER(iCom,iBaud,iParity,iDataBit,iStopBit,lTimeout,lIntervalTimems)};

全局变量

HANDLE m_hComm=NULL;staticstd::chrono::steady_clock::time_point g_threadStartTime;constexprintMAX_COM_NUM=256;constexprintDEFAULT_READ_TIMEOUT_MULTI=2;constexprintDEFAULT_READ_INTERVAL_TIMEOUT=10;constexprsize_t COM_NAME_BUF_SIZE=16;

打开串口

intopenPort(constSP4A_COM_SETUP_STRUCT&stPortParam){if(stPortParam.iCom<1||stPortParam.iCom>MAX_COM_NUM){printf("[openPort] invalid com\n");return-1;}if(stPortParam.iBaud<=0||stPortParam.iDataBit<5||stPortParam.iDataBit>8){printf("[openPort] invalid parameter\n");}closePort();charszCom[COM_NAME_BUF_SIZE]={0};if(stPortParam.iCom>9){sprintf_s(szCom,COM_NAME_BUF_SIZE,"\\\\.\\COM%d",stPortParam.iCom);}else{sprintf_s(szCom,COM_NAME_BUF_SIZE,"COM%d",stPortParam.iCom);}std::string comName{szCom};//打开串口m_hComm=CreateFileA(szCom,GENERIC_READ|GENERIC_WRITE,0,//独占访问NULL,//安全属性OPEN_EXISTING,//仅打开已存在的串口0,//同步IONULL);//无模板文件if(m_hComm==INVALID_HANDLE_VALUE){printf("[openPort] CreateFileA failed, 0x%x\n",GetLastError());closePort();return-1;}//配置串口参数DCB dcb={0};dcb.DCBlength=sizeof(DCB);if(!GetCommState(m_hComm,&dcb)){printf("[openPort] GetComState failed, 0x%x\n",GetLastError());closePort();return-1;}//更新串口参数dcb.BaudRate=stPortParam.iBaud;dcb.StopBits=stPortParam.iStopBit;dcb.ByteSize=stPortParam.iDataBit;dcb.Parity=stPortParam.iParity;dcb.fParity=(stPortParam.iParity!=NOPARITY);//显式设置校验位使能if(!SetCommState(m_hComm,&dcb)){printf("[openPort] SetCommState failed, 0x%x\n",GetLastError());closePort();return-1;}//配置超时参数COMMTIMEOUTS timeout{0};if(!GetCommTimeouts(m_hComm,&timeout)){printf("[openPort] GetCommTimeouts failed, 0x%x\n",GetLastError());closePort();return-1;}timeout.WriteTotalTimeoutConstant=stPortParam.lIntervalTimems;//写超时常量timeout.ReadTotalTimeoutMultiplier=DEFAULT_READ_TIMEOUT_MULTI;//读超时乘数timeout.ReadIntervalTimeout=DEFAULT_READ_INTERVAL_TIMEOUT;//读间隔超时if(!SetCommTimeouts(m_hComm,&timeout)){printf("[openPort] SetCommTimeouts failed, 0x%x\n",GetLastError());closePort();return-1;}//清空串口缓冲区if(!PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR)){printf("[openPort] PurgeComm failed, 0x%x\n",GetLastError());closePort();return-1;}printf("[SerialPort] COM%d opened successfully\n",stPortParam.iCom);return0;}

检查超时函数

/** *@brief *@param *@return */boolcheckThreadTimeout(inttimeoutMs){autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now()-g_threadStartTime);returnduration.count()>timeoutMs;}

重置超时计时函数

/** *@brief 重置超时计时 */voidresetThreadTimeout(){g_threadStartTime=std::chrono::steady_clock::now();}

发送指令并接收返回值

/** * @brief 串口发送指令并接收返回数据(完善版) * @param stCommand 串口指令结构体(包含指令、结束符、读取配置等) * @param sRet 输出参数,接收串口返回的数据 * @param timeoutMs 超时时间(默认3000ms) * @return int 0=成功,-1=失败 */intsendCmd(SP4A_STRING_COMMAND_RS232_STRUCT&stCommand,std::string&sRet,inttimeoutMs){if(m_hComm==NULL||m_hComm==INVALID_HANDLE_VALUE){printf("[sendCmd] error: invalid comm handle\n");return-1;}if(stCommand.sCmdData.empty()){printf("[sendCmd] error: empty command\n");return-1;}sRet.clear();autocommFunc=[&]()->int{intrc=0;std::string sFullCmd=stCommand.sCmdData+stCommand.sEndByte;constBYTE*cmdBuffer=reinterpret_cast<constBYTE*>(sFullCmd.c_str());intcmdLen=static_cast<int>(sFullCmd.size());try{//清理串口缓冲区if(!PurgeComm(m_hComm,PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR)){printf("[sendCmd] PurgeComm error, 0x%x\n",GetLastError());return-1;}//发送指令DWORD bytesSent=0;BOOL bWrite=WriteFile(m_hComm,//串口句柄cmdBuffer,//发送缓冲区cmdLen,//发送长度&bytesSent,//实际发送字节数NULL//非重叠IO);if(!bWrite||bytesSent!=cmdLen){printf("[sendCmd] failed, system code: 0x%x, send length: %d, actual length: %d\n",GetLastError(),cmdLen,bytesSent);return-1;}printf("[sendCmd] successful: %s\n",stCommand.sCmdData.c_str());//接收返回数据std::string sRetData;BYTE recvBuffer[1024]={0};DWORD bytesRead=0;boolbFinish=false;resetThreadTimeout();//重置超时计时while(!bFinish&&!checkThreadTimeout(timeoutMs)){//读取串口数据(每次最多1024字节)BOOL bRead=ReadFile(m_hComm,recvBuffer,sizeof(recvBuffer)-1,//预留终止符位置&bytesRead,NULL);if(!bRead){DWORD err=GetLastError();//无数据时继续等待(ERROR_IO_PENDING是正常等待,非错误)if(err!=ERROR_IO_PENDING&&err!=ERROR_TIMEOUT){printf("[sendCmd] error, 0x%x\n",err);rc=-1;break;}std::this_thread::sleep_for(std::chrono::milliseconds(10));//短暂延时continue;}if(bytesRead>0){sRetData.append(reinterpret_cast<char*>(recvBuffer),bytesRead);memset(recvBuffer,0,sizeof(recvBuffer));//清空接收缓冲区,用0填充if(!stCommand.sEndByte.empty())//检测结束符{size_t endPos=sRetData.rfind(stCommand.sEndByte);if(endPos!=string::npos&&endPos==sRetData.size()-stCommand.sEndByte.size()){bFinish=true;}}if(!bFinish&&!stCommand.listEndStateKey.empty())//检测结束关键字{for(constauto&key:stCommand.listEndStateKey){if(sRetData.find(key)!=string::npos){bFinish=true;break;}}}//达到期望读取长度if(!bFinish&&stCommand.iReadSize>0&&sRetData.size()>=stCommand.iReadSize){bFinish=true;}}else{std::this_thread::sleep_for(std::chrono::milliseconds(10));}}if(checkThreadTimeout(timeoutMs)){printf("[sendCmd] receive timeout, %s\n",sRetData.c_str());rc=-1;}elseif(rc==0){sRet=sRetData;printf("[sendCmd] receive successful, %s, length:%zd",sRetData.c_str(),sRetData.size());}}catch(conststd::exception&e){printf("[sendCmd] errror, %s\n",e.what());rc=-1;}catch(...){printf("[sendCmd] unknown error\n");rc=-1;}returnrc;};//异步执行//std::thread t(commFunc);//t.join();//同步执行intresult=commFunc();if(result!=0){PurgeComm(m_hComm,PURGE_RXCLEAR);}returnresult;}

关闭串口连接

intclosePort(){intrc=0;if(m_hComm!=NULL&&m_hComm!=INVALID_HANDLE_VALUE){BOOL bResult=CloseHandle(m_hComm);if(bResult!=TRUE){printf("[closePort] error, 0x%x\n",GetLastError());rc=-1;}else{m_hComm=NULL;}}returnrc;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 10:12:17

2026年腾讯云OpenClaw/Hermes Agent配置Token Plan部署入门指南

2026年腾讯云OpenClaw/Hermes Agent配置Token Plan部署入门指南。OpenClaw是开源的个人AI助手&#xff0c;Hermes Agent则是一个能自我进化的AI智能体框架。阿里云提供计算巢、轻量服务器及无影云电脑三种部署OpenClaw 与 Hermes Agent的方案、百炼Token Plan兼容主流 AI 工具&…

作者头像 李华
网站建设 2026/6/11 10:11:30

鸿蒙新特性——Gauge 仪表盘深度解析

一、引言 仪表盘&#xff08;Gauge&#xff09;是人类最古老的量化展示方式之一。从汽车仪表盘上的速度表、医疗设备上的血压计&#xff0c;到工业控制面板上的温度表——环形刻度盘以"指针刻度颜色分区"的组合&#xff0c;在极小的空间内同时传递了"当前值是多…

作者头像 李华
网站建设 2026/6/11 10:11:21

微信小程序计算机毕设之基于android studio 安卓的汽车APP基于android汽车服务企业客户评价APP(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/6/11 10:11:11

用C++玩转自幂数:从GESP二级真题到趣味数学探索(附完整代码)

用C玩转自幂数&#xff1a;从GESP二级真题到趣味数学探索数学和编程从来都不是割裂的。当你在准备CCF-GESP C二级考试时&#xff0c;可能会遇到一些看似枯燥的题目&#xff0c;但如果换个角度思考&#xff0c;它们其实隐藏着令人着迷的数学奥秘。自幂数就是这样一个完美的例子—…

作者头像 李华
网站建设 2026/6/11 10:10:51

虚环恬豆怎么玩 虚环恬豆角色玩法介绍

虚环恬豆兼顾输出与治疗&#xff0c;还能克制闪避体系&#xff0c;是开荒与进阶都绕不开的核心角色&#xff0c;用好她能大幅降低推图与对战难度。吃透虚环恬豆的技能机制与搭配思路&#xff0c;才能让她在队伍里发挥最大作用&#xff0c;稳定撑起团队续航与节奏。角色定位恬豆…

作者头像 李华
网站建设 2026/6/11 10:09:20

从零构建MMRotate旋转检测实战:自定义数据集标注、训练与调优全解析

1. 为什么需要旋转目标检测&#xff1f; 在传统的目标检测任务中&#xff0c;我们通常使用水平矩形框来标注物体。但在遥感图像、自动驾驶、文档分析等场景下&#xff0c;物体往往呈现各种角度的旋转。比如卫星图像中的飞机、停车场里的车辆、扫描文档中的文字等&#xff0c;如…

作者头像 李华