最近在研究基于QT的串口通信程序。下载了别人写的第三方类,但是感觉看得不是很懂,另外跟我想象中的事件驱动有些区别。因此自己尝试着写了一个基于WIN2api的串口类。经过调试发现性能很不错,贴出来给大家看看。
本类只包含一个H文件和一个CPP文件,应用时只要包含该H文件即可,H文件中只有一个类KQSP,只要实例化该类的对象就可以了。
下边贴出H文件的源代码:
#ifndef KQSP_H
#define KQSP_H
#include <QtCore>
#include <qt_windows.h>
/*
class THRD : public QThread
{
Q_OBJECT
public:
KQSPTHRD();
~KQSPTHRD();
public:
void run()
{
listen.setInterval(0);
QObject::connect(&listen,SIGNAL(timeout()),&P,SLOT(Monitor()));
listen.start();
exec();
}
};
*/
class KQSP : public QIODevice
{
Q_OBJECT
public:
KQSP(); //CONSTRUCTION
// ~KQSP(); //FINALIZE
public:
bool open(); //open the port
void close(); //close the port
qint64 readData(char *buf,qint64 count); //overwrit
qint64 writeData(const char* buf,qint64 count);//overwrit
void write();
private:
DWORD BAUD; //baudrate
DWORD EVTG; //EVTigger
DWORD WCNT;
HANDLE HCOM; //Handle of port
OVERLAPPED OV;
COMMTIMEOUTS TimeOuts;
DCB dcb;
public:
BYTE TXBF[100];
BYTE RXBF[100]; //RXBUF
QTimer LTMR;
QThread THRD;
signals:
void DT_RDVD(); //Data recived
public slots:
void ON_CHEK(); //checking the ev_rxchar
void ON_WRIT(); //to write to the port
};
#endif // KQSP_H
类中的方法和属性都很简单:open方法负责打开端口,这个方法已经包含进构造函数,不需要主动调用;write方法就是写,但是我是通过信号来调用的。因为本类一般工作在第二线程,不同线程之间无法直接调用方法。BAUD就是波特率。
接下来是我们的CPP文件:
#include "KQSP.h"
KQSP::KQSP()
{
this->moveToThread(&THRD);
LTMR.setInterval(0);
BAUD = BAUD_9600;
EVTG = 0;
WCNT = 10;
memset(TXBF,0x00,100);
memset(RXBF,0x00,100);
open();
TimeOuts.ReadIntervalTimeout = MAXDWORD;
TimeOuts.ReadTotalTimeoutMultiplier = 0;
TimeOuts.ReadTotalTimeoutConstant= 100;
TimeOuts.WriteTotalTimeoutMultiplier= 0;
TimeOuts.WriteTotalTimeoutConstant= 100;
SetCommTimeouts(HCOM,&TimeOuts);
SetupComm(HCOM,2048,2048);//HUANCHONG
GetCommState(HCOM,&dcb);
dcb.BaudRate = 57600; //波特率为9600
dcb.ByteSize = 8; //每个字节有8位
dcb.Parity = NOPARITY; //无奇偶校验位
dcb.StopBits = ONESTOPBIT; //1个停止位
SetCommState (HCOM,&dcb);
// Initialize the rest of the OVERLAPPED structure to zero.
OV.Internal = 0;
OV.InternalHigh = 0;
OV.Offset = 0;
OV.OffsetHigh = 0;
OV.hEvent = CreateEvent(NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // not signaled
NULL // no name
);
SetCommMask(HCOM,EV_RXCHAR);
QObject::connect(<MR,SIGNAL(timeout()),this,SLOT(ON_CHEK()));
LTMR.start();
}
/*
KQSP::~KQSP()
{
//close();
}
*/
bool KQSP::open()
{
HCOM = CreateFileA("COM1",
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if(INVALID_HANDLE_VALUE == HCOM)
return false;
else
return true;
}
void KQSP::close()
{
THRD.quit();
CloseHandle(HCOM);
}
void KQSP::ON_WRIT()
{
write();
}
void KQSP::write()
{
DWORD X = GetLastError();
if(X != 0)
return;
PurgeComm(HCOM,PURGE_TXCLEAR | PURGE_TXABORT | PURGE_RXABORT | PURGE_RXCLEAR );
WriteFile(HCOM,TXBF,9,NULL,&OV);
WaitCommEvent(HCOM,&EVTG,&OV);
}
void KQSP::ON_CHEK()
{
if(EVTG & EV_RXCHAR)
{
memset(RXBF,0x00,100);
bool x =ReadFile(HCOM,RXBF,100,NULL,&OV);
if(!x)
{
WaitForSingleObject(HCOM,100);
}
// DWORD T = GetLastError();
PurgeComm(HCOM,PURGE_TXCLEAR | PURGE_TXABORT | PURGE_RXABORT | PURGE_RXCLEAR );
EVTG = 0;
emit DT_RDVD();
}
}
qint64 KQSP::readData(char *buf,qint64 count)
{
return 0;
}
qint64 KQSP::writeData(const char* buf,qint64 count)
{
return 0;
}
程序都非常简单,一目了然。
必须指出本程序的多线程方式从性能上讲应该是第二选择:最佳的方式应该是重写qiodevice的exec方法,类似于.NET中的重写wndproc方法,这样可以利用QT自身的消息循环来做监听。但是不知为何在网上搜索没有发现这个结果,我也没有去尝试。有兴趣的可以去试一下。我用的方法是在第二线程中使用一个间隔为0的定时器实现了无限循环的方法。
接下来是利用这个类进行收发的1个例子。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "KQSP.h"
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
KQSP PORT;
private:
Ui::MainWindow *ui;
signals:
void LS_STOP();
void LS_GOON();
void WT_REDY();
private slots:
void on_pushButton_1_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void ON_DT_RCVD();
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(&PORT,SIGNAL(DT_RDVD()),this,SLOT(ON_DT_RCVD()));
QObject::connect(this,SIGNAL(LS_STOP()),&PORT.LTMR,SLOT(stop()));
QObject::connect(this,SIGNAL(LS_GOON()),&PORT.LTMR,SLOT(start()));
QObject::connect(this,SIGNAL(WT_REDY()),&PORT,SLOT(ON_WRIT()));
PORT.THRD.start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_1_clicked()
{
emit LS_STOP();
ui->pushButton_3->setText("TMSTOP_SEND");
}
void MainWindow::on_pushButton_2_clicked()
{
emit LS_GOON();
ui->pushButton_3->setText("TMGOON_SEND");
}
void MainWindow::on_pushButton_3_clicked()
{
BYTE temp[10] = {0xbb,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0xdf,0x00};
memcpy(PORT.TXBF,temp,10);
emit WT_REDY();
}
void MainWindow::ON_DT_RCVD()
{
emit LS_STOP();
QString temp;
int i = 0;
for(i = 0;i < 20;i++)
{
temp.setNum(PORT.RXBF[i]);
ui->textBrowser->append(temp);
//ui->textBrowser->append(" ");
}
emit LS_GOON();
}
好了以上就是我的串口类。enjoy qt!