FPGA零基础之Vivado-超声波驱动设计

可编程逻辑

1362人已加入

描述

一、简介

声音是我们日常生活中不可缺少的一种信号,在传递信息的同时,也在生活中的各个领域有较多的应用。根据声音的频率,我们将声音大致划分为三个阶段,人耳的听力范围,一般在20Hz~20000Hz之间。低于这个范围,我们称之为次声波;高于这个范围,称之为超声波。超声波的应用比较广泛,比如:超声波检查、超声波碎石、超声波清洗、超声波测速、超声波测距等等。此次我们就来研究一下它的其中一项应用:超声波测距。

我们用到的试验模块为HC-SR04超声波模块,它的测量距离在2cm~400cm之间。测量精度在3mm左右。模块包含了超声波的发射器、接收器和控制电路。超声波发射器在启动后会发出固定频率的方波,用声波去测量距离,不需要我们接触被测物体,在空间上使得我们测距变得方便很多。

二、工作原理

1、采用IO口TRIG触发测距,给至少10us的高电平信号,测量周期建议在60ms以上,以防止发射的信号对回响信号造成影响。

2、模块会自动发送8个40Khz的方波信号,接收器自动检测是否有回响信号返回。

3、有信号返回时,通过IO口ECHO输出一个高电平信号,高电平持续的时间就是方波从发射到返回的时间。测量距离=(高电平时间*声速(340m/s))/2;

在此需要我们注意的事,发射器是自动发送方波信号的,而且会自动检测是否有信号返回,这让我们省去了一大部分工作,使得测量变得简单。其次,在计算测量距离时,我们要将计算出来的结果除以2,因为我们测得的时间是往返的时间,也就是双倍的路程。

三、实物图

Vivado

四、电气参数

Vivado

五、超声波时序图

Vivado

在时序图中,我们可以看出,我们需要生成一个周期至少为60ms,且高电平维持时间至少为10us的一个触发信号。

六、实验要求

此次设计,要求能够正常驱动模块,计算出的距离,计算其平均值以保证准确性。数码管上显示出距离,单位为m,精确到mm。并且,蜂鸣器能够根据距离响出不同频率的报警声音,距离越近,响声频率越频繁。

七、设计框架

Vivado

Vivado

八、设计实现

在计算回响信号的时间时,我们可以检测回响信号的上升沿和下降沿来作为计时器的开始和结束。在我们计算出距离之后,可以每三个数据计算一次平均值。然后将数据输出给其他模块。

首先,我们新建工程。

Vivado

选择新建文件,然后先新建顶层文件

Vivado

Vivado

按照我们所画框架,写入顶层端口。

重复上述新建文件的过程,新建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行的作用是为了在显示时,显示一个小数点,这样数码管显示的数值单位就为米,精确度为毫米。

在蜂鸣器模块中,蜂鸣器的响声为“嘀嘀”的响声,我们可以根据距离的大小,让蜂鸣器响声的快慢作出改变。

Vivado

我们根据距离改变计数器的最大计数次数,以达到两次“嘀嘀”响声的时间间隔发生变化。

写好代码之后,我们做一下仿真,代码如下:

 

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

 

仿真图如下:

Vivado

从图中我们可以看出,距离在多次采样之后达到了稳定值。由于我们仿真时间给的较短,所以距离的数值不大,但是已经足够看出结果。






审核编辑:刘清
 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分