Qt Serial Port
【概述】
Qt Serial Port提供了基本的功能,包括配置,I/O操作,获取和设置RS-232引脚的信号。
本模块暂不支持如下特性:和setFlowControl()方法重新配置它。控制管脚的状态是根据isDataTerminalReady(),isRequestToSend()和pinoutSignals()决定的。要改变控制信息,可以使用如下方法:setDataTerminalReady()和setRequestToSend()。
一旦你知道了串口可用于读或者写,你就可以调用read()或者write()方法。可选的还有readline()和readAll()方法。如果数据不能在一次读完,那么剩下的数据接下来就会存在QSerialPort的内部缓冲区中。你可以使用setReadBufferSize()方法设置缓冲区的大小。可以使用close()方法来关闭串口和取消I/O操作。int numRead = 0, numReadTotal = 0;
char buffer[50];
forever {
numRead = serial.read(buffer, 50);
// Do whatever with the array
numReadTotal += numRead;
if (numRead == 0 && !serial.waitForReadyRead())
break;
}
在串口连接被关闭或者出现错误的时候,waitForReadyRead()就会返回false。系统路径、描述以及制造商等信息。QSerialPortInfo类也可以作为QSerialPort类的setPort()方法的一个输入参数。
示例一展示了如何使用QSerialInfo类获取系统上可用的串口,显示所有串口的信息:
#include
#include
#include
QT_USE_NAMESPACE
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextStream out(stdout);
QList serialPortInfoList = QSerialPortInfo::availablePorts();
out << QObject::tr("Total number of ports available: ") << serialPortInfoList.count() << endl;
foreach (const QSerialPortInfo &serialPortInfo, serialPortInfoList) {
out << endl
<< QObject::tr("Port: ") << serialPortInfo.portName() << endl
<< QObject::tr("Location: ") << serialPortInfo.systemLocation() << endl
<< QObject::tr("Description: ") << serialPortInfo.description() << endl
<< QObject::tr("Manufacturer: ") << serialPortInfo.manufacturer() << endl
<< QObject::tr("Vendor Identifier: ") << (serialPortInfo.hasVendorIdentifier() ? QByteArray::number(serialPortInfo.vendorIdentifier(), 16) : QByteArray()) << endl
<< QObject::tr("Product Identifier: ") << (serialPortInfo.hasProductIdentifier() ? QByteArray::number(serialPortInfo.productIdentifier(), 16) : QByteArray()) << endl
<< QObject::tr("Busy: ") << (serialPortInfo.isBusy() ? QObject::tr("Yes") : QObject::tr("No")) << endl;
}
return 0;
}
#include
#include
#include
#include
#include
QT_USE_NAMESPACE
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
w.setWindowTitle(QObject::tr("Info about all available serial ports."));
QVBoxLayout *layout = new QVBoxLayout;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
QString s = QObject::tr("Port: ") + info.portName() + "\n"
+ QObject::tr("Location: ") + info.systemLocation() + "\n"
+ QObject::tr("Description: ") + info.description() + "\n"
+ QObject::tr("Manufacturer: ") + info.manufacturer() + "\n"
+ QObject::tr("Vendor Identifier: ") + (info.hasVendorIdentifier() ? QString::number(info.vendorIdentifier(), 16) : QString()) + "\n"
+ QObject::tr("Product Identifier: ") + (info.hasProductIdentifier() ?QString::number(info.productIdentifier(), 16) : QString()) + "\n"
+ QObject::tr("Busy: ") + (info.isBusy() ? QObject::tr("Yes") : QObject::tr("No")) + "\n";
QLabel *label = new QLabel(s);
layout->addWidget(label);
}
w.setLayout(layout);
w.show();
return a.exec();
}
本示例展示了如何在非GUI线程中使用QSerialPort的同步API。
QSerialPort支持两种编程方法:
*异步(非阻塞)方式。当控制权返回到Qt的事件循环中,相应的操作就会被调度和执行。当操作执行完了QSerial就会触发一个信号。例如:
QSerialPort::write()方法返回。当数据发送到串口,QSerialPort就会触发bytesWirtten()。
*同步(阻塞)方式。在非GUI和多线程应用程序中,我们可以调用waitFor...()函数(例如QSerialPort::waitForReadyRead())使得调用线程在完成之前都是阻塞的。
在本示例中,我们展示如何使用同步的方式,在Terminal示例中展示如何使用异步方式。
本示例的目的是向你展示一种模式:简化代码而不至于使得UI失去响应。Qt中阻塞的串口编程API可以简化代码,但是由于它的阻塞特性,所以我们必须只在非GUI线程中使用它。但是跟大多数人相反,使用QThread并不会使得你的应用程序更复杂。
本示例是主程序,还有一个与之对应的从程序示例。主程序通过串口将请求发送给从程序并等待响应。
下面是我们定义的主线程类,它集成自QThread:
class MasterThread : public QThread
{
Q_OBJECT
public:
MasterThread(QObject *parent = 0);
~MasterThread();
void transaction(const QString &portName, int waitTimeout, const QString &request);
void run();
signals:
void response(const QString &s);
void error(const QString &s);
void timeout(const QString &s);
private:
QString portName;
QString request;
int waitTimeout;
QMutex mutex;
QWaitCondition cond;
bool quit;
};
在线程的run函数中主要就是向串口中写入数据,代码如下:
// write request
QByteArray requestData = currentRequest.toLocal8Bit();
serial.write(requestData);
if (serial.waitForBytesWritten(waitTimeout)) {
// read response
if (serial.waitForReadyRead(currentWaitTimeout)) {
QByteArray responseData = serial.readAll();
while (serial.waitForReadyRead(10))
responseData += serial.readAll();
QString response(responseData);
emit this->response(response);
} else {
emit timeout(tr("Wait read response timeout %1")
.arg(QTime::currentTime().toString()));
}
} else {
emit timeout(tr("Wait write request timeout %1")
.arg(QTime::currentTime().toString()));
}
从程序类似上面的,就是起线程,线程从串口中读取数据:
if (serial.waitForReadyRead(currentWaitTimeout)) {
//! [7] //! [8]
// read request
QByteArray requestData = serial.readAll();
while (serial.waitForReadyRead(10))
requestData += serial.readAll();
//! [8] //! [10]
// write response
QByteArray responseData = currentRespone.toLocal8Bit();
serial.write(responseData);
if (serial.waitForBytesWritten(waitTimeout)) {
QString request(requestData);
//! [12]
emit this->request(request);
//! [10] //! [11] //! [12]
} else {
emit timeout(tr("Wait write response timeout %1")
.arg(QTime::currentTime().toString()));
}
//! [9] //! [11]
} else {
emit timeout(tr("Wait read request timeout %1")
.arg(QTime::currentTime().toString()));
}
Terminal示例展示了如何使用Qt Serial Port创建简单的终端。
本示例展示了QSerialPort类的主要特性,例如:配置,I/O实现等等。本示例也同样调用了QSerialPortInfo类获取系统中可用的串口信息。
QSerialPort支持两种基本的编程方法:
*异步(非阻塞)方式。当控制权返回到Qt的事件循环中时,操作就得以调度和执行。当操作执行完成了就会QSerialPort就会触发一个信号。例如:QSerialPort::write()函数会立即返回,而不会阻塞在写操作上。当有数据发送到串口,QSerialPort就会触发bytesWritten()信号。
*同步(阻塞)方式。在非GUI和多线程应用程序中,可以调用waitFor...()函数(例如:QSerialPort::waitForReadyRead())挂起调用线程,直到操作完成。
在本示例中,展示异步编程方式。
示例中包含了一些GUI窗口部件:
主窗口:应用程序的主窗口,包括了串口编程的所有逻辑工作,包括配置,I/O处理等等,继承自QMainWindow。
Console:主窗口的中心部件,显示发送和接受的数据。该类继承自QPlainTextEdit类。
设置对话窗:用于配置串口的对话框,也显示了系统上可用的串口的信息。
本程序主要展示异步操作方式,也就是调用write和read函数,并且设置好相应的槽函数。
点击了connect菜单,就会触发如下信号-槽连接
connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
在终端上输入字符就会触发如下信号-槽连接
connect(console, SIGNAL(getData(QByteArray)), this, SLOT(writeData(QByteArray)));
//! [6]
void MainWindow::writeData(const QByteArray &data)
{
serial->write(data);
}
//! [6]
//! [7]
void MainWindow::readData()
{
QByteArray data = serial->readAll();
console->putData(data);
}
//! [7]