2023年6月22日发(作者:)
Windows下实现COM⼝通信c++源代码(验证可⾏) 近期接到⼀款新的硬件设备需要实现PC端与安卓终端实现COM⼝通信,以往都是⼚家封装了通信模块或者采⽤Windows的SCARD读卡器设备,调⽤SCardConnectA、SCardTransmit等⽅法来实现通信。 这⼀次⽐较惨,需要写个⽐较原始⼀点的。串⼝的连接和读写参考了如下地址: 本⼈在创建和数据读写上做了些许优化,采⽤新增写线程、读线程、和令牌回令机制来保证⼀应⼀答⽅便上层操作。秉着程序员天下为公,⼀切开源的念想,特将源码贴出望能给需要帮助之⼈提供些许的帮助。-- -- -- -- --
头⽂件 DataTransmit.h#ifndef __DATATRANSMIT__H__#define __DATATRANSMIT__H__#include
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
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
{ 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
} ::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
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
发布者:admin,转转请注明出处:http://www.yc00.com/news/1687429868a9400.html
评论列表(0条)