⑴ 求基于AT89C52超声波测距简易设计的源程序,要求用3个LED管显示其测距,精确到小数点后2位如,X.XX米。
目前国内超声波测距器的设计大多采用汇编语言设计。由于单片机应用系统的日趋复杂,要求所写
的代码规范化,模块化,并便于多人以软件工程的形式进行协同开发,汇编语言作为传统的单片机应用系
统的编程语言,已经不能满足这样的实际需要了,而C语言以其结构化和能产生高效代码满足了这样的需
求,成为电子工程师进行单片机系统编程时的首先编程语言。在本设计中,由于C语言程序有利于实现较
复杂的算法,汇编语言程序具有较高的效率并且容易精确计算程序运行的时间,而超声波测距器的程序既
有较复杂的距离计算又要求精确计算超声波测距时程序运行的时间,所以本设计采用C语言和汇编语言
混合编程来实现。本文论述的是一种基于AT89C52单片机的超声波测距器,可用于汽车倒车等场合⋯。
1设计要求
设计一个超声波测距器,可以应用于汽车倒车、建筑施工工地以及一些工业现场的位置监控,也可用
于如液位、井深、管道长度的测量等场合。要求测量范围在0.10—5.00 m,测量精度lem,测量时与被测物
体无直接接触,能够清晰稳定地显示测量结果。
2设计思路
2.1超声波及其测距原理
超声波是指频率高于20KHz的机械波。为了以超声波作为检测手段,必须产生超声波和接收超声波。
完成这种功能的装置就是超声波传感器,习惯上称为超声波换能器或超声波探头。超声波传感器有发送
器和接收器,但一个超声波传感器也可具有发送和接收声波的双重作用。超声波传感器是利用压电效应
的原理将电能和超声波相互转化,即在发射超声波的时候,将电能转换为超声波,发射超声波;而在收到回
波的时候,则将超声振动转换成电信号。
超声波测距的原理一般采用渡越时间法TOt(time of fliight)。首先测出超声波从发射到遇到障碍物返
回所经历的时间,再乘以超声波的速度就得到二倍的声源与障碍物之间的距离。测量距离的方法有很多
种,短距离的可以用尺,远距离的有激光测距等,超声波测距适用于高精度的中长距离测量。因为超声波
收稿日期:2008-04-08
作者简介:周功明(1963一),男,副教授,主要研究方向:电子信息科学技术。
·50· 绵阳师范学院学报(自然科学版) 第27卷
在标准空气中的传播速度为331.45粑秒,由单片机负责计时,单片机使用12.0M晶振,所以此系统的测
量精度理论上可以达到毫米级。由于超声波指向性强,能量消耗缓慢,在介质中传播距离远,因而超声波
可以用于距离的测量。利用超声波检测距离,设计比较方便,计算处理也较简单,并且在测量精度方面也
能达到要求。
超声波发生器可以分为两类:一类是用电气方式产生超声波,一类是用机械方式产生超声波。本课题
属于近距离测量,可以采用常用的压电式超声波换能器来实现【7】。
2.2超声波测距器的系统框图
根据设计要求并综合各方面因素,可以采用AT89C52单片机作为主控制器,用动态扫描法实现LED
数字显示,超声波驱动信号用单片机的定时器完成,超声波测距器的系统框图如下图l所示¨2|:
3系统组成
3.1硬件部分
主要由单片机系统及显示电路、超声波发射电路
和超声波检测接收电路三部分组成。采用AT89C52来
实现对CX20106A红外接收芯片和TCT40—10系列超
声波转换模块的控制。单片机通过P1.0引脚经反相
\
超声波接收E :, LED显示单片机r
/\
Z ∑
超声波发送高控制器
:> 扫描驱动
图1 超声波测距器系统设计框图
Fig.1 Ultrasonic eLangi.g system design diagram
器来控制超声波的发送,然后单片机不停的检测INT0引脚,当INTO引脚的电平由高电平变为低电平时就
认为超声波已经返回。计数器所计的数据就是超声波所经历的时间,通过换算就可以得到传感器与障碍
物之间的距离¨≈J。
3.2软件部分
主要由主程序、超声波发生子程序、超声波接收中断程序及显示子程序等部分。
4系统硬件电路设计
4.1单片机系统及显示电路
单片机采用AT89C52或其兼容系列。采用12MHz高精度的晶振,以获得较稳定的时钟频率,减小测
量误差。单片机用P1.0端口输出超声波转化器所需的40KHz方波信号,利用外中断0口检测超声波接收
电路输出的返回信号。显示电路采用简单实用的4位共阳LED数码管,段码用74LS244驱动,位码用PNP
三极管驱动。单片机系统及显示电路如下图2所示‘1。31。
图2单片机及显示电路原理图
Fig.2 MCU and display circuit schematics
第8期周功明等:基于AT89C52单片机的超声波测距器设计·51.
4.2超声波发射电路原理图
压电超声波转换器的功能:利用压电晶体谐振工作。内部结构如图3‘3Ⅲ1所示,它有两个压电晶片和
一个共振板。当它的两极外加脉冲信号,其频
率等于压电晶片的固有振荡频率时,压电晶片PI.O
将会发生共振,并带动共振板振动产生超声波,
这时它就是一超声波发生器;如没加电压,当共
振板接收到超声波时,将压迫压电振荡器作振
动,将机械能转换为电信号,这时它就成为超声
波接收转换器。超声波发射转换器与接收转换
器其结构稍有不同。
4.3超声波检测接收电路图3发射电路原理图
参考红外转化接收电路,本设计采用集成
F‘g·3 U1‘ms。nie劬啪mi‘妇c‘咖1‘∞hem蚯c
电路CX20106A,这是一款红外线检波接收的专用芯片,常用于电视机红外遥控接收器。考虑到红外遥控
常用的载波频率38KHz与测距超声波频率
40KHz较为接近,可以利用它作为超声波检测
电路。如图4【3 J[71超声波检测接收电路原理图
所示,适当改变C4的大小,可改变接收电路的
灵敏度和抗干扰能力。⋯. J。j-二
5系统程序设计
超声波测距软件设计主要由主程序,超声
波发射子程序,超声波接收中断程序及显示子
程序组成。下面对超声波测距器的算法,主程
序,超声波发射子程序和超声波接收中断程序
逐一介绍。
5.1超声波测距器的算法设计
GND
图4超声波检测接收电路原理图
Fig.4 Ultrasonic receiver and detection circuit schematic
图5【_列示意了超声波测距的原理,即超声
波发生器T在某一时刻发出的一个超声波信号,当超声波遇到被测物
体后反射回来,就被超声波接收器R所接受。这样只要计算出发生信
号到接收返回信号所用的时问,就可算出超声波发生器与反射物体的
距离。
距离计算公式:d=s/2=(c木t)/2,其中d为被测物与测距器的距
离,s为声波的来回路程,c为声速,t为声波来回所用的时间。
图5超声波测距原理图
Fig.5 Ultrasonic Ranging schematic
声速c与温度有关(见表1),如温度变化不大,则可认为声速是基
本不变的。如果测距精度要求很高,则应通过温度补偿的方法加以校正。声速确定后,只要测得超声波往
返时间,即可求得距离。在系统加入温度传感器来监测环境温度,可进行温度补偿。这里可以用DSl8820
测量环境温度,根据不同的环境温度确定一声速提高测距的稳定性。为了增强系统的可靠性,可在软硬件
上采用抗干扰措施。
表1不同温度下的超声波速表
Table I Under different temperatures ultrasonic velocity Table
·52· 绵阳师范学院学报(自然科学版) 第27卷
5.2主程序
主程序首先对系统环境初始化,设置定时器1D工作模式为16位的定时计数器模式,置位总中断允许
位EA并给显示端Po和P2清0。然后调用超声波发生子程序送出一个超声波脉冲,为避免超声波从发射
器直接传送到接收器引起的直接波触发,需延迟0.1ms(这也就是测距器会有一个最小可测距离的原因)
后,才打开外中断0接收返回的超声波信号。由于采用12MHz的晶振,机器周期为lus,当主程序检测到接
收成功的标志位后,将计数器哟中的数(即超声波来回所用的时
间)按下式计算即可测得被测物体与测距仪之间的距离,设计时取
20℃时的声速为344 m/s则有:d=(C木TO)/2=172T0/10000cm
(其中,ID为计数器,ID的计数值)。
测出距离后结果将以十进制BCD码方式LED,然后再发超声
波脉冲重复测量过程。主程序框图如图6所示。
5.3超声波发生子程序和超声波接收中断程序
超声波发生子程序的作用是通过PI.0端口发送2个左右的
超声波信号频率约40KHz的方波,脉冲宽度为12 US左右,同时把
计数器,ID打开进行计时。超声波测距器主程序利用外中断0检
测返回超声波信号,一旦接收到返回超声波信号(INT0引脚出现
低电平),立即进入中断程序。进入该中断后就立即关闭计时器
,ID停止计时,并将测距成功标志字赋值l。如果当计时器溢出时
还未检测到超声波返回信号,则定时器rID溢出中断将外中断0关
闭,并将测距成功标志字赋值2以表示此次测距不成功H旬J。
5.4超声波测距器的部分程序清单
/宰超声波测距器弹片机c程序使用Keil C51 ver 7.09
。
木/
#include<re951.h>
#define uchar unsigned int
#define uint unsigned int
#define ulong unsigned long
Extem void ca_t(void);
Extem void delay(uint);
Extem void display(unchar);
Data unehar testtok;
/木超声波测距器主程序术/
Void main(void)
{data unchar dispram[5];
data uint i;
data ulong time;
p0=0xff;
pl=0xff;
TMOD=0X11:
IE=0x80;
While(1)
{.“}
开始
系统初始化
发送超声波脉冲
等待发射超声波
计算距离
显示结果0.5s
图6主程序框图
diagram of the main program
第8期周功明等:基于AT89C52单片机的超声波测距器设计·53·
6软硬件调试
超声波测距仪的制作和调试,其中超声波发射和接收采用中15的超声波换能器TCT40一IOFl(T发
射)和TCT40—10S1(R接收),中心频率为40kHz,安装时应保持两换能器中心轴线平行并相距4—8 cm,
其余元件无特殊要求。若能将超声波接收电路用金属壳屏蔽起来,则可提高抗干扰能力。根据测量范围
要求不同,可适当调整与接收换能器并接的滤波电容C4的大小,以获得合适的接收灵敏度和抗干扰能力。
硬件电路制作完成并调试好后,便可将程序编译好下载到单片机试运行。根据实际情况可以修改超
声波发生子程序每次发送的脉冲宽度和两次测量的间隔时间,以适应不同距离的测量需要∞】【71。
7 结束语
本文设计的是基于AT89C52单片机的超声波测距器,可应用于汽车倒车等场合,提醒驾驶员倒车时有
效的避开可能对倒车造成危害的障碍物和行人,从而有效避免由于倒车造成的汽车碰撞或擦伤经济损失
和人身安全问题。具有较强的实用性。
参考文献:
[1] 周功明.基于AT89C2051弹片机的防盗自动报警电子密码锁系统设计[J].绵阳师范学院学报,2007,26(5):112—
116.
[14]
张齐.单片机应用系统设计技术一基于c语言编程[M].北京:电子工业出版社,2006.
李光飞.单片机c程序设计实例指导[M].北京:航空航天大学业出版社,2005.
楼燃苗,李光飞.51系列单片机设计实例[M].北京:航空航天大学业出版社,2003.
Zhongbo Li.Electronic Technique[M].Beijing:Mechannic Instrical Prees,2003.
赖麒文.8051单片机c语言彻底应用[M].北京:科学业出版社,2002.
何希才.传感器及其应用电路[M].北京:电子工业出版社,2001.
丁元杰.单片微机原理及应用[M].北京:机械工业出版社,2001.
孙串友,孙晓斌.感测技术基础[M].北京:电子工业出版社,2001.
马忠梅.单片机的c语言应用程序设计[M].北京:航空航天大学业出版社,1999.
刘喜昂,周志宇.基予多超声传感器的机器人安全避障技术[J].测控技术,2003,23(2):71—73.
翟国富,刘茂恺.一种实时高精度的机器人用超声波测距处理方法[J].应用声学,1990,15(1):17—24.
Cray C,Swinhoe C F,Myinl.Target controlled infusion of ketamine曲analgessia for TIV A with propof01.Can.J Anesth,1999,
40:957.
R J Higgens.Electronics and Analog Integrated Circuits[M].N.J:Prentice—Hall Inc,2001.
⑵ 基于单片机超声波测距c语言程序求解释
//上面这段什么意思?
//上下面这段什么意思? 没有code为什么也可以存16进制?
uchar dis_smg[8] ={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8};
有code和没有code的区别在于 dis_smg变量的存储的存储方式上
我记得有code的适合 dis_smg的数组元素不能改变的吧 记得不清楚了
你可以网络去
//下面是不是数码管引脚和单片机引脚对应?
//数码管位选定义
sbit smg_we1 = P3^4; //数码管位选定义
答案: 是
⑶ 1602显示超声波测距的C语言程序和Proteus仿真图
#include"reg52.h"
#include"intrins.h"
#define uchar unsigned char //无符号8位
#define uint unsigned int //无符号16位
#define ulong unsigned long //无符号32位
sbit K1=P1^0; //按下K1后,开始测距
sbit LEDRed=P1^1; //测距指示灯,亮表示正在测距,灭表示测距完成
//sbit BEEP=P1^2; //报警测量超出范围
sbit Trig=P2^5; //HC-SR04触发信号输入
sbit Echo=P2^6; //HC-SR04回响信号输出
float xdata DistanceValue=0.0; //测量的距离值
float xdata SPEEDSOUND; //声速
float xdata XTALTIME; //单片机计数周期
uchar xdata stringBuf[6]; //数值转字符串缓冲
//LCD1602提示信息
uchar code Prompts[][16]=
{
{"Measure Distance"}, //测量距离
{"- Out of Range -"}, //超出测量范围
{"MAX range 400cm "}, //测距最大值400cm
{"MIN range 2cm "}, //测距最小值2cm
{" "}, //清屏
{" Press K1 Start "} //按键开始测量
};
uchar xdata DistanceText[]="Range: "; //测量结果字符串
uchar xdata TemperatureText[]="Temperature: ";//测量温度值
extern void LCD_Initialize(); //LCD初始化
extern void LCD_Display_String(uchar *, uchar); //字符串显示
extern void ReadTemperatureFromDS18B20(); //从DS18B20读取温度值
extern int xdata CurTempInteger;
//毫秒延时函数
void DelayMS(uint ms);
//20微秒延时函数
void Delay20us();
//HCSR04初始化
void HCSR04_Initialize();
//测量距离
float MeasuringDistance();
//测距的数值排序求平均
float DistanceStatistics();
//输出距离值到LCD1602上
void DisplayDistanceValue(float dat);
//将无符号的整数转成字符串,返回字符串长度,不包括'\0'结束符
uchar UnsigedIntToString(uint value);
//蜂鸣器
//void Beep(uchar time);
//显示温度值
void DisplayTemperatureValue();
void main()
{
LCD_Initialize();//LCD1602初始化
LCD_Display_String(Prompts[0],0x00);
LCD_Display_String(Prompts[5],0x40);
ReadTemperatureFromDS18B20(); //测温度
HCSR04_Initialize(); //HC-SR04初始化
while(1)
{
if(K1==0)
{
DelayMS(5);
if(K1==0)
{
//Beep(1);
while(K1==0);
LEDRed=0;
ReadTemperatureFromDS18B20();//测温度
DisplayTemperatureValue();
if(CurTempInteger<14)
CurTempInteger=14;
else if(CurTempInteger>26)
CurTempInteger=26;
SPEEDSOUND=334.1+CurTempInteger*0.61;//计算声速
DistanceValue=DistanceStatistics(); //测距并返回距离值
DisplayDistanceValue(DistanceValue); //显示距离值
LEDRed=1;
}
}
}
}
//测距的数值排序求平均
float DistanceStatistics()
{
uchar i,j;
float disData[7],t;
//连续测距
for(i=0;i<7;i++)
{
disData=MeasuringDistance();
DelayMS(80);
}
//排序
for(j=0;j<=6;j++)
{
for(i=0;i<7-j;i++)
{
if(disData>disData[i+1])
{
t=disData;
disData=disData[i+1];
disData[i+1]=t;
}
}
}
return (disData[2]+disData[3]+disData[4])/3;
}
//测量距离
float MeasuringDistance()
{
//最大定时时间约65ms
TH0=0;
TL0=0;
//生成20us的脉冲宽度的触发信号
Trig=1;
Delay20us();
Trig=0;
//等待回响信号变高电平
while(!Echo);
TR0=1; //启动定时器0
//等待回响信号变低电平
while(Echo);
TR0=0; //关闭定时器0
//返回距离值(mm)
return (SPEEDSOUND*XTALTIME*((float)TH0*256+(float)TL0))/2000;
}
//HCSR04初始化
void HCSR04_Initialize()
{
//计算单片机计数周期 晶振=11.953M 单位us
XTALTIME=12/11.953;
//温度25度时声速的值
SPEEDSOUND=334.1+25*0.61;
Trig=0;
Echo=0;
TMOD=0x01;
}
//输出距离值到LCD1602上
void DisplayDistanceValue(float dat)
{
uchar i=0,j=0,len;
uint value;
value=(uint)dat;
//范围检查大于4000mm和小于20mm都为超出测量范围
if(value>4000)
{
LCD_Display_String(Prompts[1],0x00);
LCD_Display_String(Prompts[2],0x40);
//Beep(2);
}
else if(value<20)
{
LCD_Display_String(Prompts[1],0x00);
LCD_Display_String(Prompts[3],0x40);
//Beep(2);
}
else
{
//将数值转换成字符串
len=UnsigedIntToString(value);
//保留1位小数
while(stringBuf!='\0')
{
if(len-j==1)
{
DistanceText[6+j]='.';
j++;
}else
{
DistanceText[6+j]=stringBuf;
i++;
j++;
}
}
DistanceText[6+j]='c';
j++;
DistanceText[6+j]='m';
i=7+j;
//剩余位置补空格
while(i<16)
{
DistanceText=' ';
i++;
}
//LCD_Display_String(Prompts[0],0x00);
LCD_Display_String(DistanceText,0x40);
}
}
//显示温度值
void DisplayTemperatureValue()
{
TemperatureText[13]=CurTempInteger/10+'0';
TemperatureText[14]=CurTempInteger+'0';
TemperatureText[15]='C';
LCD_Display_String(TemperatureText,0x00);
}
//将无符号的整数转成字符串,返回字符串长度
uchar UnsigedIntToString(uint value)
{
uchar i=0,t,length;
//从个位开始转换
do
{
stringBuf='0'+value;
value=value/10;
i++;
}while(value!=0);
length=i;
//将字符串颠倒顺序
for(i=0;i<(length/2);i++)
{
t=stringBuf;
stringBuf=stringBuf[length-i-1];
stringBuf[length-i-1]=t;
}
stringBuf[length]='\0';
return length;
}
//蜂鸣器
//延时函数 毫秒 @12.000MHz
void DelayMS(uint ms)
{
uchar i, j;
while(ms--)
{
_nop_();
i = 2;
j = 239;
do
{
while (--j);
}while (--i);
}
}
//延时函数 20微秒 @12.000MHz
void Delay20us()
{
uchar i;
_nop_();
i = 7;
while (--i);
}
//定时器0中断
void Timer0() interrupt 1
{
}
//DS18B20代码:
#include
#include
#define uchar unsigned char //无符号8位
#define uint unsigned int //无符号16位
//定义DS18B20端口DS18B20_DQ
sbit DS18B20_DQ = P2^7;
//当前采集的温度值整数部分
int xdata CurTempInteger;
//当前采集的温度值小数部分
int xdata CurTempDecimal;
void Delayus(uint count)
{
while (--count);
}
uchar Reset_DS18B20()
{
uchar status;
DS18B20_DQ=1;
Delayus(1);
//开始复位过程
DS18B20_DQ=0; //数据线拉低
Delayus(100); //延时480us-960us
DS18B20_DQ=1; //数据线拉高
Delayus(10); //延时15us-60us
status=DS18B20_DQ; //读取数据线上的状态
Delayus(120);
return status;
}
void WriteByteToDS18B20(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
DS18B20_DQ=0;
DS18B20_DQ=dat&0x01; //发送1位数据
Delayus(15); //延时60us以上
DS18B20_DQ=1; //释放总线,等待总线恢复
dat>>=1; //准备下一位数据
}
}
uchar ReadByteFromDS18B20()
{
uchar i,dat=0;
for(i=0;i<8;i++)
{
DS18B20_DQ=0; //拉低总线,产生读信号
dat>>=1;
DS18B20_DQ=1; //释放总线,准备读1位数据
Delayus(2); //延时4us
if(DS18B20_DQ) dat|=0x80; //合并每位数据
Delayus(15); //延时60us
DS18B20_DQ=1; //拉高总线,准备读下1位数据
}
return dat;
}
void ReadTemperatureFromDS18B20()
{
uchar flag=0;//正负符号标志
//存储当前采集的温度值
uchar TempValue[]={0,0};
if(Reset_DS18B20())
{
CurTempInteger=255;
CurTempDecimal=0;
}
else
{
WriteByteToDS18B20(0xCC);//跳过ROM命令
WriteByteToDS18B20(0x44);//温度转换命令
Reset_DS18B20();//复位
WriteByteToDS18B20(0xCC);//跳过ROM命令
WriteByteToDS18B20(0xBE);//读取温度暂存器命令
TempValue[0]=ReadByteFromDS18B20();//先读低字节温度值
TempValue[1]=ReadByteFromDS18B20();//后读高字节温度值
Reset_DS18B20();//复位
//计算温度值
//先进行正温度与负温度判断,高5位全为1(0xF8)则为负数
if((TempValue[1]&0xF8)==0xF8)
{
//负温度计算:取反加1,低字节为0时,高字节取反加1,否则不需要。
TempValue[1]=~TempValue[1];
TempValue[0]=~TempValue[0]+1;
if(TempValue[0]==0x00) TempValue[1]++;
flag=1;//负数标志
}
//将温度值分为整数和小数两部分存储(默认为12位精度)
CurTempInteger=((TempValue[1]&0x07)<<4)|((TempValue[0]&0xF0)>>4); if(flag) CurTempInteger=-CurTempInteger;
CurTempDecimal=(TempValue[0]&0x0F)*625;
}
}
//LCD1602程序代码:
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define Delay4us(){_nop_();_nop_();_nop_();_nop_();}
sbit LCD_RS=P2^0;
sbit LCD_RW=P2^1;
sbit LCD_EN=P2^2;
void LCDDelay(uint ms)
{
uchar i, j;
while(ms--)
{
_nop_();
i = 2;
j = 239;
do
{
while (--j);
}while (--i);
}
}
bit LCD_Busy_Check()
{
bit result;
LCD_RS=0; LCD_RW=1; LCD_EN=1;
Delay4us();
result=(bit)(P0&0x80);
LCD_EN=0;
return result;
}
void Write_LCD_Command(uchar cmd)
{
while(LCD_Busy_Check());
LCD_RS=0; LCD_RW=0; LCD_EN=0; _nop_(); _nop_();
P0=cmd; Delay4us();
LCD_EN=1; Delay4us(); LCD_EN=0;
}
void Write_LCD_Data(uchar dat)
{
while(LCD_Busy_Check());
LCD_RS=1;LCD_RW=0;LCD_EN=0;
P0=dat;Delay4us();
LCD_EN=1;Delay4us();LCD_EN=0;
}
void LCD_Set_POS(uchar pos)
{
Write_LCD_Command(pos|0x80);
}
void LCD_Initialize()
{
Write_LCD_Command(0x01); LCDDelay(5);
Write_LCD_Command(0x38); LCDDelay(5);
Write_LCD_Command(0x0C); LCDDelay(5);
Write_LCD_Command(0x06); LCDDelay(5);
}
void LCD_Display_String(uchar *str, uchar LineNo)
{
uchar k;
LCD_Set_POS(LineNo);
for(k=0;k<16;k++)
{
Write_LCD_Data(str[k]);
}
}
void LCD_Display_OneChar(uchar Dat, uchar X, uchar Y)
{
Y &= 0x01; //限制Y不能大于1(2行,0-1)
X &= 0x0F; //限制X不能大于15(16个字符,0-15)
if(Y) {X |= 0x40;} //当要在第二行显示时地址码+0x40;
X |= 0x80; //算出指令码
Write_LCD_Command(X);
Write_LCD_Data(Dat);
}