Qt 与多个485设备同步通讯
Qt 与多个485设备同步通讯
参考:
由于项目需求,要求用485通讯方式与 4 个激光位移传感器进行通讯,遂写了这样一个示例:
-
使用的传感器为博亿精科的BL系列激光位移传感器
-
按照说明书将 4 个激光位移传感器的地址分别设置为:
0x01
、0x02
、0x03
、0x04
.pro文件
QT += core gui
QT += serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
crc.cpp \
main.cpp \
mainwindow.cpp \
rs485com.cpp
HEADERS += \
crc.h \
mainwindow.h \
rs485com.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
CRC 校验 crc.h 和 crc.cpp
/// CRC校验码计算
unsigned short CRC16(unsigned char* pBuf, unsigned short len);
/// CRC校验码计算
unsigned short CRC16(unsigned char* pBuf, unsigned short len)
{
unsigned short uCRC = 0xFFFF;
int i, j;
for (i = 0; i < len; i++)
{
uCRC ^= (*(pBuf + i));
for (j = 0; j < 8; j++)
{
if ((uCRC & 0x0001) == 0x0001)
{
uCRC = (uCRC >> 1);
uCRC ^= 0xA001;
}
else
uCRC = (uCRC >> 1);
}
}
return uCRC;
}
485 通讯类 rs485com.h 和 rs485com.cpp
/// rs485com.h
#include <QtSerialPort/QSerialPort>
#include <QObject>
#include <QThread>
#include <QDebug>
class Rs485Com : public QObject
{
Q_OBJECT
public:
explicit Rs485Com(QObject* parent = nullptr);
public slots:
/// 串口通讯初始化
/// 与对应串口建立连接,设置通讯波特率、读写方向、数据位等
/// @param portName 串口名,如 COM1、COM2 等
void initPort(const QString& portName);
/// 运行通信(同步)
/// 与 485 设备不断进行同步通讯
void runComSync(void);
private:
/// 用于通讯的串口类成员
QSerialPort* port;
/// 串口通讯子线程成员
QThread* subThread;
/// 串口通讯报文
QByteArray readCommand[4];
signals:
/// 建立连接失败信号函数
void failed(void);
/// 建立连接成功信号函数
void succeed(void);
/// 接收数据发送给主线程的信号函数
/// @param addr 设备地址
/// @param dis 读取和处理后得到的数据
void result(int addr,double dis);
};
/// rs485com.cpp
#include "rs485com.h"
#include "crc.h"
Rs485Com::Rs485Com(QObject *parent)
: QObject{parent}
{
// 创建用于485 通信的线程
subThread = new QThread;
// 初始化报文
unsigned char hex[8]{ 0x01, 0x04, 0x00, 0x00, 0x00, 0x02 };
unsigned short crc;
for(int i = 0; i < 4;i++)
{
hex[0] = i + 1;
crc = CRC16(hex,6);
hex[6] = static_cast<unsigned char>(crc);
hex[7] = static_cast<unsigned char>(crc >> 8);
readCommand[i] = QByteArray((char*)hex,8);
qDebug() << readCommand[i].toHex();
}
// 串口类必须在 moveToThread 之前初始化,否则程序会 crash(崩溃)
port = new QSerialPort();
this->moveToThread(subThread);
port->moveToThread(subThread);
// 启动线程
subThread->start();
}
void Rs485Com::initPort(const QString& portName)
{
// 如果串口已打开
if (this->port->isOpen())
{
this->port->clear();// 清除缓冲区数据,终止读写操作
this->port->close();// 关闭串口
}
// 设置要打开的串口名
this->port->setPortName(portName);
if(!this->port->open(QIODevice::ReadWrite))
{
emit failed();// 打开串口失败信号
}
else
{
this->port->setBaudRate(115200,QSerialPort::AllDirections);//设置波特率和读写方向
this->port->setDataBits(QSerialPort::Data8); //数据位为8位
this->port->setFlowControl(QSerialPort::NoFlowControl); //无流控制
this->port->setParity(QSerialPort::NoParity); //无校验位
this->port->setStopBits(QSerialPort::OneStop); //一位停止位
this->runComSync();
emit succeed();// 打开串口成功信号
}
}
void Rs485Com::runComSync(void)
{
int i = 0;
QByteArray bytes;
unsigned char cdata[4];
int temp;
double displacement;
while(true)
{
if(i > 3){ i = 0; }
// 发出指令
port->write(readCommand[i]);
qDebug() << "发送: " << readCommand[i].toHex();
port->flush();
port->waitForReadyRead(200);
bytes.resize(9);
bytes = port->readAll();
qDebug() << "接收: " << bytes.toHex();
for(int i = 3; i <= 6; i++)
{
cdata[i - 3] = bytes[i];
}
temp = (cdata[3]&0xff)|(cdata[2]<<8)|(cdata[1]<<16)|(cdata[0]<<24);
displacement = temp / 1000.0;
emit result(i,displacement);
i++;
}
}
界面:

主窗口类:
/// mainwindow.h
#include <QMainWindow>
#include <QtSerialPort/QSerialPortInfo>
#include "rs485com.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void refreshPortList(void);
void connect2SensorSlots(void);
void connectSlots(void);
private:
Rs485Com* lds;
Ui::MainWindow *ui;
signals:
// 连接到传感器信号
void connect2Sensor(QString portName);
};
/// mainwindow.h
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
lds = new Rs485Com;
this->refreshPortList();
this->connectSlots();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::refreshPortList(void)
{
ui->comboBox->clear();
QStringList portList;
foreach(const QSerialPortInfo& info,QSerialPortInfo::availablePorts())
{
portList.append(info.portName());
}
ui->comboBox->addItems(portList);
}
void MainWindow::connect2SensorSlots(void)
{
emit connect2Sensor(ui->comboBox->currentText());
}
void MainWindow::connectSlots(void)
{
connect(ui->refreshButton,&QPushButton::clicked,this,&MainWindow::refreshPortList);
connect(this,&MainWindow::connect2Sensor,lds,&Rs485Com::initPort);
connect(lds,&Rs485Com::failed,this,[=]{
qDebug() << "连接错误";
});
connect(lds,&Rs485Com::result,this,[=](int addr, double d){
switch(addr)
{
case 0:
ui->d1Edit->setText(QString::number(d));
break;
case 1:
ui->d2Edit->setText(QString::number(d));
break;
case 2:
ui->d3Edit->setText(QString::number(d));
break;
case 3:
ui->d4Edit->setText(QString::number(d));
break;
}
});
connect(ui->connectButton,&QPushButton::clicked,this,&MainWindow::connect2SensorSlots);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App