基于GD32VW553的MQ-9可燃气体传感器驱动移植与浓度检测实战
基于GD32VW553的MQ-9可燃气体传感器驱动移植与浓度检测实战
最近在做一个环境监测的小项目,需要检测空气中的一氧化碳和甲烷浓度,正好手头有GD32VW553开发板和MQ-9气体传感器模块。很多刚开始接触嵌入式传感器的朋友可能会觉得,把传感器用起来挺复杂的,既要接硬件又要写驱动。其实没那么难,今天我就以这个组合为例,手把手带你走一遍完整的驱动移植和浓度检测流程,让你也能快速上手。
咱们的目标很明确:让GD32VW553开发板通过MQ-9传感器,既能读取模拟量浓度值,也能判断数字量报警信号,最终在串口上看到实时的检测结果。我会从硬件接线讲起,到代码编写、调试,最后完成功能验证。只要你跟着步骤走,保证能调通。
1. 认识你的“鼻子”:MQ-9传感器模块
在动手接线写代码之前,咱们先花几分钟了解一下MQ-9传感器是怎么工作的。这能帮你更好地理解后面的数据。
MQ-9的核心是一层二氧化锡(SnO2)气敏材料。这东西在清洁空气里导电性很差,电阻很大。但是,当它遇到一氧化碳(CO)或者甲烷(CH₄)这类可燃气体时,气体分子会吸附在材料表面,导致材料的电导率上升,电阻下降。电阻变化了,我们就能通过测量电压变化来感知气体浓度。
这个模块比较聪明,它采用高低温循环检测的方式:
- 低温加热(1.5V):主要用来检测一氧化碳。
- 高温加热(5.0V):用来检测甲烷、丙烷等可燃气体,同时还能“清洗”掉低温时吸附的其他杂散气体,让传感器恢复状态。
模块给我们提供了两种读取结果的方式:
- AO(模拟输出):输出一个模拟电压,气体浓度越高,电压值一般也越高。我们需要用开发板的ADC(模数转换器)功能来读取这个电压,并换算成浓度。
- DO(数字输出):模块上有个电位器可以调节报警阈值。当气体浓度超过你设定的阈值时,DO引脚会输出一个数字信号(高电平或低电平),可以直接接到单片机的普通IO口上判断,实现简单的超标报警。
模块的基本参数如下,接线和供电时要注意:
| 参数项 | 规格说明 |
|---|---|
| 工作电压 | 3.3V ~ 5V |
| 工作电流 | 约150mA(加热时电流较大,供电要足) |
| 输出信号 | AO(模拟量输出)、DO(数字量输出) |
| 读取方式 | ADC读取AO,GPIO读取DO |
| 接口 | 4 Pin 2.54mm排针 |
2. 硬件连接:给开发板装上“鼻子”
知道了原理,咱们就来接线。这一步千万不能错,接错了可能烧模块或者读不到数据。
根据原始资料,我们需要用到开发板的以下资源:
- 一个ADC通道 来读取AO的模拟电压。
- 一个普通GPIO 来读取DO的数字电平。
- 电源和地 给模块供电。
具体的引脚连接方案已经有人帮我们验证好了,直接采用这个最稳妥的接法:
| 传感器模块引脚 | GD32VW553开发板引脚 | 说明 |
|---|---|---|
| VCC | 5V0 | 接5V电源引脚。虽然模块支持3.3V,但接5V加热更充分,响应可能更好。 |
| GND | GND | 接地,和开发板共地。 |
| DO | PB2 | 数字输出,接普通GPIO口,配置为输入模式。 |
| AO | PB0 | 模拟输出,必须接在具有ADC功能的引脚上。PB0对应ADC的通道8。 |
注意:接线时最好断开电源。接好后检查一下,别把VCC和GND接反了。
3. 软件驱动编写:让开发板学会“闻气味”
硬件准备好了,接下来就是重头戏——写代码。我们会新建两个文件 bsp_mq9.c 和 bsp_mq9.h,把所有的硬件操作封装成函数,这样主程序用起来就非常清爽。
3.1 引脚与宏定义 (bsp_mq9.h)
首先在头文件里,我们把用到的硬件资源用宏定义好,这样以后想换引脚修改起来也方便。
#ifndef BSP_CODE_BSP_MQ9_H_
#define BSP_CODE_BSP_MQ9_H_
#include "gd32vw55x.h"
#include "systick.h"
// 延时函数的宏定义,方便使用
#ifndef delay_ms
#define delay_ms(x) delay_1ms(x)
#endif
#ifndef delay_us
#define delay_us(x) delay_1us(x)
#endif
// 一键使能所有相关时钟的宏
#define MODULE_BSP_RCU_ENABLE() \
rcu_periph_clock_enable(BSP_ADC_GPIO_RCU); \
rcu_periph_clock_enable(BSP_DO_GPIO_RCU); \
rcu_periph_clock_enable(BSP_ADC_RCU);
/* 模拟输入AO引脚定义 (PB0 -> ADC 通道8) */
#define BSP_ADC_GPIO_RCU RCU_GPIOB
#define BSP_ADC_GPIO_PORT GPIOB
#define BSP_ADC_GPIO_PIN GPIO_PIN_0
#define BSP_ADC_RCU RCU_ADC
#define BSP_ADC ADC
#define BSP_ADC_CHANNEL ADC_CHANNEL_8
/* 数字输入DO引脚定义 (PB2) */
#define BSP_DO_GPIO_RCU RCU_GPIOB
#define BSP_DO_GPIO_PORT GPIOB
#define BSP_DO_GPIO_PIN GPIO_PIN_2
// 读取DO引脚电平的快捷宏
#define MQ_DO gpio_input_bit_get(BSP_DO_GPIO_PORT, BSP_DO_GPIO_PIN)
// ADC采样次数,多次采样取平均可以减小波动
#define SAMPLES 5
// 函数声明
void Module_BSP_Init(void);
int Get_Adc_MQ9_Value(void);
int Get_MQ9_Percentage_value(void);
char Get_MQ9_DO(void);
#endif /* BSP_CODE_BSP_MQ9_H_ */
3.2 初始化函数 (bsp_mq9.c)
任何外设使用前都要初始化,ADC和GPIO也不例外。Module_BSP_Init 函数就是干这个的。
void Module_BSP_Init(void)
{
/* 1. 打开时钟开关 */
MODULE_BSP_RCU_ENABLE();
/* 2. 配置DO引脚为浮空输入模式 */
gpio_mode_set(BSP_DO_GPIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, BSP_DO_GPIO_PIN);
/* 3. 配置ADC时钟为PCLK2的4分频 */
adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
/* 4. 配置AO引脚(PB0)为模拟输入模式 */
gpio_mode_set(BSP_ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, BSP_ADC_GPIO_PIN);
/* 5. 配置ADC工作模式 */
adc_special_function_config(ADC_SCAN_MODE, ENABLE); // 使能扫描模式(多通道时用)
adc_data_alignment_config(ADC_DATAALIGN_RIGHT); // 数据右对齐,方便读取
adc_resolution_config(ADC_RESOLUTION_12B); // 12位分辨率,值范围0-4095
/* 6. 配置ADC规则组(我们只用1个通道) */
adc_channel_length_config(ADC_ROUTINE_CHANNEL, 1); // 规则组通道数为1
adc_external_trigger_config(ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_DISABLE); // 禁用外部触发
adc_software_trigger_enable(ADC_ROUTINE_CHANNEL); // 使能软件触发(即用代码启动转换)
/* 7. 最后,使能ADC */
adc_enable();
}
提示:初始化顺序有个小讲究,一般是先开时钟,再配置GPIO,最后配置和使能ADC核心。另外,我们把ADC配置为软件触发,这样主动权就在我们的程序手里,想什么时候采样就什么时候采样。
3.3 核心数据读取函数
初始化好了,就能读数据了。我们分三层来实现,从最底层的单次ADC读取,到稳定的平均值获取,再到直观的百分比计算。
第一层:单次ADC读取 (ADC_GET)
这是一个静态函数,只在文件内部使用,负责完成一次完整的ADC转换。
static int ADC_GET(void)
{
int gAdcResult = 0;
int timeOut = 1000; // 超时计数器,防止卡死
// 1. 指定要采样的通道(通道8)和采样时间
adc_routine_channel_config(0, BSP_ADC_CHANNEL, ADC_SAMPLETIME_14POINT5);
// 2. 软件触发,启动一次转换
adc_software_trigger_enable(ADC_ROUTINE_CHANNEL);
// 3. 等待转换完成标志位
while(!adc_flag_get(ADC_FLAG_EOC) && timeOut--)
{
delay_us(1);
}
// 4. 清除标志位,为下次转换准备
adc_flag_clear(ADC_FLAG_EOC);
if(!timeOut) // 如果超时了,说明转换可能出了问题
{
printf("ADC_GET Failed!!!\r\n");
return 0;
}
// 5. 读取转换结果
gAdcResult = adc_routine_data_read();
return gAdcResult;
}
第二层:获取稳定的ADC平均值 (Get_Adc_MQ9_Value)
传感器读数可能会有微小波动,连续采样5次然后取平均,得到的值会更稳定。
int Get_Adc_MQ9_Value(void)
{
uint32_t Data = 0;
int i;
for(i = 0; i < SAMPLES; i++)
{
Data += ADC_GET(); // 累加5次采样值
delay_ms(5); // 每次采样间隔5ms,让传感器稳定一下
}
Data = Data / SAMPLES; // 计算平均值
return Data;
}
第三层:计算浓度百分比 (Get_MQ9_Percentage_value)
ADC读出来的是0-4095的数字,我们把它转换成0-100%的百分比,看起来更直观。注意:这个百分比是相对ADC量程的,不是精确的ppm浓度值。要得到实际浓度,需要根据传感器数据手册进行标定和公式换算。
int Get_MQ9_Percentage_value(void)
{
int adc_max = 4095; // 12位ADC的最大值
int adc_new = 0;
int Percentage_value = 0;
adc_new = Get_Adc_MQ9_Value(); // 获取稳定的ADC值
Percentage_value = ((float)adc_new / (float)adc_max) * 100.f; // 转换为百分比
return Percentage_value;
}
第四层:读取数字报警信号 (Get_MQ9_DO)
这个就简单了,直接读取PB2引脚的电平状态。模块上的电位器拧到不同位置,报警阈值就不同。
char Get_MQ9_DO(void)
{
if( MQ_DO == 0 ) // 根据你的模块逻辑判断,这里假设DO输出0表示正常
{
return 0; // 浓度未超标
}
else
{
return 1; // 浓度超标,报警
}
}
注意:
DO引脚输出0还是1表示报警,取决于模块的具体设计。最好用万用表量一下,或者看模块说明书。上面的代码是一个示例,你可能需要根据实际情况调整判断逻辑。
4. 功能验证:在主程序中调用并打印结果
驱动函数都写好了,最后就是在主函数里把它们用起来。我们在 main.c 里创建一个简单的循环,每隔半秒读取一次传感器数据并打印到串口,这样就能在电脑的串口助手上实时观察了。
#include "gd32vw55x.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "gd32vw553h_eval.h"
#include "bsp_mq9.h" // 包含我们刚写的驱动头文件
int main(void)
{
// 系统初始化(时钟、延时、LED、串口等)
systick_config();
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
gd_eval_led_init(LED1);
gd_eval_com_init(EVAL_COM0); // 初始化串口0,用于打印
// 初始化MQ-9传感器
Module_BSP_Init();
printf("MQ9 Sensor Initialization Complete\r\n");
while(1)
{
// 读取并打印所有数据
printf("\n"); // 换行,让每次输出更清晰
printf("MQ9 ADC Value = %d\r\n", Get_Adc_MQ9_Value());
printf("MQ9 Percentage = [%d%%]\r\n", Get_MQ9_Percentage_value());
printf("MQ9 DO State = %d\r\n", Get_MQ9_DO());
delay_ms(500); // 延时500ms
}
}
将代码编译、下载到GD32VW553开发板,打开串口调试助手(波特率通常为115200),你就能看到类似下面的输出:
MQ9 Sensor Initialization Complete
MQ9 ADC Value = 2048
MQ9 Percentage = [50%]
MQ9 DO State = 0
MQ9 ADC Value = 2150
MQ9 Percentage = [52%]
MQ9 DO State = 0
恭喜你! 到这里,你已经成功完成了MQ-9传感器在GD32VW553平台上的驱动移植。你可以尝试向传感器附近呼一口气(含二氧化碳)或者使用打火机释放少量气体(注意安全,远离明火!),观察ADC值和百分比的变化。同时,调节模块上的蓝色电位器,改变报警阈值,看看DO State是否会在浓度变化时从0跳变为1。
这个实战项目把硬件连接、ADC配置、数据滤波和状态判断都串起来了,是一个很好的嵌入式传感器入门案例。你可以基于这个框架,去尝试驱动其他的模拟或数字传感器。
更多推荐



所有评论(0)