基于梁山派GD32F470的MQ-4甲烷传感器ADC/DMA驱动移植与浓度监测实战
基于梁山派GD32F470的MQ-4甲烷传感器ADC/DMA驱动移植与浓度监测实战最近在做一个智能家居安全监测的小项目需要检测厨房的天然气泄漏于是就用上了MQ-4甲烷传感器。这个传感器价格便宜灵敏度也不错但怎么把它和咱们的梁山派GD32F470开发板连起来稳定地读取数据呢今天我就把整个移植和调试过程包括ADC和DMA的配置手把手分享给大家让你也能快速实现甲烷浓度的实时监测。1. 认识MQ-4甲烷传感器MQ-4传感器核心是一个二氧化锡SnO2气敏材料。简单来说它在干净空气里导电性很差电阻很大。一旦周围有甲烷、天然气这类可燃气体它的导电性就会变好电阻下降。传感器模块就是把这个电阻变化转换成一个我们能测量的电压信号。这个模块有两个输出口咱们得搞清楚AOAnalog Output模拟量输出。直接输出一个电压值气体浓度越高电压越高。这个电压是连续变化的需要我们用单片机的ADC模数转换器来读取。DODigital Output数字量输出。模块内部用一个叫LM393的电压比较器把AO的电压和一个可调的阈值电压做比较。超过阈值就输出高电平比如3.3V否则输出低电平0V。这个阈值可以通过模块上的一个蓝色电位器来调节相当于调节报警的灵敏度。DO口我们直接用单片机的普通GPIO输入模式读取就行。模块基本参数工作电压3.3V - 5V 梁山派开发板有5V和3.3V输出可以直接用工作电流约150mA接口4个2.54mm间距的排针VCC, GND, DO, AO2. 硬件连接与引脚规划首先把传感器模块和梁山派开发板连起来。连线很简单就四根线传感器引脚梁山派开发板引脚说明VCC5V电源正极接5V输出GNDGND电源地接任意GNDDOPF10数字输出接普通GPIOAOPC1模拟输出必须接带有ADC功能的引脚这里有个关键点AO引脚必须连接到具有ADC模数转换功能的单片机引脚上。GD32F470的很多引脚都有复用功能不是随便一个引脚都能接ADC。怎么知道PC1有没有ADC功能呢你需要查看芯片的数据手册Datasheet。在GD32F4xx系列的数据手册里有详细的引脚功能定义表。PC1对应的是ADC0的第11输入通道ADC0_IN11或者ADC1/2的通道11。我们代码里选择用ADC0。DO引脚我们选了PF10这是一个普通的GPIO配置成输入模式来读取高低电平。3. 工程搭建与代码移植咱们的目标是在你的工程里新建两个文件bsp_mq4.c和bsp_mq4.h“bsp”是板级支持包的意思用来放外设驱动。把下面提供的代码复制进去就行。3.1 头文件定义 (bsp_mq4.h)这个文件主要进行宏定义把引脚、外设等用容易理解的名字定义好后面改起来也方便。#ifndef _BSP_MQ4_H_ #define _BSP_MQ4_H_ #include gd32f4xx.h // 时钟定义 #define RCU_MQ4_GPIO_AO RCU_GPIOC // AO引脚(PC1)的时钟 #define RCU_MQ4_GPIO_DO RCU_GPIOF // DO引脚(PF10)的时钟 #define RCU_MQ4_ADC RCU_ADC0 // 使用的ADC0时钟 #define RCU_MQ4_DMA RCU_DMA1 // 使用的DMA1时钟 // DMA定义 #define PORT_DMA DMA1 // 使用DMA1 #define CHANNEL_DMA DMA_CH0 // 使用DMA1的通道0 // ADC定义 #define PORT_ADC ADC0 // 使用ADC0 #define CHANNEL_ADC ADC_CHANNEL_11 // 使用ADC0的通道11 (对应PC1) // GPIO引脚定义 #define PORT_MQ4_AO GPIOC // AO引脚端口 #define GPIO_MQ4_AO GPIO_PIN_1 // AO引脚号 (PC1) #define PORT_MQ4_DO GPIOF // DO引脚端口 #define GPIO_MQ4_DO GPIO_PIN_11 // DO引脚号 (PF11原文为PF10请注意核对) // 采样参数 #define SAMPLES 30 // 每个通道采样30次用于求平均滤波 #define CHANNEL_NUM 1 // 使用的ADC通道数目前就MQ4一个 // 声明DMA缓冲区在.c文件中定义 extern uint16_t gt_adc_val[SAMPLES][CHANNEL_NUM]; // 函数声明 void ADC_DMA_Init(void); unsigned int Get_Adc_Dma_Value(char CHx); unsigned int Get_MQ4_Percentage_value(void); char Get_MQ4_DO_value(void); #endif注意原文中DO引脚定义为PF10但代码里是GPIO_PIN_11这可能是笔误。请根据你的实际连接确认是PF10还是PF11并修改GPIO_MQ4_DO的定义。如果是PF10应改为GPIO_PIN_10。3.2 核心驱动实现 (bsp_mq4.c)这里是重头戏包含了ADC和DMA的初始化、数据读取和浓度计算。#include bsp_mq4.h #include systick.h // 用于延时函数 // DMA缓冲区一个二维数组用于存放DMA搬运过来的ADC原始值 // SAMPLES行30次采样CHANNEL_NUM列1个通道 uint16_t gt_adc_val[SAMPLES][CHANNEL_NUM]; /****************************************************************** * 函数名称ADC_DMA_Init * 函数说明初始化ADC和DMA实现自动采集模拟电压 * 函数形参无 * 函数返回无 ******************************************************************/ void ADC_DMA_Init(void) { dma_single_data_parameter_struct dma_single_data_parameter; // 1. 打开所有需要用到的外设时钟 rcu_periph_clock_enable(RCU_MQ4_GPIO_AO); // 打开GPIOC时钟 rcu_periph_clock_enable(RCU_MQ4_GPIO_DO); // 打开GPIOF时钟 rcu_periph_clock_enable(RCU_MQ4_ADC); // 打开ADC0时钟 rcu_periph_clock_enable(RCU_MQ4_DMA); // 打开DMA1时钟 // 2. 配置ADC时钟ADC工作时钟不能太快这里设为系统时钟的4分频 adc_clock_config(ADC_ADCCK_PCLK2_DIV4); // 3. 配置GPIO引脚模式 // DO引脚配置为上拉输入、下拉输入或浮空输入均可这里用无上下拉的输入模式 gpio_mode_set(PORT_MQ4_DO, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_MQ4_DO); // AO引脚必须配置为模拟输入模式这是ADC采集的前提 gpio_mode_set(PORT_MQ4_AO, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_MQ4_AO); // 4. 配置ADC工作模式 adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT); // 独立模式只用了一个ADC adc_special_function_config(PORT_ADC, ADC_CONTINUOUS_MODE, ENABLE); // 使能连续转换模式 adc_special_function_config(PORT_ADC, ADC_SCAN_MODE, ENABLE); // 使能扫描模式多通道时才需要单通道也建议开启 adc_data_alignment_config(PORT_ADC, ADC_DATAALIGN_RIGHT); // 数据右对齐方便阅读 adc_resolution_config(PORT_ADC, ADC_RESOLUTION_12B); // 12位分辨率0-4095 // 5. 配置ADC规则组通道告诉ADC要转换哪个通道按什么顺序 adc_channel_length_config(PORT_ADC, ADC_REGULAR_CHANNEL, CHANNEL_NUM); // 规则组通道数为1 // 配置第0个扫描顺序的通道为ADC_CHANNEL_11采样时间15个周期时间越长越准确但也越慢 adc_regular_channel_config(PORT_ADC, 0, CHANNEL_ADC, ADC_SAMPLETIME_15); // 6. 配置ADC触发方式 adc_external_trigger_config(PORT_ADC, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE); // 禁用外部触发只用软件触发 // 7. 配置ADC与DMA联动 adc_dma_request_after_last_enable(PORT_ADC); // 使能规则组通道转换完成后产生DMA请求 adc_dma_mode_enable(PORT_ADC); // 使能ADC的DMA传输 // 8. 使能ADC并校准 adc_enable(PORT_ADC); delay_1ms(1); // 等待ADC稳定 adc_calibration_enable(PORT_ADC); // ADC校准消除内部误差非常重要 // 9. 配置DMA直接存储器访问 // DMA的作用是ADC一转换完数据就自动把这个数据搬到我们指定的数组里完全不需要CPU干预。 dma_deinit(PORT_DMA, CHANNEL_DMA); // 先复位DMA通道 // 填充DMA初始化结构体 dma_single_data_parameter.periph_addr (uint32_t)(ADC_RDATA(PORT_ADC)); // 外设地址ADC0的数据寄存器 dma_single_data_parameter.periph_inc DMA_PERIPH_INCREASE_DISABLE; // 外设地址不递增始终读同一个ADC寄存器 dma_single_data_parameter.memory0_addr (uint32_t)(gt_adc_val); // 内存地址我们的数组 dma_single_data_parameter.memory_inc DMA_MEMORY_INCREASE_ENABLE; // 内存地址递增依次填充数组 dma_single_data_parameter.periph_memory_width DMA_PERIPH_WIDTH_16BIT; // 数据宽度16位ADC是12位用16位存 dma_single_data_parameter.direction DMA_PERIPH_TO_MEMORY; // 传输方向从外设ADC到内存 dma_single_data_parameter.number SAMPLES * CHANNEL_NUM; // 传输数据量30个数据 dma_single_data_parameter.priority DMA_PRIORITY_HIGH; // DMA优先级高 // 初始化DMA dma_single_data_mode_init(PORT_DMA, CHANNEL_DMA, dma_single_data_parameter); // 10. 选择DMA通道对应的外设ADC0对应DMA_SUBPERI0 dma_channel_subperipheral_select(PORT_DMA, CHANNEL_DMA, DMA_SUBPERI0); // 11. 使能DMA循环模式并启动 dma_circulation_enable(PORT_DMA, CHANNEL_DMA); // 循环模式传输完30个数据后自动从头开始实现连续采集 dma_channel_enable(PORT_DMA, CHANNEL_DMA); // 使能DMA通道 // 12. 软件触发ADC开始转换 // 一旦触发ADC就会开始连续转换DMA会自动搬运数据到gt_adc_val数组。 adc_software_trigger_enable(PORT_ADC, ADC_REGULAR_CHANNEL); } /****************************************************************** * 函数名称Get_Adc_Dma_Value * 函数说明从DMA缓冲区中取出指定通道的ADC值并计算平均值 * 函数形参CHx - 通道序号我们只有0通道 * 函数返回指定通道ADC采样的平均值 ******************************************************************/ unsigned int Get_Adc_Dma_Value(char CHx) { unsigned char i 0; unsigned int AdcValue 0; // 累加SAMPLES次采样的数据 for(i 0; i SAMPLES; i) { AdcValue gt_adc_val[i][CHx]; } // 求平均值实现简单的软件滤波使读数更稳定 AdcValue AdcValue / SAMPLES; return AdcValue; } /****************************************************************** * 函数名称Get_MQ4_Percentage_value * 函数说明读取MQ4的AO值并换算成百分比浓度0-100% * 函数形参无 * 函数返回甲烷浓度百分比整数 ******************************************************************/ unsigned int Get_MQ4_Percentage_value(void) { int adc_max 4095; // 12位ADC的最大值 int adc_new 0; int Percentage_value 0; adc_new Get_Adc_Dma_Value(0); // 获取通道0即PC1的平均ADC值 // 将ADC值0-4095线性映射到百分比0-100 Percentage_value ((float)adc_new / adc_max) * 100; return Percentage_value; } /****************************************************************** * 函数名称Get_MQ4_DO_value * 函数说明读取MQ4的DO引脚电平状态 * 函数形参无 * 函数返回0未检测到高浓度甲烷1检测到高浓度甲烷 ******************************************************************/ char Get_MQ4_DO_value(void) { // 读取PF10引脚的电平 if(gpio_input_bit_get(PORT_MQ4_DO, GPIO_MQ4_DO) RESET) // RESET表示低电平 { return 0; // 浓度低于阈值 } else { return 1; // 浓度高于阈值报警 } }4. 在主函数中调用与测试最后我们在main.c函数里初始化这些外设然后循环打印浓度值。#include gd32f4xx.h #include systick.h #include bsp_usart.h // 串口驱动用于printf打印 #include bsp_mq4.h #include stdio.h int main(void) { // 1. 系统初始化 nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 设置中断优先级分组 systick_config(); // 初始化系统滴答定时器用于delay_ms usart_gpio_config(115200U); // 初始化串口波特率115200用于打印 printf(MQ-4 Methane Sensor Demo Start\r\n); // 2. 初始化MQ-4传感器包含ADC和DMA初始化 ADC_DMA_Init(); // 3. 主循环 while(1) { // 读取并打印甲烷浓度百分比 printf(Methane Concentration: %d%%\r\n, Get_MQ4_Percentage_value()); // 读取并打印数字输出状态 if(Get_MQ4_DO_value()) { printf(Warning: High concentration detected! (DO is HIGH)\r\n); } delay_1ms(1000); // 每隔1秒读取一次 } }将代码编译下载到梁山派开发板打开串口助手波特率115200你就能看到每秒输出一次的甲烷浓度百分比了。对着传感器吹口气含二氧化碳或者靠近打火机释放的少量气体注意安全切勿明火可以看到百分比数值上升DO状态也可能跳变为1。5. 调试心得与常见问题ADC读数跳动大这是正常的传感器本身和电源都会有噪声。我们代码里已经做了软件平均滤波采样30次求平均。如果还觉得跳可以尝试增加SAMPLES的次数比如加到100。在传感器VCC和GND之间并联一个10uF和0.1uF的电容进行硬件滤波。检查开发板的3.3V或5V电源是否稳定。DO口不报警模块上那个蓝色的电位器就是用来调节报警阈值的。用螺丝刀慢慢旋转它可以改变比较器的参考电压从而调节灵敏度。顺时针旋转一般是提高灵敏度更容易报警。百分比值代表真实浓度吗不直接代表。这个百分比只是将ADC读数线性映射到了0-100%。MQ-4的输出电压与气体浓度之间并非严格的线性关系且受温湿度影响。要进行定量测量需要先校准在洁净空气中读取一个ADC值作为“零点”。在一个已知浓度的标准甲烷气体中读取另一个ADC值。根据两点拟合出转换公式通常是指数或对数关系。我们的Get_MQ4_Percentage_value函数只是一个简单的线性演示实际项目需要替换为校准后的公式。DMA缓冲区gt_adc_val的数据会自动更新吗会的。在ADC_DMA_Init函数中我们使能了DMA的循环模式。ADC会不停地转换DMA也会自动地、循环地将新数据覆盖到gt_adc_val数组中。Get_Adc_Dma_Value函数每次读取的都是最近30次采样的平均值实现了实时更新。好了整个MQ-4传感器从硬件连接到软件驱动、从数据采集到浓度显示的流程就完成了。这套ADCDMA的采集方式不占用CPU时间效率很高你可以把核心CPU资源用在其他更重要的任务上比如网络通信、用户界面显示等。希望这篇教程能帮你快速上手。

