IT技术互动交流平台

3超声波测距模块DIY笔记(三)

作者:beautifulzzzz  来源:IT165收集  发布日期:2014-11-03 21:27:30

前言

上一节我们已经研究了超声波接收模块并自己设计了一个超声波接收模块,在此基础上又尝试用单片机加反相器构成生成40KHz的超声波发射电路,可是发现采用这种设计的发射电路存在严重的发射功率太低问题,对齐的情况下最多只有10CM。本节主要介绍并制造一种大功率超声波发射装置~

一、浪里淘金,寻找最简超声波功率提高方案

1.1、优化波形发生程序

>_<' 上节讲到的利用反相器加单片机生成40KHz的超声波发射装置存在严重的功率问题,然后在上次之后的研究中我发现通过调节定时器的定时,功率会有稍微的提高,但还是比较弱~(因为条件限制,根本买不起示波器这种神器,所有只有酷比的调试代码啦!)下面的代码即改进后的51单片机代码,这次定时器采用的是定时器2,16位重装模式~

 1 /*-----------------------------------------------
 2 名称:定时器2
 3 ------------------------------------------------*/
 4 #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
 5  
 6 sbit P10=P1^0;   
 7 sbit P11=P1^1; 
 8 /*------------------------------------------------
 9 定时器初始化子程序
10 ------------------------------------------------*/
11 void TIM2Inital(void)
12 {
13     RCAP2H = (65536-12)/256;//晶振11.0592M 12us 16bit 自动重载(最好是用12M但是要实现时间同步,那边需要串口,所以这里就勉强采用11.0952)
14     RCAP2L = (65536-12)%256;
15     ET2=1;                     //打开定时器中断
16     EA=1;                      //打开总中断
17     TR2=1;                     //打开定时器开关
18 }
19 /*------------------------------------------------
20 主程序
21 ------------------------------------------------*/
22 main()
23 {
24     P10=0;
25     P11=0;
26     TIM2Inital();
27     while(1){}
28 }
29 /*------------------------------------------------
30 定时器中断子程序
31 ------------------------------------------------*/
32 void TIM2(void) interrupt 5 using 1//定时器2中断
33 {   
34     TF2=0;
35     P10=~P10;
36     P11=~P11;
37 }

1.2、尝试各种其他超声模块方案

>_<' 发现上面的方法不能大幅度改变发射功率,于是还是继续网上找资料,于是看上一节最后留下的第二个链接中的文章,找个简单的实验了下。结果在protues中仿真发现怎么也不是想要的效果,另一方面考虑到要提供9V的电压,于是就放弃了在面包板上连接实物实验。然后又在protues里实验了用555做超声波发送模块的方案,结果不知道为什么,protues里似乎不能给555加9V电压(一直报错)。最后大致浏览下这个文档中的方案,发现基本上都需要9V电压,而且模电较多(我这里电子元件不是太多,最坑的是没有示波器!)

1.3、用三极管放大信号

>_<' 一个偶然的发现某同学的毕业设计中的方案:他介绍在脉冲发生电路和脉冲发射电路中加一个三极管来放大信号,觉得这个简单易行,我在面包板上简单的用一个2N3904三极管,照着模拟电路书本上简单放大电路连接好,将上述产生40KHz的单片机脉冲发生电路的引脚和三极管的基极相连,集电极加载一个12V的电压,测试结果发现可以很有效地提高发射功率,但是只成功了一小会,然后再怎么实验都无法再收到超声波了(我怀疑是把三极管弄坏了),所以该方案又失败了~

1.4、MAX232放大信号方案

>_<' 通过上面的各种尝试,我发现上面发射部分设计方案有一个共同的特点:都需要较高的驱动电压。但是我购买的HC-SR04超过声波测距模块却只需要用5V就能发射功率很强的成声波,这点引起了我的思考。于是直接找来HC-SR04的设计图:

通过研究发现:其发射部分采用STC系列单片机作为40KHz的脉冲发生器,然后把13、14两路(他一定是让这两路提供反向电平作为输出)链接到 MAX232的两个输入端!一看到MAX232瞬间就明白了:MAX232是经常用在串口通信中用于将串口信号放大来传播更远距离的芯片,他这里采用 MAX232这个特点用于将信号放大,然后在输出端直接驱动发射头!非常机智!于是我利用手头上的串口转TTL模块做一个简单的实验,结果令人振奋,果然能够对信号进行很强的放大:

于是一鼓作气,重新设计一个信号更强劲的方案,并把电路焊接成发射模块:这次采用MAX232的2个输入和输出通道,将两个方波同时放大,将产生更加强劲的效果!

