本文為明德揚原創及錄用文章,轉載請注明出處!
1.1 總體設計
1.1.1 概述
學習了明德揚至簡設計法和明德揚設計規范,本人用FPGA設計了一個測距系統。該系統采用超聲波進行測量距離再在數碼管上顯示。在本案例的設計過程中包括了超聲波的驅動、三線式數碼管顯示等技術。經過逐步改進、調試等一系列工作后,最終完成了此設計,并進行上板驗證,下面將完整的設計記錄與大家分享。
1.1.2 設計目標
此系統將實時顯示前方障礙與裝置之間的距離。
1.1.3 系統結構框圖
系統結構框圖如下所示:

1.1.4 模塊功能
hc_sr04模塊實現功能:
該模塊通過控制觸發信號trig(10us的TTL)使內部循環發出8個40KHZ脈沖即驅動超聲波,接收回響信號echo,通過echo得到距離。
顯示模塊實現功能:
該模塊完成了對所測距離通過數碼管對其顯示。
1.1.5頂層信號

1.1.6頂層代碼
module top( clk , rst_n , echo , trig , sel, seg ); input clk ; input rst_n ; input echo ; output trig ; wire [3:0] s_g ; wire [3:0] s_s ; wire [3:0] s_b ; wire [3:0] s_q ; output [7:0] sel ; output [7:0] seg ; hc_sr04 hc_sr04_1( .clk (clk) , .rst_n (rst_n) , .echo (echo) , .trig (trig) , .s_g (s_g ), .s_s (s_s ), .s_b (s_b ), .s_q (s_q ) ); seg_disp u_seg_disp( .clk (clk ), .rst_n (rst_n), .segment_data({s_q,s_b,s_s,s_g}), .segment (seg ), .seg_sel (sel ) ); endmodule
1.2 hc_sr04模塊設計
1.2.1 接口信號

1.2.2 設計思路
我們只需要提供一個短期的10uS脈沖觸發信號trig,該模塊內部將發出8個40kHz周期電平并檢測回波,一旦檢測到有回波信號則輸出回響信號,回響信號echo是一個脈沖的寬度成正比的距離變量,可通過發射信號到收到的回響信號時間間隔可以計算得到距離。建議測量周期為60ms以上,以防止發射信號對回響信號的影響,這里我們采用的是1s測量一次。
時鐘計數器cnt0:用于計算 1 秒的時鐘個數,加一條件為1,表示一直計數;結束條件為數到 TIME_1S ,表示數到 1 秒就清零。
距離計數器 h_cnt:用于計算flag為高電平的寬度的時間,如果flag為1,h_cnt就加一;每完成1秒計數后h_cnt就變為0,此外h_cnt等于h_cnt。
模塊時序圖

1.2.3 參考代碼
module hc_sr04( clk , rst_n , echo , trig , s_g , s_s , s_b , s_q ); parameter DATA_W = 14 ; parameter TIME_1S = 50_000_000; input clk ; input rst_n ; input echo ; output trig ; output[ 3:0] s_g ; output[ 3:0] s_s ; output[ 3:0] s_b ; output[ 3:0] s_q ; wire trig ; reg [ 3:0] s_g ; reg [ 3:0] s_s ; reg [ 3:0] s_b ; reg [ 3:0] s_q ; reg [DATA_W-1:0] distance; reg [25:0] cnt0 ; reg [20:0] h_cnt ; reg echo_2 ; reg echo_1 ; wire add_cnt0; wire end_cnt0; wire flag_h ; wire flag_l ; always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt0 <= 0; end else if(add_cnt0)begin if(end_cnt0) cnt0 <= 0; else cnt0 <= cnt0 + 1'b1; end end assign add_cnt0 = 1; assign end_cnt0 = add_cnt0 && cnt0 == TIME_1S - 1; assign trig = (cnt0>=500&&cnt0<1000)?1:0; always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin echo_1 <= 0; echo_2 <= 0; end else begin echo_1 <= echo ; echo_2 <= echo_1; end end always @(posedge clk or negedge rst_n)begin if(!rst_n)begin h_cnt <= 0; end else if(add_h_cnt)begin if(end_h_cnt) h_cnt <= 0; else h_cnt <= h_cnt + 1; end else if(end_cnt0)begin h_cnt <= 0; end end assign add_h_cnt = echo_2; assign end_h_cnt = 0 ; always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin distance <= 0; end else if(add_cnt0 && cnt0 == 45_000_000-1)begin distance <= h_cnt*34/10000; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_g <= 0; end else begin s_g <= distance%10; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_s <= 0; end else begin s_s <= (distance/10)%10; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_b <= 0; end else begin s_b <= (distance/100)%10; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_q <= 0; end else begin s_q <= (distance/1000)%10; end end endmodule
1.3 顯示模塊設計
1.3.1接口信號

