MQ2烟雾传感器模块(第九天)

在这里插入图片描述


👨‍💻个人主页@开发者-削好皮的Pineapple!

👨‍💻 hello 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅!

👨‍💻 本文由 削好皮的Pineapple! 原创

👨‍💻 收录于专栏C语言到基于STM32 的智能矿探小车


请添加图片描述

请添加图片描述


前言

  • 本文主要介绍MQ2烟雾传感器模块的相关知识,包括其引脚连接方式、数据获取方法以及如何将检测到的烟雾浓度值显示到手机蓝牙串口APP上,为智能矿探小车的烟雾检测功能实现提供基础。

🎶一、MQ2模块基本介绍与引脚连接


  • MQ2模块主要用于测量烟雾浓度,相关详细说明可参考《Z-MQ-01烟雾传感器使用说明书.pdf》。
  • 引脚连接方式如下:
    • VCC引脚连接至5V电源
    • GND引脚连接至地
    • TXD引脚连接至单片机的RXD(可选择USART1、USART2或USART3的RXD)
    • RXD引脚连接至单片机的TXD(可选择USART1、USART2或USART3的TXD)

在这里插入图片描述
在这里插入图片描述

区分维度模拟输出型MQ2串口输出型MQ2
引脚数量及标识通常4脚,包含VCC、GND、AO(核心)、DO(可选)通常4~6脚,包含VCC、GND、TX(发送)、RX(接收),可能有RESET、SET等
核心引脚作用AO输出05V或03.3V模拟电压(随烟雾浓度变化);DO输出高/低电平(阈值报警)TX和RX为串口通信核心引脚,用于数据收发
是否带串口芯片无,仅含MQ2气敏元件、电阻、电容等基础元件有,含CH340(常见)、CP2102、PL2303等串口转换芯片
尺寸较小,通常为2cm×2cm左右的小型电路板相对较大(因含额外芯片)
指示灯一般无特殊指示灯可能有PWR(电源灯)、TX(发送灯)、RX(接收灯),通信时TX/RX灯会闪烁

快速判断步骤

  1. 看引脚:有TX和RX → 串口型;只有AO/DO → 模拟输出型。
  2. 找芯片:有CH340/CP2102等串口芯片 → 串口型;无则为模拟输出型。

像我们今天所用的下面就有标识串口类型。


🎶二、MQ2模块数据获取与处理


  • 单片机若要获取MQ2模块的数据,需要通过串口(USART1、USART2或USART3)向MQ2发送一帧特定数据:FF 01 86 00 00 00 00 00 79
  • MQ2模块收到上述数据后,会反馈一帧数据,例如:FF 86 00 85 00 00 00 00 F5
  • 其中,反馈数据的第二字节和第三字节表示检测到的浓度值。以上述示例为例:
    • 第二个字节为0x00
    • 第三个字节为0x85
    • 计算检测到的浓度值(十进制)的方法为:0x00 << 8 | 0x85
  • 后续需将获取到的烟雾浓度值(十进制)通过蓝牙模块发送到手机的蓝牙串口APP上进行显示。
  • 串口2连接蓝牙,串口3连接MQ2。手机蓝牙APP发送S或s,收到烟雾浓度。

uart.c

#include "stm32f4xx.h"
#include "uart.h"
#include <stdio.h>

// 全局变量定义
u8 recv; // USART2接收数据(命令指示)
u8 rdata; // USART3接收数据(MQ2传感器数据)
u8 mq2_response[9]; // 存储MQ2模块响应数据帧
u16 smoke_concentration; // 存储烟雾浓度值(百分比)
volatile uint8_t mq2_response_index = 0; // 记录MQ2响应帧索引
static uint8_t is_sending = 0; // 发送状态标志,防止数据冲突
uint8_t request_smoke_data = 0; // 请求烟雾数据标志

// 向MQ2模块发送请求数据帧(固定帧)
void send_mq2_request(void) {
    int i;
    u8 request_data[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
    for (i = 0; i < 9; i++) {
        while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
        USART_SendData(USART3, request_data[i]);
    }
}

// 解析MQ2数据接收逻辑,包括帧格式和校验位验证
void parse_mq2_response(void) {
    u8 checksum = 0;
    u16 raw_value;
    int i;

    // 1. 验证帧头格式,MQ2响应帧头为FF 86
    if (mq2_response[0] != 0xFF || mq2_response[1] != 0x86) {
        smoke_concentration = 0; // 帧格式错误,返回0
        return;
    }

    // 2. 计算校验和,确保数据有效性
    for (i = 0; i < 8; i++) {
        checksum += mq2_response[i];
    }
    checksum = 0xFF - (checksum % 0xFF);
    if (checksum != mq2_response[8]) {
        smoke_concentration = 0; // 校验失败,返回0
        return;
    }

    // 3. 提取有效数据,浓度值在第2和3字节
    raw_value = (mq2_response[2] << 8) | mq2_response[3]; // 合并字节

    // 4. 根据公式进行实际浓度转换
    if (raw_value > 5000) {
        smoke_concentration = 100;
    } else {
        smoke_concentration = (raw_value * 100) / 5000;
    }
}

// 修改字符串发送逻辑,确保数据完整发送
void send_smoke_concentration_to_bluetooth(void) {
    char buffer[20];
    int i;
    uint32_t timeout;
    
    if (is_sending) return; // 避免冲突,直接退出
    is_sending = 1;

    // 格式化字符串,确保格式正确
    snprintf(buffer, sizeof(buffer), "Smoke: %d%%\n", smoke_concentration);

    // 暂时关闭串口接收中断,防止冲突
    USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
    USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);

    // 发送字符串,添加超时机制
    for (i = 0; buffer[i] != '\0'; i++) {
        timeout = 0;
        // 等待发送缓冲区为空,超时值设置为500000
        while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET) {
            if (timeout++ > 500000) { // 简单的超时处理
                is_sending = 0;
                // 恢复中断并退出
                USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
                USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
                return;
            }
        }
        USART_SendData(USART2, buffer[i]);
    }

    // 发送完成,恢复中断
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
    is_sending = 0;
}