相关新闻

黄山派模组烧录调试通道详解:DBG_UART1接口配置与SWD模式切换实战

黄山派模组烧录调试通道详解:DBG_UART1接口配置与SWD模式切换实战

黄山派模组烧录调试通道详解:DBG_UART1接口配置与SWD模式切换实战 最近在玩黄山派的模组,发现它的调试接口设计得挺有意思,把烧录、日志输出和SWD调试都集成在了一对引脚上。很多刚开始接触的朋友可能会有点懵,这两个引脚到底该怎…

2026/7/6 2:41:50 阅读更多 →
通义千问1.5-1.8B-Chat-GPTQ-Int4项目依赖管理:使用GitHub管理模型配置与提示词模板

通义千问1.5-1.8B-Chat-GPTQ-Int4项目依赖管理:使用GitHub管理模型配置与提示词模板

通义千问1.5-1.8B-Chat-GPTQ-Int4项目依赖管理:使用GitHub管理模型配置与提示词模板 你是不是也遇到过这样的情况:好不容易在本地电脑上把通义千问模型跑起来了,各种配置调得刚刚好,提示词模板也打磨得挺顺手。结果换台机器&…

2026/5/17 11:45:36 阅读更多 →
对比传统方法:FRCRN深度学习降噪在电话录音中的效果案例

