Windows下实现COM口通信c++源代码(验证可行)

Windows下实现COM口通信c++源代码(验证可行)

2023年6月22日发(作者:)

Windows下实现COM⼝通信c++源代码(验证可⾏) 近期接到⼀款新的硬件设备需要实现PC端与安卓终端实现COM⼝通信,以往都是⼚家封装了通信模块或者采⽤Windows的SCARD读卡器设备,调⽤SCardConnectA、SCardTransmit等⽅法来实现通信。 这⼀次⽐较惨,需要写个⽐较原始⼀点的。串⼝的连接和读写参考了如下地址: 本⼈在创建和数据读写上做了些许优化,采⽤新增写线程、读线程、和令牌回令机制来保证⼀应⼀答⽅便上层操作。秉着程序员天下为公,⼀切开源的念想,特将源码贴出望能给需要帮助之⼈提供些许的帮助。-- -- -- -- --

头⽂件 DataTransmit.h#ifndef __DATATRANSMIT__H__#define __DATATRANSMIT__H__#include#include#include/*此⽂件实现com⼝端⼝通信操作作者:王邵华时间:2021/9/17*///int reqid = (fd & 0xFF )<< 24 | (uid & 0x00FFFFFF)//⼝令typedef struct _SShibboleth{ int buffsize; const unsigned char *buff; _SShibboleth(const unsigned char *b,int s){buff=b;buffsize=s;} _SShibboleth(){}}SShibboleth,*pSShibboleth;//回令typedef struct _SBackcross{ long long timestamp; int buffsize; unsigned char* buff; _SBackcross(unsigned char *b,int s,long long t){buff=b;buffsize=s;timestamp=t;} _SBackcross(){} ~_SBackcross() { if(buff) { delete buff; buff = nullptr; } }}SBackcross,*pSBackcross;typedef struct _SComBuffer{ _SShibboleth shibboleth; _SBackcross backcross; int shibbolethRet; int backcrossRet;}SComBuffer,*pSComBuffer;class CCOM{{public: CCOM(int com,UINT baud = 9600, char parity = 'N', UINT databits = 8, UINT stopsbits = 1); virtual ~CCOM(); HANDLE GetComHandle(); /* WriteBuffer: 从PC端写⼊buff的buffsize个数据到终端 params: in: const unsigned char *buff:写⼊数据指针,上传分配空间 int buffsize:写⼊数据的长度 out: int &reqid:此次请求的唯⼀ID,⽤于后续操作的令牌 return: int:成功写⼊的长度,写⼊失败返回负数 */ int WriteBuffer(const unsigned char *buff, int buffsize, int &reqid); /* ReadBufferLen: 获取终端回应该令牌的数据长度 params: in: const int reqid:WriteBuffer调⽤时得到的令牌ID out:

