第十六章 串口通信
16.1 串口通信基础
串口通信主要通过DB9接口,适用于短距离(<10米)。关键参数包括:
- 波特率:每秒传输bit数,如9600。
- 数据位:信息包中的有效数据位数。
- 停止位:单个包的最后一位,通常为1或2位。
- 奇偶校验位:检错方式,分偶、奇校验。


16.1.1 QtSerialPort模块简介
QtSerialPort是QT5的附加模块,为硬件和虚拟串口提供接口。它简化串口应用开发,提供配置、I/O操作等功能。
使用QtSerialPort,需包含以下头文件:
#include <QSerialPortInfo>
#include <QSerialPort>
并在.pro文件中添加:
QT += serialport
QSerialPort类用于访问串口,
QSerialPortInfo用于获取串口信息。
可通过setPort()或setPortName()设置要访问的串口。
使用open()和close()打开和关闭串口。
串口打开后,可使用setBaudRate()等函数重新配置。
waitForReadyRead()和waitForBytesWritten()用于阻塞操作。
16.1.2 QSerialPort 成员函数
构造函数:
// 构造一个未初始化的QSerialPort对象,可选指定父对象
QSerialPort::QSerialPort(QObject *parent = nullptr)
// 构造并初始化QSerialPort对象,指定端口名和可选的父对象
QSerialPort::QSerialPort(const QString &name, QObject *parent = nullptr)
// 构造并初始化QSerialPort对象,根据QSerialPortInfo指定端口信息和可选的父对象
QSerialPort::QSerialPort(const QSerialPortInfo &serialPortInfo, QObject *parent = nullptr)
关键方法:
bool atEnd() const:检查是否有数据可读。
qint64 bytesAvailable() const:返回可读数据字节数。
qint64 bytesToWrite() const:返回可写数据字节数。
void close():关闭串口。
void setPort(const QSerialPortInfo &serialPortInfo):设置串口信息。
void setPortName(const QString &name):设置串口名。
//波特率改变时触发信号
[signal] void baudRateChanged(qint32 baudRate, //新的波特率
QSerialPort::Directions directions) //波特率用于哪方
/*检查是否有数据可读,无数据可读返回true*/
[virtual] bool QSerialPort::atEnd() const;
/*波特率改变后,信号触发*/
[signal] void QSerialPort::baudRateChanged(qint32 baudRate, //新的波特率
QSerialPort::Directions directions) //波特率用于哪方
/*
QSerialPort::AllDirections //默认,表示读写方向都应用此波特率
QSerialPort::Input //仅用于输入方向
QSerialPort::Output //仅用于输出方向
*/
/*返回可读数据的字节数*/
[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);
16.2 QSerialPortInfo
16.2.1 QSerialPortInfo 简介
QSerialPortInfo 类用于提供系统中已有串口设备的信息。可通过其静态成员函数获取代表各串口的 QSerialPortInfo 对象链表。每个对象包含端口的详细信息,如端口名、系统位置、描述、制造商等,并可用于配置 QSerialPort 对象。
16.2.2 QSerialPortInfo 成员函数
构造函数
QSerialPortInfo(const QSerialPort &port);
QSerialPortInfo(const QString &name);
QSerialPortInfo(const QSerialPortInfo &other);
静态成员函数
static QList<QSerialPortInfo> availablePorts(); // 返回可用串口链表
static QList<qint32> standardBaudRates(); // 返回标准波特率链表
成员函数
QString description() const; // 返回串口描述
bool hasProductIdentifier() const; // 是否有生产编码
bool hasVendorIdentifier() const; // 是否有制造商编码
bool isBusy() const; // 串口是否正忙
QString manufacturer() const; // 返回制造商名称
QString portName() const; // 返回串口名称
quint16 productIdentifier() const; // 返回生产编码
QString serialNumber() const; // 返回序列号
QString systemLocation() const; // 返回系统位置
quint16 vendorIdentifier() const; // 返回制造商编码
void swap(QSerialPortInfo &other); // 交换两个对象
16.3 实现简易串口
打开/关闭串口:
当点击“打开串口”按钮时,配置串口参数并尝试打开串口。如果成功,禁用串口配置ComboBox,启用发送按钮,并更改按钮文本为“关闭串口”。
同时连接串口的 readyRead信号到 readData槽函数。
当点击“关闭串口”按钮时(即串口已打开状态),关闭串口,重新启用串口配置ComboBox,更改按钮文本为“打开串口”,并禁用发送按钮。
数据发送与接收:
点击发送按钮时,将发送文本框中的内容以Latin1编码写入串口。
点击清空接收/发送数据按钮时,清空相应的文本框内容。
当串口有数据可读时,readData槽函数被调用,读取所有可用数据,并将其追加到接收文本框中。
#ifndef MAINWINDOW_H // 防止头文件重复包含,如果已定义MAINWINDOW_H则不再次包含
#define MAINWINDOW_H
// 包含必要的Qt库头文件
#include <QMainWindow> // 主窗口类
#include <QSerialPort> // 串口通信类
#include <QSerialPortInfo> // 串口信息类,用于获取系统中可用的串口信息
#include <QList> // 用于存储串口信息列表
#include <QDebug> // 用于调试输出
// Ui命名空间,包含UI类的声明(通常由Qt Designer生成)
namespace Ui {
class MainWindow;
}
// MainWindow类,继承自QMainWindow
class MainWindow : public QMainWindow {
Q_OBJECT // 使用Qt的宏,允许该类使用信号和槽机制
public:
// 构造函数,explicit关键字防止隐式转换
explicit MainWindow(QWidget *parent = 0);
// 析构函数
~MainWindow();
private slots:
// 私有槽函数,用于处理UI按钮点击事件
void on_btn_openConsole_clicked(); // 打开控制台按钮点击事件处理
void on_btn_send_clicked(); // 发送按钮点击事件处理
void on_btn_clearRecv_clicked(); // 清空接收区按钮点击事件处理
void on_btn_clearSend_clicked(); // 清空发送区按钮点击事件处理
void readData(); // 读取串口数据
private:
// Ui类的指针,用于访问UI元素
Ui::MainWindow *ui;
// QSerialPort类的指针,用于串口通信
QSerialPort *serial;
};
#endif // MAINWINDOW_H // 结束头文件保护
#include "mainwindow.h"
#include "ui_mainwindow.h"
// 定义一个静态常量字符串,用于表示“不可用”或“无数据”的情况
static const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog", "N/A");
// MainWindow类的构造函数实现
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), // 调用基类的构造函数
ui(new Ui::MainWindow) { // 初始化UI类的指针
ui->setupUi(this); // 设置UI界面
// 初始化串口类的指针
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(); // 获取序列号
// 将串口的相关信息添加到列表中,如果信息为空则使用blankString代替
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);
// 将串口名称作为第一项,其余信息作为关联数据添加到ComboBox中
ui->comboBox_serialPort->addItem(list.first(), list);
}
// 添加一个自定义选项到串口ComboBox中
ui->comboBox_serialPort->addItem(tr("custom"));
// 设置波特率的ComboBox选项
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"));
// 设置数据位的ComboBox选项
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); // 默认选择8数据位
// 设置奇偶校验位的ComboBox选项
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);
// 设置停止位的ComboBox选项
ui->comboBox_stopBit->addItem(QStringLiteral("1"), QSerialPort::OneStop);
ui->comboBox_stopBit->addItem(QStringLiteral("2"), QSerialPort::TwoStop);
// 设置流控的ComboBox选项
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::~MainWindow() {
delete ui; // 释放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)) {
// 禁用串口配置相关的ComboBox
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("关闭串口"));
// 连接串口的readyRead信号到readData槽函数
connect(serial, &QSerialPort::readyRead, this, &MainWindow::readData);
}
} else {
// 关闭串口
serial->close(); // 关闭串口连接
// 重新启用串口配置相关的ComboBox
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() {
// 将发送文本框中的内容以Latin1编码写入串口
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); // 将读取到的数据追加到字符串中(注意:这里tr的使用可能是不必要的,除非需要翻译字节数组的内容)
ui->textEdit_recv->clear(); // 清空接收文本框的内容
ui->textEdit_recv->append(str); // 将更新后的字符串添加到接收文本框中
}
}


