基于DE2 115开发板驱动HC_SR04实现超声波测距

一、实验准备

软件:Quartus + Modelsim
硬件:DE2-E115 FPGA开发板+HC_SR04模块

二、实验要求

使用DE2开发板驱动HC_SR04模块,并将所测得数据显示到开发板上的数码管。

三、实验原理

模块实物图:
在这里插入图片描述

模块引脚介绍:
从上图我们可以看到超声波模块的4个引脚,它们的作用罗列如下:

  • VCC: 电源引脚,超声波模块工作电压为5伏。

  • Trig: 是Trigger(触发)这个单词的缩写,该引脚用于触发超声波脉冲。

  • Echo: 该引脚会在高电平和低电平之间转换,当检测到障碍物时,在高电平保持的时间就表示信号发射出去并反射回来的时间。

  • GND: 接地引脚。

模块测距工作原理:

  • 主控设备给 Trig 脚提供一个 10us 的脉冲信号。

  • HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波。

  • HC-SR04 接收到返回的超声波,把 Echo 置为低电平。

  • Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。

超声波时序图:
在这里插入图片描述

四、模块实现

hc_sr_trig模块

module 	hc_sr_trig(
	input  wire			clk		,
	input  wire 		rst_n	,
		   
	output wire  		trig	  //触发测距信号
);

	//trig:超声波触发信号,高电平至少为10us,同时考虑到信号不要重叠,所以当距离为5000mm时的来回时间做为超声波触发信号的周期T
	//T=(5/340) * 1000ms = 14.7ms,来回就是29.4ms,所以就让触发脉冲的周期至少为30ms,高电平时间为10us
	//为了防止发射信号对回响信号产生影响,这里直接设定为100ms的周期,高电平为10us
	
	parameter CNT_10US_MAX = 9'd500;
	parameter CNT_100MS_MAX = 23'd5000_000;

	reg     [22:0]      cnt_100ms           ;
	wire                cnt_100ms_flag      ;
	reg 				trig_r;
	//cnt_100ms and cnt_100ms_flag
	assign  cnt_100ms_flag = cnt_100ms==CNT_100MS_MAX - 1'b1;
	always @ (posedge clk or negedge rst_n) begin
	    if(!rst_n) begin
			cnt_100ms<=23'd0;
		end
	    else if(cnt_100ms_flag) begin
			cnt_100ms<=23'd0;
		end
	    else begin
			cnt_100ms<=cnt_100ms+1'b1;
		end
	end

	//trig
	always @ (posedge clk or negedge rst_n) begin
	    if(!rst_n) begin
			trig_r<=1'b0;
		end
	    else if(cnt_100ms<CNT_10US_MAX) begin
			trig_r<=1'b1;
		end
	    else begin
			trig_r<=1'b0;
		end
	end

	assign trig = trig_r;
	
endmodule 

hc_sr_echo模块