二、步步为营,打造高效准确超声测距算法

>_<' 到上面为止我们已经完美地把超声波发射与接收模块都做好了,那么现在就要研究下如何利用他们进行测距了~(PS:嘻嘻这里俺可不是简单的用一个发送模块一个接收模块进行直线空间上的测距,这种东西早都比较成熟了,网上一搜一把,而且非常便宜!我要做的是利用2个接收模块及一个接收模块在二维平面上对物体进行定位!)

2.1、接收MCU区分接收头信号并统计时差算法初试

>_<' 还记得我们上节用到的最最简单的信号检测算法吗?该程序是放到接收部分MCU中运行的,因为超声波接收部分一旦接收到超声波就会产生一个1-0-1信号,所以我们上一节只是简单的将接收模块输出链接到单片机的一个引脚,在单片机程序中对该引脚电平进行轮询输出。但是这里我们用了2个接收头,如果处理不当,会很难分辨是哪一个接收头产生的信号,如果采用顺序输出又不满足两个信号到达的先后顺序不同且会变的事实,综上,这里先初步用一个ok作为标记:为0表示没收到一个信号;为1表示只收到1好接收模块的信号;为2表示只收到2号接收模块的信号;为3表示两个都收到了,具体如下:[图中连续ab之间的+号的个数表示两个信号之间的时间差]


 1 /*-------------------------------------------
 2 简单的串口通信{接收}
 3 -------------------------------------------*/
 4 #include<reg51.h>
 5  
 6 #define uint unsigned int
 7 #define uchar unsigned char
 8  
 9  
10 sbit IN1 = P1^0;
11 sbit IN2 = P1^1;
12 /*--------------------------------------------
13 USAR初始函数
14 ---------------------------------------------*/
15 void USRT_init()
16 {
17     TMOD=0x20;            //设置T1定时器工作方式2
18     TH1=0xfd;            //T1定时器装初值
19     TL1=0xfd;
20     TR1=1;                //启动T1定时器
21     SM0=0;                //设定串口工作方式
22     SM1=1;
23     EA=1;                //开总中断
24 }
25 /*--------------------------------------------
26 主函数
27 ---------------------------------------------*/
28 void main()
29 {
30       int i=0,ok=0;  
31     USRT_init();
32     while(1)
33     {
34         SBUF='+';
35         if(IN1==0 && ok!=1 && ok!=3){
36             SBUF='a';
37             if(ok==2)ok=3;
38             else ok=1;
39         }
40         if(IN2==0 && ok!=2 && ok!=3){
41             SBUF='b';
42             if(ok==1)ok=3;
43             else ok=2;
44         }
45         if(ok==3){
46             i++;
47             if(i==100){
48                 ok=0;
49                 i=0;
50             }
51         }
52         while(!TI);        //每次等待发送完毕,再执行下一条
53         TI=0;            //手动清0
54     }
55 }
code

2.2、折衷——单MCU上集成收发模块实现测距