对比传统方法:FRCRN深度学习降噪在电话录音中的效果案例

对比传统方法:FRCRN深度学习降噪在电话录音中的效果案例 电话录音,听起来是个挺简单的技术,但真要把里面的声音处理干净,可不是件容易事。尤其是在客服中心、电话会议或者语音质检这些场景里,背景里混杂的键盘声、空调…

2026/7/3 1:03:30 阅读更多 →

最新新闻

Pandas 2.2 与 Sklearn 1.4.2 数据预处理对比:3类缺失值处理的效率与选择

Pandas 2.2 与 Sklearn 1.4.2 数据预处理对比:3类缺失值处理的效率与选择

Pandas 2.2 与 Sklearn 1.4.2 数据预处理实战:缺失值处理的效率对比与选型指南1. 缺失值处理的工程挑战与工具选择数据预处理是机器学习项目中最耗时且最关键的环节之一,而缺失值处理又是预处理流程中的核心痛点。根据2023年Kaggle社区调研报告&#xff…

2026/7/6 17:10:46 阅读更多 →
如何配置bili-sync实现高效B站视频自动化同步

如何配置bili-sync实现高效B站视频自动化同步

如何配置bili-sync实现高效B站视频自动化同步 【免费下载链接】bili-sync 由 Rust & Tokio 驱动的哔哩哔哩同步工具 项目地址: https://gitcode.com/gh_mirrors/bi/bili-sync 作为B站深度用户,你是否曾遇到过这样的困扰:收藏的优质视频因为平…

