Qt5 串口数据读取

由于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());
}

运行效果:


image.png
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349