一、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
#include
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仿真电路