野火STM32_HAL库版课程笔记-空气、烟雾传感器公式换算

前置介绍

回顾

通过 ADC 多通道 + DMA 采集 空气, 烟雾传感器的 ADC 数据.

引出

但是 ADC 数据只是一个电压值, 该怎么得到空气和烟雾的浓度呢?

空气, 烟雾传感器公式换算

ADC 采集到的电压值是一个 "中间量" , 并不能直接反映气体浓度.

例: 读取电压为 1V, 但是却并不知道具体的浓度.

模块手册 - 烟雾传感器 - 程序流程
原理图 - 烟雾传感器
MQ2 - 数据手册与函数拟合 Excel 表

这里的预热时间对于我们做程序实验来说, 不需要满足预热 48 小时, 一分钟即可进行简单测量.

项目配置

代码部分

复制代码
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include <stdio.h>  // 使用 printf 函数
#include <math.h>   // 使用 pow(浮点幂运算)
#include <string.h> // 使用 strncmp

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* ===================== MQ135 参数 ===================== */
#define RL_MQ135      1.0f     /* 根据硬件原理图可知:RL = 1k */
#define R0_MQ135      2.0f     /* MQ135在洁净空气中的阻值 */
#define VC_MQ135      5.0f     /* MQ135供电电压,根据实际供电修改 */
#define A_MQ135       4.17f    /* y=ax^b 的 a */
#define B_MQ135      -2.28f    /* y=ax^b 的 b */

/* ===================== MQ2 参数 ===================== */
#define RL_MQ2       10.0f     /* 根据硬件原理图可知:RL = 10k */
#define R0_MQ2       20.0f     /* MQ2在洁净空气中的阻值 */
#define VC_MQ2        5.0f     /* MQ2供电电压,根据实际供电修改 */
#define A_MQ2        43.03f    /* y=ax^b 的 a */
#define B_MQ2        -1.66f    /* y=ax^b 的 b */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

uint16_t adc_val[2];  // 存储传感器 ADC 数值
float voltage[2];     // 存储传感器 ADC 电压

/* USER CODE END PV */

  /* USER CODE BEGIN 2 */
  
  // ADC 校准
  HAL_ADCEx_Calibration_Start(&hadc1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    // 启动 ADC + DMA, adc_val 是数组(地址), 在 CubeMX 中配置了内存地址递增, 所以这里只需要给这个地址就可以了. 
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_val, 2);
    
    // 每 500ms 转换一次. 
    HAL_Delay(500);
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

/* USER CODE BEGIN 4 */

/**
  * @brief  根据ADC值计算指定气体传感器的ppm
  * @param  adc_value : ADC读取的原始值(0~4095)
  * @param  RL        : 负载电阻(kΩ)
  * @param  R0        : 传感器在洁净空气中的电阻(kΩ)
  * @param  VC        : 传感器供电电压(V)
  * @param  A         : 拟合公式系数 a
  * @param  B         : 拟合公式指数 b
  * @retval ppm
  * @note   y = A * (Rs/R0)^B
  */
float Get_PPM(uint16_t adc_value, float RL, float R0, float VC, float A, float B)
{
    float vrl;   /* AO输出的模拟电压 */
    float Rs;    /* 当前传感器电阻 */
    float ppm;   /* 气体平均浓度 */    

    /* 读取AO输出电压 */
    vrl = (float)adc_value / 4095.0f * VC;
	
    /* 换算Rs电阻 */
    Rs = (VC - vrl) * RL / vrl;
    
    /* y=ax^b,x为Rs/R0 */
    ppm = A * pow(Rs / R0, B);
    
    return ppm;
}

