【 FPGA 】超声波测距小实验(二):数码管显示回响信号脉冲宽度

版权声明:本博客内容来自于个人学习过程中的总结,参考了互联网、数据手册、帮助文档、书本以及论文等上的内容,仅供学习交流使用,如有侵权,请联系,我会重写!转载请注明地址! https://blog.csdn.net/Reborn_Lee/article/details/86551427

这篇博文快速完成,对上篇博文的一个延续,用动态扫描数码管显示模块来显示回响信号的脉冲长度,从而知道测距值。

上篇博文:超声波测距小实验(一)

关于数码管的动态扫描显示的博文之前也写了几篇,这里直接调用其模块即可:控制数码管动态扫描显示的小实验

功能框图:

同样为25MHz的时钟频率,所以段选,片选模块可以直接使用:

/////////////////////////////////////////////////////////////////////////////
//工程硬件平台: 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;
 
parameter N = 17;//高两位控制片选,其他位用于分频
//-------------------------------------------------
//分时显示数据控制单元
reg[3:0] current_display_num;	//当前显示数据
reg[N-1:0] div_cnt;	//分时计数器
 
	//分时计数器
always @(posedge clk or negedge rst_n)
	if(!rst_n) div_cnt <= 0;
	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[N-1:N-2])
			2'b00: current_display_num <= display_num[3:0];
			2'b01: current_display_num <= display_num[7:4];
			2'b10: current_display_num <= display_num[11:8];
			2'b11: 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[N-1:N-2])
			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
 

这里给出主模块:

/////////////////////////////////////////////////////////////////////////////
//工程硬件平台: Xilinx Spartan 6 FPGA
/////////////////////////////////////////////////////////////////////////////
//每秒产生1个超声波测距模块所需的10us高脉冲激励,并用数码管以16进制数据显示回响信号的高脉冲计数值(以10us为单位)
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产生一个时钟脉冲
			);			
		
//-------------------------------------
//每秒产生一个10us的高脉冲作为超声波测距模块的激励
wire[15:0] echo_pulse_num;	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值

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_num(echo_pulse_num)		//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
			);	

//-------------------------------------
//4位数码管显示驱动															

seg7		uut_seg7(
				.clk(clk_25m),		//时钟信号
				.rst_n(sys_rst_n),	//复位信号,低电平有效
				.display_num(echo_pulse_num),		//显示数据	
				.dtube_cs_n(dtube_cs_n),	//7段数码管位选信号
				.dtube_data(dtube_data)		//7段数码管段选信号(包括小数点为8段)
		);
		
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[15:0] echo_pulse_num	//以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
			);															
						
//-------------------------------------------------
//1s定时产生逻辑
reg[16:0] timer_cnt;	//1s计数器,以100KHz(10us)为单位进行计数,计数1s需要的计数范围是0~99999

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

assign ultrasound_trig = (timer_cnt == 17'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;

endmodule

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

看了这么多模块,感觉超声波控制模块ultrasound_controller 稍微有点难理解,其他都还行,这里简单讲解下这个程序:

//超声波测距模块的回响信号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信号下降沿标志位,高电平有效一个时钟周期

系统运行之前,总要先复位下,这时ultrasound_echo_r的值为00,之后若初遇回响信号ultrasound_echo(高电平),这个always块的操作是使得ultrasound_echo_r为01,此时,pos_en为1,这就找到了echo信号的上升沿,除了ultrasound_echo_r为01之外,其他任何时候都不会出现pos_en为1,秒否?

同理,如果回响信号从高电平变为低电平了,那么此时,ultrasound_echo_r为10,此时neg_echo为1,除此之外,任何时候neg_echo都不会为1,这就找到了回响信号的下降沿。

下面一段程序为:

//以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;

不难理解,上升沿到来时,以10us为单位的计数变量清零,开始计数:    else if(pos_echo) echo_cnt <= 16'd0;    //计数清零

当周期为10us的clk_100khz_en有效且回响信号ultrasound_echo_r[0]为高脉冲时,计数。

当下降沿时候,计数结束,将计数值给echo_pulse_num: else if(neg_echo) echo_pulse_num <= echo_cnt

就这样吧。

这里的计数值还是16进制,不太适合人眼看,16进制在大脑中转化为10进制还是有点障碍的,不过别急,后面的博文会解决此问题。

猜你喜欢

转载自blog.csdn.net/Reborn_Lee/article/details/86551427