>_<' 为了体现咱们是步步为营的,所以俺刚开始并没有直接去挑战发送和接收模块分开或者直接两个接收模块去测距,而是先尝试一下在一个MCU上连接一个发射模块和一个接收模块,自制一个简单的超声波测距仪(市场上卖的那种直线型的)。这个程序不难理解,但是麻雀虽小,五脏俱全,这里用到了51单片机的几乎所有中断:1)负责接收模块监听的外部中断2)负责计时的T0计数器3)负责串口的T1定时器4)负责产生方波的T2定时器~然后测距的思路很简单:首先发送模块在短时间内发送100周期的40KHz超声波,然后计时器开始计数,等到接收模块输出引脚产生1-0-1下降沿触发外部中断时停止计数,然后根据转换公式将超声波传播计数转换为距离并把数据通过串口发送给上位机。


  1 /*-----------------------------------------------
  2 名称:定时器2
  3 ------------------------------------------------*/
  4 #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
  5 #include<intrins.h>
  6 
  7 sbit P10=P1^0;   
  8 sbit P11=P1^1; 
  9 
 10 unsigned char times;//一次发射方波数
 11 char bwei,shwei,gwei;//数据
 12 
 13 /*------------------------------------------------
 14 定时器初始化子程序[定时器2,16位自动装值,用于产生40KHz方波]
 15 ------------------------------------------------*/
 16 void TIM2Inital(void)
 17 {
 18     RCAP2H = (65536-12)/256;//晶振11.0592M 12us 16bit 自动重载(最好是用12M但是要实现时间同步,那边需要串口,所以这里就勉强采用11.0952)
 19     RCAP2L = (65536-12)%256;
 20     ET2=1;                     //打开定时器中断
 21     EA=1;                      //打开总中断
 22     TR2=1;                     //打开定时器开关
 23 }
 24 /*--------------------------------------------
 25 USAR初始函数及发送一个字符
 26 ---------------------------------------------*/
 27 void USRT_init()
 28 {
 29     TMOD|=0x20;            //设置T1定时器工作方式2
 30     TH1=0xfd;                //T1定时器装初值
 31     TL1=0xfd;
 32     TR1=1;                //启动T1定时器
 33     SM0=0;                //设定串口工作方式
 34     SM1=1;
 35     EA=1;                //开总中断
 36 }
 37 void send(char a)
 38 {
 39     SBUF=a;
 40     while(!TI);        //每次等待发送完毕,再执行下一条
 41     TI=0;            //手动清0
 42 }
 43 /*------------------------------------------------
 44 定时器初始化子程序[用于计算超声波发送到收到的时间间隔]
 45 外部中断P32用于接收超声波接收低电平
 46 ------------------------------------------------*/
 47 void InterruptInit(void)
 48 {
 49     TMOD|=0x01;//T0计数,方式1
 50     TH0=0;//计数初值
 51     TL0=0;
 52     IT0=1;//INT0负脉冲触发
 53     EA=1;//开总中断
 54     EX0=1;//开外部中断INT0
 55 }
 56 /*------------------------------------------------
 57 主程序
 58 ------------------------------------------------*/
 59 main()
 60 {
 61     USRT_init();//初始化串口
 62     InterruptInit();//初始化
 63     while(1)
 64     {    
 65         P10=P11=0;
 66         TR0=1;//T0开始计数
 67         times=0;
 68         TIM2Inital();
 69         while(times<200);
 70         ET2=0;//关闭定时器中断
 71     }
 72 }
 73 /*------------------------------------------------
 74 INTO中断服务程序
 75 ------------------------------------------------*/
 76 void intersvro(void) interrupt 0 using 1
 77 {
 78       unsigned long COUNT; 
 79       unsigned long num;
 80     TR0=0; //停止计数
 81     COUNT=TH0*256+TL0;
 82     num=(344*COUNT)/10000;
 83     if(num==0)goto A;
 84     bwei=(char)('0'+num%1000/100);//取百位
 85       shwei=(char)('0'+num%100/10);//取十位
 86     gwei=(char)('0'+num%10);//取个位
 87     send(bwei);
 88     send(shwei);
 89     send(gwei);
 90     send(0x0d);
 91     send(0x0a);
 92 A:    TH0=0; 
 93       TL0=0;
 94     times=1000;
 95 }
 96 /*------------------------------------------------
 97 定时器中断子程序
 98 ------------------------------------------------*/
 99 void TIM2(void) interrupt 5 using 1//定时器2中断
100 {   
101     TF2=0;
102     P10=~P10;
103     P11=~P11;
104     times++;
105 }

