基于STM32的传感器数据采集系统设计:Qt、RS485、Modbus Rtu协议(代码示例)
一、项目概述
项目目标与用途
本项目旨在设计并实现一个基于STM32F103RCT6微控制器的传感器数据采集系统。该系统通过多个传感器实时监测环境参数,并将采集到的数据传输至上位机进行处理和分析。系统的主要应用领域包括环境监测、工业控制、智能家居等。通过该系统,用户能够实时获取传感器数据,进行数据存储和分析,并具备限位报警、状态判断、故障监测等功能,以提高监测的及时性和有效性。
技术栈关键词
-
硬件: STM32F103RCT6微控制器、RS485、RS232接口、传感器模块
-
通信协议: Modbus RTU
-
软件: Qt开发环境
-
编程语言: C/C++、Qt
二、系统架构
系统架构设计
本系统由三个主要部分组成:传感器、数据采集模块和上位机。系统架构的设计如下图所示:
系统组件选择
-
单片机: 选用STM32F103RCT6微控制器,具有高性能和丰富的外设接口,适合复杂的数据采集任务。
-
通信协议: 采用RS485接口,以Modbus RTU协议进行数据通信,适合长距离和多点通信,确保数据的完整性和可靠性。
-
传感器: 选择温度、湿度、气体等多种传感器,能够满足不同环境监测需求。
-
上位机软件: 使用Qt开发环境,提供直观的用户交互界面,支持实时数据显示和历史数据分析。
三、环境搭建和注意事项
环境搭建
-
硬件环境:
-
STM32开发板(如STM32F103RCT6)
-
RS485转USB模块
-
各类传感器(如DHT11、MQ系列气体传感器等)
-
-
软件环境:
-
安装STM32CubeIDE或Keil进行固件开发。
-
安装Qt Creator,配置Qt环境以进行上位机软件开发。
-
注意事项
-
电源管理: 确保电源电压稳定,避免对微控制器和传感器造成损害。
-
RS485通信: 在RS485通信中,确保信号的正确连接和终端电阻的匹配,以防止信号反射和干扰。
-
数据传输稳定性: 在长距离通信时,注意数据线的屏蔽和布线,以提高抗干扰能力。
四、代码实现过程
为了实现基于STM32的传感器数据采集系统,我们需要按照系统架构设计逐步实现各个功能模块。以下将详细介绍数据采集模块和上位机模块的代码实现过程,包括系统初始化、数据采集、数据传输、界面设计等方面。
4.1 数据采集模块实现
4.1.1 系统初始化
在STM32F103RCT6微控制器中,首先需要进行系统的初始化,包括时钟配置、GPIO初始化和UART配置。以下是系统初始化的代码示例:
#include "stm32f10x.h"
void SystemClock_Config(void);
void GPIO_Config(void);
void UART_Config(void);
int main(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
GPIO_Config(); // 初始化GPIO
UART_Config(); // 配置UART
while (1) {
CollectSensorData(); // 采集传感器数据
HAL_Delay(1000); // 每秒采集一次
}
}
4.1.2 GPIO配置
在数据采集模块中,GPIO配置用于控制RS485的发送和接收模式。以下是GPIO配置的代码示例:
void GPIO_Config(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置RS485 DE引脚为输出
GPIO_InitStruct.Pin = GPIO_PIN_1; // 假设DE连接在PA1
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
4.1.3 UART配置
UART配置用于RS485通信,设置波特率和数据格式。以下是UART配置的代码示例:
void UART_Config(void) {
__HAL_RCC_USART1_CLK_ENABLE(); // 使能USART1时钟
UART_HandleTypeDef huart1;
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) {
// 初始化失败,处理错误
Error_Handler();
}
}
4.1.4 传感器数据采集
在数据采集模块中,我们需要实现从传感器读取数据的功能。以下是读取温度和湿度传感器数据的示例代码:
void ReadTemperature(uint8_t *data) {
// 假设使用某种传感器读取温度
// 这里应实现具体的传感器读取逻辑
data[0] = 25; // 示例:读取到的温度值
}
void ReadHumidity(uint8_t *data) {
// 假设使用某种传感器读取湿度
// 这里应实现具体的传感器读取逻辑
data[1] = 60; // 示例:读取到的湿度值
}
void CollectSensorData() {
uint8_t sensorData[10];
ReadTemperature(sensorData);
ReadHumidity(sensorData + 1);
// 发送数据到上位机
RS485_SendData(sensorData, sizeof(sensorData));
}
4.1.5 RS485数据发送
RS485通信需要在发送数据前控制DE引脚的状态。以下是数据发送的代码示例:
void RS485_SendData(uint8_t *data, uint16_t length) {
// 控制DE引脚为高电平,设置为发送模式
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
// 发送数据到上位机
HAL_UART_Transmit(&huart1, data, length, HAL_MAX_DELAY);
// 控制DE引脚为低电平,设置为接收模式
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
}
4.2 上位机模块实现
上位机模块负责接收来自数据采集模块的数据,并在用户界面上实时显示这些数据。以下将详细介绍上位机模块的关键实现步骤,包括Qt界面的设计、数据接收与解析、数据展示等。
4.2.1 Qt环境配置
首先,需要在计算机上安装Qt SDK,并创建一个新的Qt Widgets应用程序项目。确保在项目设置中选择适当的Qt版本,并配置好编译环境。
4.2.2 界面设计
在Qt Creator中,可以使用Qt Designer工具设计用户界面。以下是一个简单的界面设计步骤:
-
创建主窗口: 在Qt Designer中创建一个QMainWindow,命名为
MainWindow
。 -
添加控件: 在主窗口中添加以下控件:
-
QLabel: 显示温度和湿度信息。
-
QPushButton: 用于开始和停止数据接收。
-
QTableWidget: 显示历史数据。
-
QTextEdit: 用于显示日志信息。
设计完成后,将界面保存。
4.2.3 代码实现
在MainWindow
类中,编写数据接收和显示的逻辑。以下是关键代码部分的实现:
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QTimer>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void readSerialData(); // 读取串口数据
void startReceiving(); // 开始接收数据
void stopReceiving(); // 停止接收数据
private:
QSerialPort *serial; // 串口对象
QLabel *temperatureLabel; // 温度标签
QLabel *humidityLabel; // 湿度标签
QTableWidget *dataTable; // 数据表格
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), serial(new QSerialPort(this)) {
// 界面初始化
setupUi(this);
// 配置串口
serial->setPortName("COM3"); // 设置串口名称
serial->setBaudRate(QSerialPort::Baud9600);
// 连接信号和槽
connect(serial, &QSerialPort::readyRead, this, &MainWindow::readSerialData);
}
MainWindow::~MainWindow() {
if (serial->isOpen()) {
serial->close();
}
}
void MainWindow::startReceiving() {
if (serial->open(QIODevice::ReadOnly)) {
// 启动接收数据
qDebug() << "串口已打开";
} else {
qDebug() << "无法打开串口";
}
}
void MainWindow::stopReceiving() {
if (serial->isOpen()) {
serial->close();
qDebug() << "串口已关闭";
}
}
void MainWindow::readSerialData() {
while (serial->canReadLine()) {
QString line = serial->readLine();
QStringList sensorData = line.split(',');
if (sensorData.size() >= 2) {
// 更新温度和湿度标签
temperatureLabel->setText("温度: " + sensorData[0] + " °C");
humidityLabel->setText("湿度: " + sensorData[1] + " %");
// 在数据表格中记录历史数据
int rowCount = dataTable->rowCount();
dataTable->insertRow(rowCount);
dataTable->setItem(rowCount, 0, new QTableWidgetItem(sensorData[0]));
dataTable->setItem(rowCount, 1, new QTableWidgetItem(sensorData[1]));
}
}
}
4.2.5 故障监测与报警功能
在上位机中,我们可以实现一个简单的故障监测机制,以便在传感器数据超过设定阈值时发出警报。以下是监测函数的完整实现代码。
void MainWindow::checkForAlerts(double temperature, double humidity) {
QString alertMessage;
// 检查温度是否超过警戒线
if (temperature > 30.0) {
alertMessage += "警报:温度超过30°C!\n";
}
// 检查湿度是否超过警戒线
if (humidity > 80.0) {
alertMessage += "警报:湿度超过80%!\n";
}
// 如果有警报信息,显示在文本框中
if (!alertMessage.isEmpty()) {
QTextEdit *logTextEdit = findChild<QTextEdit *>("logTextEdit");
if (logTextEdit) {
logTextEdit->append(alertMessage);
}
}
}
在readSerialData
函数中调用checkForAlerts
,以便在接收到数据后立即检查是否存在报警情况:
void MainWindow::readSerialData() {
while (serial->canReadLine()) {
QString line = serial->readLine();
QStringList sensorData = line.split(',');
if (sensorData.size() >= 2) {
// 更新温度和湿度标签
double temperature = sensorData[0].toDouble();
double humidity = sensorData[1].toDouble();
temperatureLabel->setText("温度: " + QString::number(temperature, 'f', 2) + " °C");
humidityLabel->setText("湿度: " + QString::number(humidity, 'f', 2) + " %");
// 调用报警检查函数
checkForAlerts(temperature, humidity);
// 在数据表格中记录历史数据
int rowCount = dataTable->rowCount();
dataTable->insertRow(rowCount);
dataTable->setItem(rowCount, 0, new QTableWidgetItem(QString::number(temperature)));
dataTable->setItem(rowCount, 1, new QTableWidgetItem(QString::number(humidity)));
}
}
}
在适当的时机(例如在关闭程序时)调用saveDataToFile
函数,以确保数据被保存。
4.2.7 主函数
最后,确保在主函数中创建并显示主窗口,同时设置接收数据的启动和停止事件。
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindow window;
// 开始接收数据
window.startReceiving();
window.show();
return app.exec();
}
4.3 整体系统流程
4.3.1 数据采集流程
-
系统启动: STM32微控制器初始化,配置时钟、GPIO和UART。
-
传感器数据采集: 定时采集传感器数据,读取温度和湿度信息。
-
数据传输: 通过RS485接口将采集到的数据发送给上位机,采用Modbus RTU协议格式化数据。
-
数据发送: 控制RS485的发送和接收模式,确保数据的完整性。
4.3.2 上位机数据处理流程
-
串口配置: 打开指定的串口,设置波特率和数据格式。
-
数据接收: 接收来自STM32的数据并解析。
-
数据展示: 实时更新界面上的数据标签,显示温度和湿度值。同时,将历史数据保存到数据表格中,以供后续查询与分析。
-
故障监测与报警: 在每次接收数据时,调用监测函数检查温度和湿度是否超出设定阈值。如果超出,则在日志框中打印警报信息,提示用户注意。
-
数据持久化: 在适当的时机(例如用户点击“保存”按钮或关闭软件时),将历史数据写入CSV文件,便于后续查看和分析。
4.3.3 整体系统工作流程图
五、项目总结
5.1 项目主要功能
本项目实现了一个基于STM32F103RCT6微控制器的传感器数据采集系统,主要功能包括:
-
传感器数据采集: 通过多个传感器(如温度、湿度传感器)实时采集环境数据。
-
数据传输: 采用RS485接口与Modbus RTU协议,将采集到的数据实时传送至上位机。
-
上位机数据处理: 使用Qt开发环境实现用户界面,实时显示温度、湿度数据,并支持历史数据记录和展示。
-
故障监测与报警: 实现对传感器数据的阈值监测,超出设定范围时发出警报,并在界面中记录相关信息。
-
数据持久化: 支持将历史数据保存为CSV文件,便于后续分析与查看。
5.2 实现过程总结
该系统的实现过程经历了以下几个阶段:
-
需求分析与设计: 针对项目需求,设计了系统架构和数据流,并选择了合适的硬件组件和通信协议。
-
硬件开发: 在STM32开发板上实现了数据采集模块,配置了GPIO、UART和RS485接口,编写了数据采集和发送的代码。
-
上位机开发: 使用Qt进行上位机软件的开发,设计了用户界面,编写了数据接收、解析及显示的逻辑代码。
-
系统集成与测试: 将硬件和上位机软件进行集成,进行了系统测试,确保数据的准确性和系统的稳定性。