N76E003 夏普GP2Y1010 PM2.5

首先來看傳感器內部結構與單片機的連接圖。由下圖可知,實際上使用的I/O就是3、5腳。其中3腳爲PWM驅動LED閃爍頻率的引腳,在下文中可以看到時序。而5腳Vo則是傳感器檢測到的灰塵,而輸出的電壓值。在下文的程序中,將使用N76E003單片機自帶的帶隙電壓進行測量,以此保證ADC不被供電電壓影響。
https://download.csdn.net/download/u014798590/10948021 (工程文件、數據手冊與電路可以在這下載)
在這裏插入圖片描述
下圖爲GP2Y1010傳感器的線序(注意,線的顏色不一定一致)
在這裏插入圖片描述

下圖爲3腳輸入的電平,這裏我們使用PWM5/P0.3(這裏注意看左側的電路,LED導通的條件)。
在這裏插入圖片描述
經下圖公式計算可得
在這裏插入圖片描述
(16MHz/8)/100HZ=20000
20000-1——》0x4e1f(PWMPH=0x4e;PWMPL=0x1f)

0.032*20000=640
640——》0x280(PWMnH=0x2;PWMnH=0x80)
在這裏插入圖片描述
如果單片機管腳的輸出能力不行,最好加上一個三極管,這時需要反向的PWM。這裏使用N76E003的極性控制功能

PWM5_OUTPUT_INVERSE;//開啓極性控制	

在這裏插入圖片描述
下圖是GP2Y10輸出的波形,但是我的9.9包郵的邏輯分析儀可能太LOW了,沒辦法測量出來,所以只能萬用表了,PWM信號出來,萬用表測量到的就是電壓(PS:這裏測量到的電壓,並不是真正的數據電壓,而只是高低電平的比例電壓,並不能體現檢測到的灰塵)。

下圖1是在GP2Y10裏插了把螺絲刀
在這裏插入圖片描述
下圖2是沒有插螺絲刀
在這裏插入圖片描述

下圖纔是正確的傳感器輸出電壓檢測方式,我們可以看到,輸出電壓峯值跟我們輸入的PWM相差0.28ms。實際測量過程中,需要選擇一個合適的觸發形式,不然測量的數值很不穩定,在程序中使用PWM0終點觸發ADC中斷(你也可以使用中點觸發,可能會更好一些)
ADC中斷觸發的相關知識看這裏N76E003 ADC中斷
在這裏插入圖片描述
/**********************************************************************************************************************/
下面就是完整程序了,具體流程是先讀取帶隙電壓,再對傳感器的輸出電壓進行讀取
有關帶隙電壓的相關程序與知識,請看這裏N76E003之ADC帶隙電壓(Band-gap)

#include "N76E003.h"
#include "Common.h"
#include "Delay.h"
#include "SFR_Macro.h"
#include "Function_define.h"
//#include "DHT11.h"
//#include "BMP180.h"
//#include "HMC5883.h"
//#include "BH1750.h"

#define uint unsigned int
#define uchar unsigned  char

double  Bandgap_Voltage,ADC_Voltage;			//please always use "double" mode for this
unsigned  char xdata ADCdataH[5], ADCdataL[5];
int ADCsumH=0, ADCsumL=0;
unsigned char ADCavgH,ADCavgL;

UINT8 BandgapHigh,BandgapLow,BandgapMark;
double Bandgap_Value,Bandgap_Voltage_Temp;
double Coe,bgvalue,RC_value;//比例係數,測量帶隙電壓

