基于FPGA的超声波测距电路.docx
《基于FPGA的超声波测距电路.docx》由会员分享,可在线阅读,更多相关《基于FPGA的超声波测距电路.docx(22页珍藏版)》请在冰豆网上搜索。
![基于FPGA的超声波测距电路.docx](https://file1.bdocx.com/fileroot1/2023-2/9/a41f5e5f-f0d7-4901-974c-332665e0c39f/a41f5e5f-f0d7-4901-974c-332665e0c39f1.gif)
基于FPGA的超声波测距电路
可编程逻辑器件及EDA技术课程设计
——基于FPGA的超声波测距电路和其扩展应用
摘要
本课设使用FPGA和其他模块为硬件基础,以VHDL为硬件描述语言进行超声波测距电路的搭建,完成数码管精度为2厘米的实时显示,并且在基本电路的基础上使用蜂鸣器扩展出“倒车雷达”功能,在距离不同的区间内蜂鸣器会响出不同的频率的声音,总体是距离越小,频率越高。
过程中,进行了需求分析,可行性分析,模块建构和调试下载等过程,从中学到很多工程实践过程中的知识。
关键字:
FPGA;QuartusII;超声波测距;倒车雷达;VHDL
一、需求分析:
本次课程设计要求用PFGA和所学VHDL知识进行设计具有某种具体功能的电路系统。
我本次课设选题为:
基于FPGA的超声波测距电路和其扩展应用。
用以实践在课堂上所学知识内容,加深理解。
超声波测距是一般的电路系统,在生活中有很多应用,比如倒车雷达和扫地机器人自动避障等。
此功能在单片机上实现更为方便,成本也更低。
但此次课设使用FPGA用硬件描述语言来构建此电路是对FPGA本身功能和各模块使用的学习和提升。
二、功能描述:
使在数码管上实时显示障碍物与测试模块的距离,要求显示米、分米和厘米位,数码管精度为2厘米,实时显示,并且在基本电路的基础上使用蜂鸣器扩展出“倒车雷达”功能,在距离不同的区间内蜂鸣器会响出不同的频率的声音,总体是距离越小,频率越高。
此功能在单片机上实现更为方便,成本也更低。
但此次课设使用FPGA用硬件描述语言来构建电路是对FPGA本身功能和各模块使用学习的提升。
三、可行性分析
要实现以上所描述的功能,我们可以从三方面进行可行性分析:
1.器件可行性分析
>FPGA
FPGA型号为EP4CE6E22C8N,其Totallogicelements为6272,满足大多数开发要求。
>PERIPHERAL
”特权同学“开发板(SF-CY4),
晶振(25MHZ),拨码开关4个,
共阴极数码管(LG3641AH),
蜂鸣器,按键等。
>超声波测距模块
网上所购得超声波测距标准模块HC-SR04。
感应角度不大于15度;
探测距离2cm-450cm ,高精度可达0.2cm;
2.功能可行性实现
用FPGA产生一满足超声波模块的触发信号,使之开始工作,等待接受回声信号。
回声引脚接受到回声信号后,开始记录整个高电平所持续时间,再根据脉冲周期计算的超声波往返的总时间,用公式S=340*T/2来计算相对距离(340为默认声速,可调)。
再开关选择打开时,蜂鸣器开始工作,根据超声波返回高电平信号记录周期数,设定判断距离标准。
到达某特定标准区间就产生一个特定频率信号给蜂鸣器,使之发出特定频率。
3.数据操作可行性
VHDL语言可以调用的IEEE库,
其中常用的
IEEE.STD_LOGIC_1164
IEEE.STD_LOGIC_ARITH
IEEE.STD_LOGIC_UNSIGNED
包含大多数数学运算需求和
数据的类型转换,在此提出类型转
换资料,方便今后的使用
四、模块化建构
根据功能要求可以分析需要建立的模块如下图:
接下来进行逐步分析构建:
1.分频模块freq
板子提供的晶振为25MHZ,而其他有关时钟的模块,考虑人眼视觉暂留,触发时间和所需精度要求,可以选取时钟频率分别为100KHZ和1KHZ。
采用“计数取反”的方法进行分频。
“计数分频”是指计数N个取反一次逻辑信号会产生周期为2N的频率信号,达到分频目的。
25MHZ为100KHZ的250倍,因而从0到124计数。
25MHZ为1KHZ的25000倍,因而从0到12499计数。
2.触发模块launch
以分频模块提供的100KHZ为时钟,周期为10微秒,其中高电平为5微秒。
已知超声波模块完成触发需要大于10微秒的高电平,所以需要至少两个周期(上升沿触发)。
另外,发射触发后需要等待接收到信号并进行处理再发射下一次触发,才能保证系统实时性。
不然会出现返回信号混叠引起计算错误。
再者,每次发射接收数据所需时间要保证两个方面:
第一,发射周期要保证大于返回高电平持续的两倍;第二,数据刷新速度要适应人眼视觉感官。
第一个方面得不到保证会影响实时性。
第二个方面得不到保证会使人感觉数据变化突兀。
因而,占空比不为50%,且可大致画时序图如下:
器件最大距离为4.5m,计量时钟为100KHZ,则触发周期可以计算为:
其中,
为触发计数上升沿数;
为测距往返最大值的上升沿计数。
(为满足发射和返回信号的实时性,应满足两倍的
)。
其中为满足触发条件,
所以取值为5。
根据器件测量极限为4.5米,所以
综合起来,N最小为5299。
再考虑人眼适应性,经过调试取N为12000。
3.接收模块receive
以分频模块提供的100KHZ为时钟,周期为10微秒。
本模块可以完成对ECHO的高电平持续时间计数。
模块要求高电平时开始计数,为低时把计数结果导出并持续到下一个下降沿的数据更新时,并把计数信号置零,等待重新计数。
注意,此时数据导出最好用UNSIGNED模式,方便后级进行从二进制到整数的转化。
数据宽度根据最大测距距离可确定为12位。
时序图如下:
由上计算,在频率为100KHZ的时钟下,最大距离往返可以计数2647次,化成二进制为:
1010_0101_0111,也即最大计数为12位宽的二进制表示。
4.扫描模块scan
本模块以1KHZ位时钟信号,产生“1110”,“1101”,“1011”,“0111”,四个数码管选择信号,完成数码管扫描的功能(数码管为低选通,当使用高选通时把上面对1和0进行取反)。
也相当于4分频,对每个数码管来说,其频率为1KHZ的四分之一。
接下来传给显示模块。
5.显示模块display
显示模块有两个总线输入,一个是显示选通信号(四位),另一个是数据输入信号(三个四位信号,对应高位,中位,低位)。
显示模块里有两个功能块,第一个是进行从BCD码到数码管的软译功能(区分数码管是共阴极还是共阳极),要注意高位为米位,其小数点位需要点亮。
第二个是完成高中低位和数码管显示的匹配。
高位在2号位,中位在1号位,低位在0号位。
6.计算模块measure
计算模块是完成周期计数数据到米、分米和厘米的BCD码转换功能。
首先,对12位宽度的二进制进行转换,是指变成能进行运算的整数类型。
再者,我们知道超声波测距原理,S=340*T/2,所以可以用周期进行运算,并通过算法分离出三位十进制数分别代表米、分米和厘米位,在对他们进行转换成二进制便于电路传输。
7.功能开关switch
此扩展功能模块,有很大的设计自由度。
设计输入为12位宽度的原始计数,在此先进行整型转换。
再根据所属不同范围产生不同的分频功能,分频结果输出到蜂鸣器。
也可以加入模块的功能控制端口和间隔音效。
五、编程下载
编程:
编程采用模块化设计的方法。
根据功能和所需参数分别设计顶层
和底层文件,通过原件例化联系到一起。
具体模块代码见附录,最终结构化效果
如右;
下载:
使用USBBlaster将生成的.sof文件下载到芯片中,并进行端口配置,注意下载时保证FPGA的正常供电。
六、课设感悟
本次课程设计的最后结果能准确实现最初设计的诸多功能,在实时性和稳定性上也良好表现。
最终实验板能显示米、分米和厘米位,数码管精度为2厘米,实时显示,并且蜂鸣器扩展出的“倒车雷达”功能,能使蜂鸣器在不同距离区间响出不同的频率的声音,总体是距离越小,频率越高。
当然,实验最有价值的东西不止是成功的喜悦,更有在实验过程中调试中不断发现问题解决问题和处理问题的经历,以及总结学习的工程实践的经验,在此作以总结:
1.做FPGA的设计时,一定要先在脑海或设计本上规划好顶层各个模块的功能实现和参数设置,也要设计好底层某一模块具体功能的实现。
不能想到哪里就做到哪里,这样会在结合的时候非常浪费时间!
2.底层模块功能设计完一定要仿真,确保能正常使用和兼容其他模块。
不能不验证就连接到整体系统中,一旦出现问题就会不知道从哪里检查问题,也很浪费时间!
3.参数设置时要充分考虑VHDL所能调用的库的函数,这样能节省很多设计时间,直接调用即可。
4.当硬件描述语言没有问题时,板级调试出现错误或者与预想的结果不同时,可以从引脚到器件进行一步步的分析,很可能是引脚映射错误或者板子自身的问题。
5.要充分熟悉板子提供的外设的使用方法,比如高触发还是低触发等。
最后,非常感谢学院能给我们这样的机会,具体的把课本知识进行实践,在实践中深刻理解知识和具体操作问题。
附录
HC-SR04工作原理:
(1)主要技术参数:
1、使用电压:
DC5V;
2、静态电流:
小于2mA;
3、电平输出:
高5V;
4、电平输出:
底0V;
5、感应角度:
不大于15度;
6、探测距离:
2cm-450cm7:
高精度可达0.2cm;
7、接线方式,VCC、trig(控制端)、echo(接收端)、GND。
(2)工作原理:
1、采用IO触发测距,给至少10us的高电平信号;
2、模块自动发送8个40khz的方波,自动检测是否有信号返回;
3、有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波从发射到返回的时
system.VHD
libraryIEEE;
useIEEE.std_logic_1164.all;
useIEEE.std_logic_arith.all;
useIEEE.std_logic_unsigned.all;
libraryaltera;
usealtera.altera_syn_attributes.all;
entitysystemis
port
(
clk_in,ECHO_N,SWITCH_N:
instd_logic;
TRIG_N,BUZZ_N:
outstd_logic;
led_dig:
outstd_logic_vector(7downto0);
led_sel:
outstd_logic_vector(3downto0)
);
endsystem;
architectureBehavioralofsystemis
signalclk_100kHz,clk_1KHz,BUZZ:
std_logic;
signalled_sel_temp:
std_logic_vector(3downto0);
signals1,s2,m1,m2,m3:
std_logic_vector(3downto0);
signalT,M:
UNSIGNED(11downto0);
componentfreq_divis
port(clk_in_25MHz:
instd_logic;
clk_out_100KHz:
outstd_logic;
clk_out_1KHz:
outstd_logic);
endcomponent;
componentMEASUREis
PORT(T_12:
INUNSIGNED(11DOWNTO0);
PLAY_H,PLAY_M,PLAY_L:
OUTSTD_LOGIC_VECTOR(3DOWNTO0)
);
ENDcomponent;
componentscanneris
port(clk_in:
instd_logic;
led_sel:
outstd_logic_vector(3downto0));
endcomponent;
componentLAUNCHis
PORT(CLK_10U:
INSTD_LOGIC;
TRIG:
OUTSTD_LOGIC
);
endcomponent;
componentdisplayis
port(led_sel_in:
instd_logic_vector(3downto0);
H_4,M_4,L_4:
instd_logic_vector(3downto0);
led_dig:
outstd_logic_vector(7downto0);
led_sel_out:
outstd_logic_vector(3downto0));
endcomponent;
componentreceiveIS
PORT(CLK_10U,ECHO:
INSTD_LOGIC;
TIM:
OUTUNSIGNED(11DOWNTO0));
ENDcomponent;
COMPONENTswitch1IS
PORT(CLK_100K,SWITCH:
INSTD_LOGIC;
TIM:
INUNSIGNED(11DOWNTO0);
BUZZ:
OUTSTD_LOGIC);
ENDCOMPONENT;
begin
u1:
freq_divportmap(clk_in,clk_100KHz,clk_1KHz);
u2:
scannerportmap(clk_1KHz,led_sel_temp);
u3:
LAUNCHportmap(CLK_100KHz,TRIG_N);
u4:
displayportmap(led_sel_temp,m1,m2,m3,led_dig,led_sel);
U5:
MEASUREportmap(T,m1,m2,m3);
U6:
receivePORTMAP(CLK_100KHz,ECHO_N,T);
U7:
switch1PORTMAP(CLK_100KHz,SWITCH_N,T,BUZZ_N);
endBehavioral;
scanner.VHD
libraryIEEE;
useIEEE.std_logic_1164.all;
useIEEE.std_logic_arith.all;
useIEEE.std_logic_unsigned.all;
entityscanneris
port(clk_in:
instd_logic;
led_sel:
outstd_logic_vector(3downto0));
endscanner;
architectureBehavioralofscanneris
begin
process(clk_in)
variablei:
integer;
begin
if(clk_in'eventandclk_in='1')then
case(i)is
when0=>led_sel<="1110";i:
=1;
when1=>led_sel<="1101";i:
=2;
when2=>led_sel<="1011";i:
=0
whenothers=>led_sel<="0000";i:
=0;
endcase;
endif;
endprocess;
endBehavioral;
LUANCH.VHD
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;
USEIEEE.STD_LOGIC_ARITH.ALL;
USEIEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITYLAUNCHIS
PORT(CLK_10U:
INSTD_LOGIC;
TRIG:
OUTSTD_LOGIC
);
ENDLAUNCH;
ARCHITECTUREONEOFLAUNCHIS
SUBTYPESUB_INISINTEGERRANGE0TO12000;
SIGNALCOUNT:
SUB_IN;
BEGIN
COM:
PROCESS(CLK_10U)
BEGIN
IFCLK_10U'EVENTANDCLK_10U='1'THEN
IFCOUNT=12000THEN
COUNT<=0;
ELSE
COUNT<=COUNT+1;
ENDIF;
ENDIF;
ENDPROCESS;
LUA:
PROCESS(COUNT)
BEGIN
IFCOUNT=0THEN
TRIG<='1';
ELSIFCOUNT=2THEN
TRIG<='0';
ENDIF;
ENDPROCESS;
ENDARCHITECTURE;
freq_div.VHD
libraryIEEE;
useIEEE.std_logic_1164.all;
useIEEE.std_logic_arith.all;
useIEEE.std_logic_unsigned.all;
entityfreq_divis
port(clk_in_25MHz:
instd_logic;
clk_out_100KHz:
outstd_logic;
clk_out_1KHz:
outstd_logic);
endfreq_div;
architectureBehavioraloffreq_divis
signaltemp_counter_1KHz:
std_logic;
signaltemp_counter_100KHz:
std_logic;
begin
process(clk_in_25MHz)
variablecnt:
integerrange0to12499;
begin
if(clk_in_25MHz'eventandclk_in_25MHz='1')then
if(cnt<12499)then
cnt:
=cnt+1;
else
cnt:
=0;temp_counter_1KHz<=nottemp_counter_1KHz;
endif;
endif;
clk_out_1KHz<=temp_counter_1KHz;
endprocess;
process(clk_in_25MHz)
variablecnt1:
integerrange0to124;
begin
if(clk_in_25MHz'eventandclk_in_25MHz='1')then
if(cnt1<124)then
cnt1:
=cnt1+1;
else
cnt1:
=0;temp_counter_100KHz<=nottemp_counter_100KHz;
endif;
endif;
clk_out_100KHz<=temp_counter_100KHz;
endprocess;
endBehavioral;
display.VHD
libraryIEEE;
useIEEE.std_logic_1164.all;
useIEEE.std_logic_arith.all;
useIEEE.std_logic_unsigned.all;
ENTITYdisplayIS
port(led_sel_in:
instd_logic_vector(3downto0);
H_4,M_4,L_4:
instd_logic_vector(3downto0);
led_dig:
outstd_logic_vector(7downto0);
led_sel_out:
outstd_logic_vector(3downto0));
enddisplay;
architectureBehavioralofdisplayis
signaldig_temp:
std_logic_vector(3downto0);
begin
process(led_sel_in,H_4,L_4)
BEGIN
IFled_sel_in="1011"THEN
case(H_4)is
when"0000"=>led_dig<="10111111";
when"0001"=>led_dig<="10000110";
when"0010"=>led_dig<="11011011";
when"0011"=>led_dig<="11001111";
when"0100"=>led_dig<="11100110";
when"0101"=>led_dig<="11101101";
when"0110"=>led_dig<="11111101";
when"0111"=>led_dig<="10000111";
when"1000"=>led_dig<="11111111";
when"1001"=>led_dig<="11101111";
whenothers=>led_dig<="11111111";
endcase;
ELSIFled_sel_in="1101"THEN
case(M_4)is
when"0000"=>led_dig<="00111111";
when"0001"=>led_dig<="00000110";
when"0010"=>led_dig<="01011011";
when"0011"=>led_dig<="01001111";
when"0100"=>led_dig<="01100110";
when"0101"=>led_dig<="01101101";
when"0110"=>led_dig<="01111101";
when"0111"=>led_dig<="00000111";
when"1000"=>led_dig<="01111111";
when"1001"=>led_dig<="01101111";
whenothers=>led_dig<="01111111";
endcase;
ELSIF(led_sel_in="1110")THEN
case(L_4)is
when"0000"=>led_dig<="00111111";
when"0001"=>led_dig<="00000110";
when"0010"=>led_dig<="01011011";
when"0011"=>led_dig<="01001111";
when"0100"=>led_dig<="01100110";
when"0101"=>led_dig<="01101101";
when"0110"=>led_dig<="01111101";
when"0111"=>led_dig<="00000111";
when"1000"=>led_dig<="01111111";
when"1001"=>led_dig<="01101111";
whenothers=>led_dig<="01111111";
endcase;
ENDIF;
endprocess;
led_sel_ou