【 FPGA 】超声波测距小实验(三)回响脉宽计数之均值滤波

这是小实验还是接着上个小实验的:超声波测距小实验(一)

数码管显示回响信号脉冲宽度

先说说实验的要求:

超声波测距回响脉宽计数之均值滤波处理,每100ms产生1个超声波测距模块所需的10us高脉冲激励(超声波测距模块的触发信号),并用数码管以16进制数据显示经过滤波处理的回响信号的高脉冲计数值(以10us为单位)。

如下为功能框图:

滤波算法与实现:

先给出主模块程序,再给出其他子模块的程序,然后选取其中的某些部分来讲解:

主模块:

/
//工程硬件平台: Xilinx Spartan 6 FPGA
/
//每100ms产生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产生一个时钟脉冲);			//-------------------------------------
//每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)	//滤波处理后的超声波测距模块回响信号高脉冲计数值);	//-------------------------------------
//4位数码管显示驱动															seg7		uut_seg7(.clk(clk_25m),		//时钟信号.rst_n(sys_rst_n),	//复位信号,低电平有效.display_num(echo_pulse_filter_num),		//显示数据	.dtube_cs_n(dtube_cs_n),	//7段数码管位选信号.dtube_data(dtube_data)		//7段数码管段选信号(包括小数点为8段));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
//开发套件型号: SF-SP6 特权打造
//版   权  申   明: 本例程由《深入浅出玩转FPGA》作者“特权同学”原创,
//				仅供SF-SP6开发套件学习使用,谢谢支持
//官方淘宝店铺: http://myfpga.taobao.com/
//最新资料下载: 百度网盘 http://pan.baidu.com/s/1jGjAhEm
//公                司: 上海或与电子科技有限公司
/
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) beginif(timer_cnt < 14'd9_999) timer_cnt <= timer_cnt+1'b1;else timer_cnt <= 14'd0;endelse ;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
/
module seg7(input clk,		//时钟信号,25MHzinput 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 begincase(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: ;endcaseend//段选数据译码
always @(posedge clk or negedge rst_n)if(!rst_n) dtube_data <= NUM0;else begincase(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: ;endcaseend//位选译码
always @(posedge clk or negedge rst_n)if(!rst_n) dtube_cs_n <= CSN;else begincase(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;endcaseendendmodule
/
//工程硬件平台: 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) beginpulse_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;endelse 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位,相当于除以8endmodule

上篇博文,也就是实验二已经讲解了超声波控制模块中的某些程序,这里再次贴出:

//超声波测距模块的回响信号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
--------------------- 

这篇博文在此基础上添加了如下程序段:

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

很简单,这小段程序的意思是:每个回波信号在neg_echo有效时结束,这时也已经对此脉冲计数完成了,以10us为单位的计数,也就是知道了回波脉宽了。

之后进入均值处理的模块,均值处理模块,将检测echo_pulse_en信号什么时候有效,每一个有效期都会得到一个回波脉宽,最后将得到的所有的回波脉宽做均值处理,可以得到更稳定的回波脉宽测量。

/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

这便是均值处理里面的程序,程序开头说缓存10拍,也就是10个echo_pulse_en有效次数,但我有一个疑问,程序是如何控制缓存了10拍的呢?

下面就直接进行均值处理了?先放在这里:

//-------------------------------------------------
//对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

 

 

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部