搜索
您的当前位置:首页正文

VC串口通讯DLL

2021-05-17 来源:步旅网
..

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 _asyn_com& operator << (T x) {

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 #include #include #include using namespace std;

..

..

#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 _sync_com& operator << (T x) {

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 _asyn_com& operator << (T x) {

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_

..

因篇幅问题不能全部显示,请点此查看更多更全内容

Top