/* 串口基础类库(WIN32) ver 0.3
编译器 : BC++ 5; C++ BUILDER 4, 5, 6, X; VC++ 5, 6; VC.NET; GCC;
使用:
定义 cnComm MyComm1(false, 0);//第1个参数为是否启动监视线程, 第2个参数为阻塞(0)/异步方式(默认) cnComm MyComm2;//默认启动监视线程, 异步 cnComm MyComm3(true);//监视线程, 阻塞
打开 MyComm1.Open(1);//1-1024 2k支持1024串口 MyComm1.Open(1, 1200); MyComm1.Open(1, 9600); MyComm1.Open(1, "9600,8,n,1");//可使用标准的设置字符串 MyComm1.SetBufferSize(1200, 4800)//设置缓冲区大小,可不用设置 MyComm1.SetState("9600,8,n,1")//修改波特率等
读写
MyComm1.Read(buf, 1000); MyComm1.Write(buf, 200);
窗口句柄 MyComm1.SetWnd(hWnd);
线程控制下面都有注明就不说了 相应的处理都有对应的处理函数见ThreadFunc; 处理函数均为虚函数,为的是继承扩展;
引脚号 作用 方向 1 DCD 数据载波检测 输入 2 RXD 接收数据 输入 3 TXD 发送数据 输出 4 DTR 数据终端准备就绪 输出 5 SG 信号地 6 DSR 数据设备准备就绪 输入 7 RTS 请求发送 输出 8 CTS 清除发送 输入 9 RI 振铃指示 输入
copyright(c) 2004.8 llbird [email protected] */
#ifndef _CN_COMM_H_ #define _CN_COMM_H_
#pragma warning(disable: 4530) #pragma warning(disable: 4786) #pragma warning(disable: 4800)
#include <assert.h> #include <stdio.h> #include <windows.h>
//当接受到数据送到窗口的消息 WPARAM 均为串口的端口号 #define ON_COM_RECEIVE WM_USER + 618 #define ON_COM_CTS WM_USER + 619 //LPARAM 1 为 有效, 0 为无效 #define ON_COM_DSR WM_USER + 621 //LPARAM 1 为 有效, 0 为无效 #define ON_COM_RING WM_USER + 623 #define ON_COM_RLSD WM_USER + 624 #define ON_COM_BREAK WM_USER + 625 #define ON_COM_TXEMPTY WM_USER + 626
class cnComm { public: cnComm(bool fAutoBeginThread = true, DWORD dwIOMode = FILE_FLAG_OVERLAPPED) : _dwIOMode(dwIOMode), _fAutoBeginThread(fAutoBeginThread) { Init(); } virtual ~cnComm() { Close(); UnInit(); } //打开串口 缺省 9600, 8, n, 1 inline bool Open(DWORD dwPort) { return Open(dwPort, 9600); } //打开串口 缺省 baud_rate, 8, n, 1 inline bool 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); } //打开串口, 使用类似"9600, 8, n, 1"的设置字符串设置串口 inline bool Open(DWORD dwPort, char *szSetStr) { if(dwPort < 1 || dwPort > 1024) return false;
BindCommPort(dwPort);
if(!OpenCommPort()) return false;
if(!SetupPort()) return false;
return SetState(szSetStr); } //判断串口是或打开 inline bool IsOpen() { return _hCommHandle != INVALID_HANDLE_VALUE; }
/*--------------------------------------------------------------------------------------------------*/
//获得串口句炳 inline HANDLE GetHandle() { return _hCommHandle; } //获得串口句炳 operator HANDLE() { return _hCommHandle; } //设置串口参数:波特率,停止位,等 支持设置字符串 "9600, 8, n, 1" bool SetState(char *szSetStr) { if(IsOpen()) { if(!::GetCommState(_hCommHandle, &_DCB)) return false; if(!::BuildCommDCB(szSetStr, &_DCB)) return false; return ::SetCommState(_hCommHandle, &_DCB) == TRUE; } return false; } //设置串口参数:波特率,停止位 bool SetState(DWORD dwBaudRate, DWORD dwByteSize = 8, DWORD dwParity = NOPARITY, DWORD dwStopBits = ONESTOPBIT) { if(IsOpen()) { if(!::GetCommState(_hCommHandle, &_DCB)) return false; _DCB.BaudRate = dwBaudRate; _DCB.ByteSize = (unsigned char)dwByteSize; _DCB.Parity = (unsigned char)dwParity; _DCB.StopBits = (unsigned char)dwStopBits; return ::SetCommState(_hCommHandle, &_DCB) == TRUE; } return false; } //设置串口的I/O缓冲区大小 bool SetBufferSize(DWORD dwInputSize, DWORD dwOutputSize) { if(IsOpen()) return ::SetupComm(_hCommHandle, dwInputSize, dwOutputSize); return false; }
/*--------------------------------------------------------------------------------------------------*/
//读取串口 dwBufferLength - 1 个字符到 szBuffer 返回实际读到的字符数 DWORD Read(char *szBuffer, DWORD dwBufferLength, DWORD dwWaitTime = 20) { if(!IsOpen()) return 0;
szBuffer[0] = '\0';
COMSTAT Stat; DWORD dwError;
if(::ClearCommError(_hCommHandle, &dwError, &Stat) && dwError > 0) //清除错误 { ::PurgeComm(_hCommHandle, PURGE_RXABORT | PURGE_RXCLEAR); /*清除输入缓冲区*/ return 0; }
if(!Stat.cbInQue)// 缓冲区无数据 return 0;
unsigned long uReadLength = 0;
dwBufferLength = dwBufferLength - 1 > Stat.cbInQue ? Stat.cbInQue : dwBufferLength - 1;
if(!::ReadFile(_hCommHandle, szBuffer, dwBufferLength, &uReadLength, &_ReadOverlapped)) //2000 下 ReadFile 始终返回 True { if(::GetLastError() == ERROR_IO_PENDING) // 结束异步I/O { WaitForSingleObject(_ReadOverlapped.hEvent, dwWaitTime); //等待20ms if(!::GetOverlappedResult(_hCommHandle, &_ReadOverlapped, &uReadLength, false)) { if(::GetLastError() != ERROR_IO_INCOMPLETE)//其他错误 uReadLength = 0; } } else uReadLength = 0; } szBuffer[uReadLength] = '\0'; return uReadLength; }
/*--------------------------------------------------------------------------------------------------*/
//写串口 szBuffer DWORD Write(char *szBuffer, DWORD dwBufferLength) { if(!IsOpen()) return 0; DWORD dwError;
if(::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0) //清除错误 ::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR);
unsigned long uWriteLength = 0;
if(!::WriteFile(_hCommHandle, szBuffer, dwBufferLength, &uWriteLength, &_WriteOverlapped)) if(::GetLastError() != ERROR_IO_PENDING) uWriteLength = 0;
return uWriteLength; } //写串口 szBuffer inline DWORD Write(char *szBuffer) { assert(szBuffer);
return Write(szBuffer, strlen(szBuffer)); } //强制同步写 inline DWORD WriteSync(char *szBuffer, DWORD dwBufferLength) { if(!IsOpen()) return 0; DWORD dwError;
if(::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0) //清除错误 ::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR);
unsigned long uWriteLength = 0;
if(!::WriteFile(_hCommHandle, szBuffer, dwBufferLength, &uWriteLength, NULL)) if(::GetLastError() != ERROR_IO_PENDING) uWriteLength = 0;
return uWriteLength; } //写串口 szBuffer 可以输出格式字符串 DWORD Write(char *szBuffer, DWORD dwBufferLength, char * szFormat, ...) { if(!IsOpen()) return 0; va_list va; va_start(va, szFormat); _vsnprintf(szBuffer, dwBufferLength, szFormat, va); va_end(va);
return Write(szBuffer); } //写串口 szBuffer 可以输出格式字符串 DWORD Write(char *szBuffer, char * szFormat, ...) { if(!IsOpen()) return 0; va_list va; va_start(va, szFormat); vsprintf(szBuffer, szFormat, va); va_end(va);
return Write(szBuffer); } /*--------------------------------------------------------------------------------------------------*/ //关闭串口 inline virtual void Close() { if(IsOpen()) { EndThread(); ::CloseHandle(_hCommHandle);
_hCommHandle = INVALID_HANDLE_VALUE; } }
/*---------------------------------------------流控制函数---------------------------------------------*/
//设置串口的DTR信号 true高电平/有效 false低电平 数据终端准备 引脚(9)4 引脚(25)20 bool SetDTR(bool OnOrOff) { return IsOpen() ? EscapeCommFunction(_hCommHandle, OnOrOff ? SETDTR : CLRDTR) : false; } //设置串口的RTS信号 true高电平 false低电平 引脚(9)4 引脚(25)20 bool SetRTS(bool OnOrOff) { return IsOpen() ? EscapeCommFunction(_hCommHandle, OnOrOff ? SETRTS : CLRRTS) : false; } //设置串口的字符传输状态 true终止传输 false恢复传输 bool SetBreak(bool OnOrOff) { return IsOpen() ? EscapeCommFunction(_hCommHandle, OnOrOff ? SETBREAK : CLRBREAK) : false; } /*-------------------------------------------辅助线程函数-----------------------------------------------*/
//设定发送通知, 接受字符最小值 inline void SetNotifyNum(int iNum) { _dwNotifyNum = iNum; } //送消息的窗口句柄 inline void SetWnd(HWND hWnd) { _hNotifyWnd = hWnd; } //辅助线程控制 建监视线程 bool BeginThread() { if(!IsThreadRunning()) { _fRunFlag = true; _hThreadHandle = NULL;
DWORD id;
_hThreadHandle = ::CreateThread(NULL, 0, CommThreadProc, this, 0, &id); //兼容98 故使用&ID
return (_hThreadHandle != NULL); //辅助线程 } return false; } //线程是否运行 inline bool IsThreadRunning() { return _hThreadHandle != NULL; } //获得线程句柄 inline HANDLE GetThread() { return _hThreadHandle; } //暂停监视线程 inline bool SuspendThread() { return IsThreadRunning() ? ::SuspendThread(_hThreadHandle) != 0xFFFFFFFF : false; } //恢复监视线程 inline bool ResumeThread() { return IsThreadRunning() ? ::ResumeThread(_hThreadHandle) != 0xFFFFFFFF : false; } //终止线程 bool EndThread(DWORD dwWaitTime = 100) { if(IsThreadRunning()) { _fRunFlag = false; ::SetCommMask(_hCommHandle, 0); ::SetEvent(_WaitOverlapped.hEvent); if(::WaitForSingleObject(_hThreadHandle, dwWaitTime) != WAIT_OBJECT_0) if(!::TerminateThread(_hThreadHandle, 0)) return false;
::CloseHandle(_hThreadHandle); ::ResetEvent(_WaitOverlapped.hEvent);
_hThreadHandle = NULL;
return true; } return false; }
protected:
volatile DWORD _dwPort; //串口号 volatile HANDLE _hCommHandle;//串口句柄 char _szCommStr[20];
DCB _DCB; //波特率,停止位,等 COMMTIMEOUTS _CO; //超时结构
DWORD _dwIOMode; // 0 同步 默认 FILE_FLAG_OVERLAPPED 重叠I/O 异步 OVERLAPPED _ReadOverlapped, _WriteOverlapped; // 重叠I/O
//线程用 volatile HANDLE _hThreadHandle; //辅助线程 volatile HWND _hNotifyWnd; // 通知窗口 volatile DWORD _dwNotifyNum;//接受多少字节(>_dwNotifyNum)发送通知消息 volatile bool _fRunFlag; //线程运行循环标志 bool _fAutoBeginThread;//Open() 自动 BeginThread(); OVERLAPPED _WaitOverlapped; //WaitCommEvent use
void Init() //初始化 { memset(_szCommStr, 0, 20); 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);
_dwNotifyNum = 0; _hNotifyWnd = NULL; _hThreadHandle = NULL;
memset(&_WaitOverlapped, 0, sizeof(_WaitOverlapped)); _WaitOverlapped.hEvent = ::CreateEvent(NULL, true, false, NULL); assert(_WaitOverlapped.hEvent != INVALID_HANDLE_VALUE); } void 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); } //绑定串口 inline void BindCommPort(DWORD dwPort) { assert(dwPort >= 1 && dwPort <= 1024);
char p[5];
_dwPort = dwPort; strcpy(_szCommStr, "\\\\.\\COM"); ltoa(_dwPort, p, 10); strcat(_szCommStr, p); } //打开串口 virtual bool OpenCommPort() { if(IsOpen()) Close();
_hCommHandle = ::CreateFile( _szCommStr, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | _dwIOMode, NULL );
if(_fAutoBeginThread) { if(IsOpen() && BeginThread()) return true; else { Close(); //创建线程失败 return false; } } return IsOpen();//检测串口是否成功打开 } //设置串口 virtual bool SetupPort() { if(!IsOpen()) return false;
//设置推荐缓冲区 if(!::SetupComm(_hCommHandle, 4096, 4096)) return false;
//设置超时时间 if(!::GetCommTimeouts(_hCommHandle, &_CO)) return false; _CO.ReadIntervalTimeout = 0xFFFFFFFF; _CO.ReadTotalTimeoutMultiplier = 0; _CO.ReadTotalTimeoutConstant = 0; _CO.WriteTotalTimeoutMultiplier = 0; _CO.WriteTotalTimeoutConstant = 2000; if(!::SetCommTimeouts(_hCommHandle, &_CO)) return false;
//清空串口缓冲区 if(!::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR )) return false;
return true; }
/*---------------------------------------线程监视处理-----------------------------------------------------*/
//线程收到消息自动调用, 如窗口句柄有效, 送出消息, 包含窗口编号 virtual void OnReceive()//EV_RXCHAR { if(_hNotifyWnd) ::PostMessage(_hNotifyWnd, ON_COM_RECEIVE, WPARAM(_dwPort), LPARAM(0)); } //获得串口的DSR信号处理 virtual void OnDSR() { if(_hNotifyWnd) { DWORD Status; if(GetCommModemStatus(_hCommHandle, &Status)) ::PostMessage(_hNotifyWnd, ON_COM_DSR, WPARAM(_dwPort), LPARAM( (Status & MS_DSR_ON) ? 1 : 0)); } } //获得串口的CTS信号处理 virtual void OnCTS() { if(_hNotifyWnd) { DWORD Status; if(GetCommModemStatus(_hCommHandle, &Status)) ::PostMessage(_hNotifyWnd, ON_COM_CTS, WPARAM(_dwPort), LPARAM( (Status & MS_CTS_ON) ? 1 : 0)); } } //获得串口字符传输状态终止传输处理 virtual void OnBreak() { if(_hNotifyWnd) ::PostMessage(_hNotifyWnd, ON_COM_BREAK , WPARAM(_dwPort), LPARAM(0)); } //获得串口输出完毕信号处理 virtual void OnTXEmpty() { if(_hNotifyWnd) ::PostMessage(_hNotifyWnd, ON_COM_TXEMPTY, WPARAM(_dwPort), LPARAM(0)); } //错误处理 virtual void OnError() { COMSTAT Stat; DWORD dwError; ::ClearCommError(_hCommHandle, &dwError, NULL); } //振玲处理 virtual void OnRing() { if(_hNotifyWnd) ::PostMessage(_hNotifyWnd, ON_COM_RING, WPARAM(_dwPort), LPARAM(0)); } //RLSD信号处理 virtual void OnRLSD() { if(_hNotifyWnd) ::PostMessage(_hNotifyWnd, ON_COM_RLSD, WPARAM(_dwPort), LPARAM(0)); }
//可扩展的线程工作函数 virtual void ThreadFunc() { if(!::SetCommMask(_hCommHandle, EV_RXCHAR | EV_ERR | EV_CTS | EV_DSR | EV_BREAK | EV_TXEMPTY | EV_RING | EV_RLSD)) MessageBox(NULL, "设置监视事件失败, 线程工作无效", "cnComm Class", MB_OK);
COMSTAT Stat; DWORD dwError;
for(DWORD dwLength, dwMask = 0; _fRunFlag && IsOpen(); dwMask = 0) { if(!::WaitCommEvent(_hCommHandle, &dwMask, &_WaitOverlapped)) { if(::GetLastError() == ERROR_IO_PENDING)//用于异步 ::GetOverlappedResult(_hCommHandle, &_WaitOverlapped, &dwLength, TRUE); else continue; }
switch(dwMask) { case EV_RXCHAR : ::ClearCommError(_hCommHandle, &dwError, &Stat); if(Stat.cbInQue > _dwNotifyNum) OnReceive(); break;
case EV_TXEMPTY : OnTXEmpty(); break;
case EV_CTS : OnCTS(); break;
case EV_DSR : OnDSR(); break; case EV_RING : OnRing(); break;
case EV_RLSD : OnRLSD(); break;
case EV_BREAK: OnBreak(); break;
case EV_ERR : OnError(); break; }//case }//for } private: //监视线程 static DWORD WINAPI CommThreadProc(LPVOID lpPara) { cnComm *pComm = (cnComm *)lpPara; pComm->ThreadFunc(); return 0; } };
#endif //_CN_COMM_H_

|