1.3.2設計思路
該模塊對數碼管的位選信號sel每隔1ms的時間移位一次,也就是1ms循環亮一個燈,由于1ms的頻率肉眼觀察不出,我們看到的就是4個燈全亮。
對輸入距離distance進行求余處理,得到每一位的數據,通過case語句,讓每一位數據形成段選信號,通過位選信號的控制顯示在對應的數碼管上。
1.3.3參考代碼
module seg_disp( clk , rst_n , segment_data, segment , seg_sel ); parameter ZERO = 8'b1100_0000 ; parameter ONE = 8'b1111_1001 ; parameter TWO = 8'b1010_0100 ; parameter THREE = 8'b1011_0000 ; parameter FOUR = 8'b1001_1001 ; parameter FIVE = 8'b1001_0010 ; parameter SIX = 8'b1000_0010 ; parameter SEVEN = 8'b1111_1000 ; parameter EIGHT = 8'b1000_0000 ; parameter NINE = 8'b1001_0000 ; input clk ; input rst_n ; input [31:0] segment_data ; output [7:0 ] segment ; output [7:0 ] seg_sel ; reg [7:0 ] segment ; reg [7:0 ] seg_sel ; reg [10:0] delay ; reg [3:0 ] delay_time ; wire add_delay_time ; wire end_delay_time ; wire add_delay ; wire end_delay ; wire [3:0 ] segment_tmp ; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin delay <= 0; end else if(add_delay) begin if(end_delay) delay <= 0; else delay <= delay+1 ; end end assign add_delay = 1; assign end_delay = add_delay && delay == 2000-1 ; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin delay_time <= 0; end else if(add_delay_time) begin if(end_delay_time) delay_time <= 0; else delay_time <= delay_time+1 ; end end assign add_delay_time = end_delay; assign end_delay_time = add_delay_time && delay_time == 8-1 ; assign segment_tmp = segment_data[(1+delay_time)*4-1 -:4]; always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin segment <= ZERO; end else begin case(segment_tmp) 4'd0:segment <= ZERO; 4'd1:segment <= ONE ; 4'd2:segment <= TWO ; 4'd3:segment <= THREE; 4'd4:segment <= FOUR ; 4'd5:segment <= FIVE ; 4'd6:segment <= SIX ; 4'd7:segment <= SEVEN; 4'd8:segment <= EIGHT; 4'd9:segment <= NINE ; default:begin segment <= segment; end endcase end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin seg_sel <= 8'b1111_1111; end else begin seg_sel <= ~(8'b1<<delay_time); end end endmodule
1.4 效果和總結
上板驗證效果



在這個設計中,使用明德揚的至簡設計法,讓我的思路非常清晰,邏輯非常嚴謹,雖然沒有做到一遍成功,但在調試過程中我都比較快速的找到問題,并快速解決。對于學習FPGA的同學,我非常推薦使用明德揚至簡設計法和明德揚模塊進行學習和設計。
教學視頻和工程源代碼請移步明德揚論壇學習!
感興趣的朋友也可以訪問明德揚論壇(www.fpgabbs.cn)進行FPGA相關工程設計學習,也歡迎大家在評論與我進行討論!
也可以看一下我們往期的文章:
《基于FPGA的密碼鎖設計》
《波形相位頻率可調DDS信號發生器》
《基于FPGA的曼徹斯特編碼解碼設計》
《基于FPGA的出租車計費系統》
《數電基礎與Verilog設計》《基于FPGA的頻率、電壓測量》
《基于FPGA的漢明碼編碼解碼設計》
《關于鎖存器問題的討論》
《阻塞賦值與非阻塞賦值》
《參數例化時自動計算位寬的解決辦法》
1.15公司簡介
明德揚是一家專注于FPGA領域的專業性公司,公司主要業務包括開發板、教育培訓、項目承接、人才服務等多個方向。
點撥開發板——學習FPGA的入門之選。
MP801開發板——千兆網、ADDA、大容量SDRAM等,學習和項目需求一步到位。
網絡培訓班——不管時間和空間,明德揚隨時在你身邊,助你快速學習FPGA。
周末培訓班——明天的你會感激現在的努力進取,升職加薪明德揚來助你。
就業培訓班——七大企業級項目實訓,獲得豐富的項目經驗,高薪就業。
專題課程——高手修煉課:提升設計能力;
實用調試技巧課:提升定位和解決問題能力;
FIFO架構設計課:助你快速成為架構設計師;
時序約束、數字信號處理、PCIE、綜合項目實踐課等你來選。
項目承接——承接企業FPGA研發項目。人才服務——提供人才推薦、人才代培、人才派遣等服務。