module 	hc_sr_echo(
	input  wire 		clk			,
	input  wire 		rst_n		,
	input  wire 		echo		,
    
	output reg          fall_flag_r1,	//下降沿标志的打拍信号
	output wire [12:0]	data_o	  		//检测距离,保留3位小数,*1000实现
);

	/*计算:s=340*t/2 m   =   340_000 mm * t /2
	记脉冲个数:N
	s=N*20*340_000/1000_000_000/2 mm = N*0.0034 mm = N*34/10000 ;
	5000/0.0034=1470588     21bit      ; 34      6bit
	*/

	reg                 echo_r1         ;
	reg                 echo_r2         ;
	wire                raise_flag      ;//上升沿标志信号
	wire                fall_flag       ;//下降沿标志信号
	reg     [20:0]      cnt_pulse       ;//考虑的范围25~4000mm
	reg     [20:0]      temp_pulse      ;
	reg 	[12:0] 		data_bin		;
	
	//raise_flag
	assign raise_flag = ((~echo_r2) && (echo_r1)) ? 1'b1 : 1'b0 ;
	assign fall_flag  = ((echo_r2) && (~echo_r1)) ? 1'b1 : 1'b0 ;
	
	//beat time
	always @ (posedge clk or negedge rst_n) begin
	    if(!rst_n) begin
	        echo_r1<=1'b0;
	        echo_r2<=1'b0;
	    end
	    else begin
	        echo_r1<=echo;
	        echo_r2<=echo_r1;
	    end
	end
	
	//cnt_pulse
	always @ (posedge clk or negedge rst_n) begin
	    if(!rst_n) begin
			cnt_pulse<=21'd0;
		end
	    else if(raise_flag) begin
			cnt_pulse<=21'd1;//直接从1开始计数,计算的时候就不需要再进行加1操作了
		end
	    else if(echo_r2) begin
			cnt_pulse<=cnt_pulse+1'b1;
		end
	    else begin
			cnt_pulse<=cnt_pulse;
		end
	end
	
	//temp_pulse
	always @ (posedge clk or negedge rst_n ) begin
	    if(!rst_n) begin
			temp_pulse<=21'd0;
		end
	    else if(fall_flag) begin
			temp_pulse<=cnt_pulse;
		end
	    else begin
			temp_pulse<=temp_pulse; 
		end    
	end
	
	//计算距离,以mm为单位,s=N*34/10000 
	always @ (posedge clk or negedge rst_n ) begin
	    if(!rst_n) begin
			fall_flag_r1<=1'b0;
		end
	    else begin
			fall_flag_r1<=fall_flag;
		end
	end
	
	//data_bin
	always @ (posedge clk or negedge rst_n ) begin
	    if(!rst_n) begin
			data_bin<=1'b0;
		end
	    else if(fall_flag_r1) begin
			data_bin<=temp_pulse*34/10000;
		end
	    else begin
			data_bin<=data_bin;
		end
	end

	assign data_o = data_bin;

endmodule 

均值滤波

//均值滤波算法

module  filter
(
    input   wire                clk             ,
    input   wire                rst_n           ,
    input   wire    [12:0]      data_bin        ,
    input   wire                fall_flag_r1    ,
    output  wire    [12:0]      data_ave             
);

    reg [12:0]  data_temp [7:0];
    reg [15:0]  data_sum       ;
    
    //data_temp:移位暂存8次测距的数值
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            data_temp[0]<=13'd0;
            data_temp[1]<=13'd0;
            data_temp[2]<=13'd0;
            data_temp[3]<=13'd0;
            data_temp[4]<=13'd0;
            data_temp[5]<=13'd0;
            data_temp[6]<=13'd0;
            data_temp[7]<=13'd0;
        end
        else if(fall_flag_r1) begin
            data_temp[0]<=data_bin  ;
            data_temp[1]<=data_temp[0] ;
            data_temp[2]<=data_temp[1] ;
            data_temp[3]<=data_temp[2] ;
            data_temp[4]<=data_temp[3] ;
            data_temp[5]<=data_temp[4] ;
            data_temp[6]<=data_temp[5] ;
            data_temp[7]<=data_temp[6] ;
        end
    end
    
    //data_sum:暂存的8个数据之和
    always @ (posedge clk or negedge rst_n ) begin
        if(!rst_n) begin
            data_sum<=1'd0;
        end
        else begin
            data_sum<=data_temp[0]+data_temp[1]+data_temp[2]+data_temp[3]+data_temp[4]+data_temp[5]+data_temp[6]+data_temp[7];
        end
    end
    
    //data_ave
    assign  data_ave    =   data_sum[15:3]      ;//截取高13位,相当于右移3位,即除以8的效果。

endmodule

二进制码转化为8421BCD码

//二进制码转化为8421BCD码

