最近学习了一下STM32中的ADC采样,由于手头正好有一个MQ-2的烟雾传感器,所以正好可以测试一把。体验ADC采样的过程。下面介绍一下这个MQ-2烟雾传感器。
1.MQ-2烟雾传感器简介
MQ-2气体传感器所使用的气敏材料是在清洁空气中电导率较低的二氧化锡(SnO2)。当传感器所处环境中存在可燃气体时,传感器的电导率随空气中可燃气体浓度的增加而增大。使用简单的电路即可将电导率的变化转换为与该气体浓度相对应的输出信号。 MQ-2气体传感器对液化气、丙烷、氢气的灵敏度高,对天然气和其它可燃蒸汽的检测也很理想。这种传感器可检测多种可燃性气体,是一款适合多种应用的低成本传感器。
2.传感器模块图
3.MQ-2传感器原理图
4.传感器规格
查找MQ-2传感器资料后,可知MQ-2传感器的加热电压为5.0v,当然经测试加热电压小于5v也是可以的,比如我在测试的时候,我给单片机的电压就是小于5V的,我的单片机没有接电源,直接是J-link接入PC然后pc进行供电,但最好还是单片机接上电源线,这样传感器比较灵敏。
5.MQ-2烟雾传感器模块特点
1、具有信号输出指示。
2、双路信号输出(模拟量输出及TTL电平输出)。
3、TTL输出有效信号为低电平。(当输出低电平时信号灯亮,可直接接单片机)
4、模拟量输出0~5V电压,浓度越高电压越高。
5、对液化气,天然气,城市煤气有较好的灵敏度。
6、结果受温湿度影响。
6.硬件连线
MQ-2传感器的VCC端用杜邦线接入32开发板的JP2(我的开发板上的JP2可输入5V电压),MQ-2传感器的GND端接32开发板的GND,32开发板上所有的GND可共用,这里最好接摄像头的GND,MQ-2传感器的AOUT端接开发板的摄像头(CAMERA)的PC1-ADC1端。
至于连线为什么这么连,与我写的代码以及我的开发板的原理图有关,如图:
注意:MQ-2模块的TTL输出端是悬空的,至于为啥是悬空的,这是厂家提供的,可以参考这个链接:点击打开链接
代码如下:
主函数:main.c
#include "printf.h"
#include "adc.h"
#include "systick.h"
extern __IO uint16_t ADC_ConvertedValue;
float ADC_ConvertedValueLocal;
int main(void)
{
adc_init();
printf_init();
SysTick_Init(); //配置SysTick为1ms中断一次
while(1)
{
ADC_ConvertedValueLocal =( float) ADC_ConvertedValue/4096*3.3;
printf("The current AD value =0x%04X\n",ADC_ConvertedValue);
printf("The current AD value =%f V\n",ADC_ConvertedValueLocal);
Delay_ms( 1000);
}
}
ADC初始化代码:
void adc_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
GPIO_Init(GPIOC,&GPIO_InitStructure);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //ADC地址
DMA_InitStructure.DMA_MemoryBaseAddr = ( uint32_t)&ADC_ConvertedValue; //内存地址(要传输的变量的指针)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //方向(从内存到外设)
DMA_InitStructure.DMA_BufferSize = 1; //传输内容的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //内存地址固定
DMA_InitStructure.DMA_PeripheralDataSize =
DMA_PeripheralDataSize_HalfWord ; //外设数据单位
DMA_InitStructure.DMA_MemoryDataSize =
DMA_MemoryDataSize_HalfWord ; //内存数据单位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ; //DMA模式:循环传输
DMA_InitStructure.DMA_Priority = DMA_Priority_High ; //优先级:高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //配置DMA1的1通道
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //禁止扫描方式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //开启连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发转换
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //采集数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //要转换的通道数目
ADC_Init(ADC1, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //配置ADC时钟,为PCLK2的8分频,即9Hz
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);//配置ADC1通道11为55.5个采样周期
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1); //复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1));//等待校准寄存器复位完成
ADC_StartCalibration(ADC1); //ADC校准
while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //由于没有采用外部触发,所以使用软件触发ADC转换
}
adc.h:
#ifndef _adc_H
#define _adc_H
#include "stm32f10x.h"
#define ADC1_DR_Address ((uint32_t)0x4001244c);
void adc_init(void);
#endif
延时函数在我这篇博客:点击打开链接
其实这里最好用用定时器来代替延时的,用定时器的话,不会占用CPU,这样会大大降低功耗,相反用延时的话会一直占用CPU,功耗增加可想而知,不过由于自己学习32也没有多久,对定时器的操作还不太熟,以后等自己熟悉了定时器的操作之后再来修改代码。
硬件连线图:
测试图:
其实在实际的应用中,会把ADC的转换值利用公式换算成为烟雾的浓度值,通过这个浓度值与预设值进行比对然后进行报警。不过为了使ADC的采样值更加精确,通常会取多个ADC采样值然后后求平均值,最后将平均值换算成为对应的烟雾浓度值。由于自己只是为了学习这个模块及了解一下ADC采样的过程,代码比较简单,写的很新手,有时间我再来优化一下这个代码。