// 串口初始化函数(固定帧)
void uart_init(int bond) {
    // 原始代码不变,确保USART2和USART3配置正确
    GPIO_InitTypeDef  GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;
    NVIC_InitTypeDef  NVIC_InitStruct;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 | RCC_APB1Periph_USART3, ENABLE);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;

    // USART2 (PA2=TX, PA3=RX)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // USART3 (PB10=TX, PB11=RX)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);

    USART_InitStruct.USART_BaudRate = bond;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART2, &USART_InitStruct);
    USART_Init(USART3, &USART_InitStruct);

    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);

    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;

    NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
    NVIC_Init(&NVIC_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
    NVIC_Init(&NVIC_InitStruct);

    USART_Cmd(USART2, ENABLE);
    USART_Cmd(USART3, ENABLE);
}

// 中断处理函数(固定帧)
void USART2_IRQHandler(void) {
    if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET) {
        recv = USART_ReceiveData(USART2);
        USART_ClearITPendingBit(USART2, USART_IT_RXNE);
        
        // 检测特定指令触发烟雾浓度请求
        if (recv == 'S' || recv == 's') { // 当接收到 'S' 或 's' 时请求数据
            request_smoke_data = 1;
        }
    }
}

void USART3_IRQHandler(void) {
    if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET) {
        rdata = USART_ReceiveData(USART3);
        mq2_response[mq2_response_index++] = rdata;
        // 接收到9字节后开始处理
        if (mq2_response_index == 9) {
            mq2_response_index = 0;
            parse_mq2_response();
            send_smoke_concentration_to_bluetooth(); // 发送浓度数据
            request_smoke_data = 0; // 重置请求标志
        }
        USART_ClearITPendingBit(USART3, USART_IT_RXNE);
    }
}

uart.h

#ifndef __UART_H__
#define __UART_H__
#include "stm32f4xx.h"

void uart_init(int bond);
void USART2_IRQHandler(void);
void USART3_IRQHandler(void);
void send_mq2_request(void);
void send_smoke_concentration_to_bluetooth(void);

extern u8 recv;  // USART2接收数据(命令)
extern u8 rdata; // USART3接收数据(MQ2传感器数据)
extern uint8_t request_smoke_data; // 请求烟雾数据标志
#endif

main.c

#include "stm32f4xx.h"
#include "exit.h"
#include "car.h"
#include "delay.h"
#include "remote.h"
#include "uart.h"
#include "led.h"

extern uint8_t request_smoke_data;

int main(void) {
    SystemInit();
    car_init();
    remote_Init();
    delay_init();
    led_init();
    uart_init(9600); // 初始化串口,用于连接MQ2和蓝牙

    while(1) {
        // 当请求标志被设置时发送MQ2请求
        if (request_smoke_data) {
            send_mq2_request();
            // 等待一段时间以确保数据接收完成
            delay_ms(500);
        }

    }
}
结束语🥇

🔥 订阅专栏持续学习:C语言到基于STM32的智能矿探小车
💬 欢迎点赞、收藏、留言讨论,一起攻克嵌入式开发!

内容概要:本文档详细介绍了基于MATLAB实现的多头长短期记忆网络(MH-LSTM)结合Transformer编码器进行多变量时间序列预测的项目实例。项目旨在通过融合MH-LSTM对时序动态的细致学习和Transformer对全局依赖的捕捉,显著提升多变量时间序列预测的精度和稳定性。文档涵盖了从项目背景、目标意义、挑战与解决方案、模型架构及代码示例,到具体的应用领域、部署与应用、未来改进方向等方面的全面内容。项目不仅展示了技术实现细节,还提供了从数据预处理、模型构建与训练到性能评估的全流程指导。 适合人群:具备一定编程基础,特别是熟悉MATLAB和深度学习基础知识的研发人员、数据科学家以及从事时间序列预测研究的专业人士。 使用场景及目标:①深入理解MH-LSTM与Transformer结合的多变量时间序列预测模型原理;②掌握MATLAB环境下复杂神经网络的搭建、训练及优化技巧;③应用于金融风险管理、智能电网负荷预测、气象预报、交通流量预测、工业设备健康监测、医疗数据分析、供应链需求预测等多个实际场景,以提高预测精度和决策质量。 阅读建议:此资源不仅适用于希望深入了解多变量时间序列预测技术的读者,也适合希望通过MATLAB实现复杂深度学习模型的开发者。建议读者在学习过程中结合提供的代码示例进行实践操作,并关注模型训练中的关键步骤和超参数调优策略,以便更好地应用于实际项目中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值