资讯动态

基于GD32VW553的MQ-5液化气传感器驱动移植与数据采集实战

发布时间:2026/8/15 16:27:45 来源:尧图企业网站定制
基于GD32VW553的MQ-5液化气传感器驱动移植与数据采集实战最近在做一个智能家居安全监测的小项目需要检测厨房的天然气泄漏于是就用上了MQ-5这款传感器。很多刚开始接触嵌入式传感器开发的朋友拿到这种模拟量数字量双输出的模块可能会有点懵AO和DO到底该怎么接ADC怎么配置数据怎么处理别急今天我就以兆易创新的GD32VW553开发板为例带大家手把手完成MQ-5传感器的驱动移植和数据采集把整个过程掰开揉碎了讲清楚。咱们的目标很简单让GD32VW553开发板能通过MQ-5传感器既读取实时的气体浓度模拟值AO也能判断浓度是否超过预设的报警阈值DO。整个过程我会从传感器原理、硬件接线、代码编写到最终验证一步步带着你做。1. 认识你的“鼻子”MQ-5传感器模块在写代码之前咱们得先了解手里这个“小玩意儿”是怎么工作的。MQ-5本质上是一个“气敏电阻”它的核心材料是二氧化锡。你可以把它想象成一个对气体“敏感”的电阻。在干净的空气里它的电阻很大导电性差。一旦周围出现了像天然气、液化气丙烷这样的可燃气体传感器材料表面的化学反应会导致它的电阻变小导电性变强。气体浓度越高导电性就越好。模块怎么把这个变化告诉我们呢它提供了两种方式模拟输出AO引脚这个引脚直接输出一个电压信号。传感器导电性变化输出的电压也跟着线性变化。浓度越高电压越高。我们需要用单片机的ADC模数转换器功能来读取这个电压值从而计算出浓度。数字输出DO引脚模块上自带了一个LM393电压比较器芯片和一个可调电位器。AO的电压会和电位器设定的一个阈值电压进行比较。当气体浓度超过阈值即AO电压高于阈值电压DO引脚就会输出高电平比如3.3V反之输出低电平0V。这样我们直接用单片机的GPIO读取高低电平就能快速判断“有没有超标”。模块的基本参数如下接线和编程时都会用到参数说明工作电压3.3V ~ 5V 咱们的开发板有5V和3.3V输出这里接5V更稳定工作电流约150mA 功耗不算小确保电源能带动输出方式AO模拟电压输出DO数字电平输出读取方式AO需使用ADC读取DO使用普通GPIO输入读取接口4个2.54mm间距的排针 (VCC, GND, DO, AO)提示模块上的那个蓝色电位器就是用来调节DO输出灵敏度的。顺时针旋转调高比较阈值需要更高的气体浓度才会触发DO变高逆时针旋转则降低阈值变得更灵敏。你可以根据实际应用场景来调整。2. 硬件连接给传感器“接上线”硬件连接是第一步千万不能接错。我们使用GD32VW553开发板引脚选择非常灵活。这里我根据例程和板子资源选择了以下连接方式传感器模块引脚连接到GD32VW553开发板说明VCC5V0引脚提供5V工作电压GNDGND引脚共地至关重要DOPB2引脚数字输出配置为GPIO输入AOPB0引脚模拟输出配置为ADC输入通道为什么选PB0和PB2一方面它们都是容易接触的普通IO口另一方面PB0刚好对应着GD32VW553内部ADC的通道8ADC_CHANNEL_8用起来很方便。你当然也可以换成其他支持ADC的引脚比如PA0、PC0等记得在代码里同步修改就行。接好线后给开发板上电可以看到MQ-5模块上的电源灯通常为红色会亮起预热指示灯通常为黄色也会亮起。MQ系列传感器需要一段预热时间约1-2分钟才能稳定工作这是正常现象。3. 代码实战手把手编写驱动程序硬件准备好了接下来就是重头戏——写代码。咱们的思路是创建两个独立的文件bsp_mq5.c和bsp_mq5.h把MQ-5相关的所有操作都封装在里面这样主程序看起来就非常清爽以后移植到其他项目也方便。3.1 头文件定义 (bsp_mq5.h)头文件主要做两件事一是定义我们刚才硬件连接时确定的引脚二是声明接下来要实现的函数。#ifndef BSP_CODE_BSP_MQ5_H_ #define BSP_CODE_BSP_MQ5_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_MQ5_Value(void); // 获取ADC原始值 int Get_MQ5_Percentage_value(void); // 获取浓度百分比 char Get_MQ5_DO(void); // 获取DO开关状态 #endif /* BSP_CODE_BSP_MQ5_H_ */3.2 初始化函数 (bsp_mq5.c)Module_BSP_Init函数负责把单片机的相关外设配置好这是驱动能够工作的基础。void Module_BSP_Init(void) { /* 第一步打开时钟开关 */ MODULE_BSP_RCU_ENABLE(); // 这个宏会同时使能GPIOB和ADC的时钟 /* 第二步配置DO引脚(PB2)为浮空输入模式 */ gpio_mode_set(BSP_DO_GPIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, BSP_DO_GPIO_PIN); // 因为DO输出已经是稳定的数字电平我们直接读取即可不需要上拉或下拉电阻。 /* 第三步配置ADC时钟 */ adc_clock_config(ADC_ADCCK_PCLK2_DIV4); // ADC时钟设为PCLK2的4分频保证ADC工作在安全频率下 /* 第四步配置AO引脚(PB0)为模拟输入模式 */ gpio_mode_set(BSP_ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, BSP_ADC_GPIO_PIN); // 注意使用ADC的引脚必须设置为模拟模式(GPIO_MODE_ANALOG) /* 第五步配置ADC工作模式 */ adc_special_function_config(ADC_SCAN_MODE, ENABLE); // 使能扫描模式虽然我们只用一个通道但习惯使能 adc_data_alignment_config(ADC_DATAALIGN_RIGHT); // 数据右对齐这样读取的12位数据在低12位处理方便 adc_resolution_config(ADC_RESOLUTION_12B); // 12位分辨率读到的值范围是0-4095 /* 第六步配置ADC通道 */ adc_channel_length_config(ADC_ROUTINE_CHANNEL, 1); // 规则组通道数设为1我们只用通道8 // 配置规则组第0个序列为我们的通道8采样时间14.5个周期 adc_routine_channel_config(0, BSP_ADC_CHANNEL, ADC_SAMPLETIME_14POINT5); /* 第七步配置触发方式并启动ADC */ adc_external_trigger_config(ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_DISABLE); // 禁用外部触发 adc_software_trigger_enable(ADC_ROUTINE_CHANNEL); // 使能软件触发后面我们用代码启动转换 adc_enable(); // 最后使能ADC模块 // 这里有个小经验ADC使能后最好稍作延时等待其稳定。可以加一句 delay_ms(1); }注意GD32的ADC配置步骤有严格的顺序要求大体上是时钟-GPIO模式-ADC基本参数-通道配置-触发配置-使能。按照这个顺序来不容易出错。3.3 核心数据读取函数初始化完成后就可以读取数据了。我们先实现一个底层函数ADC_GET它负责单次ADC转换。static int ADC_GET(void) { int gAdcResult 0; int timeOut 1000; // 超时计数器防止卡死 // 注意通道在初始化时已经配置过这里通常不需要再配。 // 但有些例程会在每次转换前配置为了兼容性这里保留。 // adc_routine_channel_config(0, BSP_ADC_CHANNEL, ADC_SAMPLETIME_14POINT5); // 启动一次软件转换 adc_software_trigger_enable(ADC_ROUTINE_CHANNEL); // 等待转换结束标志位(EOC)置位 while(!adc_flag_get(ADC_FLAG_EOC) timeOut--) { delay_us(1); } // 清除标志位为下一次转换做准备 adc_flag_clear(ADC_FLAG_EOC); if(!timeOut) // 如果超时说明转换可能出了问题 { printf(ADC_GET Failed!!!\r\n); return 0; } // 读取转换结果 gAdcResult adc_routine_data_read(); return gAdcResult; }单次读取可能受干扰所以我们用Get_Adc_MQ5_Value函数来采样多次并求平均值。int Get_Adc_MQ5_Value(void) { uint32_t Data 0; int i; for(i 0; i SAMPLES; i) { Data ADC_GET(); // 累加5次采样值 delay_ms(5); // 每次采样间隔5ms让ADC有足够时间 } Data Data / SAMPLES; // 求平均值 return (int)Data; }拿到0-4095的原始ADC值还不够直观我们把它转换成百分比方便理解。int Get_MQ5_Percentage_value(void) { int adc_max 4095; // 12位ADC的最大值 int adc_new 0; int Percentage_value 0; adc_new Get_Adc_MQ5_Value(); // 获取平均值 // 计算百分比(当前值 / 最大值) * 100 // 注意这里用浮点数计算精度更高。实际项目中如果追求速度可以用定点数。 Percentage_value ((float)adc_new / (float)adc_max) * 100.f; return Percentage_value; }最后是读取数字输出DO的函数非常简单就是读取GPIO电平。char Get_MQ5_DO(void) { if( MQ_DO 0 ) // 使用前面定义的宏读取PB2电平 { return 0; // 低电平表示气体浓度未超过阈值 } else { return 1; // 高电平表示气体浓度超过阈值报警 } }提示Get_MQ5_DO返回值的含义取决于模块上电位器的设置。顺时针调高阈值传感器需要“闻到”更浓的气体DO才输出1。你需要根据实际气体如天然气、丙烷和报警浓度要求来校准这个电位器。4. 上机验证让代码跑起来看结果驱动写好了最后一步就是在主函数里调用它看看实际效果。在你的工程主文件例如main.c中包含头文件初始化传感器然后在主循环里不断打印数据。#include gd32vw55x.h #include systick.h #include stdio.h #include main.h #include gd32vw553h_eval.h // 开发板基础驱动包含LED、串口初始化 #include bsp_mq5.h // 我们刚刚写的驱动 int main(void) { /* 系统基础初始化 */ systick_config(); // 配置系统滴答定时器 eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1); // 设置中断优先级分组 gd_eval_led_init(LED1); // 初始化LED用于指示程序运行 gd_eval_com_init(EVAL_COM0); // 初始化串口0用于打印数据到电脑 printf(\r\n MQ-5 Sensor Test Start \r\n); /* 初始化MQ-5传感器硬件 */ Module_BSP_Init(); printf(MQ5 Sensor Initialization Complete\r\n); // 等待传感器预热MQ系列需要预热 printf(Sensor preheating, please wait about 1 minute...\r\n); delay_ms(60000); // 延迟60秒 printf(Preheating complete. Start reading data.\r\n); while(1) { // 读取并打印所有数据 printf(ADC Raw Value %d\r\n, Get_Adc_MQ5_Value()); printf(Concentration [%d%%]\r\n, Get_MQ5_Percentage_value()); printf(DO Alarm Pin %d\r\n, Get_MQ5_DO()); printf(-------------------\r\n); // 根据DO状态控制LED直观显示报警 if(Get_MQ5_DO() 1) { gd_eval_led_on(LED1); // 浓度超标LED亮 } else { gd_eval_led_off(LED1); // 正常LED灭 } delay_ms(1000); // 每秒读取一次 } }将代码编译、下载到GD32VW553开发板。打开串口调试助手如Putty、SecureCRT设置好波特率通常是115200你就能看到每秒刷新一次的数据了。如何验证正常空气观察ADC原始值和百分比会是一个比较稳定的低值例如几百到一千多。DO输出应该为0LED不亮。触发测试用打火机释放少量丁烷气体注意安全远离明火在传感器附近或者用酒精棉签靠近MQ-5对酒精也有反应。你会看到ADC值和百分比迅速上升。当浓度超过你模块上电位器设定的阈值时DO输出会从0变成1同时LED点亮。做到这里恭喜你你已经成功完成了MQ-5传感器在GD32VW553上的驱动移植和数据采集。这个框架是通用的你可以轻松修改引脚定义移植到其他GD32甚至STM32项目中去。下次我们可以再聊聊如何校准传感器把ADC值转换成更精确的ppm百万分之一浓度值。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价