基于FPGA的超声波测距

文章目录

  • 一、HC-SR04超声波测距模块说明
    • 1、产品特点
    • 2、电气参数
    • 3、HC-SR04超声波测距模块
    • 4、超声波时序图
  • 二、程序设计

一、HC-SR04超声波测距模块说明

1、产品特点

HC-SR04 超声波测距模块可提供 2cm-400cm 的非接触式距离感测功能,测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。

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

2、电气参数

基于FPGA的超声波测距_第1张图片

3、HC-SR04超声波测距模块

基于FPGA的超声波测距_第2张图片
VCC 供 5V电源,GND 为地线,TRIG 触 发 控 制 信 号 输入,ECHO 回响信号输出等四个接口端。

4、超声波时序图

基于FPGA的超声波测距_第3张图片
以上时序图表明你只需要提供一个 10uS 以上脉冲触发信号,该模块内部将
发出 8 个 40kHz 周期电平并检测回波。一旦检测到有回波信号则输出回响信号。
回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号
时间间隔可以计算得到距离。公式:uS/58=厘米或者 uS/148=英寸;或是:距离= 高电平时间*声速(340M/S)/2;建议测量周期为 60ms 以上,以防止发射信号对
回响信号的影响。

注:
1、此模块不宜带电连接,若要带电连接,则先让模块的 GND 端先连接,否则会影响模块的正常工作。
2、测距时,被测物体的面积不少于 0.5 平方米且平面尽量要求平整,否则影响测量的结果

二、程序设计

基于FPGA的超声波测距_第4张图片

/*================================================*\
		  Filename ﹕
			Author ﹕
	  Description  ﹕产生周期为1us的时钟信号
		 Called by ﹕
Revision History   ﹕ mm/dd/202x
		  			  Revision 1.0
  			  Email﹕ 
			Company﹕ 
\*================================================*/
module 	clk_div(
	input  wire			Clk		, //system clock 50MHz
	input  wire 		Rst_n	, //reset ,low valid
		   
	output wire  		clk_us 	  //
);
//Parameter Declarations
	parameter CNT_MAX = 19'd50;//1us的计数值为 50 * Tclk(20ns)

//Interrnal wire/reg declarations
	reg		[5:00]	cnt		; //Counter 
	wire			add_cnt ; //Counter Enable
	wire			end_cnt ; //Counter Reset 
	
//Logic Description
	
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)begin  
			cnt <= 'd0; 
		end  
		else if(add_cnt)begin  
			if(end_cnt)begin  
				cnt <= 'd0; 
			end  
			else begin  
				cnt <= cnt + 1'b1; 
			end  
		end  
		else begin  
			cnt <= cnt;  
		end  
	end 
	
	assign add_cnt = 1'b1; 
	assign end_cnt = add_cnt && cnt >= CNT_MAX - 19'd1;
	
	assign clk_us = end_cnt;
	

endmodule 


/*================================================*\
		  Filename ﹕
			Author ﹕
	  Description  ﹕超声波检测距离模块
					本模块理论测试距离 2cm~510cm
						输出结果保留两位小数
		 Called by ﹕
Revision History   ﹕ mm/dd/202x
		  			  Revision 1.0
  			  Email﹕ 
			Company﹕ 
\*================================================*/
module 	hc_sr_echo(
	input  wire 		Clk		, //clock 50MHz
	input  wire			clk_us	, //system clock 1MHz
	input  wire 		Rst_n	, //reset ,low valid
		   
	input  wire 		echo	, //
	output wire [31:00]	data_o	  //检测距离,保留3位小数,*1000实现
);
/* 		S(um) = 17 * t 		-->  x.abc cm	*/
//Parameter Declarations
	parameter T_MAX = 16'd60_000;//510cm 对应计数值

//Interrnal wire/reg declarations
	reg				r1_echo,r2_echo; //边沿检测	
	wire			echo_pos,echo_neg; //
	
	reg		[15:00]	cnt		; //Counter 
	wire			add_cnt ; //Counter Enable
	wire			end_cnt ; //Counter Reset 
	
	reg		[31:00]	data_r	;
//Logic Description
	//如果使用clk_us 检测边沿,延时2us,差值过大
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)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 Rst_n)begin  
		if(!Rst_n)begin  
			cnt <= 'd0; 
		end 
		else if(add_cnt)begin  
			if(end_cnt)begin  
				cnt <= cnt; 
			end  
			else begin  
				cnt <= cnt + 1'b1; 
			end  
		end  
		else begin  //echo 低电平 归零
			cnt <= 'd0;  
		end  
	end 
	
	assign add_cnt = echo; 
	assign end_cnt = add_cnt && cnt >= T_MAX - 1; //超出最大测量范围则保持不变,极限
	
	always @(posedge Clk or negedge Rst_n)begin  
		if(!Rst_n)begin  
			data_r <= 'd2;
		end  
		else if(echo_neg)begin  
			data_r <= (cnt << 4) + cnt;
		end  
		else begin  
			data_r <= data_r;
		end  
	end //always end
	
	assign data_o = data_r >> 1;

