Qt串口通信,QSerialPort的使用

前言

Qt写上位机时,串口通信是个常用功能,在Qt4的时候有第三方模块QextSerialPort,到了Qt5.1官方提供了QSerialPort模块。

目录

前言

获取串口信息:QSerialPortInfo

串口IO操作:QSerialPort

实例操作

补充:16进制文本(如“0A 13 EF”)转16进制数据(如0x0A 0x13 0xEF)


使用该模块需要在pro文件中添加:QT += serialport 

主要使用两个类:QSerialPort和QSerialPortInfo

获取串口信息:QSerialPortInfo

获取串口名列表

QStringList slist;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
   //检测是否可用
   f(!info.isBusy())
       slist<<info.portName();
}

除了串口名还能获取其他的相关信息,可以看文档,或者该链接 https://blog.csdn.net/mcu_tian/article/details/43527385

串口IO操作:QSerialPort

串口IO的主要操作有参数设置/开/关/读/写等

//[1]串口设置
QSerialPort *serialIo=new QSerialPort;
serialIo->setPortName("COM4");   //串口名
serialIo->setBaudRate(115200);   //波特率
serialIo->setDataBits(QSerialPort::Data8);     //数据位
serialIo->setParity(QSerialPort::NoParity);    //校验位
serialIo->setStopBits(QSerialPort::OneStop);   //停止位
serialIo->setFlowControl(QSerialPort::NoFlowControl);//流控制一般没用

//[2]串口开启
if(serialIo->open(QIODevice::ReadWrite)){
    qDebug()<<"串口已打开,读写模式";
}else{
    qDebug()<<"串口打开异常"<<serialIo->errorString();
    serialIo->clearError();
}

//[3]数据发送
const QByteArray send_data=ui->textSend->toPlainText().toUtf8();//一般没发字符串,特别是中文
if(serialIo->isOpen()){
    serialIo->write(send_data);
    qDebug()<<"已发送:"<<QString::fromUtf8(send_data);
}else{
    qDebug()<<"发送失败,串口未打开";
}
if(!serialIo->waitForBytesWritten(30000)){
    qDebug()<<"命令发送异常"<<serialIo->errorString();
    serialIo->clearError();
}

//[4]数据接收
connect(serialIo,&QSerialPort::readyRead,this,[this](){
    if (serialIo->bytesAvailable()) {
        //串口收到的数据可能不是连续的,需要的话应该把数据缓存下来再进行协议解析,类似tcp数据处理
        const QByteArray recv_data=serialIo->readAll();
        //接收发送要一致,如果是处理字节数据,可以把QByteArray当数组一样取下标,或者用data()方法转为char*形式
        ui->textRecv->append(QString::fromUtf8(recv_data));//显示
        qDebug()<<"已接收:"<<QString::fromUtf8(recv_data);
    }
});

//[5]串口关闭
serialIo->clear();
serialIo->close();

实例操作

为了方便,ui我直接用的设计师拖得,工程文件我把百度云链接放在了最后,需要的可以下载

串口

头文件MainWidget.h(记得pro里引入serialport模块)

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>

class QSerialPort;

namespace Ui {
class MainWidget;
}

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0);
    ~MainWidget();

    void initSerial();//初始化串口设置
    void initMainUi();//初始化界面操作

    void openSerial();//槽函数-打开串口
    void closeSerial();//槽函数-关闭串口
    void refreshSerial();//槽函数-刷新串口名列表

private:
    QStringList getSerialPortNames();//获取串口名列表
    void setSerialEnable(bool enabled);//串口开启时就不能动ui配置了
    void sendData();//槽函数-发送数据
    void recvData();//槽函数-接收数据

private:
    Ui::MainWidget *ui;

    QSerialPort *serialIo;//串口io,一般可以把串口io扔到线程里去,避免阻塞ui
};

#endif // MAINWIDGET_H

实现文件MainWidget.cpp

