目前市场上的大多数空气净化器的传感器采用烟尘传感器,例如夏普的GP2Y1010AU, 该传感器能够检测出大于0.8um直径的灰尘,因为我们现在空气中的主要污染物为PM2.5,所以通过一定的算法能够大致让其表示出当前的空气质量指数。下图为CC2540最小系统与GP2Y1010AU组合起来的PM2.5检测仪,房间里计算出来的空气质量指数似乎比官方通报的差一级。
下面是GPY1010AU的采样电路与采样方式,该传感器通过光探测灰尘浓度,需要MCU一个PIN脚产生低脉冲,然后在低脉冲0.32ms的位置进行ADC的转换。
下图为该传感器的外围设计电路,也就是上图中转接板的电路
通过一个三极管控制第3个引脚产生低脉冲,将第5引脚输出的烟尘数据通过R14,R15进行分压以适应MCU ADC的参考电压。
下面是GPY1010AU在CC2540裸机上的核心代码
#include<ioCC2540.h>
#include<stdio.h>
#include "gp2y1010au.h"
int dustPin=0;
int ledPower=2;
int delayTime=10;
int delayTime2=3;
int offTime=15000;
void delayMicroseconds(unsigned int microSec)
{
unsigned int i,j;
for(i=0;i<microSec;i++)
for(j=0;j<5;j++);
return;
}
void InitGp2y1010au(void)
{
ADCCON1 = 0x33;//软件启动转换
ADCCON3 = 0x30;//AVIN0
//TR0 = 0x01; //avin, not internal
//ATEST = 0x01;
// ADCCON1 |= 0x40;//软件启动转换
P1DIR |= 0X01;/*output mode*/
}
void ledPowerSet(unsigned char val)
{
LED_POWER_CTRL = val;
}
unsigned short somkeValAdcRead(void)
{
unsigned short AdValue=0;
unsigned char ADCVal_L,ADCVal_H;
//InitGp2y1010au();
//ADCCON1 = 0x33;
ADCCON3 = 0x30;//AVIN0
ADCCON1 = 0x73;
while(!(ADCCON1 & 0x80));
ADCVal_L = ADCL;
ADCVal_H = ADCH;
AdValue = ADCVal_L >>2;
AdValue |= ADCVal_H <<6;
return AdValue;
}
unsigned short smokeDetect(void){
unsigned short dustVal=0;
// ledPower is any digital pin on the arduino connected to Pin 3 on the sensor
ledPowerSet(1);
delayMicroseconds(delayTime);
dustVal=somkeValAdcRead();
delayMicroseconds(delayTime2);
ledPowerSet(0);
delayMicroseconds(offTime);
//delayMicroseconds(1000);
/*if (dustVal>36.455)
Serial.println((float(dustVal/1024)-0.0356)*120000*0.035);*/
return dustVal;
}
>
上面的代码读出的是ADC的码字,需要转换为烟尘的浓度,转换方法参看datasheet提供的烟尘与电压关系曲线,如下图所示
转换的浓度信息为mg/m3, 在天气预报中的空气质量是叫一种空气质量指数的指标表示的,也就是AQI.
空气质量指数(Air Quality Index,简称AQI),是一个用来定量描述空气质量水平的数值。AQI的取值范围位于0 – 500 之间。
空气质量指数是根据各种污染物的浓度值换算出来的。要计算AQI,就需要事先确定各污染物在不同空气质量水平下的浓度限值。例如,美国环保局(EPA)针对PM2.5的限值定义如下:
AQI的计算公式如下:
其中:
I = 空气质量指数,即AQI,输出值;
C = 污染物浓度,输入值;
Clow= 小于或等于C的浓度限值,常量;
Chigh= 大于或等于C的浓度限值,常量;
Ilow= 对应于Clow的指数限值,常量;
Ihigh= 对应于Chigh的指数限值,常量。
转换部分的核心代码如下
unsigned short rawVal2_to_ugPerM3(unsigned short rawVal)
{
unsigned int ugPerM3Val;
unsigned int adc_mVolt;
unsigned long temp;
temp = (long)2300*(long)rawVal;
adc_mVolt = (long)temp/(long)8192;
temp = (long)10000*(long)(adc_mVolt - 625);//ug * 10times for float compute
ugPerM3Val = temp/5750;
return (unsigned short)ugPerM3Val;
}
unsigned short ugPerM3_to_AQI(unsigned short ugPerM3)
{
unsigned short AQI;
unsigned short Clow, Chigh, Ilow, Ihigh;
if((ugPerM3>C_LOW_1)&&(ugPerM3<C_HIGH_1))
{
Clow= C_LOW_1;
Chigh = C_HIGH_1;
Ilow = I_LOW_1;
Ihigh = I_HIGH_1;
}
else if((ugPerM3>C_LOW_2)&&(ugPerM3<C_HIGH_2))
{
Clow= C_LOW_2;
Chigh = C_HIGH_2;
Ilow = I_LOW_2;
Ihigh = I_HIGH_2;
}
else if((ugPerM3>C_LOW_3)&&(ugPerM3<C_HIGH_3))
{
Clow= C_LOW_3;
Chigh = C_HIGH_3;
Ilow = I_LOW_3;
Ihigh = I_HIGH_3;
}
else if((ugPerM3>C_LOW_4)&&(ugPerM3<C_HIGH_4))
{
Clow= C_LOW_4;
Chigh = C_HIGH_4;
Ilow = I_LOW_4;
Ihigh = I_HIGH_4;
}
else if((ugPerM3>C_LOW_5)&&(ugPerM3<C_HIGH_5))
{
Clow= C_LOW_5;
Chigh = C_HIGH_5;
Ilow = I_LOW_5;
Ihigh = I_HIGH_5;
}
else if((ugPerM3>C_LOW_6)&&(ugPerM3<C_HIGH_6))
{
Clow= C_LOW_6;
Chigh = C_HIGH_6;
Ilow = I_LOW_6;
Ihigh = I_HIGH_6;
}
else if((ugPerM3>C_LOW_7)&&(ugPerM3<C_HIGH_7))
{
Clow= C_LOW_7;
Chigh = C_HIGH_7;
Ilow = I_LOW_7;
Ihigh = I_HIGH_7;
}
else//full
{
return I_HIGH_7;
}
AQI = (long)(Ihigh-Ilow)*(long)(ugPerM3-Clow)/(long)(Chigh-Clow) + (long)Ilow;
return AQI;
}
/***************************
主函数
***************************/
void main(void)
{
unsigned short smokeVal;
unsigned short ugPerM3Val;
unsigned short AQIval;
//LED端口初始化
OLED_Init(); //初始化OLED
OLED_Clear();
InitGp2y1010au();
oledDisplayLabel();
while(1)
{
smokeVal = smokeDetect();
ugPerM3Val = rawVal2_to_ugPerM3(smokeVal);
AQIval = ugPerM3_to_AQI(ugPerM3Val);
gp2y1010auDisplay(AQIval);
}
}