上面的代码将数据发送给上位机的过程放大中断中去处理不是太好,于是就将串口数据发送改到main函数中了,优化后的代码如下:


  1 /*-----------------------------------------------
  2 名称:定时器2
  3 ------------------------------------------------*/
  4 #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
  5 #include<intrins.h>
  6 
  7 sbit P10=P1^0;   
  8 sbit P11=P1^1; 
  9 
 10 unsigned char times,flag;//一次发射方波数
 11 char bwei,shwei,gwei;//数据
 12 unsigned long num1,num2;
 13 
 14 /*------------------------------------------------
 15 定时器初始化子程序[定时器2,16位自动装值,用于产生40KHz方波]
 16 ------------------------------------------------*/
 17 void TIM2Inital(void)
 18 {
 19     RCAP2H = (65536-12)/256;//晶振11.0592M 12us 16bit 自动重载(最好是用12M但是要实现时间同步,那边需要串口,所以这里就勉强采用11.0952)
 20     RCAP2L = (65536-12)%256;
 21     ET2=1;                     //打开定时器中断
 22     EA=1;                      //打开总中断
 23     TR2=1;                     //打开定时器开关
 24 }
 25 /*--------------------------------------------
 26 USAR初始函数及发送一个字符
 27 ---------------------------------------------*/
 28 void USRT_init()
 29 {
 30     TMOD|=0x20;            //设置T1定时器工作方式2
 31     TH1=0xfd;                //T1定时器装初值
 32     TL1=0xfd;
 33     TR1=1;                //启动T1定时器
 34     SM0=0;                //设定串口工作方式
 35     SM1=1;
 36     EA=1;                //开总中断
 37 }
 38 void send(char a)
 39 {
 40     SBUF=a;
 41     while(!TI);        //每次等待发送完毕,再执行下一条
 42     TI=0;            //手动清0
 43 }
 44 /*------------------------------------------------
 45 定时器初始化子程序[用于计算超声波发送到收到的时间间隔]
 46 外部中断P32用于接收超声波接收低电平
 47 ------------------------------------------------*/
 48 void InterruptInit(void)
 49 {
 50     TMOD|=0x01;//T0计数,方式1
 51     TH0=0;//计数初值
 52     TL0=0;
 53     IT0=1;//INT0负脉冲触发
 54     EA=1;//开总中断
 55     EX0=1;//开外部中断INT0
 56 }
 57 /*------------------------------------------------
 58 主程序
 59 ------------------------------------------------*/
 60 main()
 61 {
 62     USRT_init();//初始化串口
 63     InterruptInit();//初始化
 64     while(1)
 65     {    
 66         P10=P11=0;
 67         flag=0;
 68         TR0=1;//T0开始计数
 69         times=0;
 70         TIM2Inital();
 71         while(times<200);
 72         ET2=0;//关闭定时器中断
 73         if(flag){
 74             bwei=(char)('0'+num1%1000/100);//取百位
 75               shwei=(char)('0'+num1%100/10);//取十位
 76             gwei=(char)('0'+num1%10);//取个位
 77             send(bwei);
 78             send(shwei);
 79             send(gwei);
 80             send(0x0d);
 81             send(0x0a);
 82         }
 83         EX0=1;
 84     }
 85 }
 86 /*------------------------------------------------
 87 INTO中断服务程序
 88 ------------------------------------------------*/
 89 void intersvro(void) interrupt 0 using 1
 90 {
 91       unsigned long COUNT; 
 92     TR0=0; //停止计数
 93     COUNT=TH0*256+TL0;
 94     num1=(344*COUNT)/10000;
 95     if(num1==0)return;
 96     flag=1;
 97     TH0=0; 
 98       TL0=0;
 99     times=1000;
100     EX0=0;
101 }
102 /*------------------------------------------------
103 定时器中断子程序
104 ------------------------------------------------*/
105 void TIM2(void) interrupt 5 using 1//定时器2中断
106 {   
107     TF2=0;
108     P10=~P10;
109     P11=~P11;
110     times++;
111 }

