野火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 数值.

注意: 不要点着火!!!, 不要吸入过量丁烷!!!, 安全第一!!!
相关推荐
talen_hx2962 分钟前
OkHttpClient的最佳实践优化方案
java·笔记·学习
济6177 分钟前
FreeRTOS传感器采集任务 ——SensorTask 传感器采集任务整体实现
stm32·单片机·嵌入式·freertos
三品吉他手会点灯8 分钟前
C语言学习笔记 - 26.C编程预备计算机专业知识 - 15~25关键内容回顾
c语言·笔记·学习
xiangw@GZ11 分钟前
ADS与HFSS 全维度异同分析
嵌入式硬件
木子单片机17 分钟前
基于51单片机出租车计费设计
stm32·单片机·嵌入式硬件·51单片机·keil
济61729 分钟前
FreeRTOS 上报任务设计---UplinkTask 上行数据上报任务详解
stm32·嵌入式·freertos
许长安10 小时前
RPC 同步调用基本使用方法:基于官方 RouteGuide 示例
c++·经验分享·笔记·rpc
做cv的小昊14 小时前
【TJU】研究生应用统计学课程笔记(8)——第四章 线性模型(4.1 一元线性回归分析)
笔记·线性代数·算法·数学建模·回归·线性回归·概率论
踏着七彩祥云的小丑14 小时前
嵌入式测试学习第1天:电路基础核心概念
单片机·嵌入式硬件