前言:
在proteus8.6版本已经有了超声波模块,之前的proteus7版本中是没有这个的,只能够通过信号模拟超声波,或者就是自己制作超声波模块的模型,不过意思都差不多,现在既然proteus已经有了这个模块,就可以拉出来玩一下,看看实际的效果如何。压缩包链接放在最后。
超声波的驱动:
有关这种超声波模块的驱动程序,应该还是比较好写的,没有像IIC协议等引脚时序那样略微复杂,而且驱动程序又比较通用,关于这个超声波模块的驱动原理,在淘宝上随便搜一下,下面都有比较详细的介绍,这里我就随便找了一家:


这个写得清楚明白,简单易懂,就是先将Trig脚拉低,然后给到Trig引脚一个至少10us的高电平信号,然后再拉低。之后模块就会自动向外发送超声波,我们只需要等待Echo管脚变为高电平就可以了。这个Echo管脚高电平的时间就带有距离信息。具体的距离计算公式也给出了:
测试距离 = (高电平时间 * 声速)/ 2;
那么,我们编程的思路就有了,先将Trig管脚置低,然后给Trig引脚一个至少10us延时的高电平信号,然后再拉低,然后等待Echo管脚的高电平信号,当Echo接收到高电平信号时,就打开定时器,在定时器中断中使用一个变量来计时,记录Echo管脚的高电平时间,当Echo变为低或者时间过长时,跳出循环,进行距离计算。因为可能前方没有障碍物,导致高电平时间很长,程序就在这里一直等待,而且此时的长距离信号也没有什么意义了,因此加上了时间过长时也会跳出循环。这个可以根据自己的实际情况来进行超时时间的调整。这样子,我们的大概的驱动函数就有了:
//超声波测量距离
void measure()
{
float ult = 0.0f;
Trig = 1;
delay_20us();
Trig = 0;
while(Echo == 0); //等待高电平
TR0 = 1; //打开定时器0
time = 0; //计时清零
while((Echo == 1)&&(time <= 2000)); //等待Echo变为低电平或超出时间限制 2000us
//距离计算
ult = (float)time;
distance = (int) (34.0f * (ult / 100.0f)); //单位cm 34 * time/100
if(distance > 1000)
{
distance = 1000;
}
TR0 = 0; //关闭定时器
time=0;
ult=0;
}
关于定时器的问题,应该已经有很多关于51单片机的定时器的内容,这里就不在一个个寄存器来解释了。可以查阅相关的资料。在上面的驱动函数中,我们使用了time这个变量来计时。我们的定时器初始化配置如下:
/**********************************
初始化函数
***********************************/
void init()
{
TMOD = 0x01; //设置定时器工作方式,第二位为定时器0
TH0 = (65536 - 10)/256; //定时器0高8位 10us
TL0 = (65536 - 10)%256; //定时器0低8位
TR0 = 1; //打开定时器
ET0 = 1; //开定时器中断
EA = 1; //开总中断
}
定时器中断如下:
void T0_time() interrupt 1
{
TH0 = (65536 - 10)/256; //定时器0高8位 10us
TL0 = (65536 - 10)%256; //定时器0低8位
time ++;
}
可以看到,time每计数一次,代表10us的时间,即0.00001秒,那么距离公式就是:
距离 = (time * 0.00001 * 340 / 2)单位:米
我们可以将单位换算为厘米,这样显示起来更加方便,而且proteus8中的超声波模块,也是按照厘米来发送电平的,因此转换为厘米就是:
距离 = time * 0.001 * 340 / 2 = time * 0.01 * 34 / 2 单位:cm
可以看到,这里正常应该是有一个除以2的,但我在实际测试中发现,proteus中的超声波模块显示的距离,发回给我的信号并没有除以2,因此我就没有除,也可能是我部分步骤有问题,欢迎指正,也可以根据自己情况进行修改。
之后是proteus原理图的绘制,正如文章开头的图片:

