第12周实验---基于FPGA的超声波测距

一、超声波模块介绍
产品特点
HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。
基本工作原理:

(1)采用IO口 TRIG触发测距,给最少10us的高电平信呈。
(⑵)模块自动发送8个40khz的方波,自动检测是否有信号返回;
(3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;
二、系统设计

module     HC_SR04_TOP(
    input              clk        , 
    input           rstn    , 
    
    input           echo    , // 距离信号
    output           trig    , // 触发测距信号
    output  wire [5:0]    sel    ,
    output  wire [7:0]    seg
);

    wire    [18:00]        data_o        ;
    wire                 clk_us        ;

    seg_driver u_seg_driver(  
        .clk        (clk    ),
        .rstn        (rstn    ),
        .data_in    (data_o    ), //待显示数据
        .sel        (sel    ),    // 我这里是8位段选,可以换6位,但是要自己改代码
        .seg        (seg    )     
    );    

    clk_div    u_clk_div(
        .clk        (clk    ), 
        .rstn        (rstn    ),
        .clk_us        (clk_us )
    );
    trig_driver    u_trig_driver(
        .clk_us        (clk_us    ),
        .rstn        (rstn    ),
        .trig        (trig    )
    );

    echo_driver    u_echo_driver(
        .clk        (clk    ),
        .clk_us        (clk_us    ),
        .rstn        (rstn    ),
        .echo        (echo    ),
        .data_o        (data_o    )
        );

//Logic Description
endmodule 

module     trig_driver(
    input  wire            clk_us    ,
    input  wire         rstn    ,
           
    output wire          trig      //触发测距信号
);

    parameter CYCLE_MAX = 19'd29_9999;

    reg        [18:00]    cnt        ;

// 10毫秒持续电平输出
    
    always @(posedge clk_us or negedge rstn) begin
        if(!rstn) begin
            cnt <= 19'd0;
        end
        else if(cnt == CYCLE_MAX) begin
            cnt <= 19'd0;
        end
        else begin
            cnt <= cnt + 19'd1;
        end
    end
    
    assign trig = cnt < 15 ? 1'b1 : 1'b0;

endmodule 

module     echo_driver(
    input  wire         clk        ,
    input  wire            clk_us    ,
    input  wire         rstn    ,
           
    input  wire         echo    ,
    output wire [18:00]    data_o      //检测距离,保留3位小数,*1000实现
);

    parameter T_MAX = 16'd5_9999;//510cm 对应计数值

    reg                r1_echo,r2_echo; //边沿检测    
    wire            echo_pos,echo_neg; //
    
    reg        [15:00]    cnt        ; 
    
    reg        [18:00]    data_r    ;
    
    //如果使用clk_us 检测边沿,延时2us,差值过大
    always @(posedge clk or negedge rstn)begin  
        if(!rstn)begin  
            r1_echo <= 1'b0;
            r2_echo <= 1'b0;
        end  
        else begin  
            r1_echo <= echo;
            r2_echo <= r1_echo;
        end  
    end
    
    assign echo_pos = r1_echo & ~r2_echo;
    assign echo_neg = ~r1_echo & r2_echo;
    
    always @(posedge clk_us or negedge rstn) begin
        if(!rstn) begin
            cnt <= 16'd0;
        end
        else if(echo) begin
            if(cnt == T_MAX) begin
                cnt <= 16'd0;
            end
            else begin
                cnt <= cnt + 16'd1;
            end
        end
        else begin
            cnt <= 16'd0;
        end
    end
    
    always @(posedge clk or negedge rstn)begin  
        if(!rstn)begin  
            data_r <= 'd2;
        end  
        else if(echo_neg)begin  
            data_r <= (cnt << 4) + cnt;
        end  
        else begin  
            data_r <= data_r;
        end  
    end
    
    assign data_o = data_r >> 1;

endmodule 

module     clk_div(
    input  wire            clk        ,
    input  wire         rstn    ,
           
    output wire          clk_us       //
);

    parameter CNT_MAX = 19'd49;//1us的计数值为 50 * Tclk(20ns)

    reg        [5:0]    cnt        ; 
    wire            add_cnt ;
    wire            end_cnt ;
    
    // 时钟分频
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt <= 6'd0;
        end
        else if(cnt == CNT_MAX) begin
            cnt <= 6'd0;
        end
        else begin
            cnt <= cnt + 6'd1;
        end
    end
    
    assign clk_us = cnt >= CNT_MAX ;
    

endmodule 

module seg_driver(  
    input    wire        clk        ,
    input    wire        rstn    ,
    
    input    wire [18:0]    data_in    , //待显示数据

    output  reg [5:0]   sel     ,    // 我这里是8位段选,可以换6位,但是要自己改代码
    output  reg [7:0]   seg     
);                                  
     //parameter define  
    localparam    NUM_0    =    8'b1100_0000,    
                NUM_1     =     8'b1111_1001,
                NUM_2   =     8'b1010_0100,
                NUM_3   =     8'b1011_0000,
                NUM_4   =     8'b1001_1001,
                NUM_5   =     8'b1001_0010,
                NUM_6   =     8'b1000_0010,
                NUM_7   =     8'b1111_1000,
                NUM_8   =     8'b1000_0000,
                NUM_9   =     8'b1001_0000,
                NUM_A   =     8'b1000_1000,
                NUM_B   =     8'b1000_0011,
                NUM_C   =     8'b1100_0110,
                NUM_D   =     8'b1010_0001,
                NUM_E   =     8'b1000_0110,
                NUM_F   =     8'b1000_1110,
                ALL_LIGHT = 8'b0000_0000,
                LIT_OUT =     8'b1111_1111,
                LINE    =   8'b1011_1111;

    localparam MAX_10us     =   10'd999     ;
     //reg 、wire define        
    reg        [3:0]    cm_hund        ;//100cm
    reg        [3:0]    cm_ten        ;//10cm
    reg        [3:0]    cm_unit        ;//1cm
    reg        [3:0]    point_1        ;//1mm
    reg        [3:0]    point_2        ;//0.1mm
    reg        [3:0]    point_3        ;//0.01mm
    reg     [9:0]   cnt_10us    ;
    reg     [7:0]   num         ;// 段选输出判断

    always @(posedge clk or negedge rstn)begin  
        if(!rstn)begin  
            cm_hund    <= 'd0;
            cm_ten    <= 'd0;
            cm_unit    <= 'd0;
            point_1    <= 'd0;
            point_2    <= 'd0;
            point_3    <= 'd0;
        end  
        else begin  
            cm_hund <= data_in / 10 ** 5;
            cm_ten    <= data_in / 10 ** 4 % 10;
            cm_unit <= data_in / 10 ** 3 % 10;
            point_1 <= data_in / 10 ** 2 % 10;
            point_2 <= data_in / 10 ** 1 % 10;
            point_3 <= data_in / 10 ** 0 % 10;
        end  
    end 

    // 修改后   段选

    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            cnt_10us <= 10'd0;
        end
        else if(cnt_10us == MAX_10us) begin
            cnt_10us <= 10'd0;
        end
        else begin
            cnt_10us <= cnt_10us + 10'd1;
        end
    end

    // 数码管位移
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            sel <= 6'b111_110;
        end
        else if(cnt_10us == MAX_10us) begin
            sel <= {sel[0],sel[5:1]};
        end
        else begin
            sel <= sel;
        end
    end

    // 确定输出数字
    always @(*) begin
        case (sel)
            6'b01_1111    :    num = hex_data(point_3);
            6'b10_1111    :    num = hex_data(point_2);
            6'b11_0111    :    num = hex_data(point_1);
            6'b11_1011    :    num = hex_data(cm_unit);
            6'b11_1101    :    num = hex_data(cm_ten)    ;
            6'b11_1110    :    num = hex_data(cm_hund);
            // 6'b11_1111    :    num = LINE;
            // 6'b11_1111    :    num = LIT_OUT;

            default         :    num = NUM_0;
        endcase
    end

    // 位选输出
    always @(posedge clk or negedge rstn) begin
        if(!rstn) begin
            seg <= LINE;
        end
        else begin
            case (num)
                NUM_0       :   seg <= NUM_0    ;
                NUM_1       :   seg <= NUM_1    ;
                NUM_2       :   seg <= NUM_2    ;
                NUM_3       :   seg <= NUM_3    ;
                NUM_4       :   seg <= NUM_4    ;
                NUM_5       :   seg <= NUM_5    ;
                NUM_6       :   seg <= NUM_6    ;
                NUM_7       :   seg <= NUM_7    ;
                NUM_8       :   seg <= NUM_8    ;
                NUM_9       :   seg <= NUM_9    ;
                LINE        :   seg <= LINE     ;
                LIT_OUT     :   seg <= LIT_OUT  ;
                ALL_LIGHT   :   seg <= ALL_LIGHT;
            endcase
        end
    end

    // 函数,4位输入,7位输出,判断要输出的数字
    function  [7:0]    hex_data; //函数不含时序逻辑相关
        input   [03:00]    data_i;//至少一个输入
        begin
            case(data_i)
                4'd0:hex_data = NUM_0;
                4'd1:hex_data = NUM_1;
                4'd2:hex_data = NUM_2;
                4'd3:hex_data = NUM_3;
                4'd4:hex_data = NUM_4;
                4'd5:hex_data = NUM_5;
                4'd6:hex_data = NUM_6;
                4'd7:hex_data = NUM_7;
                4'd8:hex_data = NUM_8;
                4'd9:hex_data = NUM_9;
                default:hex_data = ALL_LIGHT;
            endcase    
        end 
    endfunction

endmodule  

超声波测距

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值