2026/7/6 17:10:46 阅读更多 →
论文复现之AgentCF:用户/物品双代理的落地经验与关键注意事项

论文复现之AgentCF:用户/物品双代理的落地经验与关键注意事项

这是一份面向可复制的 AgentCF 复现笔记,记录了代码/配置/数据/评测的完整落地过程,给出从论文到跑通的最短路径,并逐条记录我踩过的坑与规避办法。 目录 一、论文背景与方法1.1 背景动机1.2 方法框架1.3 推断阶段的三种提示策略 二、原文结果…

2026/7/6 17:08:44 阅读更多 →
Ante语言:在系统编程中实现函数式安全性的革命性方案

Ante语言:在系统编程中实现函数式安全性的革命性方案

Ante语言:在系统编程中实现函数式安全性的革命性方案 【免费下载链接】ante A safe, easy systems language 项目地址: https://gitcode.com/gh_mirrors/an/ante 想象一下,你正在开发一个高性能的系统级应用,需要在内存安全、并发控制…

2026/7/6 17:08:44 阅读更多 →
EhViewer漫画阅读器完整指南:从入门到精通

EhViewer漫画阅读器完整指南:从入门到精通

EhViewer漫画阅读器完整指南:从入门到精通 EhViewer是一款基于Material Design 2设计风格的开源Android漫画阅读器应用,专为漫画爱好者提供流畅的阅读和管理体验。无论你是初次接触漫画阅读器的新手,还是寻找更优秀替代方案的用户&#xff0…

2026/7/6 17:08:44 阅读更多 →
AI Agent 实战:30分钟部署 Hermes Agent,打造闭环学习智能助手

AI Agent 实战:30分钟部署 Hermes Agent,打造闭环学习智能助手

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 如果你最近在关注 AI Agent 领域,大概率已经听过 Hermes Agent 这个名字。它不仅在 GitHub 上收获了超过 21 万颗星&#…

2026/7/6 17:08:44 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