但是仅仅有一个接收模块肯定不是我们的最终目标,于是再向前迈一步,这次加入一个接收模块,于是把单片机剩下的那个外部中断也给用上了,程序大致和上面的很像~但是这里出现个问题:因为定时器就一个,如果用这一个定时器去计算两个接收模块接收时间的话,看似可以(你可能会想到用一个秒表给多人计时),但是超声波不是人!一方面,当其中一个接收模块触发中断时并进行相应的处理会影响计时器;另一方面,如果强制想实现这个计时过程要添加很多标志和判断处理,这样很不明智!下面是初步尝试时的程序,他只能测出首先接受到超声波信号的接收头的数据,另一个会被自动放弃掉~


  1 /*-----------------------------------------------
  2 名称:定时器2
  3 ------------------------------------------------*/
  4 #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
  5 #include<intrins.h>
  6 
  7 sbit P10=P1^0;   
  8 sbit P11=P1^1; 
  9 
 10 unsigned char times,flag;//一次发射方波数
 11 char bwei,shwei,gwei;//数据
 12 unsigned long num1,num2;
 13 
 14 /*------------------------------------------------
 15 定时器初始化子程序[定时器2,16位自动装值,用于产生40KHz方波]
 16 ------------------------------------------------*/
 17 void TIM2Inital(void)
 18 {
 19     RCAP2H = (65536-12)/256;//晶振11.0592M 12us 16bit 自动重载(最好是用12M但是要实现时间同步,那边需要串口,所以这里就勉强采用11.0952)
 20     RCAP2L = (65536-12)%256;
 21     ET2=1;                     //打开定时器中断
 22     EA=1;                      //打开总中断
 23     TR2=1;                     //打开定时器开关
 24 }
 25 /*--------------------------------------------
 26 USAR初始函数及发送一个字符
 27 ---------------------------------------------*/
 28 void USRT_init()
 29 {
 30     TMOD|=0x20;            //设置T1定时器工作方式2
 31     TH1=0xfd;                //T1定时器装初值
 32     TL1=0xfd;
 33     TR1=1;                //启动T1定时器
 34     SM0=0;                //设定串口工作方式
 35     SM1=1;
 36     EA=1;                //开总中断
 37 }
 38 void send(char a)
 39 {
 40     SBUF=a;
 41     while(!TI);        //每次等待发送完毕,再执行下一条
 42     TI=0;            //手动清0
 43 }
 44 /*------------------------------------------------
 45 定时器初始化子程序[用于计算超声波发送到收到的时间间隔]
 46 外部中断P32用于接收超声波接收低电平
 47 ------------------------------------------------*/
 48 void InterruptInit(void)
 49 {
 50     TMOD|=0x01;//T0计数,方式1
 51     TH0=0;//计数初值
 52     TL0=0;
 53     IT0=1;//INT0负脉冲触发
 54     IT1=1;
 55     EA=1;//开总中断
 56     EX0=1;//开外部中断INT0
 57     EX1=1;
 58 }
 59 /*------------------------------------------------
 60 主程序
 61 ------------------------------------------------*/
 62 main()
 63 {
 64     USRT_init();//初始化串口
 65     InterruptInit();//初始化
 66     while(1)
 67     {    
 68         P10=P11=0;
 69         flag=0;
 70         TR0=1;//T0开始计数
 71         times=0;
 72         TIM2Inital();
 73         while(times<200);
 74         ET2=0;//关闭定时器中断
 75         if(flag==1){
 76             num1-=5;//发现所测结果比真实大5左右
 77             bwei=(char)('0'+num1%1000/100);//取百位
 78               shwei=(char)('0'+num1%100/10);//取十位
 79             gwei=(char)('0'+num1%10);//取个位
 80             send('x');
 81             send(':');
 82             send(bwei);
 83             send(shwei);
 84             send(gwei);
 85             send(0x0d);
 86             send(0x0a);
 87         }else if(flag==2){
 88             num2-=5;//发现所测结果比真实大5左右
 89             bwei=(char)('0'+num2%1000/100);//取百位
 90               shwei=(char)('0'+num2%100/10);//取十位
 91             gwei=(char)('0'+num2%10);//取个位
 92             send('y');
 93             send(':');
 94             send(bwei);
 95             send(shwei);
 96             send(gwei);
 97             send(0x0d);
 98             send(0x0a);
 99         }
100         EX0=1;
101         EX1=1;
102     }
103 }
104 /*------------------------------------------------
105 INTO中断服务程序
106 ------------------------------------------------*/
107 void intersvro0(void) interrupt 0
108 {
109       unsigned long COUNT; 
110     TR0=0; //停止计数
111     COUNT=TH0*256+TL0;
112     num1=(344*COUNT)/10000;
113     if(num1==0)return;
114     flag=1;
115     TH0=0; 
116       TL0=0;
117     times=1000;
118     EX0=0;
119 }
120 /*------------------------------------------------
121 INT1中断服务程序
122 ------------------------------------------------*/
123 void intersvro1(void) interrupt 2
124 {
125       unsigned long COUNT; 
126     TR0=0; //停止计数
127     COUNT=TH0*256+TL0;
128     num2=(344*COUNT)/10000;
129     if(num2==0)return;
130     flag=2;
131     TH0=0; 
132       TL0=0;
133     times=1000;
134     EX1=0;
135 }
136 /*------------------------------------------------
137 定时器中断子程序
138 ------------------------------------------------*/
139 void TIM2(void) interrupt 5 using 1//定时器2中断
140 {   
141     TF2=0;
142     P10=~P10;
143     P11=~P11;
144     times++;
145 }