/*
程序功能:讀取UID中帶隙電壓值;通過ADC,測量實際的帶隙電壓;得到比例係數COE;
本程序需要放在ADC正常測量前。
*/
void READ_BANDGAP()
{
		unsigned int i;
		set_IAPEN;
		IAPCN = READ_UID;
		IAPAL = 0x0d;
    IAPAH = 0x00;
    set_IAPGO;
		BandgapLow = IAPFD;
		BandgapMark = BandgapLow&0xF0;
			
		if (BandgapMark==0x80)
		{
				BandgapLow = BandgapLow&0x0F;
				IAPAL = 0x0C;
				IAPAH = 0x00;
				set_IAPGO;
				BandgapHigh = IAPFD;
				Bandgap_Value = (BandgapHigh<<4)+BandgapLow;
				Bandgap_Voltage_Temp = Bandgap_Value*3/4;
				Bandgap_Voltage = Bandgap_Voltage_Temp - 33;			//the actually banggap voltage value is similar this value.
		}
		if (BandgapMark==0x00)
		{
				BandgapLow = BandgapLow&0x0F;
				IAPAL = 0x0C;
				IAPAH = 0x00;
				set_IAPGO;
				BandgapHigh = IAPFD;
				Bandgap_Value = (BandgapHigh<<4)+BandgapLow;
				Bandgap_Voltage= Bandgap_Value*3/4;
		}
		if (BandgapMark==0x90)
		{
				IAPAL = 0x0E;
				IAPAH = 0x00;
				set_IAPGO;
				BandgapHigh = IAPFD;
				IAPAL = 0x0F;
				IAPAH = 0x00;
				set_IAPGO;
				BandgapLow = IAPFD;
				BandgapLow = BandgapLow&0x0F;
				Bandgap_Value = (BandgapHigh<<4)+BandgapLow;
				Bandgap_Voltage= Bandgap_Value*3/4;
		}
		clr_IAPEN;
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------		
				Enable_ADC_BandGap;	//使能ADC帶隙電壓											
				CKDIV = 0x02;				// IMPORTANT!! Modify system clock to 4MHz ,then add the ADC sampling clock base to add the sampling timing.
				for(i=0;i<5;i++)		//採樣5次,不要前面三次
				{																					
						clr_ADCF;
						set_ADCS;																
						while(ADCF == 0);
						ADCdataH[i] = ADCRH;
						ADCdataL[i] = ADCRL;
				}		
				CKDIV = 0x00;
//--------均值濾波--------------------------------------------
				for(i=2;i<5;i++)													// use the last 3 times data to make average 
				{
					ADCsumH = ADCsumH + ADCdataH[i];
					ADCsumL = ADCsumL + ADCdataL[i];
				}				
				ADCavgH = ADCsumH/3;
				ADCavgL = ADCsumL/3;
				bgvalue = (ADCavgH<<4) + ADCavgL;
				Coe=(Bandgap_Voltage/bgvalue);
				ADCsumH = 0;
				ADCsumL = 0;	
}



void ADC_ISR (void) interrupt 11
{
	if(ADCF)
	{
		clr_ADCF;//清除ADC轉化完成標誌,進行下一次轉換
		set_ADCS;//當單次轉換完成後,ADCS會硬件置0,需要重新使能
		RC_value= (ADCRH<<4) + ADCRL;//得到ADC轉換值
		ADC_Voltage=RC_value*Coe;//測量數據*修正係數=實際值
	}
}

void main(void)
{
   		Set_All_GPIO_Quasi_Mode;//所有IO設置爲雙向模式	
		READ_BANDGAP();
		Enable_ADC_AIN5;//配置使能P04,作爲AIN5。
		PWM0_END_TRIG_ADC;//PWM0末端觸發ADC中斷
		set_EADC;//使能ADC中斷
		EA = 1;
		set_ADCS;//使能ADCS,啓動ADC測量
//------------------------------------------------	
//		PWM5_P03_OUTPUT_ENABLE;//使能PWM5,通過P03引腳輸出
		PWM0_P12_OUTPUT_ENABLE;
		clr_PWMTYP;//邊沿對齊模式
		clr_PWMMOD0;//設置爲獨立輸出模式
		clr_PWMMOD1;
		PWM_CLOCK_DIV_8;//8分頻模式
		PWMPH = 0x4e;
		PWMPL = 0x2f;
    	set_SFRPAGE;//PWM4 and PWM5 duty seting is in SFP page 1
    	PWM0H = 0x02;               
    	PWM0L = 0x81;
   		clr_SFRPAGE;     
//		PWM0_OUTPUT_INVERSE;//開啓極性控制	
    	set_LOAD;//載入週期和佔空比
    	set_PWMRUN;//開始輸出PWM
   while(1)
    {
       //構建你的代碼,LOAD會自動重載,PWM持續輸出。
    }
}

在這裏插入圖片描述
最後得到的數據是3.48V左右(依然插螺絲刀),通過數據手冊,可以得知,在沒有污染時,電壓典型值是0.9V,而污染嚴重時,輸出電壓的最小值是3.4V,結合下圖2的輸出電壓曲線,相信大家都已經明白了(這個傳感器檢測到的污染,再高也就了這樣了。。。。)
在這裏插入圖片描述
在這裏插入圖片描述
GP2Y10精度不高,而且使用過時的紅外測灰塵方式,無法很好的測量到空氣中PM2.5,可能PM10都有些難,用來測量污染物上升或者下降的趨勢貌似可行。如果對測量精度有要求,還是推薦使用激光傳感器,我沒錢,買不起,所以…
GP2Y1023AU0F有興趣的同學可以試試這個,據說精度不錯,可以測PM2.5,使用輸入捕獲測量即可。
據說攀藤的激光傳感器精度也很不錯,只是價格比較貴。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章