当前位置: 首页 > news >正文

基于天空星HC32F4A0PITB的MQ-5液化气传感器驱动移植与浓度检测实战

基于天空星HC32F4A0PITB的MQ-5液化气传感器驱动移植与浓度检测实战

最近在做一个智能厨房安全监测的小项目,需要检测液化气(主要是丙烷)的泄漏。MQ-5传感器因为成本低、灵敏度高,成了我的首选。但网上关于国产MCU天空星(HC32F4A0)的驱动资料不多,自己摸索移植的过程踩了不少坑。今天我就把完整的驱动移植和浓度检测流程分享出来,手把手教你怎么在天空星开发板上搞定MQ-5传感器,无论是读取模拟量浓度值,还是使用数字量进行阈值报警,都给你讲明白。

这篇文章适合正在使用HC32F4A0系列MCU做项目的朋友,特别是需要接入气体传感器的新手。我会从传感器原理讲起,到引脚配置、ADC采集、代码编写,最后完成一个实时显示气体浓度百分比的小程序。跟着做一遍,你就能掌握这套方法。

1. 认识我们的“侦察兵”:MQ-5气体传感器

在写代码之前,咱们得先了解手里的“武器”。MQ-5传感器本质上是一个“气敏电阻”,它的核心材料是二氧化锡(SnO2)。

你可以把它想象成一个对特定气体“过敏”的电阻。在干净的空气中,它的电阻很大(电导率低),所以流过它的电流很小。一旦周围出现了它“过敏”的可燃气体,比如液化气(丙烷)、天然气(甲烷),它的电阻就会变小(电导率增大),电流也就变大了。

模块给我们提供了两种方式来感知这种变化:

  • AO(Analog Output)引脚:这是传感器的“原始感觉”。它直接输出一个电压信号,这个电压会随着气体浓度升高而升高。我们需要用单片机的ADC(模数转换器)功能来读取这个电压值,从而计算出具体的浓度。这种方式精度高,能知道“有多少”气体。
  • DO(Digital Output)引脚:这是模块自带的“简易报警器”。模块上有一个LM393电压比较器芯片,它会将AO的电压和一个可调的阈值电压进行比较。当气体浓度超过你设定的阈值时,DO引脚就直接输出一个数字信号(比如从高电平变成低电平)。这种方式简单粗暴,只能告诉你“有”或“没有”超标,适合做开关报警。

模块工作电压是3.3V-5V,和我们的天空星开发板完美兼容。它有4个引脚:VCC(接3.3V)、GND、AO、DO。

2. 硬件连接与引脚规划

接线非常简单,就像搭积木一样。

模块引脚连接到开发板说明
VCC3.3V 电源引脚供电
GNDGND 引脚共地
AOPC1模拟信号输出,接MCU的ADC输入引脚
DOPA1数字信号输出,接MCU的普通GPIO输入引脚

这里有个关键点:AO引脚必须连接到具有ADC(模数转换)功能的MCU引脚上。我选择了天空星HC32F4A0的PC1引脚,因为它对应着ADC1模块的第11输入通道(ADC_CH11)。DO引脚就随意多了,只要是普通的GPIO输入引脚就行,我用了PA1。

注意:在连接传感器前,最好先给开发板断电,养成好习惯避免短路。

3. 软件驱动移植详解

接下来是重头戏,咱们在代码里让传感器“活”起来。我们需要创建两个文件:bsp_mq5.c(驱动实现)和bsp_mq5.h(驱动头文件)。

3.1 头文件定义 (bsp_mq5.h)

头文件的作用是进行宏定义和函数声明,把硬件相关的引脚、通道号都定义好,这样以后要换引脚修改起来就非常方便。

