VC串口通讯DLL
串口在工业应用是极为普遍的,我用API封装了同步和异步
的串口类,以及一个具有监视线程的异步串口类;使用简单高效,具有工业强度,我在BC, BCB, VC, BCBX, GCC下编译通过,相信足够应付大多数情况,而且还可以继承扩展,下面简单介绍使用方法 库的层次结构:
_base_com:虚基类,基本接口,可自行扩展自己的串口类 _sync_com:_base_com 的子类, 同步应用,适合简单应用 _asyn_com:_base_com 的子类, 异步应用(重叠I/O),适合较高效应用,NT平台
_thread_com:_asyn_com 的子类, 异步应用,监视线程,适合较复杂应用,窗口通知消息和继承扩展的使用方式;几个问题: 结束线程 如
何
从
WaitCommEvent(pcom->_com_handle,
&mask,
&pcom->_wait_o)这个API退出以便顺利结束线程: 方案1:
SetCommMask(_com_handle, 0); 这个方法在MSDN有载,当在一些情况下并不完全有效,原因未知; 方案2:
SetEvent(_wait_o.hEvent); 直接激活重叠IO结构中的事件句柄,绝对有效; 这份代码我两种都用; 打开10以上的COM端口
..
..
在NT/2000下打开编号10以上端口用 _com_handle = CreateFile( “COM10“,
GENERIC_READ GENERIC_WRITE, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL FILE_FLAG_OVERLAPPED, //重叠I/O NULL );
将提示错误, 这样就OK: _com_handle = CreateFile(
“\\\\\\\\.\\\\COM10“,//对应的就是\\\\.\\COM10 GENERIC_READ GENERIC_WRITE, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL FILE_FLAG_OVERLAPPED, //重叠I/O NULL );
线程中循环的低效率问题
使用SetCommMask(pcom->_com_handle, EV_RXCHAR EV_ERR)监
..
..
视接受字符和错误消息;一旦有个字符来就会激活WaitCommEvent 通常作以下接受操作:
if(!WaitCommEvent(pcom->_com_handle, &mask, &pcom->_wait_o)) {
if(GetLastError() == ERROR_IO_PENDING) {
GetOverlappedResult(pcom->_com_handle, &length, true); } }
if(mask & EV_ERR) // == EV_ERR
ClearCommError(pcom->_com_handle, &error, &stat); if(mask & EV_RXCHAR) // == EV_RXCHAR {
pcom->on_receive();//接收到字符 //或发送到窗口消息 }
这样频繁的函数调用或接受发送消息,效率低下,我添加扫描缓冲区的代码,当字符数超过设定的字符数才作接受字符的操作; if(mask & EV_RXCHAR) // == EV_RXCHAR {
ClearCommError(pcom->_com_handle, &error, &stat);
..
&pcom->_wait_o,
..
if(stat.cbInQue > pcom->_notify_num) //_notify_num 是设定得字符数
pcom->on_receive(); }
类似于流的输出方式
我编了一个简单的写串口的方式,可以类似于流将简单的数据类型输出
template strstream s; s << x ; write(s.str(), s.pcount()); return *this; } 就可以这样使用 _sync_com com1; com1.open(1, 9600); com1 << “ then random() 's return value is “<< rand() << “ .\\n“ ; com1.close(); 本串口类库的主要接口 class _base_com .. .. { bool open(int port); bool open(int port, int baud_rate); bool open(int port, char * set_str); // set_str : “9600, 8, n, 1“ bool set_state(int BaudRate, int ByteSize = 8, int Parity = NOPARITY, int StopBits = ONESTOPBIT) //设置内置结构串口参数:波特率,停止位 bool set_state(char *set_str) bool is_open(); HANDLE get_handle(); virtual bool open_port()=0; //继承用的重要函数 virtual close(); } class _sync_com :public _base_com //同步 { int read(char *buf, int buf_size); //自动补上'\\0',将用去一个字符的缓冲区 int write(char *buf, int len); int write(char *buf); } class _asyn_com :public _base_com //异步 { .. .. int read(char *buf, int buf_size); //自动补上'\\0',将用去一个字符的缓冲区 int write(char *buf, int len); int write(char *buf); } class _thread_com :public _asyn_com //线程 { virtual void on_receive() //供线程接受到字符时调用, 可继承替换之 { if(_notify_hwnd) PostMessage(_notify_hwnd, WPARAM(_port), LPARAM(0)); else { if(_func) _func(_port); } } void set_hwnd(HWND hwnd); //设置窗口句柄, 发送 ON_COM_RECEIVE WM_USER + 618 void set_func(void (*f)(int)); //设置调用函数 ,窗口句柄优先 .. ON_COM_RECEIVE, .. void set_notify_num(int num); //设定发送通知, 接受字符最小值 } 一些应用范例 当然首先 #include \"_com.h\" 一、打开串口1同步写 char str[] = \"com_class test\"; _sync_com com1; //同步 com1.open(1); // 相当于 com1.open(1, 9600); com1.open(1, \"9600,8,n,1\"); for(int i=0; i<100; i++) { Sleep(500); com1.write(str); //也可以 com1.write(str, strlen(str)); } com1.close(); 二、打开串口2异步读 char str[100]; _asyn_com com2; //异步 com2.open(2); // 相当于 com2.open(2, 9600); com2.open(2, \"9600,8,n,1\"); if(!com2.is_open()) cout << \"COM2 not open , error : \" << GetLastError() << endl; .. .. /* 也可以如下用法 if(!com2.open(2)) cout << \"COM2 not open , error : \" << GetLastError() << endl; */ for(int i=0; i<100; i++) { Sleep(500); if(com2.read(str, 100) > 0) //异步读,返回读取字符数 cout << str; } com2.close(); 三、扩展应用具有监视线程的串口类 class _com_ex : public thread_com { public: virtual on_receive() { char str[100]; if(read(str, 100) > 0) //异步读,返回读取字符数 cout << str; } .. .. }; int main(int argc, char *argv[]) { try { char str[100]; _com_ex com2; //异步扩展 com2.open(2); Sleep(10000); com2.close(); } catch(exception &e) { cout << e.what() << endl; } return 0; } 四、桌面应用可发送消息到指定窗口(在C++ Builder 和 VC ++ 测试通过) VC ++ 接受消息 .. .. BEGIN_MESSAGE_MAP(ComDlg, CDialog) //{{AFX_MSG_MAP(ComDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_DESTROY() //}}AFX_MSG_MAP ON_MESSAGE(ON_COM_RECEIVE, On_Receive) END_MESSAGE_MAP() 打开串口,传递窗口句柄 _thread_com com2; com2.open(2); com2.set_hwnd(ComDlg->m_hWnd); 处理消息 LRESULT ComDlg::On_Receive(WPARAM wp, LPARAM lp) { char str[100]; com2.read(str, 100); char com_str[10]; strcpy(com_str, \"COM\"); ltoa((long)wp, com_str + 3, 10); // WPARAM 保存端口号 .. .. MessageBox(str, com_str, MB_OK); return 0; } C++ Builder class TForm1 : public TForm { __published: // IDE-managed Components void __fastcall FormClose(TObject *Sender, TCloseAction &Action); void __fastcall FormCreate(TObject *Sender); private: // User declarations public: // User declarations void On_Receive(TMessage& Message); __fastcall TForm1(TComponent* Owner); _thread_com com2; BEGIN_MESSAGE_MAP MESSAGE_HANDLER(ON_COM_RECEIVE, TMessage, On_Receive) END_MESSAGE_MAP(TForm) }; void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { com2.close(); .. .. } //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { com2.open(2); com2.set_hwnd(Handle); } //--------------------------------------------------------------------------- void TForm1::On_Receive(TMessage& Message) { char xx[20]; int port = Message.WParam; if(com2.read(xx, 20) > 0) ShowMessage(xx); } 错误和缺陷在所难免,欢迎来信批评指正;wushaojian@21cn.com 附完整源代码 _com.h /* 串口基础类库(WIN32) ver 0.1 编译器 : BC++ 5; C++ BUILDER 4, 5, 6, X; VC++ 5, 6; VC.NET; GCC; class _base_com : 虚基类 基本串口接口; .. .. class _sync_com : 同步I/O 串口类; class _asyn_com : 异步I/O 串口类; class _thread_com : 异步I/O 辅助读监视线程 可转发窗口消息 串口类(可继承虚函数on_receive用于读操作); class _com : _thread_com 同名 copyright(c) 2004.8 llbird wushaojian@21cn.com */ /* Example : */ #ifndef _COM_H_ #define _COM_H_ #pragma warning(disable: 4530) #pragma warning(disable: 4786) #pragma warning(disable: 4800) #include .. .. #include class _base_com //虚基类 基本串口接口 { protected: volatile int _port; //串口号 volatile HANDLE _com_handle;//串口句柄 char _com_str[20]; DCB _dcb; //波特率,停止位,等 COMMTIMEOUTS _co; // 超时时间 virtual bool open_port() = 0; void init() //初始化 { memset(_com_str, 0, 20); memset(&_co, 0, sizeof(_co)); memset(&_dcb, 0, sizeof(_dcb)); _dcb.DCBlength = sizeof(_dcb); _com_handle = INVALID_HANDLE_VALUE; } virtual bool setup_port() { if(!is_open()) .. return false; if(!SetupComm(_com_handle, 8192, 8192)) return false; //设置推荐缓冲区 if(!GetCommTimeouts(_com_handle, &_co)) return false; _co.ReadIntervalTimeout = 0xFFFFFFFF; _co.ReadTotalTimeoutMultiplier = 0; _co.ReadTotalTimeoutConstant = 0; _co.WriteTotalTimeoutMultiplier = 0; _co.WriteTotalTimeoutConstant = 2000; if(!SetCommTimeouts(_com_handle, &_co)) return false; //设置超时时间 if(!PurgeComm(_com_handle, PURGE_TXABORT PURGE_TXCLEAR PURGE_RXCLEAR )) return false; //清空串口缓冲区 return true; } inline void set_com_port(int port) { char p[12]; _port = port; strcpy(_com_str, \"\\\\\\\\.\\\\COM\"); .. .. PURGE_RXABORT .. ltoa(_port, p, 10); strcat(_com_str, p); } public: _base_com() { init(); } virtual ~_base_com() { close(); } //设置串口参数:波特率,停止位,等 支持设置字符串 \"9600, 8, n, 1\" bool set_state(char *set_str) { if(is_open()) { if(!GetCommState(_com_handle, &_dcb)) return false; if(!BuildCommDCB(set_str, &_dcb)) return false; .. .. return SetCommState(_com_handle, &_dcb) == TRUE; } return false; } //设置内置结构串口参数:波特率,停止位 bool set_state(int BaudRate, int ByteSize = 8, int Parity = NOPARITY, int StopBits = ONESTOPBIT) { if(is_open()) { if(!GetCommState(_com_handle, &_dcb)) return false; _dcb.BaudRate = BaudRate; _dcb.ByteSize = ByteSize; _dcb.Parity = Parity; _dcb.StopBits = StopBits; return SetCommState(_com_handle, &_dcb) == TRUE; } return false; } //打开串口 缺省 9600, 8, n, 1 inline bool open(int port) .. .. { return open(port, 9600); } //打开串口 缺省 baud_rate, 8, n, 1 inline bool open(int port, int baud_rate) { if(port < 1 port > 1024) return false; set_com_port(port); if(!open_port()) return false; if(!setup_port()) return false; return set_state(baud_rate); } //打开串口 inline bool open(int port, char *set_str) { if(port < 1 port > 1024) return false; set_com_port(port); if(!open_port()) .. .. return false; if(!setup_port()) return false; return set_state(set_str); } inline bool set_buf(int in, int out) { return is_open() ? SetupComm(_com_handle, in, out) : false; } //关闭串口 inline virtual void close() { if(is_open()) { CloseHandle(_com_handle); _com_handle = INVALID_HANDLE_VALUE; } } //判断串口是或打开 inline bool is_open() { .. .. return _com_handle != INVALID_HANDLE_VALUE; } //获得串口句炳 HANDLE get_handle() { return _com_handle; } operator HANDLE() { return _com_handle; } }; class _sync_com : public _base_com { protected: //打开串口 virtual bool open_port() { if(is_open()) close(); _com_handle = CreateFile( .. .. _com_str, GENERIC_READ GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL , NULL ); assert(is_open()); return is_open();//检测串口是否成功打开 } public: _sync_com() { } //同步读 int read(char *buf, int buf_len) { if(!is_open()) return 0; buf[0] = '\\0'; COMSTAT stat; .. .. DWORD error; if(ClearCommError(_com_handle, &error, &stat) && error > 0) //清除错误 { PurgeComm(_com_handle, PURGE_RXABORT PURGE_RXCLEAR); /*清除输入缓冲区*/ return 0; } unsigned long r_len = 0; buf_len = min(buf_len - 1, (int)stat.cbInQue); if(!ReadFile(_com_handle, buf, buf_len, &r_len, NULL)) r_len = 0; buf[r_len] = '\\0'; return r_len; } //同步写 int write(char *buf, int buf_len) { if(!is_open() !buf) return 0; DWORD error; if(ClearCommError(_com_handle, &error, NULL) && error > 0) //清除 .. .. 错误 PurgeComm(_com_handle, PURGE_TXABORT PURGE_TXCLEAR); unsigned long w_len = 0; if(!WriteFile(_com_handle, buf, buf_len, &w_len, NULL)) w_len = 0; return w_len; } //同步写 inline int write(char *buf) { assert(buf); return write(buf, strlen(buf)); } //同步写, 支持部分类型的流输出 template strstream s; s << x; write(s.str(), s.pcount()); return *this; } .. .. }; class _asyn_com : public _base_com { protected: OVERLAPPED _ro, _wo; // 重叠I/O virtual bool open_port() { if(is_open()) close(); _com_handle = CreateFile( _com_str, GENERIC_READ GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL FILE_FLAG_OVERLAPPED, //重叠I/O NULL ); assert(is_open()); return is_open();//检测串口是否成功打开 } .. .. public: _asyn_com() { memset(&_ro, 0, sizeof(_ro)); memset(&_wo, 0, sizeof(_wo)); _ro.hEvent = CreateEvent(NULL, true, false, NULL); assert(_ro.hEvent != INVALID_HANDLE_VALUE); _wo.hEvent = CreateEvent(NULL, true, false, NULL); assert(_wo.hEvent != INVALID_HANDLE_VALUE); } virtual ~_asyn_com() { close(); if(_ro.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_ro.hEvent); if(_wo.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_wo.hEvent); } //异步读 int read(char *buf, int buf_len, int time_wait = 20) { if(!is_open()) .. .. return 0; buf[0] = '\\0'; COMSTAT stat; DWORD error; if(ClearCommError(_com_handle, &error, &stat) && error > 0) //清除错误 { PurgeComm(_com_handle, PURGE_RXABORT PURGE_RXCLEAR); /*清除输入缓冲区*/ return 0; } if(!stat.cbInQue)// 缓冲区无数据 return 0; unsigned long r_len = 0; buf_len = min((int)(buf_len - 1), (int)stat.cbInQue); if(!ReadFile(_com_handle, buf, buf_len, &r_len, &_ro)) //2000 下 ReadFile 始终返回 True { if(GetLastError() == ERROR_IO_PENDING) // 结束异步I/O { //WaitForSingleObject(_ro.hEvent, time_wait); //等待20ms if(!GetOverlappedResult(_com_handle, &_ro, &r_len, false)) .. .. { if(GetLastError() != ERROR_IO_INCOMPLETE)//其他错误 r_len = 0; } } else r_len = 0; } buf[r_len] = '\\0'; return r_len; } //异步写 int write(char *buf, int buf_len) { if(!is_open()) return 0; DWORD error; if(ClearCommError(_com_handle, &error, NULL) && error > 0) //清除错误 PurgeComm(_com_handle, PURGE_TXABORT PURGE_TXCLEAR); unsigned long w_len = 0, o_len = 0; if(!WriteFile(_com_handle, buf, buf_len, &w_len, &_wo)) .. if(GetLastError() != ERROR_IO_PENDING) w_len = 0; return w_len; } //异步写 inline int write(char *buf) { assert(buf); return write(buf, strlen(buf)); } //异步写, 支持部分类型的流输出 template strstream s; s << x ; write(s.str(), s.pcount()); return *this; } }; //当接受到数据送到窗口的消息 #define ON_COM_RECEIVE WM_USER + 618 .. .. // WPARAM 端口号 .. class _thread_com : public _asyn_com { protected: volatile HANDLE _thread_handle; //辅助线程 volatile HWND _notify_hwnd; // 通知窗口 volatile long _notify_num;//接受多少字节(>_notify_num)发送通知消息 volatile bool _run_flag; //线程运行循环标志 void (*_func)(int port); OVERLAPPED _wait_o; //WaitCommEvent use //线程收到消息自动调用, 如窗口句柄有效, 送出消息, 包含窗口编号 virtual void on_receive() { if(_notify_hwnd) PostMessage(_notify_hwnd, ON_COM_RECEIVE, WPARAM(_port), LPARAM(0)); else { if(_func) _func(_port); } .. .. } //打开串口,同时打开监视线程 virtual bool open_port() { if(_asyn_com::open_port()) { _run_flag = true; DWORD id; _thread_handle = CreateThread(NULL, 0, com_thread, this, 0, &id); //辅助线程 assert(_thread_handle); if(!_thread_handle) { CloseHandle(_com_handle); _com_handle = INVALID_HANDLE_VALUE; } else return true; } return false; } public: .. .. _thread_com() { _notify_num = 0; _notify_hwnd = NULL; _thread_handle = NULL; _func = NULL; memset(&_wait_o, 0, sizeof(_wait_o)); _wait_o.hEvent = CreateEvent(NULL, true, false, NULL); assert(_wait_o.hEvent != INVALID_HANDLE_VALUE); } ~_thread_com() { close(); if(_wait_o.hEvent != INVALID_HANDLE_VALUE) CloseHandle(_wait_o.hEvent); } //设定发送通知, 接受字符最小值 void set_notify_num(int num) { _notify_num = num; } int get_notify_num() .. .. { return _notify_num; } //送消息的窗口句柄 inline void set_hwnd(HWND hWnd) { _notify_hwnd = hWnd; } inline HWND get_hwnd() { return _notify_hwnd; } inline void set_func(void (*f)(int)) { _func = f; } //关闭线程及串口 virtual void close() { if(is_open()) { _run_flag = false; .. .. SetCommMask(_com_handle, 0); SetEvent(_wait_o.hEvent); if(WaitForSingleObject(_thread_handle, 100) != WAIT_OBJECT_0) TerminateThread(_thread_handle, 0); CloseHandle(_com_handle); CloseHandle(_thread_handle); _thread_handle = NULL; _com_handle = INVALID_HANDLE_VALUE; ResetEvent(_wait_o.hEvent); } } /*辅助线程控制*/ //获得线程句柄 HANDLE get_thread() { return _thread_handle; } //暂停监视线程 bool suspend() { return _thread_handle != NULL ? SuspendThread(_thread_handle) != 0xFFFFFFFF : false; .. .. } //恢复监视线程 bool resume() { return _thread_handle != NULL ? ResumeThread(_thread_handle) != 0xFFFFFFFF : false; } //重建监视线程 bool restart() { if(_thread_handle) /*只有已有存在线程时*/ { _run_flag = false; SetCommMask(_com_handle, 0); SetEvent(_wait_o.hEvent); if(WaitForSingleObject(_thread_handle, 100) != WAIT_OBJECT_0) TerminateThread(_thread_handle, 0); CloseHandle(_thread_handle); _run_flag = true; _thread_handle = NULL; DWORD id; _thread_handle = CreateThread(NULL, 0, com_thread, this, 0, &id); .. .. return (_thread_handle != NULL); //辅助线程 } return false; } private: //监视线程 static DWORD WINAPI com_thread(LPVOID para) { _thread_com *pcom = (_thread_com *)para; if(!SetCommMask(pcom->_com_handle, EV_RXCHAR EV_ERR)) return 0; COMSTAT stat; DWORD error; for(DWORD length, mask = 0; pcom->_run_flag && pcom->is_open(); mask = 0) { if(!WaitCommEvent(pcom->_com_handle, &pcom->_wait_o)) { if(GetLastError() == ERROR_IO_PENDING) { GetOverlappedResult(pcom->_com_handle, .. &mask, &pcom->_wait_o, .. &length, true); } } if(mask & EV_ERR) // == EV_ERR ClearCommError(pcom->_com_handle, &error, &stat); if(mask & EV_RXCHAR) // == EV_RXCHAR { ClearCommError(pcom->_com_handle, &error, &stat); if(stat.cbInQue > pcom->_notify_num) pcom->on_receive(); } } return 0; } }; typedef _thread_com _com; //名称简化 #endif //_COM_H_ .. 因篇幅问题不能全部显示,请点此查看更多更全内容