利用mq2传感器对气体进行检测,无非就是利用传感器内部的电路以及材料在不同气体环境下有着不同的电阻值,通过对电阻电压的采集来检测相应气体浓度的变化。这时我们就可以利用ADC来对外部传感器的电压值进行采集,ADC呢就是将连续变量的模拟信号转换为离散的数字信号。ADC部分大家不了解可以去详细了解一下,大佬们写的都很好。
接下来是代码部分:
首先对io口进行配置,再对ADC的模式进行配置。
void Adc_Init()//初始化函数
{
GPIO_InitTypeDef GPIO_Initstructre;
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOARCC_APB2Periph_ADC1,ENABLE);//使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能io口
GPIO_Initstructre.GPIO_Mode=GPIO_Mode_AIN;
GPIO_Initstructre.GPIO_Pin=GPIO_Pin_1;
GPIO_Initstructre.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructre);
GPIO_SetBits(GPIOA,GPIO_Pin_1);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//保证不超过14M
ADC_DeInit(ADC1);//复位ADC1
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//不使能连续扫描
ADC_InitStruct.ADC_DataAlign=ADC_DataAlign_Right;//数据右对齐
ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//软件触发
ADC_InitStruct.ADC_Mode=ADC_Mode_Independent;//独立模式
ADC_InitStruct.ADC_NbrOfChannel = 1;
ADC_InitStruct.ADC_ScanConvMode = DISABLE;//不使用扫描模式
ADC_Init(ADC1,&ADC_InitStruct);
ADC_Cmd(ADC1,ENABLE);//使能指定的ADC1
ADC_ResetCalibration(ADC1);//使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1));//等待复位校准结束
ADC_StartCalibration(ADC1);//开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
然后是ADC电压采集的相关函数
u16 Get_Adc(u8 ch)
{
ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5);//ADC1,通道1,配置采集周期
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件复位
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));//状态发生改变
return ADC_GetConversionValue(ADC1);//返回值
}
利用上一个函数的返回值进一步进行数据计算
u16 Get_Adc_Average(u8 ch,u8 times)//两个入口参数,通道,取平均值的次数
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
然后是对气体PPM的转换
void MQ2_cumlate(float RS)
{
R0 = RS / pow(CAL_PPM / 613.9f, 1 / -2.074f);
}
float MQ2_GetPPM(void)
{
u16 adcx;
adcx=Get_Adc_Average(ADC_Channel_1,30);//ADC1,取30次的平均值
float Vrl = 3.3f * adcx / 4096.f;//3.3v的参考电压,4096份
Vrl = ( (float)( (int)( (Vrl+0.005)*100 ) ) )/100;
float RS = (3.3f - Vrl) / Vrl * RL;
if(times<6) // 获取系统执行时间,3s前进行校准,用到了定时器
{
R0 = RS / pow(CAL_PPM / 613.9f, 1 / -2.074f);//校准R0
}
float ppm = 613.9f * pow(RS/R0, -2.074f);
return ppm;
}
接下来是定时器部分的代码,这一部分主要是为了上方用到的校准
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSture;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//定时器3,时钟
TIM_TimeBaseInitSture.TIM_Period = arr;//自动装载值
TIM_TimeBaseInitSture.TIM_Prescaler = psc;//预分频系数
TIM_TimeBaseInitSture.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInitSture.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitSture);//定时器3
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//定时器3,更新中断,使能
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3,ENABLE);
}
void TIM3_IRQHandler(void)//中断函数,0.5秒中断一次
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)//判断状态
{
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清除中断待处理位
times++;
}
}
最后我们可以通过串口助手,或者显示屏幕将数据进行显示,MQ系列的传感器主要就是ADC和相应的浓度转换。
电压值转化部分参考了以下博主的代码
https://blog.csdn.net/qq_41422043/article/details/89138213?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7
若有错误,请大家指出。
大家自取,不经常上csdn
https://pan.baidu.com/s/1Ljk8tgpFsnpGC9zx5zyz8w
提取码:44p5