本来我以为那种掐表模式的多接收模块计时模型肯定能实现,于是浪费了很长时间,把代码改的很乱,最后还是不能完美的完成目标~就在我吃饭的路上突然受分时操作系统的影响产生了一个新的灵感:由于测量频率很快,我可以把一个测量周期分为两部分,一部分用于只用接收模块1进行测距,另一部分只用接收模块2进行测量,这样一个测量周期就能完美的测量出两个接收模块的测距数据!代码如下:

  1 /*-----------------------------------------------
  2 超声波接收一个接P32一个接P33中断INT0和INT1
  3 ------------------------------------------------*/
  4 #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
  5 #include<intrins.h>
  6 
  7 sbit P10=P1^0;   
  8 sbit P11=P1^1; 
  9 
 10 unsigned char times,flag;//一次发射方波数
 11 char bwei,shwei,gwei;//数据
 12 unsigned long num1,num2;
 13 
 14 /*------------------------------------------------
 15 定时器初始化子程序[定时器2,16位自动装值,用于产生40KHz方波]
 16 ------------------------------------------------*/
 17 void TIM2Inital(void)
 18 {
 19     RCAP2H = (65536-12)/256;//晶振11.0592M 12us 16bit 自动重载(最好是用12M但是要实现时间同步,那边需要串口,所以这里就勉强采用11.0952)
 20     RCAP2L = (65536-12)%256;
 21     ET2=1;                     //打开定时器中断
 22     EA=1;                      //打开总中断
 23     TR2=1;                     //打开定时器开关
 24 }
 25 /*--------------------------------------------
 26 USAR初始函数及发送一个字符
 27 ---------------------------------------------*/
 28 void USRT_init()
 29 {
 30     TMOD|=0x20;            //设置T1定时器工作方式2
 31     TH1=0xfd;                //T1定时器装初值
 32     TL1=0xfd;
 33     TR1=1;                //启动T1定时器
 34     SM0=0;                //设定串口工作方式
 35     SM1=1;
 36     EA=1;                //开总中断
 37 }
 38 void send(char a)
 39 {
 40     SBUF=a;
 41     while(!TI);        //每次等待发送完毕,再执行下一条
 42     TI=0;            //手动清0
 43 }
 44 /*------------------------------------------------
 45 定时器初始化子程序[用于计算超声波发送到收到的时间间隔]
 46 外部中断P32用于接收超声波接收低电平
 47 ------------------------------------------------*/
 48 void InterruptInit(void)
 49 {
 50     TMOD|=0x01;//T0计数,方式1
 51     TH0=0;//计数初值
 52     TL0=0;
 53     IT0=1;//INT0负脉冲触发
 54     IT1=1;
 55     EA=1;//开总中断
 56 //    EX0=1;//开外部中断INT0
 57 //    EX1=1;
 58 }
 59 /*------------------------------------------------
 60 主程序
 61 ------------------------------------------------*/
 62 main()
 63 {
 64     bit ok=0;
 65     USRT_init();//初始化串口
 66     InterruptInit();//初始化
 67     while(1)
 68     {
 69         if(ok==0){
 70             EX0=1;
 71 
 72             P10=P11=0;
 73             flag=0;
 74             TR0=1;//T0开始计数
 75             times=0;
 76             TIM2Inital();
 77             while(times<200);
 78             ET2=0;//关闭定时器中断
 79             
 80             if(flag==1){
 81                 num1-=5;//发现所测结果比真实大5左右
 82                 bwei=(char)('0'+num1%1000/100);//取百位
 83                   shwei=(char)('0'+num1%100/10);//取十位
 84                 gwei=(char)('0'+num1%10);//取个位
 85                 send('x');
 86                 send(':');
 87                 send(bwei);
 88                 send(shwei);
 89                 send(gwei);
 90                 send(0x0d);
 91                 send(0x0a);
 92                 ok=1;
 93             }
 94         }else{
 95             EX1=1;
 96 
 97             P10=P11=0;
 98             flag=0;
 99             TR0=1;//T0开始计数
100             times=0;
101             TIM2Inital();
102             while(times<200);
103             ET2=0;//关闭定时器中断
104 
105             if(flag==2){
106                 num2-=5;//发现所测结果比真实大5左右
107                 bwei=(char)('0'+num2%1000/100);//取百位
108                   shwei=(char)('0'+num2%100/10);//取十位
109                 gwei=(char)('0'+num2%10);//取个位
110                 send('y');
111                 send(':');
112                 send(bwei);
113                 send(shwei);
114                 send(gwei);
115                 send(0x0d);
116                 send(0x0a);
117                 ok=0;
118             }
119         }
120     }
121 }
122 /*------------------------------------------------
123 INTO中断服务程序
124 ------------------------------------------------*/
125 void intersvro0(void) interrupt 0
126 {
127       unsigned long COUNT; 
128     TR0=0; //停止计数
129     COUNT=TH0*256+TL0;
130     num1=(344*COUNT)/10000;
131     if(num1==0)return;
132     flag=1;
133     TH0=0; 
134       TL0=0;
135     times=1000;
136     EX0=0;
137 }
138 /*------------------------------------------------
139 INT1中断服务程序
140 ------------------------------------------------*/
141 void intersvro1(void) interrupt 2
142 {
143       unsigned long COUNT; 
144     TR0=0; //停止计数
145     COUNT=TH0*256+TL0;
146     num2=(344*COUNT)/10000;
147     if(num2==0)return;
148     flag=2;
149     TH0=0; 
150       TL0=0;
151     times=1000;
152     EX1=0;
153 }
154 /*------------------------------------------------
155 定时器中断子程序
156 ------------------------------------------------*/
157 void TIM2(void) interrupt 5 using 1//定时器2中断
158 {   
159     TF2=0;
160     P10=~P10;
161     P11=~P11;
162     times++;
163 }

 

