一、项目概述
该串口助手工具基于Qt/C++开发,专为简化串口通信调试与开发而设计,适合新手快速上手。工具具有直观的用户界面和丰富的功能,旨在帮助用户与串口设备建立可靠通信,便于调试、数据传输和分析。
二、主要功能
波特率:支持常见波特率(如9600、115200等),并支持自定义。
数据位:提供从5到8位的选择。
校验位:支持无校验、奇校验、偶校验等。
停止位:提供1位或2位选择。
接收数据:支持实时显示从串口设备接收的数据,用户可以选择显示格式(如十六进制、ASCII等)。
发送数据:用户可以通过界面输入并发送数据,支持单次发送和定时发送,便于测试连续传输。
-
串口自动检测与连接串口助手具备自动检测功能,可以自动识别当前系统中的串口设备。用户通过界面选择设备并一键连接,简化了配置流程。
-
串口参数配置用户可以根据实际需求设置以下参数:
-
数据收发
三、软件安装与使用
1. 使用
该串口助手提供了编译好的发布包,用户可以自行下载可以直接启动应用。在应用界面中,选择要连接的串口设备,配置相关串口参数,点击“连接”即可开始数据通信。数据收发、等功能可以根据需求自由选择和配置。
四、下载链接
发布包下载链接:串口调试助手.zip
https://pan.baidu.com/s/12RQ_fxREV7zOH-MwqIUzlQ?pwd=cedu
五、源码分享
#ifndef SERIALWORKER_H
#define SERIALWORKER_H
#include <QThread>
#include <QSerialPort>
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>
#include <QString>
#define tc(a) QString::fromLocal8Bit(a)
class SerialWorker : public QThread
{
Q_OBJECT
public:
SerialWorker();
~SerialWorker();
void setupPort(const QString &portName, qint32 baudRate, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, QSerialPort::Parity parity);
void addDataToSend(const QByteArray &data);
inline void unpackData(const QByteArray& receivedData);
public slots:
void startWorker(); // 用于替代start(),因为start()是QThread的成员函数
void stop();
signals:
void dataReceived(const QByteArray &data);
void errorOccurred(const QString &error);
// 新增信号
void informations(int index, const QString &msg); // 用于发送一般信息
void warnings(int index, const QString &msg); // 用于发送警告信息
void errors(int index, const QString &msg); // 用于发送错误信息
void connected(int index, bool success); // 用于发送连接状态
protected:
void run() override;
private:
QSerialPort *serialPort;
QQueue<QByteArray> sendQueue;
QMutex mutex;
QWaitCondition waitCondition;
bool running;
// 串口参数
QString portName;
qint32 baudRate;
QSerialPort::DataBits dataBits;
QSerialPort::StopBits stopBits;
QSerialPort::Parity parity;
};
#endif // SERIALWORKER_H
#include "serialworker.h"
#include <QDebug>
#include <QEventLoop>
#include <QTimer>
SerialWorker::SerialWorker()
: serialPort(nullptr), running(false)
{
}
SerialWorker::~SerialWorker()
{
stop();
if (serialPort && serialPort->isOpen()) {
serialPort->close();
}
delete serialPort;
}
void SerialWorker::setupPort(const QString &portName, qint32 baudRate, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, QSerialPort::Parity parity)
{
this->portName = portName;
this->baudRate = baudRate;
this->dataBits = dataBits;
this->stopBits = stopBits;
this->parity = parity;
}
void SerialWorker::addDataToSend(const QByteArray &data)
{
QMutexLocker locker(&mutex);
sendQueue.enqueue(data);
waitCondition.wakeOne();
}
void SerialWorker::unpackData(const QByteArray &receivedData)
{
emit dataReceived(receivedData);
}
void SerialWorker::startWorker()
{
if(!isRunning())
start();
}
void SerialWorker::stop()
{
QMutexLocker locker(&mutex);
running = false;
waitCondition.wakeAll();
}
void SerialWorker::run()
{
serialPort = new QSerialPort();
// 设置串口参数
serialPort->setPortName(portName);
serialPort->setBaudRate(baudRate);
serialPort->setDataBits(dataBits);
serialPort->setStopBits(stopBits);
serialPort->setParity(parity);
int index = 0; // 可以根据需要设置适当的索引值
if (!serialPort->open(QIODevice::ReadWrite)) {
emit errors(index, serialPort->errorString());
emit connected(index, false);
delete serialPort;
serialPort = nullptr;
running=false;
return;
}
running=true;
emit connected(index, true); // 成功连接
emit informations(index, tc("串口已打开"));
QEventLoop eventLoop;
QTimer timer;
timer.setInterval(10); // 设置定时器的间隔,控制事件循环的频率
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
timer.start();
while (running)
{
QByteArray data;
{
QMutexLocker locker(&mutex);
if (!sendQueue.isEmpty()) {
data = sendQueue.dequeue();
}
}
if (!data.isEmpty())
{
serialPort->write(data);
if (!serialPort->waitForBytesWritten()) {
emit errors(index, serialPort->errorString());
} else {
emit informations(index, tc("数据已发送[%1]").arg(QString::fromUtf8(data)));
}
}
if (serialPort->waitForReadyRead(10)) { // 使用一个小的超时时间
QByteArray receivedData = serialPort->readAll();
emit dataReceived(receivedData);
}
// 处理事件循环,避免阻塞信号槽机制
eventLoop.exec();
}
serialPort->close();
emit informations(index, tc("串口已关闭"));
emit connected(index, false); // 断开连接
delete serialPort;
serialPort = nullptr;
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "serialworker.h"
namespace Ui {
class MainWindow;
}
enum RunTimeStatus
{
Error, ///< 错误信息
Warning, ///< 警告信息
Information, ///< 常规信息
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void writeRunTimeMsgs(const QString&msg,const int level);
void createSendLinEdit();
// 创建配置文件的函数,如果文件不存在则创建
void createConfigFile(const QString &fileName, const QStringList &values);
// 读取配置文件的函数
QStringList readConfigFile(const QString &fileName);
private slots:
void on_openButton_clicked();
void on_closeButton_clicked();
void onDataReceived(const QByteArray &data);
void onRespondNormalMessage(const int index,const QString&msg);
void onRespondWarningMessage(const int index,const QString&msg);
void onRespondErrorMessage(const int index,const QString&msg);
void onRespondSerialConnectState(const int index,const bool state);
void on_clearRecvButton_clicked();
private:
Ui::MainWindow *ui;
SerialWorker *serialwork;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSerialPortInfo>
#include <QDateTime>
#include <QDebug>
#include <QSettings>
#include <QFile>
#include <QTextCodec>
#include "timesendwidget.h"
# define tc(a) QString::fromLocal8Bit(a)
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow), serialwork(new SerialWorker())
{
ui->setupUi(this);
// 示例值列表
QStringList values;
values << tc("极客晨风") << tc("1327666247@qq.com") << tc("微信公众号:码上Qt")<<tc("欢迎关注") ;
createConfigFile("config.ini",values);
//绘制发送界面
createSendLinEdit();
// 填充串口选项
// 遍历当前系统可用的所有串口,并将它们的名字添加到端口名的QComboBox中
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
ui->portNameComboBox->addItem(info.portName());
}
// 填充波特率选项
// 将常用的波特率值添加到波特率的QComboBox中,数据以“字符串-枚举值”形式存储
ui->baudRateComboBox->addItem("9600", QSerialPort::Baud9600); // 波特率9600
ui->baudRateComboBox->addItem("19200", QSerialPort::Baud19200); // 波特率19200
ui->baudRateComboBox->addItem("38400", QSerialPort::Baud38400); // 波特率38400
ui->baudRateComboBox->addItem("115200", QSerialPort::Baud115200); // 波特率115200
ui->baudRateComboBox->setCurrentIndex(0); // 设置默认选项为第一个(9600)
// 填充数据位选项
// 将串口通信中常用的数据位数添加到数据位的QComboBox中,数据以“字符串-枚举值”形式存储
ui->dataBitsComboBox->addItem("5", QSerialPort::Data5); // 5个数据位
ui->dataBitsComboBox->addItem("6", QSerialPort::Data6); // 6个数据位
ui->dataBitsComboBox->addItem("7", QSerialPort::Data7); // 7个数据位
ui->dataBitsComboBox->addItem("8", QSerialPort::Data8); // 8个数据位(通常最常用)
ui->dataBitsComboBox->setCurrentIndex(3); // 设置默认选项为8个数据位(索引3)
// 填充停止位选项
// 将串口通信中常用的停止位添加到停止位的QComboBox中,数据以“字符串-枚举值”形式存储
ui->stopBitsComboBox->addItem("1", QSerialPort::OneStop); // 1个停止位
ui->stopBitsComboBox->addItem("1.5", QSerialPort::OneAndHalfStop); // 1.5个停止位(较少使用)
ui->stopBitsComboBox->addItem("2", QSerialPort::TwoStop); // 2个停止位
ui->stopBitsComboBox->setCurrentIndex(0); // 设置默认选项为1个停止位
// 填充奇偶校验选项
// 将串口通信中常用的奇偶校验方式添加到奇偶校验的QComboBox中,数据以“字符串-枚举值”形式存储
ui->parityComboBox->addItem("None", QSerialPort::NoParity); // 无校验(最常用)
ui->parityComboBox->addItem("Even", QSerialPort::EvenParity); // 偶校验
ui->parityComboBox->addItem("Odd", QSerialPort::OddParity); // 奇校验
ui->parityComboBox->addItem("Mark", QSerialPort::MarkParity); // 标志位校验(较少使用)
ui->parityComboBox->addItem("Space", QSerialPort::SpaceParity); // 空格位校验(较少使用)
ui->parityComboBox->setCurrentIndex(0); // 设置默认选项为无校验
connect(serialwork, &SerialWorker::dataReceived, this, &MainWindow::onDataReceived);
// 连接信号与槽
connect(serialwork, &SerialWorker::informations, this, &MainWindow::onRespondNormalMessage);
connect(serialwork, &SerialWorker::warnings, this, &MainWindow::onRespondWarningMessage);
connect(serialwork, &SerialWorker::errors, this, &MainWindow::onRespondErrorMessage);
connect(serialwork, &SerialWorker::connected, this, &MainWindow::onRespondSerialConnectState);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::writeRunTimeMsgs(const QString &msg, const int level )
{
QString Level;
switch (level) {
case Error: Level=tc("[ ***错误信息*** ]");break;
case Warning: Level=tc("[ ***警报信息*** ]");break;
case Information:Level=tc("[ ***正常信息*** ]");break;
}
ui->statusBar->showMessage(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ") +Level+msg );
}
void MainWindow::createSendLinEdit()
{
for(auto val:readConfigFile("config.ini"))
{
TimeSendWidget *sendWidget=new TimeSendWidget;
sendWidget->setLinText(val);
connect(sendWidget,&TimeSendWidget::sendLineData,[=](const QByteArray &data )
{
if(ui->openButton->isEnabled())
{
writeRunTimeMsgs(tc("串口未打开,无法发送"),Error);
return ;
}
if(data.isEmpty())
{
writeRunTimeMsgs(tc("信息为空,拒绝发送"),Warning);
return ;
}
//主界面显示
if(ui->isShowSendButton->isChecked())
{
QString msg;
if(ui->isShowTimeButton->isChecked())
msg.append(QDateTime::currentDateTime().toString("hh:mm:ss.(zzz) "));
msg.append(tc("发送 "));
msg.append(ui->isShowHexButton->isChecked()? data.toHex(' ').toUpper():QString::fromLocal8Bit(data));
ui->receiveTextEdit->append(msg);
}
//更新显示信息
ui->sendByte->setValue(ui->sendByte->value()+data.size());
ui->sendFram->setValue(ui->sendFram->value()+1);
serialwork->addDataToSend(data);
});
ui->sendGroup->layout()->addWidget(sendWidget);
}
}
void MainWindow::createConfigFile(const QString &fileName, const QStringList &values)
{
// 检查文件是否存在
QFile file(fileName);
if (file.exists())
return;
QSettings settings(fileName, QSettings::IniFormat);
settings.setIniCodec(QTextCodec::codecForName("UTF-8"));
// 根据传入的值动态创建组和键值对
int groupIndex = 1;
for (const QString &value : values)
{
QString groupName = QString("%1").arg(groupIndex);
settings.beginGroup(groupName);
settings.setValue(QString("%1").arg(groupIndex), value);
settings.endGroup();
groupIndex++;
}
}
QStringList MainWindow::readConfigFile(const QString &fileName)
{
QStringList iniinfors;
QSettings settings(fileName, QSettings::IniFormat);
settings.setIniCodec(QTextCodec::codecForName("UTF-8"));
// 获取所有组并排序
QStringList groups = settings.childGroups();
groups.sort();
for (const QString &group : groups)
{
settings.beginGroup(group);
QStringList keys = settings.childKeys();
for (const QString &key : keys)
iniinfors<< settings.value(key).toString();
settings.endGroup();
}
return iniinfors;
}
void MainWindow::on_openButton_clicked()
{
QString portName = ui->portNameComboBox->currentText();
qint32 baudRate = ui->baudRateComboBox->currentData().toInt();
QSerialPort::DataBits dataBits = static_cast<QSerialPort::DataBits>(ui->dataBitsComboBox->currentData().toInt());
QSerialPort::StopBits stopBits = static_cast<QSerialPort::StopBits>(ui->stopBitsComboBox->currentData().toInt());
QSerialPort::Parity parity = static_cast<QSerialPort::Parity>(ui->parityComboBox->currentData().toInt());
serialwork->setupPort(portName, baudRate, dataBits, stopBits, parity);
serialwork->startWorker();
}
void MainWindow::on_closeButton_clicked()
{
serialwork->stop();
}
void MainWindow::onDataReceived(const QByteArray &data)
{
if(ui->isShowRecvButton->isChecked())
{
QString msg;
if(ui->isShowTimeButton->isChecked())
msg.append(QDateTime::currentDateTime().toString("hh:mm:ss.(zzz) "));
msg.append(tc("接收 "));
msg.append(ui->isShowHexButton->isChecked()? data.toHex(' ').toUpper():QString::fromLocal8Bit(data));
ui->receiveTextEdit->append(msg);
}
//更新显示信息
ui->recvByte->setValue(ui->recvByte->value()+data.size());
ui->recvFram->setValue(ui->recvFram->value()+1);
}
void MainWindow::onRespondNormalMessage(const int , const QString &msg)
{
writeRunTimeMsgs(msg,Information);
}
void MainWindow::onRespondWarningMessage(const int , const QString &msg)
{
writeRunTimeMsgs(msg,Warning);
}
void MainWindow::onRespondErrorMessage(const int , const QString &msg)
{
writeRunTimeMsgs(msg,Error);
}
void MainWindow::onRespondSerialConnectState(const int , const bool state)
{
ui->openButton->setEnabled(!state);
ui->closeButton->setEnabled(state);
}
void MainWindow::on_clearRecvButton_clicked()
{
ui->recvByte->setValue(0);
ui->recvFram->setValue(0);
ui->sendByte->setValue(0);
ui->sendFram->setValue(0);
ui->receiveTextEdit->clear();
}