【 FPGA 】超声波测距小实验(四):数码管显示测距结果

上篇博文:测了回响脉冲的宽度为多少个10us,这篇博文要算出距离,且用数码管显示距离的十进制结果。

功能框图如下:

 

距离计算:

还是先给出程序之后在简单解释吧。

顶层模块:

/
//工程硬件平台: Xilinx Spartan 6 FPGA
/
//每100ms产生1个超声波测距模块所需的10us高脉冲激励,并用数码管以10进制数据显示最终经过换算后的距离信息(单位mm)
module sp6(
			input ext_clk_25m,	//外部输入25MHz时钟信号
			input ext_rst_n,	//外部输入复位信号,低电平有效
			output ultrasound_trig,	//超声波测距模块脉冲激励信号,10us的高脉冲
			input ultrasound_echo,		//超声波测距模块回响信号
			output[3:0] dtube_cs_n,	//7段数码管位选信号
			output[7:0] dtube_data	//7段数码管段选信号(包括小数点为8段)			
		);													

//-------------------------------------
//PLL例化
wire clk_12m5;	//PLL输出12.5MHz时钟
wire clk_25m;	//PLL输出25MHz时钟
wire clk_50m;	//PLL输出50MHz时钟
wire clk_100m;	//PLL输出100MHz时钟
wire sys_rst_n;	//PLL输出的locked信号,作为FPGA内部的复位信号,低电平复位,高电平正常工作

  pll_controller uut_pll_controller
   (// Clock in ports
    .CLK_IN1(ext_clk_25m),      // IN
    // Clock out ports
    .CLK_OUT1(clk_12m5),     // OUT
    .CLK_OUT2(clk_25m),     // OUT
    .CLK_OUT3(clk_50m),     // OUT
    .CLK_OUT4(clk_100m),     // OUT
    // Status and control signals
    .RESET(~ext_rst_n),// IN
    .LOCKED(sys_rst_n));      // OUT		
		
//-------------------------------------
//25MHz时钟进行分频,产生一个100KHz频率的时钟使能信号
wire clk_100khz_en;	//100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲

clkdiv_generation	uut_clkdiv_generation(
				.clk(clk_25m),		//时钟信号
				.rst_n(sys_rst_n),	//复位信号,低电平有效
				.clk_100khz_en(clk_100khz_en)	//100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
			);			
		
//-------------------------------------
//每100ms产生一个10us的高脉冲作为超声波测距模块的激励
wire[15:0] echo_pulse_num;	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
wire echo_pulse_en;		//超声波测距模块回响信号计数值有效信号

ultrasound_controller	uut_ultrasound_controller(
				.clk(clk_25m),		//时钟信号
				.rst_n(sys_rst_n),	//复位信号,低电平有效
				.clk_100khz_en(clk_100khz_en),	//100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
				.ultrasound_trig(ultrasound_trig),	//超声波测距模块脉冲激励信号,10us的高脉冲
				.ultrasound_echo(ultrasound_echo),		//超声波测距模块回响信号
				.echo_pulse_en(echo_pulse_en),		//超声波测距模块回响信号计数值有效信号
				.echo_pulse_num(echo_pulse_num)		//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
			);	
			
//-------------------------------------
//缓存最近采集到的8组超声波测距回响脉冲计数值,对它们进行累加并求平均
wire[15:0] echo_pulse_filter_num;	//滤波处理后的超声波测距模块回响信号高脉冲计数值

filter		uut_filter(
				.clk(clk_25m),		//时钟信号
				.rst_n(sys_rst_n),	//复位信号,低电平有效
				.echo_pulse_en(echo_pulse_en),		//超声波测距模块回响信号计数值有效信号
				.echo_pulse_num(echo_pulse_num),		//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
				.echo_pulse_filter_num(echo_pulse_filter_num)	//滤波处理后的超声波测距模块回响信号高脉冲计数值
			);	
			
//-------------------------------------
//换算出超声波测距的实际距离,并且以十进制,单位mm形式输出	
wire[15:0] echo_pulse_f_mul_num;	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值,换算为实际距离的10进制数据

distance_compute	uut_distance_compute(
						.clk(clk_25m),		//时钟信号
						.rst_n(sys_rst_n),	//复位信号,低电平有效
						.echo_pulse_filter_num(echo_pulse_filter_num),	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
						.echo_pulse_f_mul_num(echo_pulse_f_mul_num)	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值,换算为实际距离的10进制数据
					);	
			
