一、GP2Y0A21YK0F距離傳感器分析
1.1 傳感器介紹
GP2Y0A21YK0F是一款距離測量傳感模塊。它由PSD(position sensitive detector)和IRED(infrared emitting diode)以及信號處理電路三部分組成。由於採用了三角測量的方式,被測物體的材質、環境溫度以及測量時間都不會影響測量精度。傳感器輸出電壓值對應探測距離。通過測量電壓值就可以得出所測物體的距離,所以這款傳感器可以用於距離測量、避障等場合。
1.2 傳感器參數
1.3 時序圖
由上述時序圖可知,傳感器在上電之後要花費(38.3±9.6)ms進行第一次測量,在第一次測量期間,傳感器的輸出是不穩定的。在第一次測量結束之後,傳感器還要花費最多5ms來建立穩定輸出。因此,保險起見,我選擇在上電55ms後再進行測量。
1.4 輸出特性分析
通過上述傳感器輸出特性曲線我們看到,該傳感器最令人遺憾的一點大概就是輸出特性並非是簡單的一元一次函數,而是曲線,這就給傳感器的使用帶來的不便。對此,我採用分段函數對該曲線進行近似。由於在每一段分段函數內部,測量距離與輸出電壓爲線性關係,而輸出電壓又與AD採樣值之間爲線性關係。因此,我跳過對輸出電壓的計算過程,在每一段內直接找測量距離與AD採樣值之間的線性關係。下圖是我對測量距離爲10cm和15cm時AD值得測量:
通過上述圖同門可以看出,當距離爲10cm時AD值爲115,當距離爲15cm時AD值爲82,通過“兩點法”公式可以得出當測量距離在10-15cm之間時,距離=(-5/33)*AD值+905/33。以此類推,可以得到以下分段函數
/***temp爲距離,x爲AD值***/
if(x>=82&&x<=115)
{
temp=(-5*x+905)/33.0;
}
else if(x>=65&&x<82)
{
temp=(-5*x+665)/17.0;
}
else if(x>=46&&x<65)
{
temp=(-10*x+1030)/19.0;
}
else if(x>=36&&x<46)
{
temp=76.0-x;
}
else if(x>=30&&x<36)
{
temp=(-5/3.0)*x+100.0;
}
else if(x>=26&&x<30)
{
temp=(-5/2.0)*x+125.0;
}
else if(x>=23&&x<26)
{
temp=(-10*x+440)/3;
}
else if(x>=21&&x<23)
{
temp=-5.0*x+185.0;
}
二、PCF8591AD轉換器
2.1 模塊介紹
PCF8591 是單電源,低功耗8 位CMOS 數據採集器件,具有4 個模擬輸入、一個輸出和一個串行I2C 總線接口。3 個地址引腳A0、A1 和A2 用於編程硬件地址,允許將最多8 個器件連接至I2C總線而不需要額外硬件。PCF8591由於其使用的簡單方便和集成度高,在單片機應用系統中得到了廣泛的應用。
2.2 引腳說明
2.3 器件地址說明
每一個IIC器件都有一個器件地址,來區分不同的IIC設備,下面是PCF8591的地址格式
在本例子中,我將A0、A1、A2全部接地,因此寫地址爲0x90,讀地址爲0x91。
2.4 控制字說明
在該例子中我們進行的是AD轉換,因此第6位爲0,不允許模擬電壓輸出;採用單端輸入,因此第5、4位爲0、0;該例子中只進行一個模擬輸入量的AD轉換,因此關閉自動增量使能,即第2位爲0;模擬輸入通道選擇AIN3,因此第1位與第0位爲1、1。因此,控制字爲0x30。
2.5 輸出特性曲線
在該例子中,我們讓Vagnd=0V,Vref=5V。因此可得轉換公式爲:電壓值=(5.0/256.0)*AD模塊的讀數。但由於在該例中我們跳過了計算輸出電壓這一環節,因此該公式用不到。
三、IIC總線模擬
因爲本例子採用的爲51單片機,因此需要自己模擬IIC總線協議
3.1 IIC總線工作時序如下
3.2 根據上圖所示IIC總線工作協議進行IIC工作模擬
3.2.1 IIC總線啓動
void I2c_start()
{
sda=1;
scl=1;
Delay1(DELAY_TIME);
sda=0;
Delay1(DELAY_TIME);
scl=0;
}
3.2.2 IIC發送一個字節
void I2c_sendbyte(uchar byt)
{
uchar i;
for(i=0;i<8;i++)
{
scl=0;
Delay1(DELAY_TIME);
if(byt&0x80)
{
sda=1;
}
else
{
sda=0;
}
Delay1(DELAY_TIME);
scl=1;
byt <<= 1;
Delay1(DELAY_TIME);
}
scl=0;
}
3.2.3 IIC等待響應
uchar I2c_waitack()
{
uchar ackbit;
sda=1;//釋放數據線
Delay1(DELAY_TIME);
scl=1;
Delay1(DELAY_TIME);
ackbit=sda;//獲取響應信號,低電平爲有效
scl=0;
Delay1(DELAY_TIME);
return sda;
}
3.2.4 IIC接收一個字節
uchar I2c_receivebyte()
{
uchar da;
uchar i;
for(i=0;i<8;i++)
{
scl=1;
Delay1(DELAY_TIME);
da<<=1;
if(sda)
{
da|=0x01;
}
scl=0;
Delay1(DELAY_TIME);
}
return da;
}
3.2.5 IIC發送響應
void I2c_sendack(uchar ackbit)
{
scl=0;
sda=ackbit; //0:發送應答信號;1:發送非應答信號
Delay1(DELAY_TIME);
scl=1;
Delay1(DELAY_TIME);
scl=0;
sda=1;
Delay1(DELAY_TIME);
}
3.2.6 IIC總線結束
void I2c_stop()
{
sda=0;
scl=1;
Delay1(DELAY_TIME);
sda=1;
Delay1(DELAY_TIME);
}
四、例程(例程均爲自己編寫且驗證通過)
#include<reg52.h>
#include<intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
#define DELAY_TIME 5
sbit scl = P1^0;//時鐘總線接口
sbit sda = P1^1;//數據總線接口
void I2c_start(void);//IIC總線啓動條件
void I2c_stop(void);//IIC總線結束條件
void I2c_sendbyte(uchar byt);//IIC總線發送一個字節
uchar I2c_receivebyte(void);//IIC總線接收一個字節
void I2c_sendack(uchar ackbit);//IIC總線發送應答
void Delay1(uchar t);//讀寫操作中的延時
void Delay2(uchar t);//等待初始化子函數
void Init_pcf8591(void);//PCF8951初始化
uchar I2c_waitack(void);//IIC等待迴應子函數
int Read_value(void);//讀取AD值
uchar Distance(int x);//把AD值轉換爲距離
void Show(int Out);//顯示子函數
void Delay10us(void);//10us延時函數
void Delay1ms(void);//1ms延時函數
void main()
{
int Ad,Dist,i;
for(i=0;i<55;i++)//上電先延時50ms等待測量,再延時5ms等待建立穩定輸出
{
Delay1ms();
}
Read_value();//空讀一次
Ad=Read_value();//讀AD值
Dist=Distance(Ad);//把AD值轉換爲距離
Show(Dist);
}
/***************把AD值轉換爲距離值****************/
uchar Distance(int x)
{
float temp;
uchar y;
if(x>=82&&x<=115)
{
temp=(-5*x+905)/33.0;
}
else if(x>=65&&x<82)
{
temp=(-5*x+665)/17.0;
}
else if(x>=46&&x<65)
{
temp=(-10*x+1030)/19.0;
}
else if(x>=36&&x<46)
{
temp=76.0-x;
}
else if(x>=30&&x<36)
{
temp=(-5/3.0)*x+100.0;
}
else if(x>=26&&x<30)
{
temp=(-5/2.0)*x+125.0;
}
else if(x>=23&&x<26)
{
temp=(-10*x+440)/3;
}
else if(x>=21&&x<23)
{
temp=-5.0*x+185.0;
}
y=temp;//對結果四捨五入
if(temp-y>=0.5)
{
y+=1;
}
return y;
}
/***************讀取AD值****************/
int Read_value()
{
int result;
Init_pcf8591();
I2c_start();
I2c_sendbyte(0x91);//進行讀操作(A0、A1、A2均接地)
I2c_waitack();
result=I2c_receivebyte();
I2c_sendack(1);
I2c_stop();
return result;
}
/**********************PCF8591初始化函數*************************************/
void Init_pcf8591(void)
{
I2c_start();
I2c_sendbyte(0x90);//進行寫操作(A0、A1、A2均接地)
I2c_waitack();
I2c_sendbyte(0x03); //選擇通道AIN3進行轉化
I2c_waitack();
I2c_stop();
Delay2(10);
}
/**********************IIC總線啓動*************************************/
void I2c_start()
{
sda=1;
scl=1;
Delay1(DELAY_TIME);
sda=0;
Delay1(DELAY_TIME);
scl=0;
}
/********************IIC發送一個字節***********************************/
void I2c_sendbyte(uchar byt)
{
uchar i;
for(i=0;i<8;i++)
{
scl=0;
Delay1(DELAY_TIME);
if(byt&0x80)
{
sda=1;
}
else
{
sda=0;
}
Delay1(DELAY_TIME);
scl=1;
byt <<= 1;
Delay1(DELAY_TIME);
}
scl=0;
}
/********************IIC等待迴應***********************************/
uchar I2c_waitack()
{
uchar ackbit;
sda=1;//釋放數據線
Delay1(DELAY_TIME);
scl=1;
Delay1(DELAY_TIME);
ackbit=sda;//獲取響應信號,低電平爲有效
scl=0;
Delay1(DELAY_TIME);
return sda;
}
/********************IIC接收一個字節******************************/
uchar I2c_receivebyte()
{
uchar da;
uchar i;
for(i=0;i<8;i++)
{
scl=1;
Delay1(DELAY_TIME);
da<<=1;
if(sda)
{
da|=0x01;
}
scl=0;
Delay1(DELAY_TIME);
}
return da;
}
/********************IIC發送迴應***********************************/
void I2c_sendack(uchar ackbit)
{
scl=0;
sda=ackbit; //0:發送應答信號;1:發送非應答信號
Delay1(DELAY_TIME);
scl=1;
Delay1(DELAY_TIME);
scl=0;
sda=1;
Delay1(DELAY_TIME);
}
/**********************IIC總線結束*************************************/
void I2c_stop()
{
sda=0;
scl=1;
Delay1(DELAY_TIME);
sda=1;
Delay1(DELAY_TIME);
}
/**********************IIC中延時函數*************************************/
void Delay1(uchar t)
{
do
{
_nop_();
}
while(t--);
}
/**************************等待初始化延時***************************************/
void Delay2(uchar t)
{
unsigned char i;
while(t--)
{
for(i=0; i<112; i++);
}
}
/***********************10us延時函數*****************************/
void Delay10us()
{
uchar i;
i=2;
while(--i);
}
/***********************1ms延時函數*****************************/
void Delay1ms() //誤差 0us
{
unsigned char a,b,c;
for(c=1;c>0;c--)
for(b=142;b>0;b--)
for(a=2;a>0;a--);
}
/**********************顯示函數*************************************/
void Show(int Out)
{
char duan[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//段碼
while(1)
{
P2=0x01;//第四位(個位)
Delay10us();
P3=duan[Out%10];
Delay1ms();
P3=0xff;
P2=0x02;//第三位(十位)
Delay10us();
P3=duan[(Out/10)%10];
Delay1ms();
P3=0xff;
P2=0x04;//第二位(百位)
Delay10us();
P3=duan[(Out/100)%10];
Delay1ms();
P3=0xff;
P2=0x08;//第一位(千位)
Delay10us();
P3=duan[(Out/1000)%10];
Delay1ms();
P3=0xff;
}
}
五、Proteus仿真電路
左肩理想右肩擔當,君子不怨永遠不會停下腳步!