基于STM32的传感器数据采集系统设计:Qt、RS485、Modbus Rtu协议(代码示例)

一、项目概述

项目目标与用途

本项目旨在设计并实现一个基于STM32F103RCT6微控制器的传感器数据采集系统。该系统通过多个传感器实时监测环境参数,并将采集到的数据传输至上位机进行处理和分析。系统的主要应用领域包括环境监测、工业控制、智能家居等。通过该系统,用户能够实时获取传感器数据,进行数据存储和分析,并具备限位报警、状态判断、故障监测等功能,以提高监测的及时性和有效性。

技术栈关键词

  • 硬件: STM32F103RCT6微控制器、RS485、RS232接口、传感器模块

  • 通信协议: Modbus RTU

  • 软件: Qt开发环境

  • 编程语言: C/C++、Qt

二、系统架构

系统架构设计

本系统由三个主要部分组成:传感器、数据采集模块和上位机。系统架构的设计如下图所示:
数据采集 RS485 数据处理 限位报警 状态判断 故障监测 传感器 数据采集模块 上位机 用户界面 报警模块 状态监测模块 故障检测模块

系统组件选择

  1. 单片机: 选用STM32F103RCT6微控制器,具有高性能和丰富的外设接口,适合复杂的数据采集任务。

  2. 通信协议: 采用RS485接口,以Modbus RTU协议进行数据通信,适合长距离和多点通信,确保数据的完整性和可靠性。

  3. 传感器: 选择温度、湿度、气体等多种传感器,能够满足不同环境监测需求。

  4. 上位机软件: 使用Qt开发环境,提供直观的用户交互界面,支持实时数据显示和历史数据分析。

三、环境搭建和注意事项

环境搭建

  1. 硬件环境:

    • STM32开发板(如STM32F103RCT6)

    • RS485转USB模块

    • 各类传感器(如DHT11、MQ系列气体传感器等)

  2. 软件环境:

    • 安装STM32CubeIDE或Keil进行固件开发。

    • 安装Qt Creator,配置Qt环境以进行上位机软件开发。

注意事项

  • 电源管理: 确保电源电压稳定,避免对微控制器和传感器造成损害。

  • RS485通信: 在RS485通信中,确保信号的正确连接和终端电阻的匹配,以防止信号反射和干扰。

  • 数据传输稳定性: 在长距离通信时,注意数据线的屏蔽和布线,以提高抗干扰能力。

四、代码实现过程

为了实现基于STM32的传感器数据采集系统,我们需要按照系统架构设计逐步实现各个功能模块。以下将详细介绍数据采集模块和上位机模块的代码实现过程,包括系统初始化、数据采集、数据传输、界面设计等方面。

4.1 数据采集模块实现

4.1.1 系统初始化

在STM32F103RCT6微控制器中,首先需要进行系统的初始化,包括时钟配置、GPIO初始化和UART配置。以下是系统初始化的代码示例:

c 复制代码
#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配置的代码示例:

c 复制代码
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配置的代码示例:

c 复制代码
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 传感器数据采集

在数据采集模块中,我们需要实现从传感器读取数据的功能。以下是读取温度和湿度传感器数据的示例代码:

c 复制代码
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引脚的状态。以下是数据发送的代码示例:

c 复制代码
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工具设计用户界面。以下是一个简单的界面设计步骤:

  1. 创建主窗口: 在Qt Designer中创建一个QMainWindow,命名为MainWindow

  2. 添加控件: 在主窗口中添加以下控件:

  • QLabel: 显示温度和湿度信息。

  • QPushButton: 用于开始和停止数据接收。

  • QTableWidget: 显示历史数据。

  • QTextEdit: 用于显示日志信息。

设计完成后,将界面保存。

4.2.3 代码实现

MainWindow类中,编写数据接收和显示的逻辑。以下是关键代码部分的实现:

c 复制代码
#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 故障监测与报警功能

在上位机中,我们可以实现一个简单的故障监测机制,以便在传感器数据超过设定阈值时发出警报。以下是监测函数的完整实现代码。

c 复制代码
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,以便在接收到数据后立即检查是否存在报警情况:

c 复制代码
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 主函数

最后,确保在主函数中创建并显示主窗口,同时设置接收数据的启动和停止事件。

c 复制代码
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MainWindow window;

    // 开始接收数据
    window.startReceiving();

    window.show();
    return app.exec();
}