//-------------------------------------
//4位数码管显示驱动															

seg7		uut_seg7(
				.clk(clk_25m),		//时钟信号
				.rst_n(sys_rst_n),	//复位信号,低电平有效
				.display_num(echo_pulse_f_mul_num),		//显示数据	
				.dtube_cs_n(dtube_cs_n),	//7段数码管位选信号
				.dtube_data(dtube_data)		//7段数码管段选信号(包括小数点为8段)
		);
		
endmodule

子模块:

/
//工程硬件平台: Xilinx Spartan 6 FPGA
//25°C时,声音在空气中传播的速度为346m/s
//因此取距离s的单位是米(m),时间t的单位是秒(s),有 s = 346*t/2
//若取距离s的单位是毫米(mm),时间t的单位是10微秒(10us),有s*0.001 = 346*t*0.00001/2,即s = 1.73*t
//为了便于计算,取s = ((1.73*256)*t)/256 = (443*t)/256

module distance_compute(
				input clk,		//外部输入25MHz时钟信号
				input rst_n,	//外部输入复位信号,低电平有效
				input[15:0] echo_pulse_filter_num,	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
				output[15:0] echo_pulse_f_mul_num	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值,换算为实际距离的10进制数据
			);															
						
//-------------------------------------------------
//距离换算
wire[31:0] mul_out;	//输出的乘法运算结果,取bit23-8为有效的16bit数据
	