2.3、命中注定——分手的时候到了

>_<' 上面我们已经实现了在一个MCU上实现了一个发射模块两个接收模块分别测距,但是将发射和接收放在同一个模块上是很不理想的一种折衷,那么现在是时候来一个高超的手术了!

>_<' 通过分析上面集成在一个MCU上的代码可以知道:发送部分仅占用40KHz发送函数用于适时的发送一段连续的超声波;接收部分负责计时、等待接收模块的1-0-1中断、计算距离、将数据发送给上位机等功能。其中有一个核心而隐蔽的问题,即:时间同步问题!当在一个MCU上时,我们只要简单的启动计时器同时启动超声波发送,而在两个MCU上实现这个并不是件容易的事!最终我想到了用一根线来同步时间,即:当接收模块准备好时,把该线上的电平置1-0产生下降沿(P20引脚),然后把该线的另一端连接将发送模块的外部中断0的引脚(P32引脚),采用中断方式监听起跑命令。当发送模块收到起跑命令时便发送一串超声波,同时接收模块开始计数,剩下的部分就和在同一个MCU上的原理很相似了~具体请参考代码(注意接收部分在发送起跑命令后的延时技巧!这是一种可以被中断剪短的延时,用来等待超声波接收模块接收有效的数据)


 1 /*-----------------------------------------------
 2 
 3 ------------------------------------------------*/
 4 #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
 5 #include<intrins.h>
 6 
 7 sbit P10=P1^0;   
 8 sbit P11=P1^1; 
 9 