#include "MainWidget.h"
#include "ui_MainWidget.h"

#include <QSerialPort>
#include <QSerialPortInfo>
#include <QListView>

#include <QDebug>

MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainWidget)
{
    ui->setupUi(this);
    initSerial();
    initMainUi();
}

MainWidget::~MainWidget()
{
    delete ui;
}

void MainWidget::initSerial()
{
    //[1]创建串口io对象
    serialIo=new QSerialPort(this);
    //数据接收处理
    connect(serialIo,&QSerialPort::readyRead,this,&MainWidget::recvData);
    //[2]界面初始化
    //注:items的选项值是根据文档中的枚举来写的
    //串口名
    refreshSerial();
    ui->boxPortName->setView(new QListView(this));
    //波特率
    QStringList baudrateList;
    baudrateList<<"1200"<<"2400"<<"4800"<<"9600"<<"19200"<<"38400"<<"57600"<<"115200";
    ui->boxBaudRate->addItems(baudrateList);//添加下拉列表选项
    ui->boxBaudRate->setEditable(true);//串口波特率可编辑
    ui->boxBaudRate->setCurrentText("115200");//界面中初始值
    ui->boxBaudRate->setView(new QListView(this));//该设置是配合qss的,不然item行高设置没效果
    //数据位
    QStringList databitList;
    databitList<<"5"<<"6"<<"7"<<"8";
    ui->boxDataBits->addItems(databitList);
    ui->boxDataBits->setCurrentText("8");
    ui->boxDataBits->setView(new QListView(this));
    //校验位
    QStringList parityList;
    parityList<<"No"<<"Even偶"<<"Odd奇"<<"Space"<<"Mark";
    ui->boxParity->addItems(parityList);
    ui->boxParity->setCurrentText("No");
    ui->boxParity->setView(new QListView(this));
    //停止位
    QStringList stopbitList;
    stopbitList<<"1"<<"1.5"<<"2";
    ui->boxStopBits->addItems(stopbitList);
    ui->boxStopBits->setCurrentText("1");
    ui->boxStopBits->setView(new QListView(this));
    //流控制
    QStringList flowctrlList;
    flowctrlList<<"No"<<"Hardware"<<"Software";
    ui->boxFlowControl->addItems(flowctrlList);
    ui->boxFlowControl->setCurrentText("No");
    ui->boxFlowControl->setView(new QListView(this));
}

void MainWidget::initMainUi()
{
    //点击串口[开启]/[关闭]按钮
    connect(ui->btnOpen,&QPushButton::clicked,this,[this](){
        if(ui->btnOpen->text()=="打开"){
            openSerial();
        }else{
            closeSerial();
        }
    });
    //点击串口[刷新]按钮-刷新串口名列表
    connect(ui->btnRefresh,&QPushButton::clicked,this,&MainWidget::refreshSerial);
    //点击数据[发送]按钮
    connect(ui->btnSend,&QPushButton::clicked,this,&MainWidget::sendData);
}

