【 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)/256module 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) 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
/
//缓存最近采集到的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
/
//工程硬件平台: 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
其他,前几篇博文已经讲了。这里重点讲讲模块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]};
【 FPGA 】超声波测距小实验(四):数码管显示测距结果相关推荐
- 实验四 数码管显示设计与仿真
4.1实验目的 熟练掌握组合逻辑电路的设计.掌握不定位宽乘法器以及7段数码管(不带小数点)显示原理.实现并仿真: 1)4位乘法器及数码管显示: 4.2实验内容 (1)设计4位乘法器,并将被乘数,乘数, ...
- 【 FPGA 】超声波测距小实验(一)
超声波测距原理: 超声波测距原理是在超声波发射装置发出超声波,它的根据是接收器接到超声波时的时间差,与雷达测距原理相似. 超声波发射器向某一方向发射超声波,在发射时刻的同时开始计时,超声波在空气中传播 ...
- 实验24:超声波测距仪小实验
--超声波测距小实验 --液晶显示距离 OK,简单分享一个小实验,超声波测距实验 01 硬件电路 超声波传感器 液晶 总体电路连接 接口: 第1步:建立电路 超声波传感器与 Arduino Uno 板 ...
- Arduino + ESP32-C3 + TFT(1.8‘ ST7735S)基础平台(实验四)直接显示网络图片
---------------------------------------------------------------------------------------------------- ...
- FPGA学习汇总(六)----数码管显示(1)
目录 概念 单个数码管显示单个数字 操作 代码 现象 分析 四个数码管定时单个显示数字 分析 代码 四个数码管同时显示 分析 代码 现象 四个数码管同时显示定时转换 分析 代码 概念 我们要搞懂数码管 ...
- 【Arduino实验10 数码管显示】
目录 一.实验目的 二.实验设备与环境 三.实验重点 四.实验难点 五.实验内容 5.1实验任务 5.2实验原理 5.3实验内容 5.4实验结果 5.5思考题 一.实验目的 (1)掌握1位数码管模块的 ...
- 【 FPGA 】控制数码管动态扫描显示的小实验
实验的功能很简单,就是让4个数码管每隔1s递增显示,使用动态扫描的方式来实现. 从这个功能的描述可以看出,我们首先要写一个计数器模块,来让计数值每隔1s增加1,暂时实现的是16进制的东西,从0到f,之 ...
- 【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1
1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...
- arduino UNO 与 超声波测距模块 实验详情
US-015 超声波测距模块 超声波传感器 US-020升级版 送全套资料 资料下载地址:http://pan.baidu.com/s/1c0AfkIG US-015超声波测距模块 1. 概述 US- ...
最新文章
- linux paste变量,Linux paste命令详解
- html兼容不同屏幕 代码,rem的正确使用姿势 -- 完美解决H5页面不同尺寸屏幕的适配问题...
- 出道50+年!乘风破浪的编程语言们,能二次翻红吗?
- L2-009. 抢红包 结构体排序
- python读取文件的常用方法
- 急速收藏:4套iOS SDK的H5打通方案
- how to figure out problems in the ardunio nano force senser? 1,2,3,4
- mysql5.7环境,MySQL-5.7-线上生产环境部署
- php 中文截断,PHP中实现中文字串截取无乱码的解决方法
- php MySQL定义,PHP + MySQL用户定义函数
- NSString常用方法
- 2014 Super Training #2 F The Bridges of Kolsberg --DP
- 《深入理解Java虚拟机》读书笔记十二
- Redis基础--使用treeNMS管理及监控Redis
- CF1060F Shrinking Tree
- 持续更新 iText in Action 2nd Edition中文版 个人翻译
- java控制台实现的简易计算器,实现加减乘除
- 【数据库】什么是 PostgreSQL?开源数据库系统
- 中国人寿保险软件开发机试题 java实现
- 将来用NFC也能付支付宝、微信里的钱?NFC Forum发布一项新规范