endmodule 


/*================================================*\
		  Filename ﹕
			Author ﹕
	  Description  ﹕超声波触发测距模块
					波形周期300ms,前10us高电平
		 Called by ﹕
Revision History   ﹕ mm/dd/202x
		  			  Revision 1.0
  			  Email﹕ 
			Company﹕ 
\*================================================*/
module 	hc_sr_trig(
	input  wire			clk_us	, //system clock 1MHz
	input  wire 		Rst_n	, //reset ,low valid
		   
	output wire  		trig	  //触发测距信号
);
//Parameter Declarations
	parameter CYCLE_MAX = 19'd300_000;

//Interrnal wire/reg declarations
	reg		[18:00]	cnt		; //Counter 
	wire			add_cnt ; //Counter Enable
	wire			end_cnt ; //Counter Reset 

//Logic Description	
	
	always @(posedge clk_us or negedge Rst_n)begin  
		if(!Rst_n)begin  
			cnt <= 'd0; 
		end  
		else if(add_cnt)begin  
			if(end_cnt)begin  
				cnt <= 'd0; 
			end  
			else begin  
				cnt <= cnt + 1'b1; 
			end  
		end  
		else begin  
			cnt <= cnt;  
		end  
	end 
	
	assign add_cnt = 1'b1; 
	assign end_cnt = add_cnt && cnt >= CYCLE_MAX - 9'd1; 
	
	assign trig = cnt < 15 ? 1'b1 : 1'b0;

endmodule 


数码管显示

/*================================================*\
		  Filename ﹕seg_driver.v
			Author ﹕Adolph
	  Description  ﹕对输入的数据译码,并驱动数码管显示对应数据
		 Called by ﹕seg_top.v
Revision History   ﹕ 2022-5-30 14:27:22
		  			  Revision 1.0
  			  Email﹕[email protected]
			Company﹕ 
\*================================================*/
module seg_driver(
	input			clk		,
	input			rst_n	,
	
	input	  [31:0]dis_data,//待显示的数据
	output wire [7:0]	dig_sel	,
	output wire [7:0]	dig_seg	 
);
//wire  [31:0]dis_data;


//	assign seg_r = 8'd0;
//	assign sel_r = 1'b0;
	localparam
		NUM_0  	= 8'hC0,
		NUM_1  	= 8'hF9,
		NUM_2  	= 8'hA4,
		NUM_3  	= 8'hB0,
		NUM_4  	= 8'h99,
		NUM_5  	= 8'h92,
		NUM_6  	= 8'h82,
		NUM_7  	= 8'hF8,
		NUM_8  	= 8'h80,
		NUM_9  	= 8'h90,
		NUM_A  	= 8'h88,
		NUM_B  	= 8'h83,
		NUM_C  	= 8'hC6,
		NUM_D  	= 8'hA1,
		NUM_E  	= 8'h86,
		NUM_F  	= 8'h8E,
		LIT_ALL	= 8'h00,
		BLC_ALL	= 8'hFF;
	parameter CNT_REF = 25'd1000;
	
	reg	[9:0]	cnt_20us; //20us计数器
	reg	[3:0] 	data_tmp; //用于取出不同位选的显示数据
	