#ifndef _BSP_MQ5_H_ #define _BSP_MQ5_H_ #include "hc32_ll.h" // 包含HC32的底层库头文件 // ADC相关硬件定义 #define FCG_MQ5_ADC FCG3_PERIPH_ADC1 // ADC1的时钟 #define PORT_ADC CM_ADC1 // ADC1模块 #define CHANNEL_ADC ADC_CH11 // 使用第11通道 // AO引脚定义 (ADC输入) #define PORT_MQ5_AO GPIO_PORT_C // 端口C #define GPIO_MQ5_AO GPIO_PIN_01 // 引脚1 (即PC1) #define GPIO_MS1100_AO_REMAP ADC12_PIN_PC1 // 引脚功能重映射为ADC // DO引脚定义 (GPIO输入) #define PORT_MQ5_DO GPIO_PORT_A // 端口A #define GPIO_MQ5_DO GPIO_PIN_01 // 引脚1 (即PA1) // 采样次数,用于软件滤波,取平均值 #define SAMPLES 30 // 函数声明 void ADC_MQ5_Init(void); // 初始化ADC和GPIO unsigned int Get_Adc_MQ5_Value(void); // 获取ADC原始值(平均值) unsigned int Get_MQ5_Percentage_value(void); // 获取气体浓度百分比 char Get_MQ5_DO_value(void); // 获取DO引脚数字状态 #endif

3.2 驱动初始化函数

初始化是第一步,目的是配置好ADC和GPIO引脚,为数据采集做好准备。咱们在bsp_mq5.c里实现。

#include "bsp_mq5.h" #include "board.h" #include "stdio.h" void ADC_MQ5_Init(void) { // 1. 解除寄存器写保护(操作关键寄存器前常需要这一步) LL_PERIPH_WE(LL_PERIPH_ALL); stc_gpio_init_t stcGpioInit; // 2. 初始化GPIO结构体为默认值 (void)GPIO_StructInit(&stcGpioInit); // 3. 配置AO引脚(PC1)为模拟输入模式 stcGpioInit.u16PinAttr = PIN_ATTR_ANALOG; // 关键!设为模拟属性,才能做ADC输入 stcGpioInit.u16PinDir = PIN_DIR_IN; // 方向输入 stcGpioInit.u16PullUp = PIN_PU_OFF; // 关闭内部上拉,模拟输入通常不需要 (void)GPIO_Init(PORT_MQ5_AO, GPIO_MQ5_AO, &stcGpioInit); // 4. 配置DO引脚(PA1)为数字输入模式 stcGpioInit.u16PinAttr = PIN_ATTR_DIGITAL; // 数字属性 stcGpioInit.u16PullUp = PIN_PU_ON; // 开启内部上拉,保证默认高电平 (void)GPIO_Init(PORT_MQ5_DO, GPIO_MQ5_DO, &stcGpioInit); // 5. 使能ADC1的时钟(外设要工作,必须先给时钟) FCG_Fcg3PeriphClockCmd(FCG_MQ5_ADC, ENABLE); // 6. 配置ADC模块基本参数 stc_adc_init_t stcAdcInit; (void)ADC_StructInit(&stcAdcInit); // 填充默认参数 stcAdcInit.u16ScanMode = ADC_MD_SEQA_SINGLESHOT; // 单次扫描模式,一次触发采一个通道 stcAdcInit.u16Resolution = ADC_RESOLUTION_12BIT; // 12位分辨率,值范围0-4095 stcAdcInit.u16DataAlign = ADC_DATAALIGN_RIGHT; // 数据右对齐,方便读取 (void)ADC_Init(PORT_ADC, &stcAdcInit); // 7. 通道重映射:告诉ADC模块,第11通道的信号来自PC1引脚 ADC_ChRemap(PORT_ADC, CHANNEL_ADC, GPIO_MS1100_AO_REMAP); // 8. 使能ADC的序列A通道11(我们只用了这一个通道) ADC_ChCmd(PORT_ADC, ADC_SEQ_A, CHANNEL_ADC, ENABLE); }

这个函数像是一个开机启动流程,把硬件通道都打通了。特别注意第3步,AO引脚必须设置为PIN_ATTR_ANALOG(模拟属性),如果设成数字属性,ADC是读不到正确电压的,这个坑我踩过。

3.3 核心数据采集函数

硬件准备好了,接下来就是怎么读取数据。为了得到稳定的值,我们通常不会只读一次,而是采样多次然后取平均。