void MainWidget::openSerial()
{
    const QString portnameStr=ui->boxPortName->currentText();
    if(!portnameStr.isEmpty()){
        QSerialPortInfo info(portnameStr);
        if(info.isBusy()){
            qDebug()<<"当前串口繁忙,可能已被占用,请确认后再连接"<<portnameStr;
            return;
        }
        //
        qint32 baudrate=ui->boxBaudRate->currentText().toInt();
        QSerialPort::DataBits databit;
        switch (ui->boxDataBits->currentIndex()) {
        case 0:databit=QSerialPort::Data5; break;
        case 1:databit=QSerialPort::Data6; break;
        case 2:databit=QSerialPort::Data7; break;
        case 3:databit=QSerialPort::Data8; break;
        default:databit=QSerialPort::Data8; break;
        }
        QSerialPort::Parity parity;
        switch (ui->boxParity->currentIndex()) {
        case 0:parity=QSerialPort::NoParity; break;
        case 1:parity=QSerialPort::EvenParity; break;
        case 2:parity=QSerialPort::OddParity; break;
        case 3:parity=QSerialPort::SpaceParity; break;
        case 4:parity=QSerialPort::MarkParity; break;
        default:parity=QSerialPort::NoParity; break;
        }
        QSerialPort::StopBits stopbit;
        switch (ui->boxStopBits->currentIndex()) {
        case 0:stopbit=QSerialPort::OneStop; break;
        case 1:stopbit=QSerialPort::OneAndHalfStop; break;
        case 2:stopbit=QSerialPort::TwoStop; break;
        default:stopbit=QSerialPort::OneStop; break;
        }
        QSerialPort::FlowControl flowcontrol;
        switch (ui->boxFlowControl->currentIndex()) {
        case 0:flowcontrol=QSerialPort::NoFlowControl; break;
        case 1:flowcontrol=QSerialPort::HardwareControl; break;
        case 2:flowcontrol=QSerialPort::SoftwareControl; break;
        default:flowcontrol=QSerialPort::NoFlowControl; break;
        }
        //串口配置设置
        serialIo->setPortName(portnameStr);
        serialIo->setBaudRate(baudrate);
        serialIo->setDataBits(databit);
        serialIo->setParity(parity);
        serialIo->setStopBits(stopbit);
        serialIo->setFlowControl(flowcontrol);//这个我一般没用
        if(serialIo->open(QIODevice::ReadWrite)){
            qDebug()<<"串口已打开,读写模式";
            setSerialEnable(false);//改变ui状态
        }else{
            qDebug()<<"串口打开异常"<<portnameStr<<serialIo->errorString();
            serialIo->clearError();
            setSerialEnable(true);
        }
    }else{
        qDebug()<<"未找到可用串口,请确认串口连接正常后点击刷新";
    }
}

void MainWidget::closeSerial()
{
    serialIo->clear();
    serialIo->close();
    qDebug()<<"串口已关闭";
    setSerialEnable(true);
}

void MainWidget::refreshSerial()
{
    ui->boxPortName->clear();
    ui->boxPortName->addItems(getSerialPortNames());
}

QStringList MainWidget::getSerialPortNames()
{
    QStringList slist;
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        //检测是否可用
        if(!info.isBusy())
            slist<<info.portName();
    }
    if(slist.isEmpty()){
        qDebug()<<"未找到可用串口,请确认串口连接正常后点击刷新";
    }
    return slist;
}

void MainWidget::setSerialEnable(bool enabled)
{
    //打开成功就false不能再修改配置,关闭状态true可以进行设置
    ui->btnRefresh->setEnabled(enabled);
    ui->btnOpen->setText(enabled?QString("打开"):QString("关闭"));
    //可以把btn和配置分在两个widget里,这样直接设置widget的enable就没这么麻烦了
    ui->boxPortName->setEnabled(enabled);
    ui->boxBaudRate->setEnabled(enabled);
    ui->boxDataBits->setEnabled(enabled);
    ui->boxParity->setEnabled(enabled);
    ui->boxStopBits->setEnabled(enabled);
    ui->boxFlowControl->setEnabled(enabled);
}

void MainWidget::sendData()
{
    //注意收发的编码问题,我一般只是发命令吗和字节数据,没怎么发字符串,用latin1就行了
    const QByteArray send_data=ui->textSend->toPlainText().toUtf8();
    if(send_data.size()<=0)
        return;
    if(serialIo->isOpen()){
        serialIo->write(send_data);
        qDebug()<<"已发送:"<<QString::fromUtf8(send_data);
    }else{
        qDebug()<<"发送失败,串口未打开";
        return;
    }
    //Qt新版本默认值是30 000
    if(!serialIo->waitForBytesWritten(30000)){
        qDebug()<<"命令发送异常"<<serialIo->errorString();
        serialIo->clearError();
    }
}

void MainWidget::recvData()
{
    if (serialIo->bytesAvailable()) {
        //串口收到的数据可能不是连续的,需要的话应该把数据缓存下来再进行协议解析,类似tcp数据处理
        const QByteArray recv_data=serialIo->readAll();
        //接收发送要一致,如果是处理字节数据,可以把QByteArray当数组一样取下标,或者用data()方法转为char*形式
        ui->textRecv->append(QString::fromUtf8(recv_data));
        qDebug()<<"已接收:"<<QString::fromUtf8(recv_data);
    }
}

由于时间有限,所以有两个点没有写,一是一般我把串口IO放在子线程中避免阻塞ui;二是串口协议得解析,可以参照TCP数据的处理,先缓存起来再循环判断,一般一帧数据有帧头、帧长度、帧命令码、帧数据、帧尾/CRC校验等字段。

时间仓促,可能代码有bug,望大佬纠正。

附上百度云链接 https://pan.baidu.com/s/1gzHC6Q4fBZ_G1Db7tMcIaw 

提取码 inxh   文件名TestSerialPort.rar

补充:16进制文本(如“0A 13 EF”)转16进制数据(如0x0A 0x13 0xEF)

有时候需要这种功能,输入十六进制文本,发送的是对应的十六进制数据,Qt中通过QByteArray类可以轻松的搞定。

QString str="0A 13 EF"; //假设为文本框读取到的字符串
QByteArray temp=QByteArray::fromHex(str.toLatin1());
qDebug()<<str<<temp;

(输出结果中的十六进制因为被Qt做了转义,所以0x0A是个换行符)

当然,也可以把十六进制数据转为十六进制文本。

const char arr[]={0x0A,0x13,0xEF};
QByteArray temp=QByteArray(arr,3); //假设为收到的数据
QString str=temp.toHex(' ').toUpper(); //老版本Qt的toHex没有参数设置,需要自己分割
qDebug()<<temp<<str;

(2019-07-24晨)

Qt串口通信是一种可以在Qt平台上进行串口通信的解决方案。其中,Qt串口通信模块qserialport是实现串口通信的关键模块。本文将为大家介绍Qt串口通信模块qserialport的开发完整实例。 首先,我们需要在Qt中打开串口通信模块qserialport。在Qt 5.2及以上版本中,qserialport已经被包含在QtCore模块中。因此,我们只需要在代码中引入QtCore模块即可使用qserialport。 接下来,我们需要定义串口参数。我们可以利用QSerialPort类中提供的函数setBaudRate()、setDataBits()、setParity()等来设置参数。例如: ```cpp QSerialPort serial; serial.setBaudRate(QSerialPort::Baud9600); serial.setDataBits(QSerialPort::Data8); serial.setParity(QSerialPort::NoParity); serial.setStopBits(QSerialPort::OneStop); serial.setFlowControl(QSerialPort::NoFlowControl); ``` 然后,我们需要打开串口。我们可以使用QSerialPort类的open()函数来打开串口。例如: ```cpp serial.setPortName("COM1"); // 设置串口号为COM1 if (serial.open(QIODevice::ReadWrite)) { qDebug() << "串口已打开"; } else { qDebug() << "串口打开失败"; } ``` 接下来,我们可以通过QSerialPort类的函数write()和read()来进行数据读写。例如: ```cpp // 向串口写入数据 char buf[] = "hello world"; serial.write(buf, sizeof(buf)); // 从串口读取数据 QByteArray data = serial.readAll(); qDebug() << data; ``` 最后,在程序结束时我们需要关闭串口。我们可以使用QSerialPort类的close()函数来关闭串口。例如: ```cpp serial.close(); ``` 综上所述,以上就是Qt串口通信模块qserialport的开发完整实例。在实际应用中,我们可以根据需要对以上代码进行修改,从而实现更加复杂的串口通信功能。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值