4.3 整体系统流程

4.3.1 数据采集流程

  1. 系统启动: STM32微控制器初始化,配置时钟、GPIO和UART。

  2. 传感器数据采集: 定时采集传感器数据,读取温度和湿度信息。

  3. 数据传输: 通过RS485接口将采集到的数据发送给上位机,采用Modbus RTU协议格式化数据。

  4. 数据发送: 控制RS485的发送和接收模式,确保数据的完整性。

4.3.2 上位机数据处理流程

  1. 串口配置: 打开指定的串口,设置波特率和数据格式。

  2. 数据接收: 接收来自STM32的数据并解析。

  3. 数据展示: 实时更新界面上的数据标签,显示温度和湿度值。同时,将历史数据保存到数据表格中,以供后续查询与分析。

  4. 故障监测与报警: 在每次接收数据时,调用监测函数检查温度和湿度是否超出设定阈值。如果超出,则在日志框中打印警报信息,提示用户注意。

  5. 数据持久化: 在适当的时机(例如用户点击"保存"按钮或关闭软件时),将历史数据写入CSV文件,便于后续查看和分析。

4.3.3 整体系统工作流程图

用户 上位机 STM32微控制器 传感器 启动上位机软件 打开串口 确认串口打开 采集传感器数据 返回温度、湿度数据 发送数据(温度, 湿度) loop 数据采集与发送 解析数据 更新界面显示 检查限位 触发报警 显示警报信息 记录正常数据 alt 超出限位 正常状态 保存数据 将数据写入文件 关闭串口 用户 上位机 STM32微控制器 传感器

五、项目总结

5.1 项目主要功能

本项目实现了一个基于STM32F103RCT6微控制器的传感器数据采集系统,主要功能包括:

  1. 传感器数据采集: 通过多个传感器(如温度、湿度传感器)实时采集环境数据。

  2. 数据传输: 采用RS485接口与Modbus RTU协议,将采集到的数据实时传送至上位机。

  3. 上位机数据处理: 使用Qt开发环境实现用户界面,实时显示温度、湿度数据,并支持历史数据记录和展示。

  4. 故障监测与报警: 实现对传感器数据的阈值监测,超出设定范围时发出警报,并在界面中记录相关信息。

  5. 数据持久化: 支持将历史数据保存为CSV文件,便于后续分析与查看。

5.2 实现过程总结

该系统的实现过程经历了以下几个阶段:

  • 需求分析与设计: 针对项目需求,设计了系统架构和数据流,并选择了合适的硬件组件和通信协议。

  • 硬件开发: 在STM32开发板上实现了数据采集模块,配置了GPIO、UART和RS485接口,编写了数据采集和发送的代码。

  • 上位机开发: 使用Qt进行上位机软件的开发,设计了用户界面,编写了数据接收、解析及显示的逻辑代码。

  • 系统集成与测试: 将硬件和上位机软件进行集成,进行了系统测试,确保数据的准确性和系统的稳定性。

相关推荐
azwsm17 小时前
电路元器件和GPIO控制器
单片机·嵌入式硬件
KaMeidebaby19 小时前
卡梅德生物技术快报|纯化重组蛋白实操详解
人工智能·python·tcp/ip·算法·机器学习
kebidaixu20 小时前
FreeRTOS 移植到 STM32F407VETX 记录(一)
stm32·单片机·嵌入式硬件
zjun100120 小时前
TCP专栏-4.四次挥手
网络协议·tcp/ip
CSDN官方博客21 小时前
「谁说嵌入式只是调包和焊板子?」—— 2026嵌入式全栈技术征锋令
嵌入式硬件·物联网·embedding
半条-咸鱼21 小时前
【INACCESSIBLE_BOOT_DEVICE】安装 Config Tool 后 Windows 蓝屏,最终通过 VMware 虚拟机解决
windows·stm32·vmware·芯片
点灯小铭21 小时前
基于单片机的数码管定时插座设计与定时开关功能实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
云栖梦泽21 小时前
玩转RK3506SDK
linux·嵌入式硬件
ytttr8731 天前
Qt 数字键盘实现
开发语言·qt
酉鬼女又兒1 天前
零基础入门计算机网络:网络层核心任务、三大关键问题、两种服务类型与 TCP/IP 网际层协议体系全解析
服务器·网络·网络协议·tcp/ip·计算机网络·php·求职招聘