前言
串口通信是很古老的通信方式,适合数据传输量少的设备搭建为通信模块,本篇不延展串口历史,而是用一个应用工具关联介绍一些应用场景。
效果图


本篇的源码可以通用编译(window、Linux等),源码请查看资源。
开源的串口工具
串口应用功能不复杂,网络上可以下载到相关的工具,很方便做技术调研,下面简要介绍下window系统和Linux系统常用的串口工具。
window串口工具
1、串口助手

功能比较齐全,可以以十六进制/字符进行发送/接收,提供文件发送/接收功能等。
2、串口模拟工具vspd

如上图是在右边填写COM1和COM2,并点击【添加端口】之后,在左边就看到有两个虚拟串口了,这个工具是帮助我们在window的驱动层常见了COM1->COM2,COM2->COM1的虚拟串口,这两个串口是绑定使用的,在下面的【设备管理器】界面更容易理解:

因为大部分的笔记本都没有串口了,使用这个工具就能在自己的笔记本上完成串口应用调试。
基于这样虚拟串口工具的特性,我们还能开发一个远程串口工具,流程如下:
终端A部署了[客户端]--通过socket发送信息-->终端B部署[服务端]--接收客户端发送过来的信息,把命令信息写入到串口COM1-->COM2绑定瘦终端C(比如读卡器)
绑定瘦终端C(比如读卡器)通过COM2--返回结果-->COM1--终端B[服务端]接收信息后,通过socket返回信息-->终端A[客户端]
有远程桌面操作串口需求场景的,可以使用以上思路构建。
Linux串口工具
1、自带的cutecom

Linux串口工具cuteCOM源码(工具包含发送文件功能)这个是我分享过的工具源码
其他的需要apt下载,功能都比较简单,我这里就不列举了,如果是用测试ARM架构的Linux,可以试试Minicom。
功能讲解
1、串并口文件设置
这个菜单是用来添加自定义的串口,如果你的信创终端,要增加多个串口,这些串口不一定是COM前缀开头的文件,这个时候,市面上通用的串口工具都没法用,本篇的应用来源于实际的信创终端项目串口检测应用。
配置完成之后,在串口测试下拉框就可以选择了,这里就不展开讲解了,提供过思路给刚好有此类需求场景的人。
2、串口测试
1、获取系统串口信息
通过QSerialPortInfo 获取
// 搜索所有可用串口
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
QSerialPort serial;
serial.setPort(info);
ui->comboBox->addItem(serial.portName());
if (serial.open(QIODevice::ReadWrite)){
qDebug() << "==>" << serial.portName();
}
}
2、波特率设置
手动设置上去的

3、打开串口
核心代码就是:serialPort->open(QIODevice::ReadWrite)
void MainWindow::on_pushButton_8_clicked()
{
if(ui->pushButton_8->text() == tr("打开"))
{
// 串口设置
serialPort->setPortName(ui->comboBox->currentText());
serialPort->setBaudRate(ui->comboBox_2->currentText().toInt());
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setParity(QSerialPort::NoParity);
// 打开串口
if (serialPort->open(QIODevice::ReadWrite))
{
qDebug() << "串口打开成功";
m_recvData.clear();
connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::readSerialData);
}
else
{
qDebug() << "串口打开失败";
QMessageBox::warning(this, "错误", "无法打开串口: " + serialPort->portName());
return;
}
ui->pushButton_8->setText("关闭");
ui->comboBox->setEnabled(false);
ui->comboBox_2->setEnabled(false);
}
else
{
serialPort->close();
qDebug() << "串口关闭成功";
ui->pushButton_8->setText("打开");
ui->comboBox->setEnabled(true);
ui->comboBox_2->setEnabled(true);
}
}
4、串口发送功能
核心代码是:serialPort->write(data);
// 发送数据函数
void MainWindow::sendData(const QByteArray &data)
{
if (!serialPort->isOpen()) {
QMessageBox::warning(this, "警告", "请先打开串口!");
return;
}
qint64 bytesWritten = serialPort->write(data);
if (bytesWritten == -1) {
QMessageBox::warning(this, "错误", "发送数据失败");
}
else if (bytesWritten != data.size()) {
QMessageBox::warning(this, "警告", "数据未完全发送");
}
}
5、 十六进制模式方式发送
发送之前,对内容转换为十六进制
QByteArray data = hexStringToBytes(sendText);
if (!data.isEmpty()) {
qDebug() << "发送十六进制数据:" << bytesToHexString(data);
sendData(data);
}
// 十六进制字符串转字节数组
QByteArray MainWindow::hexStringToBytes(const QString &hexStr)
{
QByteArray byteArray;
QString trimmed = hexStr.trimmed();
QStringList hexValues = trimmed.split(' ');
for (const QString &hexValue : hexValues) {
if (hexValue.isEmpty()) continue;
bool ok;
char byte = static_cast<char>(hexValue.toInt(&ok, 16));
if (ok) {
byteArray.append(byte);
}
else {
QMessageBox::warning(this, "错误", "无效的十六进制值: " + hexValue);
return QByteArray();
}
}
return byteArray;
}
6、字符方式发送
发送之前,对内容转换为utf-8格式
// 字符模式
QByteArray data = sendText.toUtf8();
qDebug() << "发送字符数据:" << sendText;
sendData(data);
7、文件发送
以块方式循环读取文件内容,然后发送到对端
void MainWindow::on_pushButton_sendFile_clicked()
{
if (!serialPort->isOpen()) {
QMessageBox::warning(this, "警告", "请先打开串口!");
return;
}
// 直接弹出文件选择对话框
QString fileName = QFileDialog::getOpenFileName(this, "选择要发送的文件", "", "所有文件 (*)");
if (fileName.isEmpty()) {
return;
}
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, "错误", "无法打开文件: " + fileName);
return;
}
QFileInfo fileInfo(fileName);
qDebug() << "准备发送文件:" << fileName << "大小:" << fileInfo.size() << "字节";
// 创建进度对话框
QProgressDialog progress("发送文件中...", "取消", 0, file.size(), this);
progress.setWindowTitle("发送文件");
progress.setWindowModality(Qt::WindowModal);
progress.show();
// 分块读取和发送文件
const int chunkSize = 1024;
qint64 totalSent = 0;
QByteArray data;
while (!file.atEnd()) {
if (progress.wasCanceled()) {
break;
}
data = file.read(chunkSize);
qint64 bytesWritten = serialPort->write(data);
if (bytesWritten == -1) {
QMessageBox::warning(this, "错误", "发送文件失败");
break;
}
if (!serialPort->waitForBytesWritten(5000)) {
QMessageBox::warning(this, "错误", "写入超时");
break;
}
totalSent += bytesWritten;
progress.setValue(totalSent);
QApplication::processEvents();
// 添加小延迟,避免过快发送导致接收方处理不过来
QThread::msleep(10);
}
file.close();
if (totalSent == fileInfo.size()) {
QMessageBox::information(this, "完成", "文件发送完成!\n发送字节数: " + QString::number(totalSent));
} else {
QMessageBox::information(this, "中断", "文件发送已中断\n已发送字节数: " + QString::number(totalSent));
}
}
使用本应用的文件发送功能,发送给串口助手,经过测试,支持文本文件、图片文件传输。可能你会绝得串口工具没必要传输这么多字符的内容,常用场景确实是这样。但是,在不方便使用USB、网络挂载的情况下,串口通常是可以作为临时传输通道的,这个不好明说。