return: int:回令的长度,失败返回-1 */ int ReadBufferLen(const int reqid); /* ReadBuffer: 从终端读取该令牌的回令数据 params: in: const int reqid:WriteBuffer调⽤时得到的令牌ID out: unsigned char *buff:写⼊数据指针,上传分配空间 int buffsize:写⼊数据的长度 return: int:成功写⼊的长度,写⼊失败返回负数 备注: 回令只能被读取⼀次,⾃动失效,超时未回令也会⾃动失效 */ int ReadBuffer(const int reqid, unsigned char *buff, int buffsize); /* SetTimeout: 设置超时时间 params: in: int time:超时时间,单位毫秒.时间不合法[20-300000]20毫秒-5分钟按默认6秒 out:

return: bool:成功true,失败false 备注: 回令只能被读取⼀次,⾃动失效,超时未回令也会⾃动失效 */ bool SetTimeout(int time){ time = time * 1000; if(time>=20 && time <= 300000)timeout = time; else

timeout = 6000; return true; }private: CCOM(const CCOM&){}; CCOM(const CCOM&&){}; CCOM& operator=(const CCOM&){}; CCOM& operator=(const CCOM&&){}; static unsigned int __stdcall ListenReadThread(void* pParam); static unsigned int __stdcall ListenWriteThread(void* pParam); bool WriteComData(int reqid, const unsigned char* pData, unsigned int length); bool ReadComData(int reqid, unsigned char* pData, unsigned int length); void deleteReqidFront(); unsigned int GetBytesInCOM(); bool openPort(UINT portNo); bool InitPort(UINT portNo, const LPDCB& plDCB); /** 初始化串⼝函数 * * @param: UINT portNo 串⼝编号,默认值为1,即COM1,注意,尽量不要⼤于9 * @param: UINT baud 波特率,默认为9600 * @param: char parity 是否进⾏奇偶校验,'Y'表⽰需要奇偶校验,'N'表⽰不需要奇偶校验 * @param: UINT databits 数据位的个数,默认值为8个数据位 * @param: UINT stopsbits 停⽌位使⽤格式,默认值为1 * @param: DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意⼀个字符,则产⽣⼀个事件 * @return: bool 初始化是否成功 * @note: 在使⽤其他本类提供的函数前,请先调⽤本函数进⾏串⼝的初始化 *      /n本函数提供了⼀些常⽤的串⼝参数设置,若需要⾃⾏设置详细的DCB参数,可使⽤重载函数 * /n本串⼝类析构时会⾃动关闭串⼝,⽆需额外执⾏关闭串⼝ * @see: */ bool InitPort(UINT portNo = 1, UINT baud = CBR_9600, char parity = 'N', UINT databits = 8, UINT stopsbits = 1, DWORD dwCommEvents = EV_RXCHAR);private: HANDLE m_hComm; int m_comPort; int m_reqid; /** 线程句柄 */ volatile HANDLE m_hListenReadThread; volatile HANDLE m_hListenWriteThread; unsigned int m_ListenReadThreadId; unsigned int m_ListenWriteThreadId; volatile HANDLE hEventListenReadThread; //开启读数据事件 volatile HANDLE hEventListenWriteThread; //开启写数据事件 volatile HANDLE hEventExit; char szEventListenReadThread[128], szEventListenWriteThread[128], szEventExit[128]; bool isRun; int timeout; /** 同步互斥,临界区保护 */ CRITICAL_SECTION m_csSync; //!< 互斥操作串⼝

std::deque m_Reqid; std::map m_SComBuffer;};class CComHelper{public: HANDLE CreatePort(UINT portNo = 1, UINT baud = CBR_9600, char parity = 'N', UINT databits = 8, UINT stopsbits = 1); bool RemovePort(HANDLE h); int Write(HANDLE h,const unsigned char *buff, int buffsize, int &reqid); int ReadLen(HANDLE h, const int reqid); int Read(HANDLE h, const int reqid, unsigned char *buff, int buffsize); bool SetTimeout(HANDLE h,int time); static CComHelper* getInstance(); virtual ~CComHelper() { m_(m_(),m_()); }private: CComHelper(){}; CComHelper(const CComHelper&){}; CComHelper(const CComHelper&&){}; CComHelper& operator=(const CComHelper&){}; CComHelper& operator=(const CComHelper&&){}; std::map m_mapPortCom; std::map m_mapHandlePort; /* HANDLE h = CreatePort(3); unsigned char writebuff[5] = {0xAA,0AA,0XAA,0X99,0X96}; int reqid = 0; int len = 0; int ret = 0; if(0 < Write(h, writebuff,5,reqid)) { if(0 < (len = ReadBufferLen(h,reqid))) { unsigned char *readbuff = new unsigned char[len]; if(0 < Read(h,reqid,readbuff,len)) { // do your things //... } if(readbuff){delete[] readbuff; readbuff=nullptr;} } } if(INVALID_HANDLE_VALUE != h) { RemovePort(h); } */};#endif源⽂件 #include "DataTransmit.h"#include #include #define ERROR_DEFAULT -1#define ERROR_NOEXECUTE -9999#define ERROR_INVALID_HANDLE_VALUE -9988#define ERROR_READORWRITE -9977#define ERROR_UNCONNECTION -9966#define ERROR_PARAMS -2#define MAX_TIMEOUT 6000CCOM::CCOM(int com, UINT baud, char parity, UINT databits, UINT stopsbits){ m_comPort = com; m_reqid = 0; m_hComm = INVALID_HANDLE_VALUE; m_hComm = INVALID_HANDLE_VALUE; m_hListenReadThread = INVALID_HANDLE_VALUE; m_hListenWriteThread = INVALID_HANDLE_VALUE; hEventListenReadThread = INVALID_HANDLE_VALUE; hEventListenWriteThread = INVALID_HANDLE_VALUE; //开启端⼝通信句柄 if(InitPort(com,baud,parity,databits,stopsbits)) { //创建读写线程 m_hListenReadThread = (HANDLE)_beginthreadex(NULL, 0, ListenReadThread, this, 0, &m_ListenReadThreadId); m_hListenWriteThread = (HANDLE)_beginthreadex(NULL, 0, ListenWriteThread, this, 0, &m_ListenWriteThreadId);

