一、简介
声音是我们日常生活中不可缺少的一种信号,在传递信息的同时,也在生活中的各个领域有较多的应用。根据声音的频率,我们将声音大致划分为三个阶段,人耳的听力范围,一般在20Hz~20000Hz之间。低于这个范围,我们称之为次声波;高于这个范围,称之为超声波。超声波的应用比较广泛,比如:超声波检查、超声波碎石、超声波清洗、超声波测速、超声波测距等等。此次我们就来研究一下它的其中一项应用:超声波测距。
我们用到的试验模块为HC-SR04超声波模块,它的测量距离在2cm~400cm之间。测量精度在3mm左右。模块包含了超声波的发射器、接收器和控制电路。超声波发射器在启动后会发出固定频率的方波,用声波去测量距离,不需要我们接触被测物体,在空间上使得我们测距变得方便很多。
二、工作原理
1、采用IO口TRIG触发测距,给至少10us的高电平信号,测量周期建议在60ms以上,以防止发射的信号对回响信号造成影响。
2、模块会自动发送8个40Khz的方波信号,接收器自动检测是否有回响信号返回。
3、有信号返回时,通过IO口ECHO输出一个高电平信号,高电平持续的时间就是方波从发射到返回的时间。测量距离=(高电平时间*声速(340m/s))/2;
在此需要我们注意的事,发射器是自动发送方波信号的,而且会自动检测是否有信号返回,这让我们省去了一大部分工作,使得测量变得简单。其次,在计算测量距离时,我们要将计算出来的结果除以2,因为我们测得的时间是往返的时间,也就是双倍的路程。
三、实物图
五、超声波时序图
在时序图中,我们可以看出,我们需要生成一个周期至少为60ms,且高电平维持时间至少为10us的一个触发信号。
六、实验要求
此次设计,要求能够正常驱动模块,计算出的距离,计算其平均值以保证准确性。数码管上显示出距离,单位为m,精确到mm。并且,蜂鸣器能够根据距离响出不同频率的报警声音,距离越近,响声频率越频繁。
七、设计框架
八、设计实现
在计算回响信号的时间时,我们可以检测回响信号的上升沿和下降沿来作为计时器的开始和结束。在我们计算出距离之后,可以每三个数据计算一次平均值。然后将数据输出给其他模块。
首先,我们新建工程。
选择新建文件,然后先新建顶层文件
按照我们所画框架,写入顶层端口。
重复上述新建文件的过程,新建ultrasonic_driver文件,代码如下:
1 module ultrasonic_driver( 2 3 input wire clk, 4 input wire rst_n, 5 input wire echo, 6 output reg trig, 7 output reg [11:0] distance, 8 output reg data_valid 9 ); 10 11 parameter t = 3_000_000; 12 13 reg [21:0] cnt; 14 reg state; 15 reg echo_r, echo_rr; 16 reg [20:0] echo_cnt; 17 reg [20:0] cnt_temp; 18 wire [11:0] d_r; 19 reg [35:0] temp; 20 reg data_valid_r; 21 22 always @ (posedge clk) echo_r <= echo; 23 always @ (posedge clk) echo_rr <= echo_r; 24 25 always @ (posedge clk, negedge rst_n) 26 begin 27 if(rst_n == 1'b0) 28 cnt <= 22'd0; 29 else if(cnt == t - 1) 30 cnt <= 22'd0; 31 else 32 cnt <= cnt + 1'b1; 33 end 34 35 always @ (posedge clk, negedge rst_n) 36 begin 37 if(rst_n == 1'b0) 38 trig <= 1'b0; 39 else if(cnt < 1000) 40 trig <= 1'b1; 41 else 42 trig <= 1'b0; 43 end 44 45 always @ (posedge clk, negedge rst_n) 46 begin 47 if(rst_n == 1'b0) 48 begin 49 echo_cnt <= 21'd0; 50 state <= 1'd0; 51 data_valid_r <= 1'b0; 52 echo_cnt <= 21'd0; 53 end 54 else 55 case(state) 56 1'd0 : begin 57 if(echo_r & (~echo_rr)) 58 state <= 1'd1; 59 else 60 begin 61 state <= 1'd0; 62 data_valid_r <= 1'b0; 63 end 64 end 65 1'd1 : begin 66 if((~echo_r) & echo_rr) 67 begin 68 state <= 1'd0; 69 echo_cnt <= 21'd0; 70 cnt_temp <= echo_cnt; 71 data_valid_r <= 1'b1; 72 end 73 else 74 begin 75 state <= 1'd1; 76 echo_cnt <= echo_cnt + 1'b1; 77 cnt_temp <= cnt_temp; 78 end 79 end 80 endcase 81 end 82 83 assign d_r = cnt_temp * 34 / 10_000; 84 85 always @ (posedge clk, negedge rst_n) 86 begin 87 if(rst_n == 1'b0) 88 temp <= 36'd0; 89 else if(data_valid_r) 90 temp <= {temp[23:0],d_r}; 91 else 92 temp <= temp; 93 end 94 95 always @ (posedge clk) data_valid <= data_valid_r; 96 97 always @ (posedge clk, negedge rst_n) 98 begin 99 if(rst_n == 1'b0) 100 distance <= 12'd0; 101 else if(data_valid) 102 distance <= (temp[35:24] + temp[23:12] + temp[11:0]) / 3; 103 else 104 distance <= distance; 105 end 106 107 endmodule
在完成测距时,输出一个valid信号,这个信号要作为后续我们保存数据以及计算平均值的标志信号。
数码管代码如下:
1 module seven_tube_driver( 2 3 input wire clk, 4 input wire rst_n, 5 input wire [11:0] data, 6 7 output reg [5:0] sel, 8 output wire [7:0] seg 9 ); 10 11 parameter t = 50000; 12 13 reg [15:0] cnt; 14 reg [3:0] show_data; 15 reg [7:0] seg_r; 16 17 always @ (posedge clk, negedge rst_n) 18 begin 19 if(rst_n == 1'b0) 20 cnt <= 16'd0; 21 else if(cnt == t - 1) 22 cnt <= 16'd0; 23 else 24 cnt <= cnt + 1'b1; 25 end 26 27 always @ (posedge clk, negedge rst_n) 28 begin 29 if(rst_n == 1'b0) 30 sel <= 6'b111_110; 31 else if(cnt == t - 1) 32 sel <= {sel[4:0],sel[5]}; 33 else 34 sel <= sel; 35 end 36 37 always @ (*) 38 begin 39 case(sel) 40 6'b111_110 : show_data = 4'hf; 41 6'b111_101 : show_data = 4'hf; 42 6'b111_011 : show_data = data/1000; 43 6'b110_111 : show_data = data/100%10; 44 6'b101_111 : show_data = data/10%10; 45 6'b011_111 : show_data = data%10; 46 default : show_data = 4'd0; 47 endcase 48 end 49 50 always @ (*) 51 begin 52 case(show_data) 53 4'd0 : seg_r = 8'b1100_0000; 54 4'd1 : seg_r = 8'b1111_1001; 55 4'd2 : seg_r = 8'b1010_0100; 56 4'd3 : seg_r = 8'b1011_0000; 57 4'd4 : seg_r = 8'b1001_1001; 58 4'd5 : seg_r = 8'b1001_0010; 59 4'd6 : seg_r = 8'b1000_0010; 60 4'd7 : seg_r = 8'b1111_1000; 61 4'd8 : seg_r = 8'b1000_0000; 62 4'd9 : seg_r = 8'b1001_0000; 63 default: seg_r = 8'd0; 64 endcase 65 end 66 67 assign seg = (sel == 6'b111_011) ? (seg_r & 8'b0111_1111) : seg_r; 68 69 endmodule
第67行的作用是为了在显示时,显示一个小数点,这样数码管显示的数值单位就为米,精确度为毫米。
在蜂鸣器模块中,蜂鸣器的响声为“嘀嘀”的响声,我们可以根据距离的大小,让蜂鸣器响声的快慢作出改变。
我们根据距离改变计数器的最大计数次数,以达到两次“嘀嘀”响声的时间间隔发生变化。
写好代码之后,我们做一下仿真,代码如下:
1 `timescale 1ns / 1ps 2 3 module ultrasonic_tb; 4 5 reg clk; 6 reg rst_n; 7 reg echo; 8 wire trig; 9 wire [5:0] sel; 10 wire [7:0] seg; 11 wire beep; 12 13 defparam ultrasonic_inst.ultrasonic_driver_inst.t = 3000; 14 15 initial begin 16 clk = 1'b0; 17 rst_n = 1'b0; 18 echo = 1'b0; 19 #105; 20 rst_n = 1'b1; 21 #1000; 22 23 repeat(10) begin 24 @ (negedge trig); 25 #1002; 26 echo = 1'b1; 27 #20000; 28 echo = 1'b0; 29 end 30 #10000; 31 $stop; 32 end 33 34 always #10 clk = ~clk; 35 36 ultrasonic ultrasonic_inst( 37 .clk (clk ), 38 .rst_n (rst_n ), 39 .echo (echo ), 40 .trig (trig ), 41 .sel (sel ), 42 .seg (seg ), 43 .beep (beep ) 44 ); 45 46 endmodule
仿真图如下:
从图中我们可以看出,距离在多次采样之后达到了稳定值。由于我们仿真时间给的较短,所以距离的数值不大,但是已经足够看出结果。
审核编辑:刘清
评论
查看更多