明德扬吴老师 发表于 2020-8-19 15:21:58

【每周FPGA案例】基于FPGA的超声波测距系统设计 编号:000700000011

至简设计系列_基于FPGA的测距系统
--作者:喝喝本案例的编号为:000700000011,如果有疑问,请按编号在下面贴子查找答案:MDY案例交流【汇总贴】_FPGA-明德扬科教 (mdy-edu.com)
本文为明德扬原创及录用文章,转载请注明出处!
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顶层信号
信号名接口方向定义
clk输入系统时钟
rst_n输入系统复位
echo输入输出回响信号
trig输出10us高电平的触发信号,驱动超声波
1.1.6顶层代码module top(
    clk    ,
    rst_n,
    echo   ,

    trig   ,
    sel,
      seg
    );


    input               clk   ;
    input               rst_n   ;
    input               echo    ;


    output            trig    ;

         
         wire           s_g   ;
         wire           s_s   ;
         wire           s_b   ;
         wire           s_q   ;
         output       sel   ;
         output       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 接口信号



信号接口方向定义
clk输入系统时钟
rst_n输入系统复位
echo输入输出回响信号
trig输出10us高电平的触发信号,用于驱动发出超声波
s_g输出显示distance的个位数字
s_s输出显示distance的十位数字
s_b输出显示distance的百位数字
s_q 输出显示distance的千位数字

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   distance;
         

    reg         cnt0    ;
    reg         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接口信号



信号接口方向定义
clk输入系统时钟
rst_n输入系统复位
segment_data输入测得距离
seg_sel输出数码管位选,选择当前要显示的数码管
segment输出数码管段选,当前要显示的内容

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       segment_data    ;
output      segment         ;
output      seg_sel         ;

reg         segment         ;
reg         seg_sel         ;
reg         delay         ;
reg         delay_time      ;
wire            add_delay_time;
wire            end_delay_time;
wire            add_delay       ;
wire            end_delay       ;
wire      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的同学,我非常推荐使用明德杨至简设计法和明德杨模块进行学习和设计。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也欢迎大家在评论与我进行讨论!

也可以看一下我们往期的文章:《基于FPGA的密码锁设计》《波形相位频率可调DDS信号发生器》《基于FPGA的曼彻斯特编码解码设计》《基于FPGA的出租车计费系统》《数电基础与Verilog设计》《基于FPGA的频率、电压测量》《基于FPGA的汉明码编码解码设计》《关于锁存器问题的讨论》《阻塞赋值与非阻塞赋值》《参数例化时自动计算位宽的解决办法》
1.15公司简介

明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。
【设计视频教程】https://www.bilibili.com/video/BV1Af4y117H4?p=22
【工程源码】







lixin 发表于 2021-4-27 17:21:32

【问题1】非MP603、MP801的管脚配置问题。
超声波模块有4根管脚,分别是VCC、GND、echo和trig。如果您使用非MP603和非MP801开发板(案例中的开发板),则记得电流和GND要接正确,而echo和trig可以插在任意通用IO口上,然后FPGA管脚要配置正确,这样就可以了。

【问题2】MP603点拨开发板中,绿色线连到哪里?图上不清晰
答:连到P1座子的1位置上,即电源3.3V位置上。
页: [1]
查看完整版本: 【每周FPGA案例】基于FPGA的超声波测距系统设计 编号:000700000011