标题: [讨论]超声波测距 [打印本页]
作者: 单片机材料 时间: 2009-12-9 21:54
标题: [讨论]超声波测距
请教有关超声波测距的问题,有经验的可以交流下
作者: 幸甚至哉 时间: 2010-3-6 15:30
目前,现有的超声波发生器有40KHz,48KHz,58KHz等频率。根据声波在空气中(25摄氏度时)的速度340米每秒,来计算距离。其中一关键数据是超声波从发射到返回之间的时间(除以2),以此来计算时间;从而得到距离,之后再进行距离方面的提示工作,现有的倒车雷达便是如此。以上请知悉......
作者: zhengjianqi2010 时间: 2010-3-20 19:53
正在等程序,新手,谁搞过的共享一下程序啊- -
作者: CQYCHXL 时间: 2010-11-2 20:32
我也等!!!!
作者: liumei 时间: 2014-4-5 04:10
这个程序在分的很细,可以看超声部分或温湿检测部分程序。
程序如下:
#include <reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define Data P1 //LCD1602的数据口
#define NOP() {_nop_();_nop_();_nop_();_nop_();} //用于延时
//LCD1602控制引脚定义
//sbit RS = P2^6;
//sbit RW = P2^5;
//sbit E = P2^7;
sbit RS = P2^0; //复位端
sbit RW = P2^1; //写数据端
sbit E = P2^2; //使能端
//温湿DHT11的数据引脚定义
sbit DHT=P2^3;
//这些数组用于数据的缓存
uchar wendu_char[]=" temperature";//12 用于提示的字符
uchar shidu_char[]=" humidity";//10 用于提示的字符
uchar wendu[] ="wen:00.00 d";//11 数据格式,当读取的数据会保存到数组的相应的位上
uchar shidu[]="shi:00.00 RH";//12 //同上一句
uchar table[]={" make ^!^ in "};//14 //用于提示
uchar table1[]={" 2013-12-20 "};//12 //用于提示
//uchar qingxu1[]={" ^!^ "};//11
//uchar qingxu2[]={"good weather"};//12
//uchar qingxu3[]={" -_-! "};//12
//uchar qingxu4[]={"bad weather!"};//12
unsigned char shiZ,shiX,wenZ,wenX,check; //用于存读出传感器的数据,因为读出的是整型数据,之后用于转换成字符存于上面的数组中
unsigned char shiH,shiL,wenH,wenL; //
unsigned char flag; //定义一个标志位
unsigned int n=20,m; //定义一些会在下面用到的全局变量
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void delay(uint a); //超声波模块和温湿模块的公用延时函数
void delay_10us() ;
void delay_1ms(unsigned int i) ;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//超声波部分的函数
/////////////////////////////////////////////////////
sbit Tx=P3^3; //超声波模块发射脉冲的引脚定义
sbit Rx=P3^2; //超声波模块接收回波的引脚定义
uchar code table2[]={"Distance Test:"}; //14 //用于提示
uchar temp_dis[]= {"000.0 cm"};// 8 //用于显示数据的缓冲数组
long int t;//读出的数据现存于这些变量,再转换成字符赋给上面的数组
double distance;
uchar cache[4]={0,0,0,0}; //用于装入分离的数据的各个位
//外部中断0用于计算超声波模块接收到回波的时间,用于计算距离
void init0() //外部中断0在接收到Rx脚的高电平触发定时器计时,当高电平回波结束时是下降沿
{//定时器0的初始化
TMOD=0x01;
TH0=0;
TL0=0;
//外部中0的初始化
EX0=1; //外部中断的
IT0=1; //设置为低电平触发,下降沿有效
//打开总中断
//EA=1;
}
//超声波模块初始化
void HC05_Init()
{ //依据模块的资料,把发射脚拉高大于10us,再把发射脚拉低,模块就会自动发送探测的脉冲
EA=1;
Tx=1;
NOP();NOP();NOP();NOP(); //用于延时,保持大于8us的要求
Tx=0; //发射脚拉低
while(!Rx); //当RX为零时等待
TR0=1; //开启计数
while(Rx); //当RX为1计数并等待
TR0=0; //关闭计数
//TR0=1; //打开定时器0,以便从发送脉冲开始计时,到接收到外部中断0后关闭
distance=0.17*t; //计算距离的公式
// distance=(340*t)/2;
}
//数值转换
void distance_convert(long int dat)
{
//把得到的数据分离出个,十,百等位,装入数组中
//0.17*t知道测量的值有两个小数点,且该超声波模块可以测量到几百厘米,所以,得到的数据格式位000.00cm,
//我们要保留1为小数,又因为\,%的运算为整数,则先把数据乘以10,再求出各个对应的位放到对应的数组
//dat=dat*10;
cache[0]=dat/1000; //实际对应原数据的百位,因为把数据*10处理了,向高位移了一位
cache[1]=dat/100;
cache[2]=dat/10;
cache[3]=dat; //
//装入数组,加'0'是将整型的数据转换成字符数据
temp_dis[0]=cache[0]+'0'; //数组的格式位uchar temp_dis[]= {"000.0 cm"};
temp_dis[1]=cache[1]+'0';
temp_dis[2]=cache[2]+'0';
temp_dis[4]=cache[3]+'0';
}
//温度度部分的函数(DHT11)
////////////////////////////////////
//延时函数1ms
void delay_1ms(unsigned int i)
{
unsigned int j=88;
for(;i>0;i--)
{
while(j>0)j--;
}
}
//延时函数10us
void delay_10us()
{
unsigned char i;
i--;
i--;
i--;
i--;
i--;
i--;
}
//延时函数可调
void delay(uint a)
{
uint y;
for(;a>0;a--)
for(y=100;y>0;y--);
}
//读取DHT11的数据,其为单总线通讯,只用一条线,已经定义为DHT,区别于I2C通讯
char read_data()
{
unsigned char i,num,temp;
num=0;
for(i=0;i<8;i++)
{
flag=2;
while((!DHT)&&flag++); //判断从机是否发出80us的低电平相应信号是否结束
delay_10us();
delay_10us();
delay_10us();
if(DHT==1)
{
temp=1;
flag=2;
while(DHT&&flag++);
} //超时则跳出
else
temp=0;
num<<=1; //一位一位的读出存入变量num中
num|=temp;
}
return(num); //读8个位的数据,为一个字节
}
//读取DHT11的40位数据函数//////////////////////////////////////////////////////////////////////////////
void read_init()
{
DHT=0; //主机拉低21ms
delay_1ms(21);
DHT=1; //总线由上拉电阻拉高40us
delay_10us();
delay_10us();
delay_10us();
delay_10us();
DHT=1; //主机设为输入 判断从机相应信号
if(!DHT) //判断从机是否把总线拉低
{
flag=2;
while((!DHT)&&flag++); //等待从机把总线拉低
flag=2;
while(DHT&&flag++); //判断从机是否拉低总线,即响应
//接收数据
shiH=read_data();//因为read_data()只读出一个字节,而DHT读出的是连续的5个字节,所以不用for()
shiL=read_data();
wenH=read_data();
wenL=read_data();
check=read_data();
DHT=1;
}
}
//DH11数据处理
void DHT11dada()
{
unsigned char temp;
read_init();
temp=shiH+shiL+wenH+wenL; //检验也为8位的二进制,等于前面的数据之和
if(check==temp)
{ //检验数据对了就赋给另外几个变量来转换
shiZ=shiH;
shiX=shiL;
wenZ=wenH;
wenX=wenL;
}
wendu[4]='0'+wenZ/10; //分离出各个位,并转换成字符数据,依据上面的数组定义wendu[] ="wen:00.00 d";的数据格式,把对应的数据写到对应的位
wendu[5]='0'+wenZ; //分理出的是温度的个位,在数组中的第六个位置,所以写入该位更新,以便显示
wendu[8]='0'+wenX/10; //第七位为 .
wendu[9]='0'+wenX;
shidu[4]='0'+shiZ/10; //同理,湿度的格式为shidu[]="shi:00.00 RH";
shidu[5]='0'+shiZ;
shidu[8]='0'+shiX/10;
shidu[9]='0'+shiX;
}
//LCD1602部分函数 各个函数依据LCD1602的时序编写
////////////////////////////////////////////////////////
//LCD1602写指令函数
void lcd_write_com(uchar c)
{
delay_1ms(5);
E=0;
RS=0;
RW=0;
_nop_();
E=1;
Data=c;
E=0;
}
//LCD1602写数据函数
void lcd_write_dat(uchar c)
{
delay_1ms(5);
E=0;
RS=1;
RW=0;
_nop_();
E=1;
Data=c;
E=0;
RS=0;
}
//LCD1602初始化
void lcd_init()
{
delay_1ms(5);
lcd_write_com(0x38);
lcd_write_com(0x38);
lcd_write_com(0x38);
lcd_write_com(0x06);
lcd_write_com(0x0c);
lcd_write_com(0x01);
}
//LCD1602清屏函数
void qingping()
{
lcd_write_com(0x01);
delay(5);
}
//显示开始标题
void disp(void) //显示子函数
{ uchar i,j;
lcd_write_com(0x80);//往第一行写入字符串,写完一个字符会自动调到第二个地址写入数据
for(i=0;i<13;i++)
{
lcd_write_dat(table[ i]);
}
lcd_write_com(0x80+40);//往第二行写入日期
for(j=0;j<11;j++)
{
lcd_write_dat( table1[j]);
}
delay(3000);//显示的时间长短
qingping();//清屏
}
//显示温湿函数
void display_DHT11()
{ uchar h,n,e,m;
//uchar x,y;
lcd_write_com(0x80);
for(e=0;e<12;e++) //写入温度数据
{
lcd_write_dat(wendu_char[e]);
}
lcd_write_com(0x80+40);
for(n=0;n<11;n++) //写入温度数据
{
lcd_write_dat(wendu[n]);
}
delay(3000);
qingping();//清屏
lcd_write_com(0x80);
for(h=0;h<10;h++) //写入湿度数据
{
lcd_write_dat(shidu_char[h]);
}
lcd_write_com(0x80+40);
for(m=0;m<12;m++) //写入湿度数据
{
lcd_write_dat(shidu[m]);
}
delay(3000);
qingping();
// //情绪表达部分 //因为51内存写满了,只能舍去这部分
// if(shiZ>=16&&shiZ<=28)
// {
// lcd_write_com(0x80);
// for(y=0;y<11;y++)
// {
// lcd_write_dat(qingxu1[y]);
// }
// lcd_write_com(0x80+0x40);
// for(x=0;x<12;x++)
// {
// lcd_write_dat(qingxu2[x]);
// }
// delay(3000);
// qingping();
// }
// if(shiZ<=16&&shiZ>=28)
// {
// lcd_write_com(0x80);
// for(y=0;y<12;y++)
// {
// lcd_write_dat(qingxu3[y]);
// }
// lcd_write_com(0x80+0x40);
// for(x=0;x<12;x++)
// {
// lcd_write_dat(qingxu4[x]);
// }
// delay(3000);
// qingping();
// }
}
//显示超声波数据
void display_HC05()
{
uchar k,w;
lcd_write_com(0x80);//超声波测距显示部分:K,W
for(k=0;k<14;k++)
{
lcd_write_dat(table2[k]);
}
lcd_write_com(0x80+0x40);
for(w=0;w<8;w++)
{
lcd_write_dat(temp_dis[w]);
}
delay(3000);
qingping();
}
////不写在一起是为了及时将数据输出,因为读完传感器才输出,有的传感器的数据已经变化了
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//主函数
void main()
{ uchar x,s;
lcd_init();
init0();
Tx=0;//超声波发送口初始化,
while(1)
{
disp();//显示开始的字符
for(x=0;x<2;x++)
{
DHT11dada();//温湿的数据
}
display_DHT11();//显示温湿的数据
for(s=0;s<2;s++)
{
HC05_Init(); //需要放到while(1)中 因为要不断触发超声波模块的脉冲得出距离
distance_convert(distance);
}
display_HC05();
}
}
//外部中断0的中断函数,记回波的时间,即发送测量脉冲开始计时,到接收到回波时产生外部中断,把得到的时间赋给t
void int0() interrupt 0
{ EA=0;
t=(TH0*256+TL0); //读取定时器的时间
TH0=0; //清空定时器
TL0=0;
}
作者: xi_chang 时间: 2014-5-2 19:50
谢谢分享
作者: wzgzjx 时间: 2014-5-3 17:02
几块钱的超声波测距模决是不是测距不稳定,我做的测同一距离会测出不同的数据。误差有几个毫米
作者: wzgzjx 时间: 2014-5-3 17:04
我不知道是程序有问题或是超声波模块有问题
作者: yyxtj 时间: 2015-4-18 14:12
好东西,学习一下,谢谢!
作者: 张医生强大 时间: 2016-6-30 15:36
卖的模块,普遍测量角度都在15度以内,分离模块能达到多大的角度?
作者: vadzbchva 时间: 2017-8-3 18:35
学习一下,楼主分享的很好!
欢迎光临 (http://www.51hei.com/bbs/) |
Powered by Discuz! X3.1 |