/**
 * @brief   ADC + DMA 转换完成回调函数 
 * @param   hadc 指向 ADC 句柄的指针
 * @retval  无
 * @note    当 ADC1 的规则通道转换完成并且DMA传输完成后被自动调用,用于计算电压值并打印显示 
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	if (hadc -> Instance == ADC1)
	{
		// 将 ADC 数值转换为电压(假设参考电压为 3.3V,12位精度)
		voltage[0] = (float)adc_val[0] / 4095 * 3.3f;
		voltage[1] = (float)adc_val[1] / 4095 * 3.3f;

		printf("\r\n空气(IN4): %.3f V,烟雾(IN5): %.3f V\r\n",
		voltage[0],voltage[1]);
		
		// 打印80个*的分隔线
		printf("\r\n/*");
		for (uint32_t i = 0; i < 80; i++) printf("*");
		printf("*/");

		// MQ135空气质量计算,基于传感器电阻与经验参数
		float ppm_air = Get_PPM(adc_val[0], RL_MQ135, R0_MQ135, VC_MQ135, A_MQ135, B_MQ135);
		if (ppm_air < 10)
				printf("\r\n空气质量:低于检测范围!");
		else if (ppm_air > 1000)
				printf("\r\n空气质量:高于检测范围!");
		else
				printf("\r\n空气质量:%.2f ppm!", ppm_air);

		// MQ2烟雾浓度计算
		float ppm_smoke = Get_PPM(adc_val[1], RL_MQ2, R0_MQ2, VC_MQ2, A_MQ2, B_MQ2);
		if (ppm_smoke < 300)
				printf("\r\n烟雾浓度:低于检测范围!");
		else if (ppm_smoke > 10000)
				printf("\r\n烟雾浓度:高于检测范围!");
		else
				printf("\r\n烟雾浓度:%.2f ppm!", ppm_smoke);

		// 结尾分隔线
		printf("\r\n/*");
		for (uint32_t i = 0; i < 80; i++) printf("*");
		printf("*/\r\n");
	}
}


/**
  * @brief  重定向 printf 的输出到串口
  * @param  ch 要发送的字符
  * @param  f  文件指针(标准库要求的参数,一般不使用)
  * @retval 返回发送的字符
  */
int fputc(int ch, FILE *f)
{
         HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
         return ch;
}

/* USER CODE END 4 */

程序现象

使用普通打火机, 轻按(不点火) 将丁烷朝烟雾或空气质量传感器释放, 可以检测到空气质量和烟雾浓度达到检测范围, 并显示 ppm 数值.

注意: 不要点着火!!!, 不要吸入过量丁烷!!!, 安全第一!!!
相关推荐
夜瞬2 小时前
NLP学习笔记01:文本预处理详解——从清洗、分词到词性标注
笔记·学习·自然语言处理
中屹指纹浏览器3 小时前
指纹浏览器内核级渲染伪造技术:Canvas/WebGL/AudioContext深度伪造与检测绕过实战
经验分享·笔记
-Springer-3 小时前
STM32 学习 —— 个人学习笔记11-1(SPI 通信协议及 W25Q64 简介 & 软件 SPI 读写 W25Q64)
笔记·stm32·学习
LN花开富贵3 小时前
【ROS】鱼香ROS2学习笔记一
linux·笔记·python·学习·嵌入式·ros·agv
yrx0203073 小时前
串口空闲中断+DMA接收+环形缓冲区 && 串口DMA发送+环形缓冲区
stm32·单片机
LCG元4 小时前
STM32实战:基于STM32F103的4G模块(EC20)HTTP通信
stm32·嵌入式硬件·http
IT19954 小时前
Wireshark笔记-对AI连接标准MCP抓包分析
笔记·测试工具·wireshark
送外卖的CV工程师5 小时前
STM32+Makefile编译+OpenOCD 烧录调试
stm32·单片机·嵌入式硬件·makefile·调试·烧录·openocd
豆包公子6 小时前
程序流监控:AUTOSAR CP 功能安全在裸机 MCU 上的实现(理论篇)
运维·单片机·嵌入式硬件·安全·车载系统·autosar
羊群智妍6 小时前
2026 AI搜索优化实战:GEO优化全流程
笔记