// 底层函数:执行一次ADC转换并读取结果 unsigned int adc_GET(void) { static uint16_t adcValue; __IO uint32_t TimeOut = 0UL; // 启动ADC转换(序列A) ADC_Start(PORT_ADC); // 等待转换完成标志位 while(SET != ADC_GetStatus(PORT_ADC, ADC_FLAG_EOCA)) { // 加入超时判断,防止程序卡死 if(TimeOut > 500) { ADC_ClearStatus(PORT_ADC, ADC_FLAG_EOCA); ADC_Stop(PORT_ADC); printf("ERROR = ADC 等待序列 A 转换【超时】!!\r\n"); return 0; // 超时返回0 } TimeOut++; delay_ms(1); // 延时1ms } // 转换完成,清除标志位(重要!不清除下次无法触发) ADC_ClearStatus(PORT_ADC, ADC_FLAG_EOCA); // 从ADC数据寄存器中读取通道11的转换值 adcValue = ADC_GetValue(PORT_ADC, CHANNEL_ADC); return adcValue; } // 应用层函数:采样SAMPLES次并取平均值,得到稳定的ADC原始值 unsigned int Get_Adc_MQ5_Value(void) { uint32_t Data = 0; for(int i = 0; i < SAMPLES; i++) { Data += adc_GET(); // 累加采样值 delay_ms(5); // 每次采样间隔5ms,让传感器稳定 } Data = Data / SAMPLES; // 求平均值 return (unsigned int)Data; }

这里SAMPLES定义为30次,这是一个经验值。次数太少,数值跳动大;次数太多,响应又太慢。30次在稳定性和实时性之间是个不错的平衡。delay_ms(5)的间隔也是为了避开传感器自身的波动。

3.4 数据换算与数字量读取

拿到ADC原始值(0-4095)后,我们更关心它代表的气体浓度百分比。同时,DO引脚的状态可以直接告诉我们是否超过预设阈值。

// 将ADC原始值转换为百分比浓度 (0-100%) unsigned 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(); // 获取稳定的ADC值 // 核心换算公式: (当前值 / 最大值) * 100% // 注意:这里用了强制类型转换为float,保证计算精度 Percentage_value = ((float)adc_new / (float)adc_max) * 100.f; return Percentage_value; } // 读取DO引脚的数字状态 char Get_MQ5_DO_value(void) { // 读取指定引脚的电平 if( GPIO_ReadInputPins(PORT_MQ5_DO, GPIO_MQ5_DO) == RESET ) { return 0; // 低电平,表示气体浓度**超过**模块上电位器设定的阈值 } else { return 1; // 高电平,表示气体浓度**低于**阈值 } }

这里有两个重点:

  1. 百分比计算:这是一个线性化的简易算法。它假设传感器输出电压与气体浓度成完美的正比关系。实际上MQ-5的响应曲线并非完全线性,在要求高的场合需要查表或使用更复杂的公式校准。但对于一般泄漏报警,这个算法简单有效。
  2. DO引脚逻辑:模块上的LM393比较器,通常是当AO电压(代表浓度)高于某个阈值时,DO输出低电平。所以返回0意味着浓度超标了。这个阈值可以通过模块上的蓝色电位器(一个可调电阻)来调节,顺时针拧一般是提高报警阈值。

4. 在主程序中测试我们的驱动

驱动写好了,最后一步就是把它用起来。我们在main函数里初始化它,并循环打印浓度值。

#include "board.h" #include "bsp_uart.h" #include "stdio.h" #include "bsp_mq5.h" // 包含我们自己的MQ-5驱动头文件 int32_t main(void) { // 1. 开发板基础初始化(系统时钟、滴答定时器等) board_init(); // 2. 初始化串口1,用于打印信息到电脑,波特率115200 uart1_init(115200U); // 3. 初始化MQ-5传感器(配置ADC和GPIO) ADC_MQ5_Init(); printf("MQ-5液化气传感器Demo开始...\r\n"); while(1) { // 4. 获取并打印气体浓度百分比 printf("液化气体含量 = %d%%\r\n", Get_MQ5_Percentage_value() ); // 5. 获取并打印DO报警状态 if(Get_MQ5_DO_value() == 0) { printf("警告:检测到气体浓度超标!\r\n"); } else { printf("状态:气体浓度正常。\r\n"); } printf("\r\n"); // 空行分隔 // 延时1秒,避免打印太快 delay_ms(1000); } }

把程序编译下载到天空星开发板,打开串口助手(波特率115200),你就能看到实时的气体浓度百分比和报警状态了。对着传感器吹口气(含二氧化碳)或者用打火机(不要点火!)释放少量丁烷气体,就能看到百分比上升,DO状态也可能变化。

提示:第一次使用MQ-5传感器,需要预热几分钟。刚上电时读数会不稳定,这是正常的,预热后就会稳定下来。

5. 实际应用中的几点心得

代码跑起来只是第一步,在实际项目里用得好,还得注意下面几点:

  1. 校准与标定:上面的百分比只是一个相对值。要知道具体的“ppm”(百万分之一浓度)值,你需要用标准浓度的气体进行标定,建立ADC值与实际浓度的对应关系曲线。
  2. 软件滤波:除了我们做的多次平均,在数据波动大的场合,可以加上更复杂的滤波算法,比如中位值平均滤波、一阶滞后滤波等。
  3. DO阈值的调节:模块上的电位器可以调节报警灵敏度。顺时针拧,需要更高的浓度才会触发报警(DO变低);逆时针拧则更灵敏。根据你的应用场景仔细调节。
  4. 传感器寿命与环境:MQ系列传感器对湿度、温度也比较敏感,尽量避免在极端环境下使用。长期暴露在高浓度气体中也会影响其灵敏度。

好了,整个从硬件连接到软件驱动,再到数据读取和处理的流程就完整了。希望这篇教程能帮你顺利把MQ-5传感器用起来。遇到问题,多看看数据手册,多调试,嵌入式开发就是这么一点点积累起来的。

http://www.cnnetsun.cn/news/1244416.html

相关文章:

  • 绝地求生罗技鼠标宏系统技术指南:从问题诊断到安全优化
  • 个人数据管理新方案:3步实现QQ空间历史记录完整备份
  • AI人脸隐私卫士应用场景:新闻媒体快速匿名群众面孔的智能解决方案
  • 无需显卡!用Z-Image-Turbo云端创作室5分钟搞定AI绘画
  • GD32F450四轮麦克纳姆轮全向移动平台设计
  • 电动玩具声光协同升级:四态硬件触发语音系统设计
  • 水墨江南模型作品集:二十四节气AI诗词创作全景展示
  • 突破硬件限制:Equalizer APO解锁专业级音效定制新体验
  • 从零搭建:基于Dify工作流整合Ollama与DeepSeek-R1的联网搜索助手
  • SEER‘S EYE 预言家之眼部署指南:Ubuntu 20.04系统环境快速搭建
  • 黑丝空姐-造相Z-Turbo技术社区实践:在CSDN分享模型部署与创新应用
  • 扣子(Coze)案例教程:打造你的AI老黄历视频生成器
  • 若依权限系统集成PageOffice:实现前后端分离下的在线文档协同
  • LeagueAkari:提升英雄联盟游戏效率的开源工具解决方案
  • Canal vs mysql-binlog-connector:如何选择最适合你的MySQL数据同步方案?
  • Qml地图开发进阶(一):MapQuickItem与动态图元绘制
  • 仅限PHP 8.9.4+可用!基于JIT-aware mmap预加载的大文件随机读取方案(基准测试:seek延迟从42ms降至0.8ms)
  • 告别手动配置:使用CMake与VSCode构建现代化C++开发环境
  • 开源工具百度网盘直链解析实现满速下载的技术方案
  • 掌握NVIDIA Profile Inspector:从入门到精通的显卡参数调校指南
  • STM32G47x FDCAN外设配置与波特率计算实战指南
  • 别再只用饼图了!用Echarts旭日图可视化你的组织架构与预算分配
  • 突破游戏帧率限制:OpenSpeedy变速工具革新玩家体验,卡顿降低70%
  • PP-DocLayoutV3与Python爬虫结合实战:自动化文档解析与数据提取
  • 小白也能懂:Open-AutoGLM工作原理揭秘,截图-分析-执行三步走
  • Aspen Plus V14 从零到一:手把手安装指南与避坑实战
  • wan2.1-vae开源协议解读:Apache 2.0许可下商用/修改/分发边界说明
  • Navicat16/17数据库密码安全解析与实战解密指南
  • PotPlayer字幕翻译插件:突破语言壁垒的4个实战技巧
  • GPU显存友好型部署:Nano-Banana软萌拆拆屋SDXL优化实践