mul 	uut_mul (
		  .clk(clk), // input clk
		  .a(16'd443), // input [15 : 0] a
		  .b(echo_pulse_filter_num), // input [15 : 0] b
		  .p(mul_out) // output [31 : 0] p
		);	
	
//-------------------------------------------------	
//将16进制数据转换为10进制,由于我们已知有效的16bit数据的有效范围是0-4000mm
wire[15:0] thousand_quotint,thousand_fractional;	//千位除法运算结果与余数寄存器

	//千位运算
div 		thousand_div (
				.clk(clk), // input clk
				.rfd(), // output rfd
				.dividend(mul_out[23:8]), // input [15 : 0] dividend
				.divisor(16'd1000), // input [15 : 0] divisor
				.quotient(thousand_quotint), // output [15 : 0] quotient
				.fractional(thousand_fractional) // output [15 : 0] fractional
			);	
	
wire[15:0] hundred_quotint,hundred_fractional;	//百位除法运算结果与余数寄存器

	//百位运算
div 		hundred_div (
				.clk(clk), // input clk
				.rfd(), // output rfd
				.dividend(thousand_fractional), // input [15 : 0] dividend
				.divisor(16'd100), // input [15 : 0] divisor
				.quotient(hundred_quotint), // output [15 : 0] quotient
				.fractional(hundred_fractional) // output [15 : 0] fractional
			);	

wire[15:0] ten_quotint,ten_fractional;	//十位除法运算结果与余数寄存器
			
	//十位运算
div 		ten_div (
				.clk(clk), // input clk
				.rfd(), // output rfd
				.dividend(hundred_fractional), // input [15 : 0] dividend
				.divisor(16'd10), // input [15 : 0] divisor
				.quotient(ten_quotint), // output [15 : 0] quotient
				.fractional(ten_fractional) // output [15 : 0] fractional
			);

assign echo_pulse_f_mul_num = {thousand_quotint[3:0],hundred_quotint[3:0],ten_quotint[3:0],ten_fractional[3:0]};

			
endmodule

/
//工程硬件平台: Xilinx Spartan 6 FPGA
/
module clkdiv_generation(
				input clk,		//外部输入25MHz时钟信号
				input rst_n,	//外部输入复位信号,低电平有效
				output clk_100khz_en	//100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
			);															
						
//-------------------------------------------------
//时钟分频产生
reg[7:0] cnt;	//时钟分频计数器,0-249

	//1s定时计数
always @(posedge clk or negedge rst_n)
	if(!rst_n) cnt <= 8'd0;
	else if(cnt < 8'd249) cnt <= cnt+1'b1;
	else cnt <= 8'd0;

assign clk_100khz_en = (cnt == 8'd249) ? 1'b1:1'b0;		//每40us产生一个40ns的高脉冲				

endmodule
/
//工程硬件平台: Xilinx Spartan 6 FPGA
/
module ultrasound_controller(
				input clk,		//外部输入25MHz时钟信号
				input rst_n,	//外部输入复位信号,低电平有效
				input clk_100khz_en,	//100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
				output ultrasound_trig,	//超声波测距模块脉冲激励信号,10us的高脉冲
				input ultrasound_echo,		//超声波测距模块回响信号
				output reg echo_pulse_en,		//超声波测距模块回响信号计数值有效信号
				output reg[15:0] echo_pulse_num	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
			);															
						
//-------------------------------------------------
//1s定时产生逻辑
reg[13:0] timer_cnt;	//1s计数器,以100KHz(10us)为单位进行计数,计数100ms需要的计数范围是0~9999

	//1s定时计数
always @(posedge clk or negedge rst_n)
	if(!rst_n) timer_cnt <= 14'd0;
	else if(clk_100khz_en) begin
		if(timer_cnt < 14'd9_999) timer_cnt <= timer_cnt+1'b1;
		else timer_cnt <= 14'd0;
	end
	else ;

assign ultrasound_trig = (timer_cnt == 14'd1) ? 1'b1:1'b0;		//10us高脉冲生成						

//-------------------------------------------------
//超声波测距模块的回响信号echo打两拍,产生上升沿和下降沿标志位
reg[1:0] ultrasound_echo_r;

always @(posedge clk or negedge rst_n)
	if(!rst_n) ultrasound_echo_r <= 2'b00;
	else ultrasound_echo_r <= {ultrasound_echo_r[0],ultrasound_echo};

wire pos_echo = ~ultrasound_echo_r[1] & ultrasound_echo_r[0];	//echo信号上升沿标志位,高电平有效一个时钟周期
wire neg_echo = ultrasound_echo_r[1] & ~ultrasound_echo_r[0];	//echo信号下降沿标志位,高电平有效一个时钟周期

//-------------------------------------------------
//以10us为单位对超声波测距模块回响信号高脉冲进行计数
reg[15:0] echo_cnt;		//回响高脉冲计数器

always @(posedge clk or negedge rst_n)
	if(!rst_n) echo_cnt <= 16'd0;
	else if(pos_echo) echo_cnt <= 16'd0;	//计数清零
	else if(clk_100khz_en && ultrasound_echo_r[0]) echo_cnt <= echo_cnt+1'b1;
	else ;
	
	//计数脉冲数锁存
always @(posedge clk or negedge rst_n)
	if(!rst_n) echo_pulse_num <= 16'd0;	
	else if(neg_echo) echo_pulse_num <= echo_cnt;

	//计数脉冲有效使能信号锁存
always @(posedge clk or negedge rst_n)
	if(!rst_n) echo_pulse_en <= 1'b0;
	else echo_pulse_en <= neg_echo;
	
endmodule

/
//工程硬件平台: Xilinx Spartan 6 FPGA
/
//缓存最近采集到的8组超声波测距回响脉冲计数值,对它们进行累加并求平均
module filter(
				input clk,		//外部输入25MHz时钟信号
				input rst_n,	//外部输入复位信号,低电平有效
				input echo_pulse_en,		//超声波测距模块回响信号计数值有效信号
				input[15:0] echo_pulse_num,	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
				output[15:0] echo_pulse_filter_num	//滤波处理后的超声波测距模块回响信号高脉冲计数值
			);															
						
//-------------------------------------------------
//echo_pulse_num信号缓存10拍
reg[15:0] pulse_reg[7:0];	//echo_pulse_num信号缓存寄存器
				
always @(posedge clk or negedge rst_n)
	if(!rst_n) begin
		pulse_reg[0] <= 16'd0;
		pulse_reg[1] <= 16'd0;
		pulse_reg[2] <= 16'd0;
		pulse_reg[3] <= 16'd0;
		pulse_reg[4] <= 16'd0;
		pulse_reg[5] <= 16'd0;
		pulse_reg[6] <= 16'd0;
		pulse_reg[7] <= 16'd0;
	end
	else if(echo_pulse_en) begin	//缓存最新的数据,使用移位寄存器的方式推进最新数据,推出最老的数据
		pulse_reg[0] <= echo_pulse_num;
		pulse_reg[1] <= pulse_reg[0];
		pulse_reg[2] <= pulse_reg[1];
		pulse_reg[3] <= pulse_reg[2];
		pulse_reg[4] <= pulse_reg[3];
		pulse_reg[5] <= pulse_reg[4];
		pulse_reg[6] <= pulse_reg[5];
		pulse_reg[7] <= pulse_reg[6];
	end

//-------------------------------------------------
//对8个数据累加并输出平均值
reg[15:0] sum_pulse_reg;

always @(posedge clk or negedge rst_n)
	if(!rst_n) sum_pulse_reg <= 16'd0;
	else sum_pulse_reg <= pulse_reg[0]+pulse_reg[1]+pulse_reg[2]+pulse_reg[3]+pulse_reg[4]+pulse_reg[5]+pulse_reg[6]+pulse_reg[7];

assign echo_pulse_filter_num = {3'b000,sum_pulse_reg[15:3]};	//右移3位,相当于除以8


endmodule

/
//工程硬件平台: Xilinx Spartan 6 FPGA
/
module seg7(
			input clk,		//时钟信号,25MHz
			input rst_n,	//复位信号,低电平有效
			input[15:0] display_num,	//数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
			output reg[3:0] dtube_cs_n,	//7段数码管位选信号
			output reg[7:0] dtube_data	//7段数码管段选信号(包括小数点为8段)
		);

//-------------------------------------------------
//参数定义

//数码管显示 0~F 对应段选输出
parameter 	NUM0 	= 8'h3f,//c0,
			NUM1 	= 8'h06,//f9,
			NUM2 	= 8'h5b,//a4,
			NUM3 	= 8'h4f,//b0,
			NUM4 	= 8'h66,//99,
			NUM5 	= 8'h6d,//92,
			NUM6 	= 8'h7d,//82,
			NUM7 	= 8'h07,//F8,
			NUM8 	= 8'h7f,//80,
			NUM9 	= 8'h6f,//90,
			NUMA 	= 8'h77,//88,
			NUMB 	= 8'h7c,//83,
			NUMC 	= 8'h39,//c6,
			NUMD 	= 8'h5e,//a1,
			NUME 	= 8'h79,//86,
			NUMF 	= 8'h71,//8e;
			NDOT	= 8'h80;	//小数点显示

//数码管位选 0~3 对应输出
parameter	CSN		= 4'b1111,
			CS0		= 4'b1110,
			CS1		= 4'b1101,
			CS2		= 4'b1011,
			CS3		= 4'b0111;

//-------------------------------------------------
//分时显示数据控制单元
reg[3:0] current_display_num;	//当前显示数据
reg[7:0] div_cnt;	//分时计数器

	//分时计数器
always @(posedge clk or negedge rst_n)
	if(!rst_n) div_cnt <= 8'd0;
	else div_cnt <= div_cnt+1'b1;

	//显示数据
always @(posedge clk or negedge rst_n)
	if(!rst_n) current_display_num <= 4'h0;
	else begin
		case(div_cnt)
			8'hff: current_display_num <= display_num[3:0];
			8'h3f: current_display_num <= display_num[7:4];
			8'h7f: current_display_num <= display_num[11:8];
			8'hbf: current_display_num <= display_num[15:12];
			default: ;
		endcase
	end
		
	//段选数据译码
always @(posedge clk or negedge rst_n)
	if(!rst_n) dtube_data <= NUM0;
	else begin
		case(current_display_num) 
			4'h0: dtube_data <= NUM0;
			4'h1: dtube_data <= NUM1;
			4'h2: dtube_data <= NUM2;
			4'h3: dtube_data <= NUM3;
			4'h4: dtube_data <= NUM4;
			4'h5: dtube_data <= NUM5;
			4'h6: dtube_data <= NUM6;
			4'h7: dtube_data <= NUM7;
			4'h8: dtube_data <= NUM8;
			4'h9: dtube_data <= NUM9;
			4'ha: dtube_data <= NUMA;
			4'hb: dtube_data <= NUMB;
			4'hc: dtube_data <= NUMC;
			4'hd: dtube_data <= NUMD;
			4'he: dtube_data <= NUME;
			4'hf: dtube_data <= NUMF;
			default: ;
		endcase
	end

	//位选译码
always @(posedge clk or negedge rst_n)
	if(!rst_n) dtube_cs_n <= CSN;
	else begin
		case(div_cnt[7:6])
			2'b00: dtube_cs_n <= CS0;
			2'b01: dtube_cs_n <= CS1;
			2'b10: dtube_cs_n <= CS2;
			2'b11: dtube_cs_n <= CS3;
			default:  dtube_cs_n <= CSN;
		endcase
	end
	

endmodule

其他,前几篇博文已经讲了。这里重点讲讲模块distance_compute:

原理:

//25°C时,声音在空气中传播的速度为346m/s
//因此取距离s的单位是米(m),时间t的单位是秒(s),有 s = 346*t/2
//若取距离s的单位是毫米(mm),时间t的单位是10微秒(10us),有s*0.001 = 346*t*0.00001/2,即s = 1.73*t
//为了便于计算,取s = ((1.73*256)*t)/256 = (443*t)/256

关于443与脉宽t相乘,用了一个相乘的IP核,IP核模板为:

mul your_instance_name (
  .clk(clk), // input clk
  .a(a), // input [15 : 0] a
  .b(b), // input [15 : 0] b
  .p(p) // output [31 : 0] p
);

//距离换算
wire[31:0] mul_out;    //输出的乘法运算结果,取bit23-8为有效的16bit数据
    
mul     uut_mul (
          .clk(clk), // input clk
          .a(16'd443), // input [15 : 0] a
          .b(echo_pulse_filter_num), // input [15 : 0] b
          .p(mul_out) // output [31 : 0] p
        );    

相乘的结果是32位的mul_out;

之后对这个结果进行除以256以及转换为10进制的运算:

除以256相当于被除数右移8位,并且被除数有最大值,可以确定32位中的多少位有数,这里是mul_out[23:8],作为结果。

之后除以1000得到结果作为十进制的千位,余数除以100,得到结果作为百位,余数除以10得到十位,余数作为个位。

程序体现:

wire[15:0] thousand_quotint,thousand_fractional;    //千位除法运算结果与余数寄存器

    //千位运算
div         thousand_div (
                .clk(clk), // input clk
                .rfd(), // output rfd
                .dividend(mul_out[23:8]), // input [15 : 0] dividend
                .divisor(16'd1000), // input [15 : 0] divisor
                .quotient(thousand_quotint), // output [15 : 0] quotient
                .fractional(thousand_fractional) // output [15 : 0] fractional
            );    
    
wire[15:0] hundred_quotint,hundred_fractional;    //百位除法运算结果与余数寄存器

    //百位运算
div         hundred_div (
                .clk(clk), // input clk
                .rfd(), // output rfd
                .dividend(thousand_fractional), // input [15 : 0] dividend
                .divisor(16'd100), // input [15 : 0] divisor
                .quotient(hundred_quotint), // output [15 : 0] quotient
                .fractional(hundred_fractional) // output [15 : 0] fractional
            );    

wire[15:0] ten_quotint,ten_fractional;    //十位除法运算结果与余数寄存器
            
    //十位运算
div         ten_div (
                .clk(clk), // input clk
                .rfd(), // output rfd
                .dividend(hundred_fractional), // input [15 : 0] dividend
                .divisor(16'd10), // input [15 : 0] divisor
                .quotient(ten_quotint), // output [15 : 0] quotient
                .fractional(ten_fractional) // output [15 : 0] fractional
            );

assign echo_pulse_f_mul_num = {thousand_quotint[3:0],hundred_quotint[3:0],ten_quotint[3:0],ten_fractional[3:0]};
 

 

 

 

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
以下是使用US-100超声波传感器实现FPGA超声波测距的驱动代码,使用VHDL语言编写: ```vhdl library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity us100 is Port ( trig : in STD_LOGIC; echo : out STD_LOGIC; clk : in STD_LOGIC; rst : in STD_LOGIC; distance : out integer range 0 to 4000); end us100; architecture Behavioral of us100 is signal start : std_logic := '0'; signal done : std_logic := '0'; signal count : integer range 0 to 40000 := 0; signal distance_raw : integer range 0 to 4000 := 0; begin -- 计数器计数 count <= count + 1 when clk'event and clk = '1' and rst = '0' and done = '0'; -- 触发信号 start <= trig and not trig'last_value when trig'event and trig = '1'; -- 计算距离 process (clk, rst) begin if rst = '1' then distance_raw <= 0; done <= '0'; elsif clk'event and clk = '1' then if start = '1' then distance_raw <= count / 58; done <= '1'; end if; end if; end process; -- 距离输出 distance <= distance_raw when done = '1' else 0; -- 回波信号输出 echo <= '1' when count < distance_raw * 58 else '0'; end Behavioral; ``` 在代码中,`trig`为超声波传感器的触发信号输入,`echo`为回波信号输出,`clk`为时钟信号输入,`rst`为复位信号输入,`distance`为距离输出。在代码中,使用计数器来计算超声波发射和接收的时间,并根据时间计算距离,最终输出距离和回波信号。需要注意的是,本代码中使用的距离单位为厘米,因此需要将计算结果除以58转换为厘米。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李锐博恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值