使用一个1602来显示距离,具体1602的驱动程序的文章,可以查看我之前的文章:【Proteus】单片机配合矩阵键盘LCD1602制作简易计算器 - 知乎
之后便是使用proteus来调试。
小技巧:我们可以在proteus的Message窗口查看proteus的仿真过程


当然,我这里是已经成功的截图,最开始仿真我也是不成功的,这里有一个小插曲:

如上图,我最开始的main中初始化时,是将Echo引脚置0的,之后仿真时,Echo引脚的电平就不会变化了,一直等不到想要的高电平,此时Message窗口的提示信息如下:

Message窗口提示Echo引脚发生了逻辑争用,之后我就一直等不到高电平信号了,解决办法就是在初始化时不要管这个Echo,可能只有Proteus仿真需要这样,如果是真实的超声波模块,应该不会有这种问题,最后的main如下:
void main()
{
lcd_init();
Trig=0;
// Echo=0;
init(); //定时器初始化
display(); //显示函数
while(1)
{
measure(); //测量距离
display(); //显示函数
delay_ms(100);
}
}
main.c中所有代码如下:
#include <reg52.h>
#include <stdio.h>
#include "1602.h"
#define uchar unsigned char
#define uint unsigned int
uchar direction;
int distance = 0;
int time = 0; //超声波计时
sbit Trig = P1^0;
sbit Echo = P1^1;
//延时20us 使能超声波
void delay_20us()
{
uint bt ;
for(bt = 0;bt < 100;bt ++);
}
//延时10us 超声波使用
void delay_10us()
{
uint bt ;
for(bt = 0;bt < 30;bt ++);
}
/**********************************
初始化函数
***********************************/
void init()
{
TMOD = 0x01; //设置定时器工作方式,第二位为定时器0
TH0 = (65536 - 10)/256; //定时器0高8位 10us
TL0 = (65536 - 10)%256; //定时器0低8位
TR0 = 1; //打开定时器
ET0 = 1; //开定时器中断
EA = 1; //开总中断
}
//超声波测量距离
void measure()
{
float ult = 0.0f;
Trig = 1;
delay_20us();
Trig = 0;
while(Echo == 0); //等待高电平
TR0 = 1; //打开定时器0
time = 0; //计时清零
while((Echo == 1)&&(time <= 2000)); //等待Echo变为低电平或超出时间限制 2000us
//距离计算
ult = (float)time;
distance = (int) (34.0f * (ult / 100.0f)); //单位cm 34 * time/100
if(distance > 1000)
{
distance = 1000;
}
TR0 = 0; //关闭定时器
time=0;
ult=0;
}
//显示函数
void display()
{
char *string = "distance :";
char *string2 = "cm";
char Z[16];
//显示距离
sprintf((char*)Z,"%s%d%s",string,distance,string2);
print_string(Z,1);
// write_com(0x8a);
}
void main()
{
lcd_init();
Trig=0;
// Echo=0;
init(); //定时器初始化
display(); //显示函数
while(1)
{
measure(); //测量距离
display(); //显示函数
delay_ms(100);
}
}
void T0_time() interrupt 1
{
TH0 = (65536 - 10)/256; //定时器0高8位 10us
TL0 = (65536 - 10)%256; //定时器0低8位
time ++;
}
工程链接:通过网盘分享的文件:超声波测距
链接: https://pan.baidu.com/s/1mbsQwYxBNk83EfTRcvhIow 提取码: h35t
本文详细介绍了在Proteus8.6版本中使用超声波模块进行测距的原理和方法,包括驱动程序的编写、定时器的配置以及1602液晶屏的显示。在驱动程序中,通过拉高Trig引脚10us触发超声波发射,然后通过Echo引脚检测返回时间并计算距离。在遇到Echo引脚逻辑争用问题时,作者给出了解决方案,并提供了完整的C语言代码实现。
7036





