文章目录
- 串口参数结构体
- 全局变量
- 打开串口
- 检查超时函数
- 重置超时计时函数
- 发送指令并接收返回值
- 关闭串口连接
串口参数结构体
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;}