reg [7:0] seg_r;
reg [7:0] sel_r;
//	assign dis_data = 32'hABCD_4413;
//描述位选信号切换
	//描述刷新计数器
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			cnt_20us <= 25'd0;
		end
		else if(cnt_20us >= CNT_REF - 25'd1)begin
			cnt_20us <= 25'd0;
		end
		else begin
			cnt_20us <= cnt_20us + 25'd1;
		end
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			sel_r <= 8'hfe;//8'b1111_1110
		end
		else if(cnt_20us >= CNT_REF - 25'd1)begin
			sel_r <= {sel_r[6:0],sel_r[7]};
		end
		else begin
			sel_r <= sel_r;
		end
	end
	
//段选信号描述
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			data_tmp <= 4'd0;
		end
		else begin
			case(sel_r)
				8'b1111_1110:data_tmp <= dis_data[ 3-:4];
				8'b1111_1101:data_tmp <= dis_data[ 7-:4];
				8'b1111_1011:data_tmp <= dis_data[11-:4];
				8'b1111_0111:data_tmp <= dis_data[15-:4];
				8'b1110_1111:data_tmp <= dis_data[19-:4];
				8'b1101_1111:data_tmp <= dis_data[23-:4];
				8'b1011_1111:data_tmp <= dis_data[27-:4];
				8'b0111_1111:data_tmp <= dis_data[31-:4];
				default: data_tmp <= 4'hF;
			endcase
		end
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			seg_r <= BLC_ALL;
		end
		else begin
			case(data_tmp)
				4'h0 : seg_r <= NUM_0;
				4'h1 : seg_r <= NUM_1;
				4'h2 : seg_r <= NUM_2;
				4'h3 : seg_r <= NUM_3;
				4'h4 : seg_r <= NUM_4;
				4'h5 : seg_r <= NUM_5;
				4'h6 : seg_r <= NUM_6;
				4'h7 : seg_r <= NUM_7;
				4'h8 : seg_r <= NUM_8;
				4'h9 : seg_r <= NUM_9;
				4'hA : seg_r <= NUM_A;
				4'hB : seg_r <= NUM_B;
				4'hC : seg_r <= NUM_C;
				4'hD : seg_r <= NUM_D;
				4'hE : seg_r <= NUM_E;
				4'hF : seg_r <= NUM_F;
				default: ;
			endcase 
		end
	end

assign dig_seg = seg_r;
assign dig_sel = sel_r;

endmodule 

顶层文件

module HC_SR04_TOP (
    input       wire        clk,
    input       wire        rst_n,
    input       wire        echo,

    output      wire        trig,
    output      wire [7:0]   sel,
    output      wire [7:0]   seg
);

wire clk_us;
wire [31:0] data;

clk_div clk_div_inst(
	.		Clk		(clk), //system clock 50MHz
	.		Rst_n	(rst_n), //reset ,low valid

	.		clk_us 	 (clk_us) //
);

hc_sr_trig hc_sr_trig_inst(
	.		clk_us	(clk_us), //system clock 1MHz
	.		Rst_n	(rst_n), //reset ,low valid

	. 		trig	 (trig) //触发测距信号
);

hc_sr_echo hc_sr_echo_inst(
	.Clk		(clk), //clock 50MHz
	.clk_us	(clk_us), //system clock 1MHz
	.Rst_n	(rst_n), //reset ,low valid

	.echo	(echo), //
	.data_o	 (data) //检测距离,保留3位小数,*1000实现
);

seg_driver seg_driver_inst(
	.clk	(clk)	,
	.rst_n	(rst_n),
	
	.dis_data (data),//待显示的数据
	.dig_sel    (sel),
	.dig_seg	(seg)
);

endmodule //top

仿真测试

/*================================================*\
		  Filename ﹕tb_hc_sr.v
			Author ﹕Adolph
	  Description  ﹕超声波驱动测试文件
		 Called by ﹕
Revision History   ﹕ mm/dd/202x
		  			  Revision 1.0
  			  Email﹕ 
			Company﹕ 
\*================================================*/
`timescale 1ns/1ns 		//仿真系统时间尺度定义

`define clk_period 20  	//时钟周期参数定义	

module tb_hc_sr(); 
//激励信号定义  
	reg				clk		; 
	reg				rst_n	; 
	reg				echo; //
	
//响应信号定义	  
	wire	 		trig	;
	
	wire[7:0]       seg     ;

    wire[7:0]       sel     ;            
//实例化
	HC_SR04_TOP		HC_SR04_TOP(
		/*input  		*/.clk		(clk		), //system clock 50MHz
		/*input   		*/.rst_n	(rst_n		), //reset ,low valid
		
		/*input   		*/.echo		(echo		), //
		/*output   		*/.trig		(trig		), //触发测距信号
		
		/*output   		*/.seg		(seg		), //
		/*output   		*/.sel		(sel		) //触发测距信号
	);

//产生时钟							       		 
	initial clk = 1'b0;		       		 
	always #(`clk_period / 2) clk = ~clk;  		 

//产生激励	 
	initial  begin	 
		rst_n = 1'b0;	 
		echo  = 1'b0;
		#(`clk_period * 20 + 3);	 
		rst_n = 1'b1;	 
		#(`clk_period * 20); 
		
		wait(HC_SR04_TOP.hc_sr_echo_inst.cnt == 240);
		echo = 1'b1;//测试超声波信号发送完成,echo拉高
		
		#(50 * `clk_period * 2500 + 7);
		echo = 1'b0;
		
		#(`clk_period * 200);
		$stop(2); 
	end	 

endmodule

参考链接
https://blog.csdn.net/qq_43546203/article/details/125281386
https://blog.csdn.net/qq_43546203/article/details/125282050

你可能感兴趣的:(fpga开发)