sprintf_s(szEventListenReadThread, "EventListenReadThread_COM%d_%d",com,m_hComm); sprintf_s(szEventListenWriteThread, "EventListenWriteThread_COM%d_%d",com,m_hComm); sprintf_s(szEventExit, "szEventExit_COM%d_%d",com,m_hComm); hEventListenReadThread = ::CreateEventA(NULL,true,false,szEventListenReadThread); hEventListenWriteThread = ::CreateEventA(NULL,true,false,szEventListenWriteThread); hEventExit = ::CreateEventA(NULL,true,false,szEventExit); } ::InitializeCriticalSection(&m_csSync); timeout = MAX_TIMEOUT;}bool CCOM::InitPort(UINT portNo, const LPDCB& plDCB){ /** 打开指定串⼝,该函数内部已经有临界区保护,上⾯请不要加保护 */ if (!openPort(portNo)) { return false; } /** 配置串⼝参数 */ if (!SetCommState(m_hComm, plDCB)) { return false; } /** 清空串⼝缓冲区 */ PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); return true;}bool CCOM::openPort(UINT portNo){ /** 把串⼝的编号转换为设备名 */ char szPort[50]; sprintf_s(szPort, ".COM%d", portNo); /** 打开指定的串⼝ */ m_hComm = CreateFileA(szPort, /** 设备名,COM1,COM2等 */ GENERIC_READ | GENERIC_WRITE, /** 访问模式,可同时读写 */ 0, /** 共享模式,0表⽰不共享 */ NULL, /** 安全性设置,⼀般使⽤NULL */ OPEN_EXISTING, /** 该参数表⽰设备必须存在,否则创建失败 */ 0, 0); /** 如果打开失败,释放资源并返回 */ if (m_hComm == INVALID_HANDLE_VALUE) { return false; } return true;}bool CCOM::InitPort(UINT portNo /*= 1*/, UINT baud /*= CBR_9600*/, char parity /*= 'N'*/, UINT databits /*= 8*/, UINT stopsbits /*= 1*/, DWORD dwCommEvents /*= EV_RXCHAR*/){ /** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */ char szDCBparam[50]; sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits); /** 打开指定串⼝,该函数内部已经有临界区保护,上⾯请不要加保护 */ if (!openPort(portNo)) { return false; } /** 是否有错误发⽣ */ BOOL bIsSuccess = TRUE; /** 在此可以设置输⼊输出的缓冲区⼤⼩,如果不设置,则系统会设置默认值. * ⾃⼰设置缓冲区⼤⼩时,要注意设置稍⼤⼀些,避免缓冲区溢出 */ /*if (bIsSuccess ) { bIsSuccess = SetupComm(m_hComm,10,10); }*/ /** 设置串⼝的超时时间,均设为0,表⽰不使⽤超时限制 */ COMMTIMEOUTS CommTimeouts; tervalTimeout = 0; talTimeoutMultiplier = 0; talTimeoutConstant = 0; otalTimeoutMultiplier = 0; otalTimeoutConstant = 0; if (bIsSuccess) { bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts); } DCB dcb; if (bIsSuccess) { /** 获取当前串⼝配置参数,并且构造串⼝DCB参数 */ bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(szDCBparam, &dcb); /** 开启RTS flow控制 */ ntrol = RTS_CONTROL_ENABLE; } if (bIsSuccess) { /** 使⽤DCB参数配置串⼝状态 */ bIsSuccess = SetCommState(m_hComm, &dcb); } /** 清空串⼝缓冲区 */ PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); return bIsSuccess == TRUE;}#define CLOSEHANDLE(x) if((x) != INVALID_HANDLE_VALUE) { ::CloseHandle((x)); (x) = INVALID_HANDLE_VALUE; }CCOM::~CCOM(){ m_(m_(),m_()); ::SetEvent(hEventExit); ::Sleep(200); ::DeleteCriticalSection(&m_csSync); CLOSEHANDLE(m_hComm) CLOSEHANDLE(m_hListenReadThread) CLOSEHANDLE(m_hListenWriteThread) CLOSEHANDLE(hEventListenReadThread) CLOSEHANDLE(hEventListenWriteThread) CLOSEHANDLE(hEventExit)}HANDLE CCOM::GetComHandle(){ return m_hComm;}unsigned int __stdcall CCOM::ListenReadThread(void* pParam){ CCOM *pSerialPort = reinterpret_cast(pParam); HANDLE handles[2] = {pSerialPort->hEventListenReadThread,pSerialPort->hEventExit}; while(true) { int waitobject = ::WaitForMultipleObjects(2,handles,false,INFINITE); // int errorno = ::GetLastError(); switch (waitobject) { case WAIT_OBJECT_0: //读数据 { auto reqid = pSerialPort->m_(); pSerialPort->deleteReqidFront(); auto &itr = pSerialPort->m_(reqid); if(itr == pSerialPort->m_()) break; long start = ::GetTickCount(); long end = 0; unsigned int size = 0; while(size <= 0) { size = pSerialPort->GetBytesInCOM(); end = ::GetTickCount(); if(end - start > pSerialPort->timeout) break; //超时未应答 } if(size <= 0)

