基于FPGA的超声波测距系统设计
首先介绍超声波测量距离的原理,其次介绍超声波测距模块。超声波测距的基本原理可以这样认为,我们知道声音在空气里的传播速度大约是0.34千米/秒。超声波在空气中的传播速度大概也是0.34千米/秒,而这个速度受外界环境变化特别的小;况且超声波的波长相对于空气中的悬浮颗粒直径较大,因而可以有效的绕射过空气中的悬浮颗粒等直径比较小的障碍物,所以可以认为它在空气中的传播速度基本可以维持在0.34千米/秒。基于这个基本不变的速度,只需测出其发出去以及反射回来的时间差,然后在通过一定的计算,就可以得到发射端到目标物体的距离了。
由以上的超声波测量距离的原理图就可以知道距离S可以被很简洁的表达出来了。可以把声速表示成V,超声波从发射到接收的时间差为T,这样距离就可以被表达成S=V*T/2。图3.2中的方法可称为间接测距法。还有一种直接测距的方法,直接测距就是将测距系统的发射端口和接收端口对准。发射的超声波直接进入接收端,这样就可以直接计算出其测量的距离了。这种测距系统并不仅仅局限于超声波测距系统,在一些激光测距、红外测距等系统方面也有着一定价值的应用。
图3.2 超声波测量距离的原理图
在本设计中利用超声波在空气中的传播原理,按照S=V*T/2的关系计算出1毫米的时钟高电平个数。利用距离单位的逐项叠加,逐个进位的方式计算出其距离。
图3.3 超声波模块
基于FPGA的超声波测距系统利用的超声波模块型号为HC_SR04,它实际上采用的是给模块上的trig端口一个触发电平,让其触发超声波的发出。
首先,在使用该模块之前,对该超声波模块进行了简单的端口测试。利用电压源给该模块接上5V的电压。利用信号发生器产生一个10us的高电平脉冲信号,将此信号加到trig接口上,这样就完成了对该超声波模块的触发。然后将示波器探头接在超声波模块的echo端口上,观察示波器会显示以下信号:
图3.4 示波器观测图
图3.5 信号源信号图
由图3.5中的信号源可以看出,设置占空比为20%,脉冲电压为5V,周期为50us。这样信号源就可以产生一个10us的连续脉冲,将此脉冲信号送给trig端口,然后在超声波模块上的echo端口上加上示波器来测量echo上的信号变化。图3.4所示,可以清楚的看到echo信号也呈现周期性变化,其原因就是给trig输入的信号成周期性变化,每次经过10us的连续高电平,超声波就会发出一个调制的脉冲信号,该调制信号返回后,echo端的信号就会由高电平瞬间变回低电平了,所以echo信号的周期性变化是由trig信号的周期性变化引起的。还有一个值得注意的问题,观察示波器的峰峰值可以看出echo反馈的高电平和给超声波模块的VCC端口所输入的电压信号的高电平值是一样大的,因此就要考虑FPGA开发板的I/O接口所承受的最大耐压值;比如像Xilinx的Basys系列的开发板I/O接口电压承受值为3.3V,这种情况就要给echo接口加一个分压电阻。以免返回电压过大而对芯片造成一定程度的损伤。而ALTERA系列开发板的I/O接口一般情况可以承受5V的电压值,因此可以直接将I/O接口与echo端口和trig端口接在一起。
超声波测距系统程序设计及仿真
下图给出了系统软件设计的整体框架,实际上由于FPGA内的各个子进程都是并行执行的,且每个子进程都是是独立的模块,因此程序设计并不是按照顺序的方法设计的,而是分离成各个小的子模块进行设计综合的,需要严格控制时序。
超声波收发部分负责产生超声波驱动信号,要求频率为50MHz,占空比为50的方波信号以驱动超声波换能器,同时高速计数器开始计数,检测回波后,计数器停止计数,计算后控制显示输出。
图4.1 系统软件设计框图
主控制模块代码:
- <font style="font-size: 12pt">module temper(
- input CLOCK_50, // 板载时钟50MHz
- input Q_KEY, // 板载按键RST
- input clk_1_sec,
- input echo, //回响信号输出
- output reg trig, //超声波模块触发信号输入
- output [15:0]codeshow
- );
- //-------------------------------------
- //reg trig;
- reg [11:0]cnt; //计数器,计250个50ns,12.5us,为echo提供时序
- reg [13:0] cnt1; //计数器,计8000个12.5us,100ms
- reg [20:0]cnt3; //计数124个时钟周期是1mm
- reg [19:0] dis ;
- reg
- echo_buf, //echo的上一个时钟的状态
- echo_rising, //捕捉echo的上升沿
- echo_falling, //捕捉echo的下降沿
- flag1; //echo为高电平时flag1会是1,低电平时会是0
- reg[3:0]beed1;
- reg[3:0]beed2;
- reg[3:0]beed3;
- reg[3:0]beed4;
- assign codeshow={beed4,beed3,beed2,beed1};
- //产生echo控制信号250*8000*50ns,echo高电平持续12.5us,即250个时钟周期
- [email protected](posedge CLOCK_50)
- begin
- if(cnt == 2)//599
- cnt <= 1'b0;
- else
- cnt <= cnt +1;
- end
- [email protected](posedge CLOCK_50)
- begin
- if(cnt == 2)//599
- if(cnt1 == 20)//9999
- begin
- trig <= 1'b1;
- cnt1 <= 1'b0;
- end
- else
- begin
- trig <= 1'b0;
- cnt1 <= cnt1 + 1'b1;
- end
- end
- //捕捉trig上升沿与下降沿,并产生标志位
- [email protected](posedge CLOCK_50, negedge Q_KEY)
- begin
- if (!Q_KEY) echo_buf <= 1'b0;
- else
- begin
- echo_buf <= echo;
- echo_rising <= echo & (~echo_buf);
- echo_falling <= (~echo) & echo_buf;
- end
- end
- [email protected](posedge CLOCK_50)
- begin
- if(echo_rising == 1'b1)
- begin
- flag1 = 1'b1; //echo已经变为高电平
- end
- else if(echo_falling == 1'b1)
- begin
- flag1 = 1'b0; //echo已经变为低电平
- end
- end
- //在flag1 = 1 期间计时,即对时钟进行计数。(4m的距离会是25ms,1mm是125个时钟周期)
- [email protected](posedge CLOCK_50, negedge Q_KEY)
- begin
- if (!Q_KEY) dis<= 19'b0;
- else
- begin
- if(flag1 == 1'b1)
- begin
- if(cnt3 == 25) // 700000 //1mm
- cnt3 <= 1'b0;
- else
- cnt3 <= cnt3 + 1'b1;
- end
- else if(echo_rising == 1'b1)
- begin
- dis <=cnt3/1; //dis <=cnt3/290;
- cnt3 <= 1'b0;
- end
- end
- end
- always @ (posedge clk_1_sec, negedge Q_KEY)
- begin
- if (!Q_KEY)
- Begin
- beed <= 1'b0
- beed1 <= 4'b0;
- beed2 <= 4'b0;
- beed3 <= 4'b0;
- beed4 <= 4'b0;
- end
- else begin
- beed1 <=dis/1000;
- beed2 <=dis%1000/100;
- beed3 <=dis%100/10;
- beed4 <=dis%10;
- if(dis>16)begin beed<= 1'b0; end
- else begin beed<=1'b1; end
- end
- end
-
endmodule</font>
通过主控制程序,可以实现超声波测距以及当测量距离小于一定长度时,蜂鸣器报警,报警距离可以通过改代码,实现想要的报警的距离。下面是通过波形仿真的结果。如图所示:
图4.2 测距并报警
分频模块代码:
- <font style="font-size: 12pt">module div_50M(
- input clk,//50
- input rst_n,//
- output o_clk,//1HZ
- output o_clk1hz//1KHZ
- );
- // 分频模块开始
- reg [25:0] cnt; // 计数子
- reg [25:0] cntt; // 计数子
- reg regclk1hz;
- reg rego_clk;
- assign o_clk1hz=regclk1hz;
- assign o_clk=rego_clk;
- always @ (posedge clk, negedge rst_n)
- if (!rst_n)
- cntt <= 0;
- else
- if (cntt == 2)//cnt == 24_999_)//50M/1000=X,X/2-1=24999
- begin
- cntt <= 0;
- regclk1hz=(~regclk1hz);//1KHZ
- end
- else
- cntt <= cntt + 1'b1;
- always @ (posedge clk, negedge rst_n)
- if (!rst_n)
- cnt <= 0;
- else
- if (cnt == 1)//cnt == 24_999_999)
- begin
- cnt <= 0;
- rego_clk=(~rego_clk);//1KHZ
- end
- else
- cnt <= cnt + 1'b1;
- // 分频模块结束</font>
Endmodule
上述代码中输出的1KHZ时钟,在之后进行译码器扫描。分频模块仿真如下图示:
图4.3 分频模块仿真
译码器模块代码:
- <font style="font-size: 12pt">module yimaqi(clkshow ,codeshow,duan,wei);
- input clkshow;//1K的扫描时钟输入
- input [15:0]codeshow;//4个数码管的显示数据输入,共16位,每4位一个密码
- output [7:0]duan;//数据段选输出
- output [3:0]wei;//动态扫描的位选输出
- reg [3:0]dig1;
- reg [3:0]SEGAA;
- reg [3:0]countt;
- reg [7:0]LED;
- always @(posedge clkshow )
- begin
- countt=countt+1'b1;
- if(countt>3)begin countt=0;end
- case (countt)
- 0: SEGAA<=codeshow[3 : 0];
- 1: SEGAA<=codeshow[7 :4];
- 2: SEGAA<=codeshow[11 : 8];
- 3: SEGAA<=codeshow[15 : 12];
- default: countt=0;
- endcase
- case (countt)//动态扫描的位选输出
- 0:dig1=4'b0001 ;
- 1:dig1=4'b0010 ;
- 2:dig1=4'b0100 ;
- 3:dig1=4'b1000 ;
- default: countt=0;
- endcase
- end
- assign wei=dig1;
- always @(SEGAA )//数据段选输出
- begin
- case (SEGAA) //hgfedcba
- 4'h0:LED=8'b11000000;//--0
- 4'h1:LED=8'b11111001;//--1
- 4'h2:LED=8'b10100100;//--2
- 4'h3:LED=8'b10110000;//--3
- 4'h4:LED=8'b10011001;//--4
- 4'h5:LED=8'b10010010;//--5
- 4'h6:LED=8'b10000010;//--6
- 4'h7:LED=8'b11111000;//--7
- 4'h8:LED=8'b10000000;//--8
- 4'h9:LED=8'b10010000;//--9
- 4'ha:LED=8'b10001000;//--a
- 4'hb:LED=8'b10000011;//--b
- 4'hc:LED=8'b11000110;//--c
- 4'hd:LED=8'b10100001;//--d
- 4'he:LED=8'b10000110;//--e
- 4'hf:LED=8'b11111111;//--off
- endcase
- end
- assign duan=(dig1==4'b0001)?(LED&8'b01111111):LED;
- Endmodule</font>
译码器模块,通过扫描检测到的1KHZ时钟输入,把测量到的距离显示到数码管上,译码器模块仿真如下图所示:
图4.4 数码管显示仿真
顶层原理图
顶层原理图主要分为三个模块,分别为分频模块,主控制模块,译码器模块。还有超声波模块接口,以及数码管模块接口。如下图所示:
图4.5 原理图