#include "stdafx.h" #include "Serial.h" DCB *CBaseSerial::GetState() { return IsOpen() && ::GetCommState(_hCommHandle, &_DCB) == TRUE ? &_DCB : NULL; } bool CBaseSerial::SetState(DCB *pdcb /*= NULL*/) { return IsOpen() ? ::SetCommState(_hCommHandle, pdcb == NULL ? &_DCB : pdcb) == TRUE : false; } bool CBaseSerial::SetState(TCHAR *lpszSetStr /* = _T("baud=115200 parity=N data=8 stop=1") */) { if (lpszSetStr && lpszSetStr[0] != '\0' && IsOpen()) { if (::GetCommState(_hCommHandle, &_DCB) != TRUE) return false; // COMx[:][baud=b][parity=p][data=d][stop=s][to={on|off}][xon={on|off}][odsr={on|off}][octs={on|off}][dtr={on|off|hs}][rts={on|off|hs|tg}][idsr={on|off}] if (::BuildCommDCB(lpszSetStr, &_DCB) != TRUE) return false; return ::SetCommState(_hCommHandle, &_DCB) == TRUE; } return false; } bool CBaseSerial::SetState(DWORD dwBaudRate, DWORD dwByteSize /* = 8 */, DWORD dwParity /* = NOPARITY */, DWORD dwStopBits /* = ONESTOPBIT */) { if (!IsOpen()) return false; if (::GetCommState(_hCommHandle, &_DCB) != TRUE) return false; _DCB.BaudRate = dwBaudRate; _DCB.ByteSize = (BYTE)dwByteSize; _DCB.Parity = (BYTE)dwParity; _DCB.StopBits = (BYTE)dwStopBits; return ::SetCommState(_hCommHandle, &_DCB) == TRUE; } LPCOMMTIMEOUTS CBaseSerial::GetTimeouts() { return IsOpen() && ::GetCommTimeouts(_hCommHandle, &_CO) == TRUE ? &_CO : NULL; } bool CBaseSerial::SetTimeouts(DWORD ReadIntervalTimeout /* = 0 */, DWORD ReadTotalTimeoutMultiplier /* = 10 */, DWORD ReadTotalTimeoutConstant /* = 1500 */, DWORD WriteTotalTimeoutMultiplier /* = 10 */, DWORD WriteTotalTimeoutConstant /* = 1500 */) { COMMTIMEOUTS CO = { ReadIntervalTimeout, ReadTotalTimeoutMultiplier, ReadTotalTimeoutConstant, WriteTotalTimeoutMultiplier, WriteTotalTimeoutConstant }; return IsOpen() ? ::SetCommTimeouts(_hCommHandle, &CO) == TRUE : false; } bool CBaseSerial::SetTimeouts(LPCOMMTIMEOUTS lpCO) { return IsOpen() ? ::SetCommTimeouts(_hCommHandle, lpCO) == TRUE : false; } bool CBaseSerial::SetBufferSize(DWORD dwInputSize, DWORD dwOutputSize) { // SetupComm设置输入输出缓冲区大小; return IsOpen() ? ::SetupComm(_hCommHandle, dwInputSize, dwOutputSize) == TRUE : false; } void CBaseSerial::SetMaskEvent(DWORD dwEvent /* = DEFAULT_COM_MASK_EVENT */) { _dwMaskEvent = dwEvent; } int CBaseSerial::GetInputSize() { COMSTAT Stat; DWORD dwError; return ::ClearCommError(_hCommHandle, &dwError, &Stat) == TRUE ? Stat.cbInQue : (DWORD)-1L; } bool CBaseSerial::Open(DWORD dwPort) { return Open(dwPort, 19200); } bool CBaseSerial::Open(DWORD dwPort, DWORD dwBaudRate) { if (dwPort < 1 || dwPort > 1024) return false; BindCommPort(dwPort); if (!OpenCommPort()) return false; if (!SetupPort()) return false; return SetState(dwBaudRate); } bool CBaseSerial::Open(DWORD dwPort, TCHAR *lpszSetStr /* = _T("baud/* =115200 parity/* =N data/* =8 stop/* =1") */ ) { if (dwPort < 1 || dwPort > 1024) return false; BindCommPort(dwPort); if (!OpenCommPort()) return false; if (!SetupPort()) return false; return SetState(lpszSetStr); } DWORD CBaseSerial::Read(LPVOID Buffer, DWORD dwBufferLength, DWORD dwWaitTime /* = 100 */) { if (!IsOpen()) return 0; COMSTAT Stat; DWORD dwError; if (::ClearCommError(_hCommHandle, &dwError, &Stat) && dwError > 0) { ::PurgeComm(_hCommHandle, PURGE_RXABORT | PURGE_RXCLEAR); // 清空输入缓冲区(PURGE_RXCLEAR) return 0; } #if 0 // 暂时保留:要视机器情况而定,有些响应慢缓冲区会暂时性无数据; if (!Stat.cbInQue) return 0; // 缓冲区无数据 #endif utils::_dprintf("缓冲区数据量:%ld", Stat.cbInQue); unsigned long uReadLength = 0; // dwBufferLength = dwBufferLength > Stat.cbInQue ? Stat.cbInQue : dwBufferLength;//会越界; if (!::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength, &_ReadOverlapped)) { // 可能Buffer不够大,装不完; WaitForSingleObject(_ReadOverlapped.hEvent, dwWaitTime); // 等待异步操作完成; if (::GetLastError() == ERROR_IO_PENDING) { while (!::GetOverlappedResult(_hCommHandle, &_ReadOverlapped, &uReadLength, false)) { dwError = ::GetLastError(); if ( dwError == ERROR_IO_PENDING ) { Sleep(50); utils::_dprintf("读等待:%ld", dwError); } else if ( dwError == ERROR_SUCCESS || dwError == ERROR_IO_INCOMPLETE ) { utils::_dprintf("读完成:%ld,%ld,%ld", dwError, uReadLength,_ReadOverlapped.InternalHigh); // 奇葩:_ReadOverlapped并不能马上出长度,在超时后才会刷新出来(不知是否与Win平台有关); if ( _ReadOverlapped.InternalHigh ) uReadLength = _ReadOverlapped.InternalHigh; else uReadLength = Stat.cbInQue; break; } else { utils::_dprintf("读错误:%ld", dwError); uReadLength = 0; break; } } } } return uReadLength; } char* CBaseSerial::ReadString(char *szBuffer, DWORD dwBufferLength, DWORD dwWaitTime /* = 20 */) { unsigned long uReadLength = Read(szBuffer, dwBufferLength - 1, dwWaitTime); szBuffer[uReadLength] = '\0'; return szBuffer; } DWORD CBaseSerial::Write(LPVOID Buffer, DWORD dwBufferLength) { if (!IsOpen()) return 0; DWORD dwError; if (::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0) ::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR); // 清空输出缓冲区(PURGE_TXCLEAR) unsigned long uWriteLength = 0; if (!::WriteFile(_hCommHandle, Buffer, dwBufferLength, &uWriteLength, &_WriteOverlapped)) { if (ERROR_IO_PENDING == GetLastError()) { while (!GetOverlappedResult(_hCommHandle, &_WriteOverlapped, &uWriteLength, FALSE)) { dwError = GetLastError(); if (ERROR_IO_PENDING == dwError) { utils::_dprintf("写等待"); continue; } else if (dwError == ERROR_SUCCESS || dwError == ERROR_IO_INCOMPLETE){ uWriteLength = _WriteOverlapped.InternalHigh; utils::_dprintf("写完成:%ld", dwError); break; } else { utils::_dprintf("写出错:%ld",dwError); uWriteLength = 0; ClearCommError(_hCommHandle, &dwError, NULL); break; } } } } return uWriteLength; } DWORD CBaseSerial::Write(const TCHAR *szBuffer) { assert(szBuffer); return Write((void *)szBuffer, _tclen(szBuffer)); } DWORD CBaseSerial::ReadSync(LPVOID Buffer, DWORD dwBufferLength) { if (!IsOpen()) return 0; COMSTAT Stat; DWORD dwError; DWORD dwLastLen = 0; ULONGLONG ulTick = GetTickCount(); // 直到有数据为止,超时3秒; while (true) {// cbInQue表示输入缓冲区的字节数; if (GetTickCount() - ulTick > 3000) { utils::_dprintf("读出错: 超过3秒仍未读完"); break; } if ( ::ClearCommError(_hCommHandle, &dwError, &Stat) && dwError > 0 ) { ::PurgeComm(_hCommHandle, PURGE_RXABORT | PURGE_RXCLEAR); // 清空输入缓冲区(PURGE_RXCLEAR) utils::_dprintf("读出错: ClearCommError出错"); break; } // 防止读一次没读完,再读一次;二次长度一样表示读完; if (Stat.cbInQue != 0 && dwLastLen == Stat.cbInQue) break; dwLastLen = Stat.cbInQue; Sleep(100); } if (Stat.cbInQue == 0) { // 串口超时,无数据返回; return 0; } if (dwBufferLength < Stat.cbInQue) { utils::_dprintf("读出错: 缓冲数据过大 %ld > %ld", dwBufferLength, Stat.cbInQue); } DWORD uReadLength = 0; if ( !::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength, NULL) ) { DWORD dwError = GetLastError(); utils::_dprintf("读出错:%ld", dwError); } return uReadLength; } DWORD CBaseSerial::WriteSync(LPVOID Buffer, DWORD dwBufferLength) { if (!IsOpen()) return 0; DWORD dwError; if (::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0) ::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR); // 清空输出缓冲区(PURGE_TXCLEAR) unsigned long uWriteLength = 0; if ( !::WriteFile(_hCommHandle, Buffer, dwBufferLength, &uWriteLength, NULL) ) { DWORD dwError = GetLastError(); utils::_dprintf("写出错:%ld", dwError); } return uWriteLength; } DWORD CBaseSerial::Write(TCHAR *szBuffer, DWORD dwBufferLength, TCHAR *szFormat, ...) { if (!IsOpen()) return 0; va_list va; va_start(va, szFormat); #if _MSC_VER < 1500 _vsnprintf(szBuffer, dwBufferLength, szFormat, va); #elif _MSC_VER >=1500 // VC9.0以上版本; //_vsntprintf(szBuffer, dwBufferLength, szFormat, va); _vsntprintf_s(szBuffer, dwBufferLength, _TRUNCATE, szFormat, va); #endif va_end(va); return Write(szBuffer); } DWORD CBaseSerial::Write(TCHAR *szBuffer, TCHAR *szFormat, ...) { if (!IsOpen()) return 0; va_list va; va_start(va, szFormat); #if _MSC_VER < 1500 vsprintf(szBuffer, szFormat, va); #elif _MSC_VER >=1500 // VC9.0以上版本; _vstprintf(szBuffer, szFormat, va); #endif va_end(va); return Write(szBuffer); } void CBaseSerial::Close() { if (!IsOpen()) return; PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR); // 清空输出缓冲区(PURGE_TXCLEAR) ::CloseHandle(_hCommHandle); _hCommHandle = INVALID_HANDLE_VALUE; } bool CBaseSerial::SetDTR(bool OnOrOff) { return IsOpen() ? EscapeCommFunction(_hCommHandle, OnOrOff ? SETDTR : CLRDTR) : false; } bool CBaseSerial::SetRTS(bool OnOrOff) { return IsOpen() ? EscapeCommFunction(_hCommHandle, OnOrOff ? SETRTS : CLRRTS) : false; } bool CBaseSerial::SetBreak(bool OnOrOff) { return IsOpen() ? EscapeCommFunction(_hCommHandle, OnOrOff ? SETBREAK : CLRBREAK) : false; } void CBaseSerial::Init() { memset(_szCommStr, 0, 20*sizeof(TCHAR)); memset(&_DCB, 0, sizeof(_DCB)); _DCB.DCBlength = sizeof(_DCB); _hCommHandle = INVALID_HANDLE_VALUE; memset(&_ReadOverlapped, 0, sizeof(_ReadOverlapped)); memset(&_WriteOverlapped, 0, sizeof(_WriteOverlapped)); _ReadOverlapped.hEvent = ::CreateEvent(NULL, true, false, NULL); assert(_ReadOverlapped.hEvent != INVALID_HANDLE_VALUE); _WriteOverlapped.hEvent = ::CreateEvent(NULL, true, false, NULL); assert(_WriteOverlapped.hEvent != INVALID_HANDLE_VALUE); _hNotifyWnd = NULL; _dwNotifyNum = 0; _dwMaskEvent = DEFAULT_COM_MASK_EVENT; memset(&_WaitOverlapped, 0, sizeof(_WaitOverlapped)); _WaitOverlapped.hEvent = ::CreateEvent(NULL, true, false, NULL); assert(_WaitOverlapped.hEvent != INVALID_HANDLE_VALUE); } void CBaseSerial::UnInit() { if (_ReadOverlapped.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_ReadOverlapped.hEvent); if (_WriteOverlapped.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_WriteOverlapped.hEvent); if (_WaitOverlapped.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_WaitOverlapped.hEvent); } void CBaseSerial::BindCommPort(DWORD dwPort) { assert(dwPort >= 1 && dwPort <= 1024); TCHAR p[5] = {0}; _dwPort = dwPort; #if _MSC_VER >= 1200 && _MSC_VER < 1500 strcpy(_szCommStr, _T("\\\\.\\COM")); ltoa(_dwPort, p, 10); strcat(_szCommStr, p); #elif _MSC_VER >= 1500 _tcscpy_s(_szCommStr, _T("\\\\.\\COM")); _ltot_s(_dwPort, p, 10); _tcscat_s(_szCommStr, p); #endif } bool CBaseSerial::OpenCommPort() { if (IsOpen()) Close(); _hCommHandle = ::CreateFile(_szCommStr, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | _dwIOMode, NULL); return IsOpen(); } bool CBaseSerial::SetupPort() { if (!IsOpen()) return false; // SetupComm设置输入输出缓冲区大小; if (!::SetupComm(_hCommHandle, 8192, 8192)) return false; if (!::GetCommTimeouts(_hCommHandle, &_CO)) return false; _CO.ReadIntervalTimeout = 0; _CO.ReadTotalTimeoutMultiplier = 10; _CO.ReadTotalTimeoutConstant = 1500; _CO.WriteTotalTimeoutMultiplier = 10; _CO.WriteTotalTimeoutConstant = 1500; if (!::SetCommTimeouts(_hCommHandle, &_CO)) return false; // 清空输入输出缓冲区; if (!::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) return false; return true; }