前言:
在proteus8.6版本已经有了超声波模块,以前proteus7版本中没有这样的模型,只能通过信号模拟超声波,或者自己制作超声波模块,但意思是一样的。proteus已经有了这个模块,就可以拉出来玩一下,看看实际的效果如何。
这个超声波模块的驱动程序应该更容易写,不像IIC协议和其他引脚顺序有点复杂,驱动程序更通用,关于这个超声波模块的驱动原理,在淘宝上搜索,下面有更详细的介绍,这里我随便找到一个:
这个写得很清楚,很容易理解,就是先把Trig低脚,然后给Trig至少10个引脚us然后降低高电平信号。然后模块会自动向外发送超声波,我们只需要等待Echo管脚可以变成高电平。Echo管脚高电平时间有距离信息。还给出了具体的距离计算公式:
测试距离 = (高电平时间 * 声速)/ 2;
所以,我们有编程的想法,先将Trig把管脚放低,然后给Trig至少10个引脚us高电平信号延迟,然后降低,然后等待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)%6; //定时器0低8位 TR0 = 1; ///打开定时器 ET0 = 1; ///开定时器中断 EA = 1; ///开总中断 }
定时器中断如下:
void T0_time() interrupt 1 { TH0 = (65536 - 10)/256; //定时器0高8位 10us TL0 = (65536 - 10)%6; //定时器0低8位 time ; }
可以看到,time每次计数一次,代表10次us的时间,即0.0001秒,距离公式为:
距离 = (time * 0.00001 * 340 / 2)单位:米
我们可以将单位换算为厘米,这样显示起来更加方便,而且proteus8中的超声波模块也按厘米发送电平,因此转换为厘米为:
距离 = time * 0.001 * 340 / 2 = time * 0.01 * 34 / 2 单位:cm
可以看出,这里应该有一个除以2,但我在实际测试中发现,proteus超声波模块中显示的距离没有除以2,所以我没有除以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)%6; //定时器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() interupt 1
{
TH0 = (65536 - 10)/256; //定时器0高8位 10us
TL0 = (65536 - 10)%256; //定时器0低8位
time ++;
}