由于RS232串口操作简单、通讯可靠,所以在工业领域中有大量的应用。
而普通家用PC已经逐步淘汰该串口,但usb转串口的设备依然存储,所以掌握Qt的串行数据读取便非常重要。
Qt以前的版本中,没有提供官方的对RS232串口的支持,编写串口程序很不方便。但在 Qt5.1 之后的版本提供了QtSerialPort模块,方便编程人员快速的开发应用串口的应用程序。
这里我们看一下具体的实现流程:
主要的类
当前的QtSerialPort模块中提供了两个C++类,分别是QSerialPort 和QSerialPortInfo。
QSerialPort 类提供了操作串口的各种接口。
QSerialPortInfo 是一个辅助类,可以提供计算机中可用串口的各种信息。
要在应用程序中使用这些类,请使用以下include语句:
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
要链接到模块,请将此行添加到XXX.pro文件中:
QT += serialport
QSerialPort
可以使用qserialPortInfo帮助程序类获取有关可用串行端口的信息,该类允许枚举系统中的所有串行端口。这对于获得要使用的串行端口的正确名称很有用。可以将helper类的对象作为参数传递给setport()或setportname()方法,以分配所需的串行设备。
设置端口后,可以使用open()方法以只读(r/o)、只写(w/o)或读写(r/w)模式打开它。
注意:串行端口总是以独占访问方式打开(即,没有其他进程或线程可以访问已经打开的串行端口)。
成功打开后,qserialport尝试确定端口的当前配置并初始化自身。可以使用setBaudRate()、setDataBits()、setParity()、setTopBits()和setFlowControl()方法将端口重新配置为所需的设置。
有两个属性可用于pinout信号,即:qserialport::dataterminalready、qserialport::requestToSend。也可以使用pinout signals()方法查询当前的pinout信号集。
一旦知道端口已准备好读或写,就可以使用read()或write()方法。或者,也可以调用readline()和readall()方便方法。如果不是一次读取所有数据,其余数据将在稍后可用,因为新的传入数据将附加到qserialport的内部读取缓冲区。可以使用setReadBufferSize()限制读取缓冲区的大小。
使用close()方法关闭端口并取消I/O操作。
QSerialPortInfo
提供有关现有串行端口的信息。
使用静态函数生成qserialPortInfo对象列表。列表中的每个qserialPortInfo对象表示单个串行端口,可以查询端口名、系统位置、说明和制造商。qserialPortInfo类也可以用作qserialPort类的setport()方法的输入参数。
实例
简单的验证性例程
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
qDebug() << "Name : " << info.portName();
qDebug() << "Description : " << info.description();
qDebug() << "Manufacturer: " << info.manufacturer();
qDebug() << "Serial Number: " << info.serialNumber();
qDebug() << "System Location: " << info.systemLocation();
}
return a.exec();
}
通常需要指定程序使用某一个确定的串口,这时不能只使用串口名称,因为USB串口每次插在不同的USB口上时获得的串口名称都可能有变化。这时可以利用串口的序列号,这个号码一般来说是唯一的。
刚才我的序列号是:5583332383935110B111
例程如下:
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSerialPortInfo com_info;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
{
if( info.serialNumber() == "5583332383935110B111" )
{
com_info = info;
break;
}
}
qDebug() << "Name : " << com_info.portName();
qDebug() << "Description : " << com_info.description();
qDebug() << "serialNumber: " << com_info.serialNumber();
return a.exec();
}
涉及的函数
1、availablePorts
static QList<QSerialPortInfo> availablePorts();
返回系统上可用串行端口的列表。
2、setPort
void setPort(const QSerialPortInfo &info);
设置存储在串行端口信息实例serial port info中的端口。
3、open
bool QSerialPort::open(OpenMode mode)
从qiodevice::open()重新实现。
使用openmode模式打开串行端口,如果成功,则返回true;否则返回false,并设置一个错误代码,可以通过调用error()方法获得该代码。
注意:如果打开端口成功,该方法将返回false,但无法成功设置任何端口设置。在这种情况下,端口将自动关闭,以避免使用不正确的设置离开端口。
警告:模式必须是qiodevice::readonly、qiodevice::writeonly或qiodevice::readwrite。不支持其他模式。
4、setPortName
void setPortName(const QString &name);
设置串行端口的名称。如果需要,可以将串行端口的名称作为短名称或长系统位置传递。
5、setBaudRate
bool setBaudRate(qint32 baudRate, Directions directions = AllDirections);
qint32 baudRate(Directions directions = AllDirections) const;
设备波特率的函数,对应此属性保留所需方向的数据波特率。
如果设置成功或在打开端口之前设置,则返回“真”;否则返回“假”,并设置一个错误代码,可以通过访问qserialport::error属性的值来获取该代码。要设置波特率,请使用枚举qserialport::baud rate或任何正qint32值。
注意:如果在打开端口之前设置了该设置,则实际的串行端口设置将在qserialPort::open()方法中自动完成,然后端口打开成功。
默认值为baud9600,即每秒9600位。
6、setDataBits
bool setDataBits(DataBits dataBits);
DataBits dataBits() const;
设置数据的函数,此属性将数据位保存在帧中。
如果设置成功或在打开端口之前设置,则返回“真”;否则返回“假”,并设置一个错误代码,可以通过访问qserialport::error属性的值来获取该代码。
注意:如果在打开端口之前设置了该设置,则实际的串行端口设置将在qserialPort::open()方法中自动完成,然后端口打开成功。
默认值为data8,即8个数据位。
7、setParity
bool setParity(Parity parity);
Parity parity() const;
设置奇偶校验函数,此属性保持奇偶校验模式。
如果设置成功或在打开端口之前设置,则返回“真”;否则返回“假”,并设置一个错误代码,可以通过访问qserialport::error属性的值来获取该代码。
注意:如果在打开端口之前设置了该设置,则实际的串行端口设置将在qserialPort::open()方法中自动完成,然后端口打开成功。
默认值为no parity,即无奇偶校验。
8、setStopBits
bool setStopBits(StopBits stopBits);
StopBits stopBits() const;
设置停止位的函数,此属性保存帧中的停止位数。
如果设置成功或在打开端口之前设置,则返回“真”;否则返回“假”,并设置一个错误代码,可以通过访问qserialport::error属性的值来获取该代码。
注意:如果在打开端口之前设置了该设置,则实际的串行端口设置将在qserialPort::open()方法中自动完成,然后端口打开成功。
默认值为OneStop,即1个停止位。
9、setFlowControl
bool setFlowControl(FlowControl flowControl);
FlowControl flowControl() const;
设置控制流模式的函数,此属性保持所需的流控制模式。
如果设置成功或在打开端口之前设置,则返回“真”;否则返回“假”,并设置一个错误代码,可以通过访问qserialport::error属性的值来获取该代码。
注意:如果在打开端口之前设置了该设置,则实际的串行端口设置将在qserialPort::open()方法中自动完成,然后端口打开成功。
默认值为NoFlowControl,即没有流控制。
读取串口设备
QSerialPort 负责具体的串口操作。选定串口后,要先打开串口,才能设置波特率等参数。这些参数都设置好了就可以使用了。最基本的操作无非是read() 和 write()。需要注意的是这两个操作都是非阻塞的。还有一个重要的signal 也需要用到,那就是 void QIODevice::readyRead()。每次串口收到数据后都会发出这个signal。我们的程序中需要定义一个slot,并将其与这个signal 相连接。这样,每次新数据到来后,我们就可以在slot中读取数据了。这时一定要将串口缓冲区中的数据全部读出来,可以利用readAll() 来实现。
综合例程:
#include "widget.h"
#include "ui_widget.h"
#include <QSerialPort>
#include <QSerialPortInfo>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//查找可用的串口
foreach (const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
QSerialPort serial;
serial.setPort(info);
if(serial.open(QIODevice::ReadWrite))
{
ui->PortBox->addItem(serial.portName());
serial.close();
}
}
//设置波特率下拉菜单默认显示第0项
ui->BaudBox->setCurrentIndex(0);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_OpenSerialButton_clicked()
{
if(ui->OpenSerialButton->text() == tr("打开串口"))
{
serial = new QSerialPort;
//设置串口名
serial->setPortName(ui->PortBox->currentText());
//打卡串口
serial->open(QIODevice::ReadWrite);
//设置波特率
serial->setBaudRate(ui->BaudBox->currentText().toInt());
//设置数据位数
switch (ui->BitBox->currentIndex())
{
case 8:
serial->setDataBits(QSerialPort::Data8);
case 7:
serial->setDataBits(QSerialPort::Data7);
case 6:
serial->setDataBits(QSerialPort::Data6);
case 5:
serial->setDataBits(QSerialPort::Data5);
break;
default:
break;
}
//设置校验位
switch (ui->ParityBox->currentIndex())
{
case 0:
serial->setParity(QSerialPort::NoParity);
break;
default:
break;
}
//设置停止位
switch (ui->BitBox->currentIndex())
{
case 1:
serial->setStopBits(QSerialPort::OneStop);
break;
case 2:
serial->setStopBits(QSerialPort::TwoStop);
default:
break;
}
//设置流控制
serial->setFlowControl(QSerialPort::NoFlowControl);
//关闭设置菜单使能
ui->PortBox->setEnabled(false);
ui->BaudBox->setEnabled(false);
ui->BitBox->setEnabled(false);
ui->ParityBox->setEnabled(false);
ui->StopBox->setEnabled(false);
ui->OpenSerialButton->setText(tr("关闭串口"));
//连接信号槽
connect(serial,SIGNAL(readyRead),this,SLOT(ReadData));
}
else
{
//关闭串口
serial->clear();
serial->close();
serial->deleteLater();
//恢复设置使能
ui->PortBox->setEnabled(true);
ui->BaudBox->setEnabled(true);
ui->BitBox->setEnabled(true);
ui->ParityBox->setEnabled(true);
ui->StopBox->setEnabled(true);
ui->OpenSerialButton->setText(tr("打开串口"));
}
}
//读取接收到的信息
void Widget::ReadData()
{
QByteArray buf;
buf = serial->readAll();
if(!buf.isEmpty())
{
QString str = ui->textEdit->toPlainText();
str+=tr(buf);
ui->textEdit->clear();
ui->textEdit->append(str);
}
buf.clear();
}
//发送按钮槽函数
void Widget::on_SendButton_clicked()
{
serial->write(ui->textEdit_2->toPlainText().toUtf8());
}
运行效果: