MQ-9可燃气体传感器在TI TMS320F28P550开发板上的ADC与GPIO驱动移植实战
最近在做一个环境监测的小项目,需要检测一氧化碳和可燃气体,用到了MQ-9传感器。很多刚开始接触TI C2000系列DSP(比如TMS320F28P550)的朋友,可能会觉得配置ADC和GPIO有点复杂。其实,只要理清思路,一步步来,把传感器用起来并不难。
这篇教程,我就以立创的TI电赛开发板(TMS320F28P550核心)为例,手把手带你完成MQ-9传感器的驱动移植。咱们的目标很明确:用ADC读取模拟浓度值(AO引脚),用GPIO读取数字报警信号(DO引脚)。我会把原理、接线、配置、代码编写和调试的整个过程都讲清楚,即使你是第一次用这个开发板,也能跟着做出来。
1. 认识你的“伙伴”:MQ-9传感器模块
在动手接线写代码之前,咱们先花几分钟了解一下MQ-9传感器。这就像打仗前先熟悉自己的武器,非常重要。
MQ-9的核心是一层二氧化锡(SnO2)气敏材料。这东西在干净空气里导电性很差(电阻大),但一旦遇到一氧化碳(CO)或者甲烷、丙烷这类可燃气体,它的电导率就会上升(电阻变小)。传感器就是通过测量这个电阻变化来感知气体浓度的。
它有个挺巧妙的工作方式:高低温循环检测。
- 低温阶段(加热电压约1.5V):主要用来检测一氧化碳(CO)。在这个温度下,传感器对CO最敏感。
- 高温阶段(加热电压约5.0V):主要用来检测可燃气体(如甲烷),同时还有一个重要作用,就是“清洗”掉低温时吸附在传感器上的其他杂散气体,为下一次测量做准备。
你买到的模块通常长这样,有4个引脚(2.54mm间距,很好接):
- VCC:供电脚,接3.3V或5V都行。
- GND:电源地。
- DO:数字量输出。当气体浓度超过你设定的阈值(通过模块上的蓝色电位器调节)时,这个引脚会输出低电平(0);否则输出高电平(1)。简单理解就是一个“超标报警”开关信号。
- AO:模拟量输出。这个引脚会输出一个电压值,电压大小随气体浓度变化而变化。浓度越高,输出电压一般也越高。我们需要用开发板的ADC(模数转换器)来读取这个电压,从而得到具体的浓度信息。
模块关键参数速览:
| 参数 | 说明 |
|---|---|
| 工作电压 | 3.3V ~ 5V |
| 工作电流 | 约150mA(加热时电流较大,注意电源带载能力) |
| 输出信号 | AO(模拟电压输出),DO(数字开关输出) |
| 读取方式 | AO需ADC读取,DO可直接用GPIO读取 |
| 接口 | 4 Pin 排针 |
提示:模块资料(数据手册、原理图等)可以在提供的链接中下载,里面包含了更详细的灵敏度特性曲线等信息,对于需要精确标定的项目很有帮助。
2. 硬件连接:把传感器“插”到开发板上
接线是第一步,千万不能错。根据原文的指引,我们使用开发板的以下资源:
- 电源:模块VCC接开发板的5V0输出引脚,为传感器提供工作电压。
- 地线:模块GND接开发板的GND。
- 数字输出(DO):接开发板的GPIO54。这个引脚我们将配置为数字输入,用来读取高低电平。
- 模拟输出(AO):接开发板的GPIO-A6。注意,这个“A6”指的是ADC的通道编号,它对应着某个具体的GPIO引脚(例如可能是GPIO16,具体看开发板引脚图)。在SysConfig工具里,我们直接选择ADC通道即可,工具会自动映射到正确的GPIO。
接线对照表:
| 传感器模块引脚 | 开发板对应引脚/功能 |
|---|---|
| VCC | 5V0 (5V电源输出) |
| GND | GND (电源地) |
| DO | GPIO54 |
| AO | ADC-A通道6 (对应GPIO-A6) |
接好线后,给开发板上电,你应该能看到MQ-9模块上的加热丝微微发红(小心烫),这说明它开始工作了。
3. 软件配置:用SysConfig图形化工具“画”出硬件
TI的C2000系列现在推荐使用SysConfig图形化配置工具来初始化外设,这比直接怼寄存器代码方便直观多了。咱们就在CCS(Code Composer Studio)里完成这个配置。
步骤详解:
打开配置文件:在你的CCS工程里,找到并双击
c2000.syscfg这个文件。SysConfig界面就会在CCS内打开。添加ADC配置:
- 在界面中找到“ADC”或“Analog-to-Digital Converter”相关的部分,点击ADD按钮。
- 我们需要配置一个ADC转换通道。在参数中,找到
SOC0(可以理解为第0个采样转换序列)的配置。 - 将
Channel(通道)选择为A6。这就是对应我们接线的GPIO-A6模拟输入。 - 其他参数如采样窗口时间(
AcqPs)可以先保持默认,后续根据信号特性可以微调。
添加GPIO配置:
- 找到“GPIO”或“PinMux”(引脚复用)相关的部分,点击ADD按钮。
- 我们需要配置两个GPIO:
- GPIO54:将其功能设置为GPIO Input(GPIO输入模式)。这就是用来读取DO数字信号的。
- 找到ADC-A6对应的那个GPIO引脚(例如GPIO16):将其功能设置为ADC-A6或者
Analog(模拟功能)。这一步很关键,它把物理引脚切换到了ADC输入模式,而不是普通的数字IO。
保存并生成代码:
- 按
Ctrl + S保存SysConfig配置。 - 按
Ctrl + B编译一次工程。这里可能会弹出一些警告,但通常不影响,可以按原文提示先不管它。 - 编译成功后,SysConfig会自动根据你的图形化配置,生成对应的C代码。这些代码主要位于
board.h和board.c文件中,里面定义了你刚才配置的所有引脚和ADC通道的宏、初始化函数等。
- 按
包含头文件:原文的工程模板已经做得很好,它把
board.h等必要的文件都汇总在了tjx_init.h这个头文件里。所以,在我们自己写的驱动文件中,只需要包含#include "tjx_init.h"就可以了,非常省心。
4. 驱动代码编写:让传感器“说话”
配置好了硬件接口,接下来就是写代码让它们动起来。我们在工程里新建一个module_driver文件夹,专门放各种传感器驱动。在里面创建bsp_mq9.c和bsp_mq9.h两个文件。
注意:新建文件夹、文件以及添加头文件路径到编译器的方法,如果不会,可以参考原文中提到的DHT11章节教程,步骤是完全一样的。
4.1 头文件定义 (bsp_mq9.h)
头文件主要做两件事:声明外部可用的函数,以及定义一些宏来简化操作。
#ifndef _BSP_MQ9_H_ #define _BSP_MQ9_H_ #include "tjx_init.h" // 包含开发板所有初始化定义 // 宏定义:读取DO引脚的电平状态。GPIO_DO应在board.h中由SysConfig定义为GPIO54的引脚常量 #define MQ_DO GPIO_readPin( GPIO_DO ) // 定义ADC采样次数,通过多次采样取平均可以滤除偶然干扰 #define SAMPLES 5 // 函数声明 uint16_t Get_Adc_MQ9_Value(void); // 获取ADC原始值(平均值) uint8_t Get_MQ9_Percentage_value(void); // 获取浓度百分比(0-100%) char Get_MQ9_DO(void); // 获取DO引脚数字状态 #endif4.2 核心源文件实现 (bsp_mq9.c)
这里是驱动逻辑的核心,我们逐个函数来分析。
首先,是一个内部使用的ADC读取辅助函数ADC_GET:这个函数负责触发一次ADC转换并读取结果。它被设计为静态函数,意味着它只在当前.c文件内有效,是对外隐藏的实现细节。
static uint16_t ADC_GET(void) { uint16_t gAdcResult = 0; uint16_t timeOut = 200; // 设置一个超时计数器,防止死等 // 1. 软件触发ADC开始转换。Module_ADC_BASE和Module_ADC_FORCE_SOC0由SysConfig生成 ADC_forceMultipleSOC(Module_ADC_BASE, Module_ADC_FORCE_SOC0); // 2. 等待ADC转换完成 while(ADC_isBusy(Module_ADC_BASE) && timeOut--) { delay_us(5); // 短暂延时,等待转换 } // 3. 检查是否超时(转换失败) if(!timeOut) { lc_printf("ADC_GET Failed!!!\r\n"); return 0; } // 4. 读取转换结果。ADC是12位的,所以结果范围是0-4095 gAdcResult = ADC_readResult(Module_ADC_RESULT_BASE, Module_ADC_SOC0); return gAdcResult; }然后,是获取MQ-9 ADC值的函数Get_Adc_MQ9_Value:直接读一次ADC可能受噪声影响,所以我们采样多次求平均值。
uint16_t Get_Adc_MQ9_Value(void) { uint16_t Data = 0; int i; for(i = 0; i < SAMPLES; i++) { Data += ADC_GET(); // 累加SAMPLES次采样值 delay_ms(5); // 每次采样间隔5ms,让传感器和ADC稳定一下 } Data = Data / SAMPLES; // 计算平均值 return Data; }接着,是将ADC值转换为百分比的函数Get_MQ9_Percentage_value:这个函数非常有用,它把原始的0-4095的ADC数值,映射到了0-100%的范围,更直观。
uint8_t Get_MQ9_Percentage_value(void) { int adc_max = 4095; // 12位ADC的最大值 int adc_new = 0; float Percentage_value = 0; adc_new = Get_Adc_MQ9_Value(); // 获取平均ADC值 // 计算百分比:(当前值 / 最大值) * 100 Percentage_value = ((float)adc_new / (float)adc_max) * 100.f; return (uint8_t)Percentage_value; // 转换为整数百分比返回 }最后,是读取数字输出DO的函数Get_MQ9_DO:这个最简单,直接返回我们在头文件里用宏定义好的引脚状态。MQ_DO宏展开就是GPIO_readPin(GPIO_DO)。
char Get_MQ9_DO(void) { return MQ_DO; // 返回0(气体超标)或1(正常) }5. 功能验证:在主程序中测试
驱动写好了,最后一步就是把它用起来。我们修改主函数(例如empty_driverlib_main.c)来进行测试。
#include "driverlib.h" #include "device.h" #include "board.h" #include "c2000ware_libraries.h" #include "tjx_init.h" #include "bsp_mq9.h" // 包含我们刚写的驱动头文件 void main(void) { // CCS自动生成的系统初始化代码 [Start] Device_init(); Device_initGPIO(); Interrupt_initModule(); Interrupt_initVectorTable(); Board_init(); // 这里会调用SysConfig生成的板级初始化函数,配置好ADC和GPIO C2000Ware_libraries_init(); EINT; ERTM; // CCS自动生成的系统初始化代码 [End] // 初始化串口,用于打印信息(lc_printf函数依赖此初始化,通常在tjx_init.h中已包含) lc_printf("\r\n=== MQ-9 Sensor Test Start ===\r\n"); while(1) { // 1. 打印ADC原始值 (0-4095) lc_printf("MQ9 ADC Raw Value = %d\r\n", Get_Adc_MQ9_Value()); // 2. 打印浓度百分比 (0%-100%) lc_printf("MQ9 Concentration = [%d%%]\r\n", Get_MQ9_Percentage_value()); // 3. 打印数字输出状态 (0:报警, 1:正常) lc_printf("MQ9 DO State = %d (0:Gas Detected)\r\n", Get_MQ9_DO()); lc_printf("----------------------------\r\n"); // 用一个简单的LED闪烁指示程序在运行 GPIO_writePin(RGB_B, 0); // 蓝灯亮 GPIO_writePin(RGB_G, 1); // 绿灯灭 delay_ms(500); GPIO_writePin(RGB_B, 1); // 蓝灯灭 GPIO_writePin(RGB_G, 0); // 绿灯亮 delay_ms(500); GPIO_writePin(RGB_B, 1); // 蓝灯灭 GPIO_writePin(RGB_G, 1); // 绿灯灭 delay_ms(500); } }将代码编译、下载到开发板,然后打开串口调试助手(波特率等参数根据你的板子串口配置设置)。你就能看到终端里不断打印出三行信息:
- ADC原始数值。
- 浓度百分比。
- DO引脚状态(0或1)。
测试方法:
- 对着MQ-9传感器吹一口气(含二氧化碳)或者用打火机(注意安全,别点火!)释放少量丁烷气体靠近,观察ADC值和百分比是否会上升。
- 旋转模块上的蓝色电位器,可以改变DO输出的灵敏度阈值。当浓度超过阈值时,DO状态应从1变为0。
如果一切正常,恭喜你!你已经成功在TMS320F28P550开发板上驱动了MQ-9气体传感器。这个框架也可以作为模板,轻松移植到其他模拟传感器上。