作者:李西銳??校對:陸輝
大俠好,歡迎來到FPGA技術(shù)江湖。本系列將帶來FPGA的系統(tǒng)性學習,從最基本的數(shù)字電路基礎(chǔ)開始,最詳細操作步驟,最直白的言語描述,手把手的“傻瓜式”講解,讓電子、信息、通信類專業(yè)學生、初入職場小白及打算進階提升的職業(yè)開發(fā)者都可以有系統(tǒng)性學習的機會。
系統(tǒng)性的掌握技術(shù)開發(fā)以及相關(guān)要求,對個人就業(yè)以及職業(yè)發(fā)展都有著潛在的幫助,希望對大家有所幫助。本次帶來Vivado系列,超聲波驅(qū)動設(shè)計。話不多說,上貨。
一、簡介
聲音是我們?nèi)粘I钪胁豢扇鄙俚囊环N信號,在傳遞信息的同時,也在生活中的各個領(lǐng)域有較多的應(yīng)用。根據(jù)聲音的頻率,我們將聲音大致劃分為三個階段,人耳的聽力范圍,一般在20Hz~20000Hz之間。低于這個范圍,我們稱之為次聲波;高于這個范圍,稱之為超聲波。超聲波的應(yīng)用比較廣泛,比如:超聲波檢查、超聲波碎石、超聲波清洗、超聲波測速、超聲波測距等等。此次我們就來研究一下它的其中一項應(yīng)用:超聲波測距。
我們用到的試驗?zāi)K為HC-SR04超聲波模塊,它的測量距離在2cm~400cm之間。測量精度在3mm左右。模塊包含了超聲波的發(fā)射器、接收器和控制電路。超聲波發(fā)射器在啟動后會發(fā)出固定頻率的方波,用聲波去測量距離,不需要我們接觸被測物體,在空間上使得我們測距變得方便很多。
二、工作原理
1、采用IO口TRIG觸發(fā)測距,給至少10us的高電平信號,測量周期建議在60ms以上,以防止發(fā)射的信號對回響信號造成影響。
2、模塊會自動發(fā)送8個40Khz的方波信號,接收器自動檢測是否有回響信號返回。
3、有信號返回時,通過IO口ECHO輸出一個高電平信號,高電平持續(xù)的時間就是方波從發(fā)射到返回的時間。測量距離=(高電平時間*聲速(340m/s))/2;
在此需要我們注意的事,發(fā)射器是自動發(fā)送方波信號的,而且會自動檢測是否有信號返回,這讓我們省去了一大部分工作,使得測量變得簡單。其次,在計算測量距離時,我們要將計算出來的結(jié)果除以2,因為我們測得的時間是往返的時間,也就是雙倍的路程。
三、實物圖
四、電氣參數(shù)
五、超聲波時序圖
在時序圖中,我們可以看出,我們需要生成一個周期至少為60ms,且高電平維持時間至少為10us的一個觸發(fā)信號。
六、實驗要求
此次設(shè)計,要求能夠正常驅(qū)動模塊,計算出的距離,計算其平均值以保證準確性。數(shù)碼管上顯示出距離,單位為m,精確到mm。并且,蜂鳴器能夠根據(jù)距離響出不同頻率的報警聲音,距離越近,響聲頻率越頻繁。
七、設(shè)計框架
?
?八、設(shè)計實現(xiàn)
在計算回響信號的時間時,我們可以檢測回響信號的上升沿和下降沿來作為計時器的開始和結(jié)束。在我們計算出距離之后,可以每三個數(shù)據(jù)計算一次平均值。然后將數(shù)據(jù)輸出給其他模塊。
首先,我們新建工程。
選擇新建文件,然后先新建頂層文件
按照我們所畫框架,寫入頂層端口。
重復(fù)上述新建文件的過程,新建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信號,這個信號要作為后續(xù)我們保存數(shù)據(jù)以及計算平均值的標志信號。
數(shù)碼管代碼如下:
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行的作用是為了在顯示時,顯示一個小數(shù)點,這樣數(shù)碼管顯示的數(shù)值單位就為米,精確度為毫米。
在蜂鳴器模塊中,蜂鳴器的響聲為“嘀嘀”的響聲,我們可以根據(jù)距離的大小,讓蜂鳴器響聲的快慢作出改變。
我們根據(jù)距離改變計數(shù)器的最大計數(shù)次數(shù),以達到兩次“嘀嘀”響聲的時間間隔發(fā)生變化。
寫好代碼之后,我們做一下仿真,代碼如下:
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
仿真圖如下:
從圖中我們可以看出,距離在多次采樣之后達到了穩(wěn)定值。由于我們仿真時間給的較短,所以距離的數(shù)值不大,但是已經(jīng)足夠看出結(jié)果。