{ pSerialPort->m_SComBuffer[reqid].backcrossRet = ERROR_UNCONNECTION; break; } pSerialPort->m_SComBuffer[reqid]. = new unsigned char[size]; memset(pSerialPort->m_SComBuffer[reqid].,0x00,size); pSerialPort->m_SComBuffer[reqid].ze = size; pSerialPort->m_SComBuffer[reqid].amp = ::GetTickCount(); pSerialPort->ReadComData(reqid,pSerialPort->m_SComBuffer[reqid].,pSerialPort->m_SComBuffer[reqid].ze); break; } case WAIT_OBJECT_0 + 1: //结束 { return 0; break; } default: break;

} ::ResetEvent(pSerialPort->hEventListenReadThread); } return 0;}unsigned int __stdcall CCOM::ListenWriteThread(void* pParam){ CCOM *pSerialPort = reinterpret_cast(pParam); HANDLE handles[2] = {pSerialPort->hEventListenWriteThread,pSerialPort->hEventExit}; while(true) { int waitobject = ::WaitForMultipleObjects(2,handles,false,INFINITE); // int errorno = ::GetLastError(); switch (waitobject) { case WAIT_OBJECT_0: //写数据 { auto reqid = pSerialPort->m_(); // auto itr = pSerialPort->m_(reqid); if(itr != pSerialPort->m_()) { if(pSerialPort->WriteComData(reqid, itr->,itr->ze)) { ::SetEvent(pSerialPort->hEventListenReadThread); //触发令牌获取回令 } } // break; } case WAIT_OBJECT_0 + 1: //结束 { return 0; break; } default: break;

} ::ResetEvent(pSerialPort->hEventListenWriteThread); } return 0;}int CCOM::WriteBuffer(const unsigned char *buff, int buffsize, int &reqid){ //m_Shibboleth if(nullptr == buff || 0 >= buffsize) return ERROR_PARAMS; ::EnterCriticalSection(&m_csSync); reqid = (m_comPort & 0xFF )<< 24 | (++m_reqid & 0x00FFFFFF); m__back(reqid); SComBuffer combuffer; = buff; ze = buffsize; lethRet = ERROR_NOEXECUTE; ossRet = ERROR_NOEXECUTE; = nullptr; ze = 0; amp = 0; m_(std::make_pair(reqid,combuffer)); ::LeaveCriticalSection(&m_csSync); ::SetEvent(hEventListenWriteThread);//触发写

int ret = ERROR_DEFAULT; auto itr = m_(reqid); if(itr != m_()) { long start = ::GetTickCount(); long end = 0; while(true){ //等待发送完成通知 ret = m_SComBuffer[reqid].shibbolethRet; if(ERROR_NOEXECUTE != ret) return ret; //未知值 ::Sleep(200); end = ::GetTickCount(); if(end - start > timeout) break; //6秒超时 } } return ret;}bool CCOM::WriteComData(int reqid, const unsigned char* pData, unsigned int length){ bool bResult = TRUE; DWORD BytesToSend = 0; auto &itr = m_(reqid); if(itr == m_()) { return false; } if (m_hComm == INVALID_HANDLE_VALUE) { itr->lethRet = ERROR_INVALID_HANDLE_VALUE; return false; } printf("object[%X] reqid[%X] WRITEn",this,reqid); for(int i=0;i timeout) break; //6秒超时 } return ret;}int CCOM::ReadBuffer(const int reqid, unsigned char *buff, int buffsize){ auto itr = m_(reqid); if(itr == m_()) { return ERROR_PARAMS; } if(buffsize < itr->ze) return ERROR_PARAMS; int ret = ERROR_DEFAULT; memcpy(buff,itr->,itr->ze); ret = itr->ze; m_(itr);//⼝令 回令 销毁 return ret;}CComHelper* CComHelper::getInstance(){ static CComHelper comHelper; return &comHelper;}HANDLE CComHelper::CreatePort(UINT portNo, UINT baud, char parity, UINT databits, UINT stopsbits){ if(0 < m_(portNo)) return m_mapPortCom[portNo]->GetComHandle(); CCOM* com = new CCOM(portNo,baud,parity,databits,stopsbits); m_mapPortCom[portNo] = com; m_mapHandlePort[com->GetComHandle()] = portNo; return com->GetComHandle();}bool CComHelper::RemovePort(HANDLE h){ if(0 >= m_(h)) return false; auto itr = m_(m_mapHandlePort[h]); if(m_() != itr) m_(itr); return true;}int CComHelper::Write(HANDLE h,const unsigned char *buff, int buffsize, int &reqid){ if(0 >= m_(h)) if(0 >= m_(h)) return -1; auto itr = m_(m_mapHandlePort[h]); if(m_() != itr) return itr->second->WriteBuffer(buff,buffsize,reqid); return -1;}int CComHelper::ReadLen(HANDLE h,const int reqid){ if(0 >= m_(h)) return -1; auto itr = m_(m_mapHandlePort[h]); if(m_() != itr) return itr->second->ReadBufferLen(reqid); return -1;}int CComHelper::Read(HANDLE h,const int reqid, unsigned char *buff, int buffsize){ if(0 >= m_(h)) return -1; auto itr = m_(m_mapHandlePort[h]); if(m_() != itr) return itr->second->ReadBuffer(reqid,buff,buffsize); return -1;}bool CComHelper::SetTimeout(HANDLE h,int time){ if(0 >= m_(h)) return false; auto itr = m_(m_mapHandlePort[h]); if(m_() != itr) return itr->second->SetTimeout(time); return false;}使⽤案例:获取端⼝数据长度和获取端⼝数据2个接⼝,内部实现了超时机制,上传只需阻塞式调⽤。 啥也不说了,⼀切都在代码⾥。如果有⽤记得⼀键三连。笔芯

发布者:admin,转转请注明出处:http://www.yc00.com/news/1687429868a9400.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信