module  bin_to_bcd
(
    input   wire                clk         ,//The onboard clock frequency is 50MHz.
    input   wire                rst_n       ,
    input   wire    [12:0]      data_ave    ,
    output  reg     [15:0]      data_bcd             
);

    parameter   SHIFT_CNT_MAX = 5'd14       ;//输入的二进制数有多少位就进行多少次比较移位操作
    
    reg                 shift_flag      ;
    reg     [4:0]       shift_cnt       ;
    reg     [28:0]      data_shift      ;
    wire                complete_flag   ;
    wire                shift_cnt_flag  ;

    //shift_flag
    always @ (posedge clk or negedge rst_n ) begin
        if(!rst_n) begin
            shift_flag <= 1'b0;
        end
        else begin
            shift_flag <= ~shift_flag;
        end
    end
    
    //shift_cnt
    assign  shift_cnt_flag = shift_cnt==SHIFT_CNT_MAX ;
    
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            shift_cnt <= 5'd0;
        end
        else if(shift_flag) begin
            if(shift_cnt_flag) begin
                shift_cnt  <=  5'd0;
            end
            else begin
                shift_cnt <= shift_cnt+1'b1;
            end
        end
        else begin
            shift_cnt <= shift_cnt;
        end
    end
    
    //data_shift
    always @ (posedge clk or negedge rst_n ) begin
        if(!rst_n) begin
            data_shift <= 29'd0;
        end
        else if((!shift_cnt) && (shift_flag)) begin
            data_shift <= {16'b0,data_ave}; 
        end
        else if((shift_cnt>=1'b1)  && (shift_cnt <= 5'd13)) begin
            if(!shift_flag) begin //shift_flag==0,比较
                data_shift[16:13] <= (data_shift[16:13] > 4'd4) ? (data_shift[16:13]+4'd3) : data_shift[16:13];
                data_shift[20:17] <= (data_shift[20:17] > 4'd4) ? (data_shift[20:17]+4'd3) : data_shift[20:17];
                data_shift[24:21] <= (data_shift[24:21] > 4'd4) ? (data_shift[24:21]+4'd3) : data_shift[24:21];
                data_shift[28:25] <= (data_shift[28:25] > 4'd4) ? (data_shift[28:25]+4'd3) : data_shift[28:25];
            end
            else begin//shift_flag==1,移位
                data_shift <= data_shift<<1'b1;
            end
        end
        else begin
            data_shift <= data_shift;
        end
    end
    
    //complete_flag
    assign  complete_flag = (shift_cnt_flag && shift_flag) ? 1'b1: 1'b0 ;
    
    //data_bcd
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            data_bcd <= 16'd0;
        end
        else if(complete_flag) begin
            data_bcd <= data_shift[28:13];
        end
        else begin
            data_bcd <= data_bcd;
        end
    end

endmodule

RS232 UART

module FPGA_UART (
	input 	wire  			clk       ,
	input 	wire  			rst_n     ,
	input	wire  			uart_rx   ,
    input 	wire [16:00] 	data_o    ,
    
	output 	wire 			uart_tx
);
	parameter 	state_wait = 4'd0;
    parameter 	state_send = 4'd1;

    reg 	[7:0] 	uart_send_data = 8'd0;
    reg 			uart_send_valid;
    wire 			uart_send_ready;

    reg 	[31:0] 	counter = 32'd0;

    reg 	[3:0] 	state = state_wait;

    reg 			send_flag = 0; // 这个标志位的作用应该是表示移出数据标志位,这里懒得改了
	reg 			end_flag = 0;
	wire 	[15:00] data_buf; // 测试数据

	assign data_buf = data_o[15:0];

	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			uart_send_valid <= 0;
			counter <= 32'd0;
			uart_send_data <= 8'd0;
			state <= state_wait;
			send_flag = 0;
		end
		else begin
			case (state)
				state_wait: begin
					counter <= counter + 32'd1;
					if(counter >= 32'd24999999) begin
						counter <= 32'd0;
						state <= state_send;
						uart_send_data <= data_buf[15:8]; // 提前移出第一个字节
					end
				end
				state_send: begin
					if(end_flag) begin
						end_flag <= 0;
						uart_send_valid <= 0;
						send_flag <= 0;
						state <= state_wait;
					end
					else begin
						uart_send_valid <= 1;
						send_flag <= 1;
						if(send_flag) begin // 这里是为了让uart_send_valid维持一个周期后等第一个字节发送完成后移出后面的字节
							uart_send_data <= data_buf[7:0]; // 模拟字节移出
							end_flag = 1; // 强行退出发送状态,实际运用中会判断数据长度
						end
					end
				end
			endcase
		end
	end

    uart u0 (
    	.clk_clk (clk), // clk.clk
    	.reset_reset_n (rst_n), // reset.reset_n
    //	.rs232_0_from_uart_ready (<connected-to-rs232_0_from_uart_ready>), // rs232_0_avalon_data_receive_source.ready
    //	.rs232_0_from_uart_data (<connected-to-rs232_0_from_uart_data>), // .data
    //	.rs232_0_from_uart_error (<connected-to-rs232_0_from_uart_error>), // .error
    //	.rs232_0_from_uart_valid (<connected-to-rs232_0_from_uart_valid>), // .valid
    	.rs232_0_to_uart_data (uart_send_data), // rs232_0_avalon_data_transmit_sink.data
    //	.rs232_0_to_uart_error (<connected-to-rs232_0_to_uart_error>), // .error
    	.rs232_0_to_uart_valid (uart_send_valid), // .valid
    	.rs232_0_to_uart_ready (uart_send_ready), // .ready
    	.rs232_0_UART_RXD (uart_rx), // rs232_0_external_interface.RXD
    	.rs232_0_UART_TXD (uart_tx) // .TXD
    );


endmodule

顶层文件

module hc_sr_top (
    input   wire            clk     ,
    input   wire            rst_n   ,
    input   wire            echo    ,
    input   wire            uart_rx ,

    output  wire            trig    ,
    output  wire            uart_tx	,
    output 	wire [6:0]   	hex0	,
    output 	wire [6:0]   	hex1	,
    output 	wire [6:0]   	hex2	,
    output 	wire [6:0]   	hex3	,
    output 	wire [6:0]   	hex4	,
    output 	wire [6:0]   	hex5	,
    output 	wire [6:0]   	hex6
);

    wire           fall_flag_r1 ;
    wire [12:0]    data_o       ;//得到的超声波测得的距离值,以mm为单位
    wire [12:0]    data_ave     ;//均值滤波后的数据
    wire [15:0]    data_bcd     ;//显示的四位BCD码数据

    hc_sr_trig u_hc_sr_trig(
        .clk        (clk    ),
        .rst_n      (rst_n  ),

        .trig       (trig   )
    );

    hc_sr_echo u_hc_sr_echo(
        .clk            (clk            ),
        .rst_n          (rst_n          ),
        .echo           (echo           ),

        .fall_flag_r1   (fall_flag_r1   ),
        .data_o         (data_o         )
    );

    filter u_filter
    (
        .clk            (clk            ),
        .rst_n          (rst_n          ),
        .data_bin       (data_o         ),
        .fall_flag_r1   (fall_flag_r1   ),

        .data_ave       (data_ave       )      
    );

    //二进制码转化为8421BCD码
    bin_to_bcd u_bin_to_bcd
    (
        .clk        	(clk        ),//The onboard clock frequency is 50MHz.
        .rst_n      	(rst_n      ),
        .data_ave  		(data_ave   ),

        .data_bcd   	(data_bcd   )        
    );

    FPGA_UART u_FPGA_UART(
        .clk        	(clk    	),
        .rst_n      	(rst_n  	),
        .uart_rx    	(uart_rx	),
        .data_o     	(data_o 	),
	
        .uart_tx    	(uart_tx	)
    );
    
endmodule

五、参考与总结

参考:https://blog.csdn.net/jynyyhd/article/details/130899810

总结:
模块功能基本实现,能够测量距离
存在距离和延时问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值