目录
串口通信大致分为四个部分:打开串口、发送数据、接收数据、关闭串口,总的来说并不难,本文是结合qml界面实现完整功能,以下是部分代码:
CMake:
find_package(Qt6 REQUIRED COMPONENTS SerialPort)
target_link_libraries(mytarget PRIVATE Qt6::SerialPort)
qmake:
QT += serialport
一、打开串口
//连接串口
bool SerialPort::connectSerial(QString port)
{
qint32 baud_rate = 9600; //自定义波特率
m_serial.setPortName(port); //设置串口名称
m_serial.setBaudRate(baud_rate); //设置波特率
m_serial.setDataBits(QSerialPort::Data8); //设置数据位8
m_serial.setParity(QSerialPort::NoParity); //校验位设置为0
m_serial.setStopBits(QSerialPort::OneStop); //停止位设置为1
m_serial.setFlowControl(QSerialPort::NoFlowControl); //设置为无流控制
m_serial.setReadBufferSize(1); //设置内部读缓冲区的大小(如果不设置默认最大是4096字节)
if(!m_serial.open(QIODevice::ReadWrite))
{
qDebug() << "Serial port open error!";
return false;
}
qDebug() << "Serial port open success!";
return true;
}
注意:
1.函数传参port为需要打开的串口号,用一个下拉框接收识别到的所有串口号并实时刷新,参考如下代码:
新建一个serialportmanager.h文件
#ifndef SERIALPORTMANAGER_H
#define SERIALPORTMANAGER_H
#include <QObject>
#include <QSerialPortInfo>
#include <QTimer>
class SerialPortManager : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList availablePorts READ availablePorts NOTIFY availablePortsChanged)
public:
explicit SerialPortManager(QObject *parent = nullptr) : QObject(parent)
{
updateAvailablePorts();
// 定期检查可用串口列表
m_timer.setInterval(1000); // 每秒检查一次
connect(&m_timer, &QTimer::timeout, this, &SerialPortManager::updateAvailablePorts);
m_timer.start();
}
QStringList availablePorts() const
{
return m_availablePorts;
}
signals:
void availablePortsChanged();
private:
void updateAvailablePorts()
{
m_availablePorts.clear();
const QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
for (const QSerialPortInfo &portInfo : ports) {
m_availablePorts.append(portInfo.portName());
}
emit availablePortsChanged();
}
private:
QStringList m_availablePorts;
QTimer m_timer;
};
#endif // SERIALPORTMANAGER_H
在main.cpp里实例化
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "serialport.h"
#include "serialportmanager.h"
...
...
...
QQmlApplicationEngine engine;
SerialPortManager serialPortManager;
engine.rootContext()->setContextProperty("SerialPortManager", &serialPortManager);
qml的下拉框代码
import QtCharts 2.15
...
//串口下拉框
ComboBox{
id: portComboBox
width: 80
height: 40
font.family: "楷体"
model: SerialPortManager.availablePorts
//设置默认选中的串口号
onModelChanged: {
if (SerialPortManager.availablePorts.length > 0)
currentIndex = 0;
}
// 更新串口列表槽函数
Connections {
target: SerialPortManager
function onAvailablePortsChanged() {
// 更新串口列表
portComboBox.model = SerialPortManager.availablePorts;
}
}
}
2.m_serial.setReadBufferSize(1)设置内部读缓冲区的大小,一般不需要设置,在下面实时接收数据再详细解释这里为什么设置成1;
二、发送数据
//串口发送
void SerialPort::sendData()
{
// 构造协议数据
QByteArray protocolData;
protocolData.append('\x01');
protocolData.append('\x11');
protocolData.append('\x02');
protocolData.append('\x22');
protocolData.append('\x03');
// 计算并添加异或校验位
quint8 checksum = xorChecksum(protocolData);
protocolData.append(checksum);
// 发送协议数据
qint64 bytesWritten = m_serial.write(protocolData);
if (bytesWritten == -1) {
qDebug() << "Failed to write data to serial port:" << m_serial.errorString();
}
qDebug() << "发送字节数:" << bytesWritten;
}
//计算异或校验位
quint8 SerialPort::xorChecksum(QByteArray processedData)
{
quint8 checksum = 0;
for (int i = 0; i < processedData.size(); ++i) {
checksum ^= processedData.at(i);
}
return checksum;
}
串口发送直接使用write()函数就行,不过串口通信一般需要做校验,以上代码是奇偶校验的一个方法,最终应该会发送一串16进制的数:01 11 02 22 03 33
三、接收数据
在构造函数里建立信号和槽
// 读取串口数据
connect(&m_serial, SIGNAL(readyRead()), this, SLOT(receiveData()));
//接收数据
void SerialPort::receiveData()
{
receivedData.append(m_serial.readAll());
...
数据处理
...
数据处理完后可以用receivedData.clear()把缓冲区清除
...
}
如果串口设置了内部读缓冲区的大小为1,即m_serial.setReadBufferSize(1),这样每来一个字节数据就会触发一次readyRead()信号,这个方法对于需要接收一段完整协议来说非常适用。反之,只会触发一次readyRead()信号,readAll()就会把全部数据协议读出来,且最多4096个字节,超出的话会直接丢掉。所以当接收数据量多的时候推荐设置内部读缓冲区大小,这样不但数据不会丢失,而且响应速度也会更快。
四、关闭串口
//关闭串口
void SerialPort::closeSerial()
{
if(m_serial.isOpen())
{
m_serial.close();
}
}
以上就是串口通信的主要部分代码,下面附带一下.h文件给予参考:
#ifndef SERIALPORT_H
#define SERIALPORT_H
#include <QObject>
#include <QDebug>
#include <QCoreApplication>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QDataStream>
class SerialPort : public QObject
{
Q_OBJECT
public:
explicit SerialPort(QObject *parent = nullptr);
~SerialPort();
quint8 xorChecksum(QByteArray processedData);
bool connectSerial(QString port);
void sendData();
void closeSerial();
public slots:
void receiveData();
private:
QSerialPort m_serial;
QByteArray receivedData;
};
#endif // SERIALPORT_H