• 【案例】超声波测距系统设计


    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

    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< end
    end

    endmodule

    1.4 效果和总结

    上板验证效果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这个设计中,使用明德扬的至简设计法,让我的思路非常清晰,逻辑非常严谨,虽然没有做到一遍成功,但在调试过程中我都比较快速的找到问题,并快速解决。对于学习FPGA的同学,我非常推荐使用明德扬至简设计法和明德扬模块进行学习和设计。

    教学视频和工程源代码请移步明德扬论坛学习!

    好消息!FPGA至简设计200例已更新,👉🏻学习链接:https://pan.baidu.com/s/181l9fKI8BXwR7HuAF-ok0w 提取码:yt5p

    【FPGA至简设计200例】毕业设计案例由浅入深步骤性教学明德扬

    温馨提示:明德扬2023推出了全新课程——逻辑设计基本功修炼课,降低学习FPGA门槛的同时,增加了学习的趣味性,并组织了考试赢积分活动

    http://www.mdy-edu.com/ffkc/415.html

    (点击→了解课程详情☝)

  • 相关阅读:
    Android音乐播放器(一)启动动画
    浅谈One API
    通过ISO9001认证,如何实现质量体系有效性
    vue基础概念
    亚商投资顾问 早餐FM/0913
    呆头鹅-全自动视频混剪,批量剪辑批量剪视频,探店带货系统,精细化顺序混剪,故事影视解说,视频处理大全,精细化顺序混剪,多场景裂变,多视频混剪
    肾囊肿会出现什么异常?
    软考 系统架构设计师系列知识点之数字孪生体(2)
    19年厦门大学计算机系上机题目及答案
    Helm3模板-模板函数与管道
  • 原文地址:https://blog.csdn.net/MDYFPGA/article/details/134368891