16.4 ubuntu下相关操作
ubuntu下串口打不开。
lsusb //显示系统中 USB 总线及其连接设备信息。
发现串口设备成功连接了,
总线 001 设备 001:这表示这是连接到第一个 USB 总线的第一个设备。
ID 1d6b:0002:这是设备的供应商 ID(VID)和产品 ID(PID)。
sudo apt-get install minicom //安装串口通信工具
minicom -D /dev/ttyUSB0 //访问串行设备文件
到这一步,如果提示没权限,问题就找到了。
通过以下命令将用户添加到dialout组:
sudo adduser USER_NAME dialout
dialout 组在 Linux 系统中是一个特殊的用户组,它主要负责管理对串口设备的访问权限。
添加完成后,注销并重新登录系统以激活权限更改。
第十六点零章 minicom用法
一、安装Minicom
在Linux系统中,通常可以使用包管理器来安装Minicom。以Debian系发行版(如Ubuntu)为例,可以使用以下命令安装:
sudo apt-get install minicom |
二、配置Minicom
在首次使用Minicom或需要更改配置时,可以通过以下步骤进行配置:
-
启动配置界面:
打开终端,输入sudo minicom -s命令,进入Minicom的配置界面。 -
选择配置选项:
使用上下键选择“Serial port setup”(串行端口设置)选项,按回车键进入。 -
设置串行端口参数:
在“Serial port setup”菜单中,可以设置串行端口设备(如/dev/ttyUSB0或/dev/ttyS0)、波特率(如115200)、数据位、停止位、奇偶校验等参数。设置完成后,按回车键返回主菜单。 -
保存配置:
在主菜单中,选择“Save setup as dfl”(保存设置为默认)选项,将当前配置保存为默认设置。然后选择“Exit”(退出)选项,退出配置界面。
三、使用Minicom
配置完成后,可以通过以下步骤使用Minicom进行串口通信:
-
启动Minicom:
在终端中输入sudo minicom命令,启动Minicom程序。此时,Minicom将自动连接到之前配置的串行端口设备。 -
发送和接收数据:
在Minicom的主界面中,可以直接输入要发送的数据,然后按回车键发送。接收到的数据将实时显示在屏幕上。 -
使用特殊按键:
Minicom支持多种特殊按键组合,用于实现不同的功能。例如,可以使用Ctrl+A Z组合键显示帮助信息,使用Ctrl+A X组合键退出Minicom等。 -
文件传输:
Minicom还支持通过串口进行文件传输。例如,可以使用Ctrl+A S组合键发送文件,使用Ctrl+A R组合键接收文件。但请注意,文件传输协议(如Xmodem、Ymodem等)需要事先在配置界面中进行设置。
注意字符编码
字符编码主要注意
UTF8、GBK(国标扩)、gb18030(国标),大多数编码格式都兼容ASCII码的。
编码格式指的是,我汉字、英语这种字符用多少字节表示,用什么二进制表示。
兼容ASCII码,那么 ascii码包含的128个字符,就通用了。
但其他字符,编码就不一样了。
sudo minicom -R utf8 //设置字符编码
-D /dev/ttyUSB0 //设置打开的设备节点
QT串口通信打不开设备和乱码问题解决

把当前用户添加到 dialout 组以后,访问串口没问题了。但还是乱码,改字符编码。
QString::toUtf8是输出UTF-8编码的字符集
QString::toLatin1是相当与ASCii码不包含中文的遇到中文默认转换为ascii码0x3f也就是字符’?‘
QString::Local8bit是本地操作系统设置的字符集编码,一般为GB2312.
qtcreator默认代码里的汉字使用utf8字符集.
所以如果串口传进来的字符格式不匹配解析就是乱码。
// 设置GB18030编码
QTextCodec *codec = QTextCodec::codecForName("GB18030");
// 从GB18030编码转换为QString
QString decodedString = codec->toUnicode(buf);

6468

被折叠的 条评论
为什么被折叠?



