👨💻个人主页:@开发者-削好皮的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灯会闪烁 |
快速判断步骤
- 看引脚:有TX和RX → 串口型;只有AO/DO → 模拟输出型。
- 找芯片:有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的智能矿探小车
💬 欢迎点赞、收藏、留言讨论,一起攻克嵌入式开发!