10 unsigned char times,flag;//一次发射方波数
11 
12 /*------------------------------------------------
13 定时器初始化子程序[定时器2,16位自动装值,用于产生40KHz方波]
14 ------------------------------------------------*/
15 void TIM2Inital(void)
16 {
17     RCAP2H = (65536-12)/256;//晶振11.0592M 12us 16bit 自动重载(最好是用12M但是要实现时间同步,那边需要串口,所以这里就勉强采用11.0952)
18     RCAP2L = (65536-12)%256;
19     ET2=1;                     //打开定时器中断
20     EA=1;                      //打开总中断
21     TR2=1;                     //打开定时器开关
22 }
23 /*------------------------------------------------
24 用于接收接收端发送过来的发送超声波的命令
25 ------------------------------------------------*/
26 void InterruptInit(void)
27 {
28     TMOD|=0x01;//T0计数,方式1
29     TH0=0;//计数初值
30     TL0=0;
31     IT0=1;//INT0负脉冲触发
32     EA=1;//开总中断
33     EX0=1;//开外部中断INT0
34 }
35 /*------------------------------------------------
36 主程序
37 ------------------------------------------------*/
38 main()
39 {
40     InterruptInit();//初始化
41     while(1)
42     {
43         flag=0;
44         while(!flag);
45         EX0=0;//关闭中断
46         times=0;
47         TIM2Inital();
48         while(times<80);
49         ET2=0;//关闭定时器中断
50         EX0=1;//开中断    
51     }
52 }
53 /*------------------------------------------------
54 INTO中断服务程序
55 ------------------------------------------------*/
56 void intersvro0(void) interrupt 0
57 {
58       flag=1;
59 }
60 /*------------------------------------------------
61 定时器中断子程序
62 ------------------------------------------------*/
63 void TIM2(void) interrupt 5 using 1//定时器2中断
64 {   
65     TF2=0;
66     P10=~P10;
67     P11=~P11;
68     times++;
69 }
发送代码
  1 /*-----------------------------------------------
  2 超声波接收一个接P32一个接P33中断INT0和INT1
  3 ------------------------------------------------*/
  4 #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
  5 #include<intrins.h>
  6 
  7 
  8 sbit P20=P2^0;//通知发送的发送超声波(由高电平变低电平)
  9 unsigned char flag;
 10 char bwei,shwei,gwei;//数据
 11 unsigned long num1,num2,times;
 12 
 13 /*--------------------------------------------
 14 延时函数
 15 ---------------------------------------------*/
 16 void delays()
 17 {
 18     times=0;
 19     while(times<1000)times++;
 20 }
 21 
 22 /*--------------------------------------------
 23 USAR初始函数及发送一个字符
 24 ---------------------------------------------*/
 25 void USRT_init()
 26 {
 27     TMOD|=0x20;            //设置T1定时器工作方式2
 28     TH1=0xfd;                //T1定时器装初值
 29     TL1=0xfd;
 30     TR1=1;                //启动T1定时器
 31     SM0=0;                //设定串口工作方式
 32     SM1=1;
 33     EA=1;                //开总中断
 34 }
 35 void send(char a)
 36 {
 37     SBUF=a;
 38     while(!TI);        //每次等待发送完毕,再执行下一条
 39     TI=0;            //手动清0
 40 }
 41 /*------------------------------------------------
 42 定时器初始化子程序[用于计算超声波发送到收到的时间间隔]
 43 外部中断P32用于接收超声波接收低电平
 44 ------------------------------------------------*/
 45 void InterruptInit(void)
 46 {
 47     TMOD|=0x01;//T0计数,方式1
 48     TH0=0;//计数初值
 49     TL0=0;
 50     IT0=1;//INT0负脉冲触发
 51     IT1=1;
 52     EA=1;//开总中断
 53 //    EX0=1;//开外部中断INT0
 54 //    EX1=1;
 55 }
 56 /*------------------------------------------------
 57 主程序
 58 ------------------------------------------------*/
 59 main()
 60 {
 61     bit ok=0;
 62     USRT_init();//初始化串口
 63     InterruptInit();//初始化
 64     while(1)
 65     {
 66         P20=1;
 67         if(ok==0){
 68             EX0=1;//开外部INT0中断
 69             flag=0;
 70             P20=0;//P20由高变低产生一个下降沿来触发发送模块进行发送超声波
 71             TR0=1;//T0开始计数
 72             delays();
 73             if(flag==1){
 74                 num1-=5;//发现所测结果比真实大5左右
 75                 bwei=(char)('0'+num1%1000/100);//取百位
 76                   shwei=(char)('0'+num1%100/10);//取十位
 77                 gwei=(char)('0'+num1%10);//取个位
 78                 send('x');
 79                 send(':');
 80                 send(bwei);
 81                 send(shwei);
 82                 send(gwei);
 83                 send(0x0d);
 84                 send(0x0a);
 85                 ok=1;
 86             }
 87         }else{
 88             EX1=1;//开外部中断INT1
 89             flag=0;
 90             P20=0;//P20由高变低产生一个下降沿来触发发送模块进行发送超声波
 91             TR0=1;//T0开始计数
 92             delays();
 93             if(flag==2){
 94                 num2-=5;//发现所测结果比真实大5左右
 95                 bwei=(char)('0'+num2%1000/100);//取百位
 96                   shwei=(char)('0'+num2%100/10);//取十位
 97                 gwei=(char)('0'+num2%10);//取个位
 98                 send('y');
 99                 send(':');
100                 send(bwei);
101                 send(shwei);
102                 send(gwei);
103                 send(0x0d);
104                 send(0x0a);
105                 ok=0;
106             }
107         }
108     }
109 }
110 /*------------------------------------------------
111 INTO中断服务程序
112 ------------------------------------------------*/
113 void intersvro0(void) interrupt 0
114 {
115       unsigned long COUNT; 
116     TR0=0; //停止计数
117     COUNT=TH0*256+TL0;
118     num1=(344*COUNT)/10000;
119     if(num1==0)return;
120     flag=1;
121     TH0=0; 
122       TL0=0;
123     times=10000;
124     EX0=0;
125 }
126 /*------------------------------------------------
127 INT1中断服务程序
128 ------------------------------------------------*/
129 void intersvro1(void) interrupt 2
130 {
131       unsigned long COUNT; 
132     TR0=0; //停止计数
133     COUNT=TH0*256+TL0;
134     num2=(344*COUNT)/10000;
135     if(num2==0)return;
136     flag=2;
137     TH0=0; 
138       TL0=0;
139     times=10000;
140     EX1=0;
141 }
接收代码

  

三、阶段小结

>_<' 经过这三个阶段,我们已经从分析需求->调研市场->研究资料->购买原料->动手实践->模数结合->软硬兼修->调试修改->优化....最终完成了我们的超声波测距的硬件模块(鼓掌)!然后下一阶段将进入PC上基于C#的测距客户端软件的开发,更多有趣小制作敬请关注~

 

Tag标签: 超声波   模块   笔记  
  • 专题推荐

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规