使用QT实现DB9串口数据接收与发送
代码针对readAll()函数出现的数据读取不完整问题使用定时器进行了改进
首先,放上代码的UI设计图以及相应的变量名称
下面是UI布局图的控件变量描述
下面放上项目的结构图
废话不多说,下面按照项目的目录直接放代码。
首先是demo3_serialTest.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
然后是mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QList>
#include <QDebug>
#include <QTimer>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QStringList Data_analysis(QString data_received);
void serialPort_readyRead();
QTimer *timerSerial;
QByteArray buffer;
private slots:
void on_btn_openConsole_clicked();
void on_btn_send_clicked();
void on_btn_clearRecv_clicked();
void on_btn_clearSend_clicked();
void readData();
void TimerUpdate();
private:
Ui::MainWindow *ui;
QSerialPort *serial;
};
#endif // MAINWINDOW_H
其次是main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
最后是mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 添加定时器处理数据接受不完整问题
QTimer *timeSerial;
timerSerial = new QTimer(this);
serial = new QSerialPort;
// 端口描述
QString description;
// 制造商字符串
QString manufacturer;
// 端口序列号
QString serialNumber;
// 空串,表示不可用
QString blankString = "";
//获取可以用的串口
QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();
//输出当前系统可以使用的串口个数
// qDebug() << "Total numbers of ports: " << serialPortInfos.count();
cout << "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) //description : blankString
<< (!manufacturer.isEmpty() ? manufacturer:blankString)//manufacturer:blankString
<< (!serialNumber.isEmpty() ? serialNumber:blankString)//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 ui;
}
//打开串口按钮槽函数
void MainWindow::on_btn_openConsole_clicked()
{
cout << 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);
connect(serial, &QSerialPort::readyRead, this, &MainWindow::serialPort_readyRead);
connect(timerSerial,SIGNAL(timeout()),this,SLOT(TimerUpdate()));
}
}
else
{
//关闭串口
serial->close();
//恢复设置功能
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::serialPort_readyRead()
{
timerSerial->start(100);
buffer.append(serial->readAll());
}
void MainWindow::TimerUpdate()
{
timerSerial->stop();
if(buffer.length() !=0)
{
// 显示数据处理结果
QStringList lists_data;
// 调用数据处理函数,返回数据处理的QSTringList数据,逐条显示
lists_data = Data_analysis(buffer);
foreach(QString each_item,lists_data){
ui->textEdit_recv->append(each_item);
}
}
cout << buffer;
buffer.clear();
}
//发送数据槽函数
void MainWindow::on_btn_send_clicked()
{
//serial->write(ui->textEdit_send->toPlainText().toLatin1());
// 可以发汉字
serial->write(ui->textEdit_send->toPlainText().toUtf8());
}
//清空接收数据槽函数
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;
buf = serial->readAll();
if (!buf.isEmpty()){
QString str = ui->textEdit_recv->toPlainText();
str += tr(buf);
ui->textEdit_recv->clear();
ui->textEdit_recv->append(str);
}
}
QStringList MainWindow::Data_analysis(QString data_received){
// 数据存储表
QStringList data_save_list;
// 接收数据,以参数进行传递
// QString data_received = "$GPGGA,033547.000,3027.27207487,N,11425.22216796,E,1,8,1.151,32.686,M,0.000,M,,,1.678*66";
// 获取参数列表
QStringList data_list;
// 卫星种类标识
QString flag_bd = "$BD";
QString flag_gps = "$GP";
// 数据头
QString head;
// 数据UTC时间
QString utc_time;
// 维度
QString dimension;
// 维度标识
QString dimenson_flag;
// 经度
QString longitude;
// 经度标识
QString longitude_flag;
// 定位质量指示
QString position_quality_indication;
// 使用卫星数量
QString satellite_number;
// 水平精度
QString horizontal_accuracy;
// 天线距离海平面高度
QString height_antenna_sea_level;
// 大地水准面高度
QString geoid_height;
// 差分GPS数据期限
QString data_period_GPS;
// 差分参考基站标号
QString reference_base_station_label;
data_list = data_received.split(',');
cout << data_list;
cout << data_list[15].split("*")[0];
cout << data_list[15].split("*")[1];
head = data_list[0].left(3);
// 解析头
if(!head.isEmpty() && !(data_list[15].split("*")[1].isEmpty())){
// 定位质量标识,0无效,1有效
if(data_list[6] == "1"){
if(head == flag_bd){
data_save_list.append("北斗");
}
else if(head == flag_gps){
data_save_list.append("GPS");
}
// 其它制式的处理
else{
data_save_list.append("暂无解析");
}
// 解析时间
utc_time += "UTC:";
utc_time += data_list[1];
data_save_list.append(utc_time);
// 解析纬度
if(data_list[3] == "N"){
dimension += "北纬:";
}
else{
dimension += "南纬:";
}
dimension += data_list[2];
data_save_list.append(dimension);
// 解析经度
if(data_list[5] == "E"){
longitude += "东经:";
}
else{
longitude += "西经:";
}
longitude += data_list[4];
data_save_list.append(longitude);
// 解析卫星数量
satellite_number += "卫星数量:";
satellite_number += data_list[7];
data_save_list.append(satellite_number);
// 解析水平经度
horizontal_accuracy += "水平经度:";
horizontal_accuracy += data_list[8];
data_save_list.append(horizontal_accuracy);
// 解析天线距离海平面高度
height_antenna_sea_level += "天线距离海平面高度:";
height_antenna_sea_level += data_list[9];
height_antenna_sea_level += "M";
data_save_list.append(height_antenna_sea_level);
// 解析大地水准面高度
geoid_height += "大地水平面高度:";
geoid_height += data_list[11];
geoid_height += "M";
data_save_list.append(geoid_height);
// 差分GPS数据期限
data_period_GPS += "差分GPS数据期限:";
data_period_GPS += data_list[13];
data_save_list.append(data_period_GPS);
// 差分参考基站标号
reference_base_station_label += "差分参考基站标号";
reference_base_station_label += data_list[15].split("*")[0];
data_save_list.append(reference_base_station_label);
cout << data_save_list;
foreach(QString each, data_save_list){
qDebug() << each;
}
return data_save_list;
}
else{
cout << "定位经度无效";
}
}
}
顺便放一下mainwindow.ui
的设计代码吧,方便直接拿去用
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>698</width>
<height>631</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>201</width>
<height>581</height>
</rect>
</property>
<property name="title">
<string>串口参数配置</string>
</property>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>164</width>
<height>345</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>端口号:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBox_serialPort"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>波特率:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboBox_baudRate"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>数据位:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBox_dataBits"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>校验位:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboBox_parity"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>停止位:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboBox_stopBit"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>流控位:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="comboBox_flowBit"/>
</item>
<item row="6" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>128</height>
</size>
</property>
</spacer>
</item>
<item row="7" column="0" colspan="2">
<widget class="QPushButton" name="btn_openConsole">
<property name="text">
<string>打开串口</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">
<rect>
<x>210</x>
<y>0</y>
<width>481</width>
<height>581</height>
</rect>
</property>
<property name="title">
<string/>
</property>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>68</width>
<height>15</height>
</rect>
</property>
<property name="text">
<string>接收数据</string>
</property>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>10</x>
<y>260</y>
<width>68</width>
<height>15</height>
</rect>
</property>
<property name="text">
<string>发送数据</string>
</property>
</widget>
<widget class="QTextEdit" name="textEdit_recv">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>461</width>
<height>201</height>
</rect>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<widget class="QTextEdit" name="textEdit_send">
<property name="geometry">
<rect>
<x>10</x>
<y>280</y>
<width>461</width>
<height>201</height>
</rect>
</property>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>500</y>
<width>461</width>
<height>61</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>38</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_send">
<property name="text">
<string>发送数据</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>28</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_clearSend">
<property name="text">
<string>清空发送区</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_clearRecv">
<property name="text">
<string>清空接收区</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>698</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
关于本文档的说明,文档主要针对GPS和北斗数据的协议解析,如果不需要解析协议只需要完成数据发送仅需要对函数QStringList MainWindow::Data_analysis(QString data_received)
进行注释即可,同时将mainwindows.cpp文件中第147-152行代码QStringList lists_data; // 调用数据处理函数,返回数据处理的QSTringList数据,逐条显示 lists_data = Data_analysis(buffer); foreach(QString each_item,lists_data){ ui->textEdit_recv->append(each_item); }
替换为ui->textEdit_recv->append(buffer);
即可。
最后附上几张结果图