QT - 串口QserialPort应用
https://doc.qt.io/qt-5.9/qserialport.html //文档 |
使用该模块需要在pro文件中添加:QT += serialport
主要使用两个类:QSerialPort (#include<QSerialPort>)和QSerialPortInfo (#include<QSerialPortInfo>)
获取串口信息:QserialPortInfo
QSerialPortInfo类提供已存在串口设备的信息。使用QSerialPortInfo类的静态成员函数生成QSerialPortInfo对象的链表。链表中的每个QSerialPortInfo对象代表一个串口,每个串口可以使用端口名、系统定位、描述、制造商查询。QSerialPortInfo类对象也可以用做QSerialPort类的setPort()成员函数的参数。
QSerialPortInfo类成员函数
//构造函数 QSerialPortInfo() QSerialPortInfo(const QSerialPort &port) QSerialPortInfo(const QString &name) QSerialPortInfo(const QSerialPortInfo &other) //析构函数 ~QSerialPortInfo() //返回当前系统可用串口的链表 [static] QList<QSerialPortInfo> QSerialPortInfo::availablePorts() //如果串口可用,返回串口的描述信息 QString QSerialPortInfo::description() const //如果有一个合法的16位生产码,返回true bool QSerialPortInfo::hasProductIdentifier() const //如果有一个合法的16位制造商编码,返回true bool QSerialPortInfo::hasVendorIdentifier() const //如果串口当前正忙,返回true bool QSerialPortInfo::isBusy() const //如果串口可用,返回串口的制造商的名字 QString QSerialPortInfo::manufacturer() const //返回串口的名字 QString QSerialPortInfo::portName() const //如果串口可用,返回串口的16位的生产编码 quint16 QSerialPortInfo::productIdentifier() const //如果串口可用,返回串口的序列号 QString QSerialPortInfo::serialNumber() const //返回目标平台支持的可用的标准波特率的链表 [static] QList<qint32> QSerialPortInfo::standardBaudRates() //使用other交换QSerialPortInfo对象 void QSerialPortInfo::swap(QSerialPortInfo &other) //返回串口的系统位置 QString QSerialPortInfo::systemLocation() const //如果串口可用,返回16位的制造商编码 quint16 QSerialPortInfo::vendorIdentifier() const |
程序示例
#include "widget.h" #include <QDebug> #include <QList> #include <QtSerialPort/QSerialPortInfo> Widget::Widget(QWidget *parent) : QWidget(parent) { //获取当前系统下所有可以用的串口 QList<QSerialPortInfo> serialPortInfo = QSerialPortInfo::availablePorts(); qDebug() << "串口的个数: " << serialPortInfo.count(); //显示目标串口支持的波特率列表 QList<qint32> baudRates = QSerialPortInfo::standardBaudRates(); qDebug() << baudRates; qDebug() << "串口的描述:" << serialPortInfo.at(0).description(); qDebug() << "hasProductIdentifier(): " << serialPortInfo.at(0).hasProductIdentifier(); qDebug() << "hasVendorIdentifier(): " << serialPortInfo.at(0).hasVendorIdentifier(); qDebug() << "isBusy: " << serialPortInfo.at(0).isBusy(); qDebug() << "manufacturer: " << serialPortInfo.at(0).manufacturer(); qDebug() << "portName: " << serialPortInfo.at(0).portName(); qDebug() << "productIdentifier: " << serialPortInfo.at(0).productIdentifier(); qDebug() << "serialNumber: " << serialPortInfo.at(0).serialNumber(); qDebug() << "vendorIdentifier: " << serialPortInfo.at(0).vendorIdentifier(); qDebug() << "systemLocation: " << serialPortInfo.at(0).systemLocation(); } Widget::~Widget() { } |
QIODevice
QIODevice用于对输入输出设备进行管理。输入设备有两种类型,一种是随机访问设备(Random-accessdevices),如文件、缓冲区等;另一种是时序设备(Sequential device),如网络、进程等。可以通过isSequential()函数分辨设备是哪种类型的。
OpenMode
返回类型 | 函数 | 说明 |
OpenMode | openMode() const | QIODevice::OpenMode |
void [protected] | setOpenMode(OpenMode openMode) | |
bool | isOpen() const | QIODevice::NotOpen |
bool | isReadable() const | QIODevice::ReadOnly |
bool | isWritable() const | QIODevice::WriteOnly |
bool | isTextModeEnabled() const | QIODevice::Text |
void | setTextModeEnabled(bool enabled) | QIODevice::Text |
bool [virtual] | isSequential() const |
打开关闭设备
返回类型 | 函数 | 说明 |
bool [virtual] | open(OpenMode mode) | |
bool | isOpen() const | |
void [virtual] | close() | |
void [signal] | aboutToClose() | |
Bool [virtual] | reset() | 重打开设备。 不适用于QIODevice::Text类设备。 |
数据读写操作及控制
输入函数 | ||
返回类型 | 函数 | 说明 |
bool [virtual] | waitForReadyRead(int msecs) | 等待可读 |
void [signal] | readyRead() | 中断式可读 |
qint64 [virtual] | bytesAvailable() const | 查询式可读 |
qint64 [protected] [pure virtual] | readData(char* data, qint64 maxSize) |
|
qint64 [protected] [pure virtual] | readData(char * data, qint64 maxSize) |
|
qint64 [protected] [pure virtual] | readData(char * data, qint64 maxSize) |
|
qint64 [protected] [pure virtual] | readData(char * data, qint64 maxSize) |
|
qint64 [protected] [pure virtual] | readData(char* data, qint64 maxSize) |
|
qint64 [protected] [pure virtual] | readData(char* data, qint64 maxSize) |
|
qint64 [protected] [pure virtual] | readData(char* data, qint64 maxSize) |
|
qint64 [virtual] [protected] | readLineData(char* data, qint64 maxSize) |
|
qint64 | readLine(char * data, qint64 maxSize) | 读行(maxSize包括行结束字符) |
QByteArray | readLine(qint64 maxSize = 0) | 读行 |
bool | getChar(char * c) | 读char |
void | ungetChar(char c) | undo char |
void [signal] | readChannelFinished() | 输入通道结束。 但此时可能仍有数据可读。 |
输出函数 | ||
返回类型 | 函数 | 说明 |
bool [virtual] | waitForBytesWritten(int msecs) | 等待已写 |
void [signal] | bytesWritten(qint64 bytes) | 中断式已写 |
qint64 [virtual] | bytesToWrite() const | 查询式写完 |
qint64 [protected] [pure virtual] | writeData(const char* data,qint64 maxSize) |
写函数 |
qint64 | write(const char * data, qint64 maxSize) | 写 |
qint64 | write(const char * data) | |
qint64 | write(const QByteArray & byteArray) | |
bool | putChar(char c) | 写char |
isSequential()不同有区别的函数 | ||
返回类型 | 函数 | 说明 |
qint64 [virtual] | pos() const | |
bool [virtual] | seek(qint64 pos) | |
qint64 [virtual] | size() const | 随机访问设备为区域大小,时序设备为bytesAvailable() |
其他函数 | ||
返回类型 | 函数 | 说明 |
bool [virtual] | atEnd() const | |
QString | errorString() const | |
void [protected] | setErrorString(const QString & str) |
串口IO操作:QserialPort
设置好端口后,可以使用open()函数以只读、只写或读写的模式打开使用。
注意,串口使用独占方式打开。使用close()函数关闭串口并且取消IO操作。
串口成功打开后,QSerialPort会尝试确定串口的当前配置并初始化。可以使用setBaudRate()、setDataBits()、setParity()、setStopBits()和setFlowControl()函数重新配置端口设置。
有一对名为QSerialPort::dataTerminalReady、QSerialPort::requestToSend的属性
QSerialPort提供了中止正在调用线程直到信号触发的一系列函数。这些函数用于阻塞串口。
waitForReadyRead():阻塞调用,直到有新的数据可读
waitForBytesWritten():阻塞调用,直到数据以及写入串口
阻塞串口编程与非阻塞串口编程完全不同。阻塞串口不会要求时间循环并且通常会简化代码。然而,在GUI程序中,为了避免冻结用户界面,阻塞串口编程只能用于非GUI线程。
QSerialPort也能使用QTextStream和QDataStream的流操作符。在试图使用流操作符>>读时,需要确保有足够可用的数据。
Qt串口通信模块QSerialPort类成员函数
//构造函数 QSerialPort::QSerialPort(QObject *parent = Q_NULLPTR) QSerialPort::QSerialPort(const QString &name, QObject *parent = Q_NULLPTR) QSerialPort::QSerialPort(const QSerialPortInfo &serialPortInfo, QObject *parent = Q_NULLPTR) //如果当前没有数据可读,返回true [virtual] bool QSerialPort::atEnd() const //波特率改变后,信号触发 [signal] void QSerialPort::baudRateChanged(qint32 baudRate, QSerialPort::Directions directions) //返回可读数据的字节数 [virtual] qint64 QSerialPort::bytesAvailable() const //返回可写数据的字节数 [virtual] qint64 QSerialPort::bytesToWrite() const //关闭串口 [virtual] void QSerialPort::close() //设置串口端口信息为serialPortInfo void QSerialPort::setPort(const QSerialPortInfo &serialPortInfo) //设置串口名为name void QSerialPort::setPortName(const QString &name) qint32 baudRate(QSerialPort::Directions directions = AllDirections) const bool clear(QSerialPort::Directions directions = AllDirections) void clearError() QSerialPort::DataBits dataBits() const QSerialPort::SerialPortError error() const QSerialPort::FlowControl flowControl() const bool flush() QSerialPort::Handle handle() const Bool isBreakEnabled() const bool isDataTerminalReady() bool isRequestToSend() QSerialPort::Parity parity() const QSerialPort::PinoutSignals pinoutSignals() QString portName() const qint64 readBufferSize() const bool setBaudRate(qint32 baudRate, QSerialPort::Directions directions = AllDirections) bool setBreakEnabled(bool set = true) bool setDataBits(QSerialPort::DataBits dataBits) bool setDataTerminalReady(bool set) bool setFlowControl(QSerialPort::FlowControl flowControl) bool setParity(QSerialPort::Parity parity) void setPort(const QSerialPortInfo &serialPortInfo) void setPortName(const QString &name) void setReadBufferSize(qint64 size) bool setRequestToSend(bool set) bool setStopBits(QSerialPort::StopBits stopBits) QSerialPort::StopBits stopBits() const 重新实现的公共函数 virtual qint64 bytesAvailable() const override virtual qint64 bytesToWrite() const override virtual bool canReadLine() const override virtual void close() override virtual bool isSequential() const override virtual bool open(QIODeviceBase::OpenMode mode) override virtual bool waitForBytesWritten(int msecs = 30000) override virtual bool waitForReadyRead(int msecs = 30000) override Signals void baudRateChanged(qint32 baudRate, QSerialPort::Directions directions) void breakEnabledChanged(bool set) void dataBitsChanged(QSerialPort::DataBits dataBits) void dataTerminalReadyChanged(bool set) void errorOccurred(QSerialPort::SerialPortError error) void flowControlChanged(QSerialPort::FlowControl flow) void parityChanged(QSerialPort::Parity parity) void requestToSendChanged(bool set) void stopBitsChanged(QSerialPort::StopBits stopBits) Reimplemented Protected Functions virtual qint64 readData(char *data, qint64 maxSize) override virtual qint64 readLineData(char *data, qint64 maxSize) override virtual qint64 writeData(const char *data, qint64 maxSize) override |
串口IO的主要操作有参数设置/开/关/读/写等
QSerialPort *serial = new QSerialPort; //设置串口名 serial->setPortName(name); //打开串口 serial->open(QIODevice::ReadWrite); //设置波特率 serial->setBaudRate(BaudRate); //设置数据位数 serial->setDataBits(QSerialPort::Data8); //设置奇偶校验 serial->setParity(QSerialPort::NoParity); //设置停止位 serial->setStopBits(QSerialPort::OneStop); //设置流控制 serial->setFlowControl(QSerialPort::NoFlowControl); //[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(); |
串口例程
Qt串口通信模块QSerialPort简单串口实例
mainwindows.h代码参考如下:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo> #include <QList> #include <QDebug> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void on_btn_openConsole_clicked(); void on_btn_send_clicked(); void on_btn_clearRecv_clicked(); void on_btn_clearSend_clicked(); void readData(); private: Ui::MainWindow *ui; QSerialPort *serial; }; #endif // MAINWINDOW_H |
mainwindows.cpp代码
#include "mainwindow.h" #include "ui_mainwindow.h" static const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog", "N/A"); MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); serial = new QSerialPort; QString description; QString manufacturer; QString serialNumber; //获取可以用的串口 QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts(); //输出当前系统可以使用的串口个数 qDebug() << "Total numbers of ports: " << serialPortInfos.count(); //将所有可以使用的串口设备添加到ComboBox中 for (const QSerialPortInfo &serialPortInfo : serialPortInfos) { QStringList list; description = serialPortInfo.description(); manufacturer = serialPortInfo.manufacturer(); serialNumber = serialPortInfo.serialNumber(); list << serialPortInfo.portName() << (!description.isEmpty() ? description : blankString) << (!manufacturer.isEmpty() ? manufacturer : blankString) << (!serialNumber.isEmpty() ? serialNumber : blankString) << serialPortInfo.systemLocation() << (serialPortInfo.vendorIdentifier() ? QString::number(serialPortInfo.vendorIdentifier(), 16) : blankString) << (serialPortInfo.productIdentifier() ? QString::number(serialPortInfo.productIdentifier(), 16) : blankString); ui->comboBox_serialPort->addItem(list.first(), list); } ui->comboBox_serialPort->addItem(tr("custom")); //设置波特率 ui->comboBox_baudRate->addItem(QStringLiteral("9600"), QSerialPort::Baud9600); ui->comboBox_baudRate->addItem(QStringLiteral("19200"), QSerialPort::Baud19200); ui->comboBox_baudRate->addItem(QStringLiteral("38400"), QSerialPort::Baud38400); ui->comboBox_baudRate->addItem(QStringLiteral("115200"), QSerialPort::Baud115200); ui->comboBox_baudRate->addItem(tr("Custom")); //设置数据位 ui->comboBox_dataBits->addItem(QStringLiteral("5"), QSerialPort::Data5); ui->comboBox_dataBits->addItem(QStringLiteral("6"), QSerialPort::Data6); ui->comboBox_dataBits->addItem(QStringLiteral("7"), QSerialPort::Data7); ui->comboBox_dataBits->addItem(QStringLiteral("8"), QSerialPort::Data8); ui->comboBox_dataBits->setCurrentIndex(3); //设置奇偶校验位 ui->comboBox_parity->addItem(tr("None"), QSerialPort::NoParity); ui->comboBox_parity->addItem(tr("Even"), QSerialPort::EvenParity); ui->comboBox_parity->addItem(tr("Odd"), QSerialPort::OddParity); ui->comboBox_parity->addItem(tr("Mark"), QSerialPort::MarkParity); ui->comboBox_parity->addItem(tr("Space"), QSerialPort::SpaceParity); //设置停止位 ui->comboBox_stopBit->addItem(QStringLiteral("1"), QSerialPort::OneStop); ui->comboBox_stopBit->addItem(QStringLiteral("2"), QSerialPort::TwoStop); //添加流控 ui->comboBox_flowBit->addItem(tr("None"), QSerialPort::NoFlowControl); ui->comboBox_flowBit->addItem(tr("RTS/CTS"), QSerialPort::HardwareControl); ui->comboBox_flowBit->addItem(tr("XON/XOFF"), QSerialPort::SoftwareControl); //禁用发送按钮 ui->btn_send->setEnabled(false); } MainWindow::~MainWindow() { //delete serial; delete ui; } //打开串口按钮槽函数 void MainWindow::on_btn_openConsole_clicked() { qDebug() << ui->btn_openConsole->text(); if (ui->btn_openConsole->text() == tr("打开串口")) { //设置串口名字 serial->setPortName(ui->comboBox_serialPort->currentText()); //设置波特率 serial->setBaudRate(ui->comboBox_baudRate->currentText().toInt()); //设置数据位 serial->setDataBits(QSerialPort::Data8); //设置奇偶校验位 serial->setParity(QSerialPort::NoParity); //设置停止位 serial->setStopBits(QSerialPort::OneStop); //设置流控 serial->setFlowControl(QSerialPort::NoFlowControl); //打开串口 if (serial->open(QIODevice::ReadWrite)) { ui->comboBox_baudRate->setEnabled(false); ui->comboBox_dataBits->setEnabled(false); ui->comboBox_flowBit->setEnabled(false); ui->comboBox_parity->setEnabled(false); ui->comboBox_serialPort->setEnabled(false); ui->comboBox_stopBit->setEnabled(false); ui->btn_send->setEnabled(true); ui->btn_openConsole->setText(tr("关闭串口")); //信号与槽函数关联 connect(serial, &QSerialPort::readyRead, this, &MainWindow::readData); } } else { //关闭串口 //serial->clear(); serial->close(); //serial->deleteLater(); //恢复设置功能 ui->comboBox_baudRate->setEnabled(true); ui->comboBox_dataBits->setEnabled(true); ui->comboBox_flowBit->setEnabled(true); ui->comboBox_parity->setEnabled(true); ui->comboBox_serialPort->setEnabled(true); ui->comboBox_stopBit->setEnabled(true); ui->btn_openConsole->setText(tr("打开串口")); ui->btn_send->setEnabled(false); } } //发送数据槽函数 void MainWindow::on_btn_send_clicked() { serial->write(ui->textEdit_send->toPlainText().toLatin1()); } //清空接收数据槽函数 void MainWindow::on_btn_clearRecv_clicked() { ui->textEdit_recv->clear(); } //清空发送区槽函数 void MainWindow::on_btn_clearSend_clicked() { ui->textEdit_send->clear(); } void MainWindow::readData() { QByteArray buf; qDebug() << "readData: " << endl; buf = serial->readAll(); if (!buf.isEmpty()) { QString str = ui->textEdit_recv->toPlainText(); str += tr(buf); ui->textEdit_recv->clear(); ui->textEdit_recv->append(str); } } |
图形界面设计
图形界面相关属性设置:
用的时候在 pro 添加这句导入模块 QT += serialport
1.连接串口
第一步就是 要先获取到 可连接的所有的串口的名字
QSerialPortInfo::availablePorts() [static] QList<QSerialPortInfo> QSerialPortInfo::availablePorts() Returns a list of available serial ports on the system. 返回系统上可用串行端口的列表 |
QSerialPortInfo
Provides information about existing serial ports. Use the static functions to generate a list of QSerialPortInfo objects. Each QSerialPortInfo object in the list represents a single serial port and can be queried for the port name, system location, description, and manufacturer. The QSerialPortInfo class can also be used as an input parameter for the setPort() method of the QSerialPort class. See also QSerialPort. 提供关于现有串行端口的信息。 使用静态函数生成QSerialPortInfo对象列表。列表中的每个QSerialPortInfo对象表示一个串行端口,可以查询端口名称、系统位置、描述和制造商。QSerialPortInfo类还可以用作QSerialPort类的setPort()方法的输入参数。 |
//这样我们就获取到 可用的串口名字了 QStringList m_serialPortName; foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) { m_serialPortName << info.portName(); qDebug()<<"serialPortName:"<<info.portName(); } |
获取到串口名字列表以后,我们需要选择一个需要连接的 (自行根据选择)
2.根据串口名字 打开串口
#include<QSerialPort> QSerialPort *m_serialPort = new QSerialPort(); //实例化串口类一个对象 if(m_serialPort->isOpen()) //如果串口已经打开了 先给他关闭了 { m_serialPort->clear(); m_serialPort->close(); } //设置串口名字 假设我们上面已经成功获取到了 并且使用第一个 m_serialPort->setPortName(m_serialPortName[0]); if(!m_serialPort->open(QIODevice::ReadWrite)) //用ReadWrite 的模式尝试打开串口 { qDebug()<<m_serialPortName[0]<<"打开失败!"; return; } //打开成功 m_serialPort->setBaudRate(QSerialPort::Baud115200,QSerialPort::AllDirections);//设置波特率和读写方向 m_serialPort->setDataBits(QSerialPort::Data8); //数据位为8位 m_serialPort->setFlowControl(QSerialPort::NoFlowControl); //无流控制 m_serialPort->setParity(QSerialPort::NoParity); //无校验位 m_serialPort->setStopBits(QSerialPort::OneStop); //一位停止位 //连接信号槽 当下位机发送数据QSerialPortInfo 会发送个 readyRead 信号,我们定义个槽void receiveInfo()解析数据 connect(m_serialPort,SIGNAL(readyRead()),this,SLOT(receiveInfo())); |
3.下位机(单片机) ,上位机(Qt程序) 发送交互数据
//接收单片机的数据 void receiveInfo() { QByteArray info = m_serialPort->readAll(); QByteArray hexData = info.toHex(); //这里面的协议 你们自己定义就行 单片机发什么 代表什么 我们这里简单模拟一下 if(hexData == "0x10000") { //do something } else if(hexData == "0x100001") { //do something } } //向单片机发送数据 //基本和单片机交互 数据 都是16进制的 我们这里自己写一个 Qstring 转为 16进制的函数 void convertStringToHex(const QString &str, QByteArray &byteData) { int hexdata,lowhexdata; int hexdatalen = 0; int len = str.length(); byteData.resize(len/2); char lstr,hstr; for(int i=0; i<len; ) { //char lstr, hstr=str[i].toLatin1(); if(hstr == ' ') { i++; continue; } i++; if(i >= len) break; lstr = str[i].toLatin1(); hexdata = convertCharToHex(hstr); lowhexdata = convertCharToHex(lstr); if((hexdata == 16) || (lowhexdata == 16)) break; else hexdata = hexdata*16+lowhexdata; i++; byteData[hexdatalen] = (char)hexdata; hexdatalen++; } byteData.resize(hexdatalen); } //另一个 函数 char 转为 16进制 char SerialPort::convertCharToHex(char ch) { /*0x30等于十进制的48,48也是0的ASCII值,1-9的ASCII值是49-57,,所以某一个值-0x30,就是将字符0-9转换为0-9*/ if((ch >= '0') && (ch <= '9')) return ch-0x30; else if((ch >= 'A') && (ch <= 'F')) return ch-'A'+10; else if((ch >= 'a') && (ch <= 'f')) return ch-'a'+10; else return (-1); } //写两个函数 向单片机发送数据 void sendInfo(char* info,int len){ for(int i=0; i<len; ++i) { printf("0x%x\n", info[i]); } m_serialPort->write(info,len);//这句是真正的给单片机发数据 用到的是QIODevice::write 具体可以看文档 } void sendInfo(const QString &info){ QByteArray sendBuf; if (info.contains(" ")) { info.replace(QString(" "),QString("")); //我这里是把空格去掉,根据你们定的协议来 } qDebug()<<"Write to serial: "<<info; convertStringToHex(info, sendBuf); //把QString 转换 为 hex m_serialPort->write(sendBuf);//这句是真正的给单片机发数据 用到的是QIODevice::write 具体可以看文档 } |
4.析构的时候 关闭串口
if (m_serialPort->isOpen()) { m_serialPort->close(); } delete m_serialPort; |
源码:
widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QSerialPort> #include <QSerialPortInfo> #include <QComboBox> #include <QPushButton> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); void initUI(); QStringList getPortNameList(); //获取所有可用的串口列表 void openPort();//打开串口 public slots: void receiveInfo(); private: Ui::Widget *ui; QSerialPort* m_serialPort; //串口类 QStringList m_portNameList; QComboBox* m_PortNameComboBox; QPushButton* m_OpenPortButton; }; #endif // WIDGET_H |
widget.cpp
#include "widget.h" #include "ui_widget.h" #include <QLayout> #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); m_serialPort = new QSerialPort(); initUI(); m_portNameList = getPortNameList(); m_PortNameComboBox->addItems(m_portNameList); connect(m_OpenPortButton,&QPushButton::clicked,this,&Widget::openPort); } Widget::~Widget() { if (m_serialPort->isOpen()) { m_serialPort->close(); } delete m_serialPort; delete ui; } void Widget::initUI() { this->setWindowTitle("码农小明 test QSerialPort"); m_OpenPortButton = new QPushButton(); m_OpenPortButton->setText("打开串口"); m_PortNameComboBox = new QComboBox(); QHBoxLayout *m_layout = new QHBoxLayout(); m_layout->addWidget(m_PortNameComboBox); m_layout->addWidget(m_OpenPortButton); this->setLayout(m_layout); } QStringList Widget::getPortNameList() { QStringList m_serialPortName; foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) { m_serialPortName << info.portName(); qDebug()<<"serialPortName:"<<info.portName(); } return m_serialPortName; } void Widget::openPort() { if(m_serialPort->isOpen()) //如果串口已经打开了 先给他关闭了 { m_serialPort->clear(); m_serialPort->close(); } m_serialPort->setPortName(m_PortNameComboBox->currentText());//当前选择的串口名字 if(!m_serialPort->open(QIODevice::ReadWrite)) //用ReadWrite 的模式尝试打开串口 { qDebug()<<"打开失败!"; return; } qDebug()<<"串口打开成功!"; m_serialPort->setBaudRate(QSerialPort::Baud115200,QSerialPort::AllDirections);//设置波特率和读写方向 m_serialPort->setDataBits(QSerialPort::Data8); //数据位为8位 m_serialPort->setFlowControl(QSerialPort::NoFlowControl);//无流控制 m_serialPort->setParity(QSerialPort::NoParity); //无校验位 m_serialPort->setStopBits(QSerialPort::OneStop); //一位停止位 connect(m_serialPort,SIGNAL(readyRead()),this,SLOT(receiveInfo())); } //接收到单片机发送的数据进行解析 void Widget::receiveInfo() { QByteArray info = m_serialPort->readAll(); qDebug()<<"receive info:"<<info; } |
QT实现串口通信
要实现串口通信,需要知道串口通信需要的信息
主要参数有:波特率、校验位、数据位、停止位、控制流
主要操作有:串口的打开和关闭、刷新设备串口、接发数据、开关显示灯等。
实现效果如图:
界面设计如下:
每个控件类名如下:
LED灯是QLable控件,设置它的长宽都是24px,然后鼠标右击,选择“样式表”,在样式表中添加代码。
最后附赠完整源码。
第一步:在头文件中引入 QtSerialPort 类的两个头文件
// 引入串口通信的两个头文件(第一步) #include <QtSerialPort/QSerialPort> // 提供访问串口的功能 #include <QtSerialPort/QSerialPortInfo> // 提供系统中存在的串口信息 |
第二步:在工程文件中添加以下代码
# 引入串口工程类型 QT += serialport |
第三步:在头文件中定义全局的串口对象
QSerialPort *serial; // 定义全局的串口对象(第三步) |
第四步:参数设置,在头文件中定义初始化参数的函数和参数变量名,在.cpp文件中实现函数
public: void SerialPortInit(); // 串口初始化(参数配置) private: // 参数配置 QStringList baudList; //波特率 QStringList parityList; //校验位 QStringList dataBitsList; //数据位 QStringList stopBitsList; //停止位 QStringList flowControlList; //控制流 |
// 串口初始化(参数配置) void MainWindow::SerialPortInit() { serial = new QSerialPort; //申请内存,并设置父对象 // 获取计算机中有效的端口号,然后将端口号的名称给端口选择控件 foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) { serial->setPort(info); // 在对象中设置串口 if(serial->open(QIODevice::ReadWrite)) // 以读写方式打开串口 { ui->PortBox->addItem(info.portName()); // 添加计算机中的端口 serial->close(); // 关闭 } else { qDebug() << "串口打开失败,请重试"; } } // 参数配置 // 波特率,波特率默认选择57600 ,禁止用户点击 ui->BaudBox->addItem("57600"); serial->setBaudRate(QSerialPort::Baud57600); ui->BaudBox->setDisabled(true); // 校验,校验默认选择无 ui->ParityBox->addItem("无"); serial->setParity(QSerialPort::NoParity); // 数据位,数据位默认选择8位 ui->BitBox->addItem("8"); serial->setDataBits(QSerialPort::Data8); // 停止位,停止位默认选择1位 ui->StopBox->addItem("1"); serial->setStopBits(QSerialPort::OneStop); // 控制流,默认选择无 ui->ControlBox->addItem("无"); serial->setFlowControl(QSerialPort::NoFlowControl); // 刷新串口 RefreshSerialPort(0); // 信号 connect(serial,&QSerialPort::readyRead,this,&MainWindow::DataReceived); //接收数据 connect(ui->SendWordOrder,&QPushButton::clicked,this,&MainWindow::DataSend); //发送数据 connect(ui->SendButton,&QPushButton::clicked,this,&MainWindow::DataSend); //发送数据 connect(ui->SendEditBtn1,&QPushButton::clicked,this,&MainWindow::DataSend); //发送数据 connect(ui->SendEditBtn2,&QPushButton::clicked,this,&MainWindow::DataSend); //发送数据 connect(ui->SendEditBtn3,&QPushButton::clicked,this,&MainWindow::DataSend); //发送数据 } |
第五步:刷新串口,及时更新可用的串口
// 刷新串口 void MainWindow::RefreshSerialPort(int index) { QStringList portNameList; // 存储所有串口名 if(index != 0) { serial->setPortName(ui->PortBox->currentText()); //设置串口号 } else { ui->PortBox->clear(); //关闭串口号 ui->PortBox->addItem("刷新"); //添加刷新 foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) //添加新串口 { portNameList.append(info.portName()); } ui->PortBox->addItems(portNameList); ui->PortBox->setCurrentIndex(1); // 当前串口号为COM1 serial->setPortName(ui->PortBox->currentText()); //设置串口号 } } |
第六步:发送数据和接收数据
// 接收数据,使用read () / readLine () / readAll () void MainWindow::DataReceived() { char BUF[512] = {0}; // 存储转换类型后的数据 QByteArray data = serial->readAll(); // 读取数据 if(!data.isEmpty()) // 接收到数据 { QString str = ui->DataReceived->toPlainText(); // 返回纯文本 str += tr(data); // 数据是一行一行传送的,要保存所有数据 ui->DataReceived->clear(); // 清空之前的数据 ui->DataReceived->append(str); // 将数据放入控件中 qDebug() << "str info: " << ui->DataReceived->toPlainText(); // 清除之前的数据,防止追加,因为每次获取的数据不一样 int index = str.indexOf("\r\n"); // 找到,返回索引值,找不到,返回-1 if(index != -1) { snprintf(BUF,500,"%s", str.left(index + 2).toUtf8().data()); // QString转为char * 类型 qDebug() << "BUF info: " << BUF; // 数据类型转换成功 str.remove(0,index + 2); // 处理获取到的数据,将其放入对应的控件中 } } } // 发送数据,write () void MainWindow::DataSend() { serial->write(ui->DataSend->toPlainText().toLatin1()); // 串口发送数据 } |
第七步:打开串口和关闭串口,当打开串口后,显示绿灯;关闭串口后,显示红灯
// 串口开关 void MainWindow::on_OpenSerialButton_clicked() { if(serial->isOpen()) // 如果串口打开了,先给他关闭 { serial->clear(); serial->close(); // 关闭状态,按钮显示“打开串口” ui->OpenSerialButton->setText("打开串口"); // 关闭状态,允许用户操作 ui->BaudBox->setDisabled(false); ui->ParityBox->setDisabled(false); ui->BitBox->setDisabled(false); ui->StopBox->setDisabled(false); ui->ControlBox->setDisabled(false); // 禁止操作“发送字符操作” ui->SendWordOrder->setDisabled(true); ui->SendButton->setDisabled(true); // 关闭状态,颜色为绿色 ui->OpenSerialButton->setStyleSheet("color: green;"); // 关闭,显示灯为红色 LED(true); // 清空数据 ui->DataReceived->clear(); ui->DataSend->clear(); } else // 如果串口关闭了,先给他打开 { //当前选择的串口名字 serial->setPortName(ui->PortBox->currentText()); //用ReadWrite 的模式尝试打开串口,无法收发数据时,发出警告 if(!serial->open(QIODevice::ReadWrite)) { QMessageBox::warning(this,tr("提示"),tr("串口打开失败!"),QMessageBox::Ok); return; } // 打开状态,按钮显示“关闭串口” ui->OpenSerialButton->setText("关闭串口"); // 打开状态,禁止用户操作 ui->BaudBox->setDisabled(true); ui->ParityBox->setDisabled(true); ui->BitBox->setDisabled(true); ui->StopBox->setDisabled(true); ui->ControlBox->setDisabled(true); // 允许操作“发送字符操作” ui->SendWordOrder->setDisabled(false); ui->SendButton->setDisabled(false); // 打开状态,颜色为红色 ui->OpenSerialButton->setStyleSheet("color: red;"); // 打开,显示灯为绿色 LED(false); } } // 开关显示灯 void MainWindow::LED(bool changeColor) { if(changeColor == false) { // 显示绿色 ui->LED->setStyleSheet("background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(0, 229, 0, 255), stop:1 rgba(255, 255, 255, 255));border-radius:12px;"); } else { // 显示红色 ui->LED->setStyleSheet("background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(255, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));border-radius:12px;"); } } |
第八步:相关槽函数
// 控件中添加 指令“###” void MainWindow::on_SendButton_clicked() { on_ClearButton_clicked(); ui->DataSend->append("###"); } // 清空控件 void MainWindow::on_ClearButton_clicked() { ui->DataSend->setText(""); } // 清空接收到的数据 void MainWindow::on_ClearShowButton_clicked() { ui->DataReceived->setText(""); } // 点击发送后,获取串口信息并展示在接受控件中 void MainWindow::on_SendEditBtn1_clicked() { on_ClearButton_clicked(); QString EditText = ui->Edit1->text(); //获取发送框内容 ui->DataSend->setText(EditText); //将文本内容放在发送栏中 } void MainWindow::on_SendEditBtn2_clicked() { on_ClearButton_clicked(); QString EditText = ui->Edit2->text(); //获取发送框内容 // qDebug() << "Edit1 text: " << EditText; ui->DataSend->append(EditText); //将文本内容放在发送栏中 } void MainWindow::on_SendEditBtn3_clicked() { on_ClearButton_clicked(); QString EditText = ui->Edit3->text(); //获取发送框内容 // qDebug() << "Edit1 text: " << EditText; ui->DataSend->append(EditText); //将文本内容放在发送栏中 } void MainWindow::on_SendWordOrder_clicked() { on_SendButton_clicked(); } |
-
-
- 源码:
-
工程文件.pro文件源码:
QT += core gui # 引入串口工程类型 QT += serialport greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mainwindow.cpp HEADERS += \ mainwindow.h FORMS += \ mainwindow.ui # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target |
头文件源码:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> // 引入串口通信的两个头文件(第一步) #include <QtSerialPort/QSerialPort> // 提供访问串口的功能 #include <QtSerialPort/QSerialPortInfo> // 提供系统中存在的串口信息 QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); // 串口功能 void SerialPortInit(); // 串口初始化(参数配置) void RefreshSerialPort(int index); // 刷新串口 public slots: // 串口槽函数 void DataReceived(); // 接收数据 private slots: // 串口槽函数 void DataSend(); // 发送数据 void on_OpenSerialButton_clicked(); // 串口开关 void on_SendButton_clicked(); // 控件中添加 # void on_ClearButton_clicked(); // 清空控件中的所有 # void on_ClearShowButton_clicked(); // 清空接收到的数据 void LED(bool changeColor); // 开关显示灯 // 点击发送,接收数据 void on_SendEditBtn1_clicked(); void on_SendEditBtn2_clicked(); void on_SendEditBtn3_clicked(); void on_SendWordOrder_clicked(); private: Ui::MainWindow *ui; // 串口变量 QSerialPort *serial; // 定义全局的串口对象(第三步) // 参数配置 QStringList baudList; //波特率 QStringList parityList; //校验位 QStringList dataBitsList; //数据位 QStringList stopBitsList; //停止位 QStringList flowControlList; //控制流 }; #endif // MAINWINDOW_H |
.cpp文件源码:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> #include <QMessageBox> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); SerialPortInit(); } // 串口初始化(参数配置) void MainWindow::SerialPortInit() { serial = new QSerialPort; //申请内存,并设置父对象 // 获取计算机中有效的端口号,然后将端口号的名称给端口选择控件 foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) { serial->setPort(info); // 在对象中设置串口 if(serial->open(QIODevice::ReadWrite)) // 以读写方式打开串口 { ui->PortBox->addItem(info.portName()); // 添加计算机中的端口 serial->close(); // 关闭 } else { qDebug() << "串口打开失败,请重试"; } } // 参数配置 // 波特率,波特率默认选择57600 ,禁止用户点击 ui->BaudBox->addItem("57600"); serial->setBaudRate(QSerialPort::Baud57600); ui->BaudBox->setDisabled(true); // 校验,校验默认选择无 ui->ParityBox->addItem("无"); serial->setParity(QSerialPort::NoParity); // 数据位,数据位默认选择8位 ui->BitBox->addItem("8"); serial->setDataBits(QSerialPort::Data8); // 停止位,停止位默认选择1位 ui->StopBox->addItem("1"); serial->setStopBits(QSerialPort::OneStop); // 控制流,默认选择无 ui->ControlBox->addItem("无"); serial->setFlowControl(QSerialPort::NoFlowControl); // 刷新串口 RefreshSerialPort(0); // 信号 connect(serial,&QSerialPort::readyRead,this,&MainWindow::DataReceived); // 接收数据 connect(ui->SendWordOrder,&QPushButton::clicked,this,&MainWindow::DataSend); // 发送数据 connect(ui->SendButton,&QPushButton::clicked,this,&MainWindow::DataSend); // 发送数据 connect(ui->SendEditBtn1,&QPushButton::clicked,this,&MainWindow::DataSend); // 发送数据 connect(ui->SendEditBtn2,&QPushButton::clicked,this,&MainWindow::DataSend); // 发送数据 connect(ui->SendEditBtn3,&QPushButton::clicked,this,&MainWindow::DataSend); // 发送数据 } // 刷新串口 void MainWindow::RefreshSerialPort(int index) { QStringList portNameList; // 存储所有串口名 if(index != 0) { serial->setPortName(ui->PortBox->currentText()); //设置串口号 } else { ui->PortBox->clear(); //关闭串口号 ui->PortBox->addItem("刷新"); //添加刷新 foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) //添加新串口 { portNameList.append(info.portName()); } ui->PortBox->addItems(portNameList); ui->PortBox->setCurrentIndex(1); // 当前串口号为COM1 serial->setPortName(ui->PortBox->currentText()); //设置串口号 } } // 接收数据,使用read () / readLine () / readAll () void MainWindow::DataReceived() { char BUF[512] = {0}; // 存储转换类型后的数据 QByteArray data = serial->readAll(); // 读取数据 if(!data.isEmpty()) // 接收到数据 { QString str = ui->DataReceived->toPlainText(); // 返回纯文本 str += tr(data); // 数据是一行一行传送的,要保存所有数据 ui->DataReceived->clear(); // 清空之前的数据 ui->DataReceived->append(str); // 将数据放入控件中 qDebug() << "str info: " << ui->DataReceived->toPlainText(); // 清除之前的数据,防止追加,因为每次获取的数据不一样 int index = str.indexOf("\r\n"); // 找到,返回索引值,找不到,返回-1 if(index != -1) { snprintf(BUF,500,"%s", str.left(index + 2).toUtf8().data()); // QString转为char * 类型 qDebug() << "BUF info: " << BUF; str.remove(0,index + 2); // 处理获取到的数据,将其放入对应的控件中 } } } // 发送数据,write () void MainWindow::DataSend() { serial->write(ui->DataSend->toPlainText().toLatin1()); // 串口发送数据 } // 开关显示灯 void MainWindow::LED(bool changeColor) { if(changeColor == false) { // 显示绿色 ui->LED->setStyleSheet("background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(0, 229, 0, 255), stop:1 rgba(255, 255, 255, 255));border-radius:12px;"); } else { // 显示红色 ui->LED->setStyleSheet("background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(255, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));border-radius:12px;"); } } MainWindow::~MainWindow() { delete ui; } // 串口开关 void MainWindow::on_OpenSerialButton_clicked() { if(serial->isOpen()) // 如果串口打开了,先给他关闭 { serial->clear(); serial->close(); // 关闭状态,按钮显示“打开串口” ui->OpenSerialButton->setText("打开串口"); // 关闭状态,允许用户操作 ui->BaudBox->setDisabled(false); ui->ParityBox->setDisabled(false); ui->BitBox->setDisabled(false); ui->StopBox->setDisabled(false); ui->ControlBox->setDisabled(false); // 禁止操作“发送字符操作” ui->SendWordOrder->setDisabled(true); ui->SendButton->setDisabled(true); // 关闭状态,颜色为绿色 ui->OpenSerialButton->setStyleSheet("color: green;"); // 关闭,显示灯为红色 LED(true); // 清空数据 ui->DataReceived->clear(); ui->DataSend->clear(); } else // 如果串口关闭了,先给他打开 { //当前选择的串口名字 serial->setPortName(ui->PortBox->currentText()); //用ReadWrite 的模式尝试打开串口,无法收发数据时,发出警告 if(!serial->open(QIODevice::ReadWrite)) { QMessageBox::warning(this,tr("提示"),tr("串口打开失败!"),QMessageBox::Ok); return; } // 打开状态,按钮显示“关闭串口” ui->OpenSerialButton->setText("关闭串口"); // 打开状态,禁止用户操作 ui->BaudBox->setDisabled(true); ui->ParityBox->setDisabled(true); ui->BitBox->setDisabled(true); ui->StopBox->setDisabled(true); ui->ControlBox->setDisabled(true); // 允许操作“发送字符操作” ui->SendWordOrder->setDisabled(false); ui->SendButton->setDisabled(false); // 打开状态,颜色为红色 ui->OpenSerialButton->setStyleSheet("color: red;"); // 打开,显示灯为绿色 LED(false); } } // 控件中添加 # void MainWindow::on_SendButton_clicked() { on_ClearButton_clicked(); ui->DataSend->append("###"); } // 清空控件 void MainWindow::on_ClearButton_clicked() { ui->DataSend->setText(""); } // 清空接收到的数据 void MainWindow::on_ClearShowButton_clicked() { ui->DataReceived->setText(""); } // 点击发送后,获取串口信息并展示在接受控件中 void MainWindow::on_SendEditBtn1_clicked() { on_ClearButton_clicked(); QString EditText = ui->Edit1->text(); //获取发送框内容 ui->DataSend->setText(EditText); //将文本内容放在发送栏中 } void MainWindow::on_SendEditBtn2_clicked() { on_ClearButton_clicked(); QString EditText = ui->Edit2->text(); //获取发送框内容 // qDebug() << "Edit1 text: " << EditText; ui->DataSend->append(EditText); //将文本内容放在发送栏中 } void MainWindow::on_SendEditBtn3_clicked() { on_ClearButton_clicked(); QString EditText = ui->Edit3->text(); //获取发送框内容 // qDebug() << "Edit1 text: " << EditText; ui->DataSend->append(EditText); //将文本内容放在发送栏中 } void MainWindow::on_SendWordOrder_clicked() { on_SendButton_clicked(); } |
运行后效果: