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

举报
李锐博恩 发表于 2021/07/15 05:12:46 2021/07/15
【摘要】 上篇博文:测了回响脉冲的宽度为多少个10us,这篇博文要算出距离,且用数码管显示距离的十进制结果。 功能框图如下:   、 距离计算: 还是先给出程序之后在简单解释吧。 顶层模块: ///工程硬件平台: Xilinx Spartan 6 FPGA///每100ms产生1个超声波测距模块所需的10us高脉冲激励,并用数码管以10进制数据显示最...

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

功能框图如下:

 

距离计算:

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

顶层模块:


  
  1. /
  2. //工程硬件平台: Xilinx Spartan 6 FPGA
  3. /
  4. //每100ms产生1个超声波测距模块所需的10us高脉冲激励,并用数码管以10进制数据显示最终经过换算后的距离信息(单位mm)
  5. module sp6(
  6. input ext_clk_25m, //外部输入25MHz时钟信号
  7. input ext_rst_n, //外部输入复位信号,低电平有效
  8. output ultrasound_trig, //超声波测距模块脉冲激励信号,10us的高脉冲
  9. input ultrasound_echo, //超声波测距模块回响信号
  10. output[3:0] dtube_cs_n, //7段数码管位选信号
  11. output[7:0] dtube_data //7段数码管段选信号(包括小数点为8段)
  12. );
  13. //-------------------------------------
  14. //PLL例化
  15. wire clk_12m5; //PLL输出12.5MHz时钟
  16. wire clk_25m; //PLL输出25MHz时钟
  17. wire clk_50m; //PLL输出50MHz时钟
  18. wire clk_100m; //PLL输出100MHz时钟
  19. wire sys_rst_n; //PLL输出的locked信号,作为FPGA内部的复位信号,低电平复位,高电平正常工作
  20. pll_controller uut_pll_controller
  21. (// Clock in ports
  22. .CLK_IN1(ext_clk_25m), // IN
  23. // Clock out ports
  24. .CLK_OUT1(clk_12m5), // OUT
  25. .CLK_OUT2(clk_25m), // OUT
  26. .CLK_OUT3(clk_50m), // OUT
  27. .CLK_OUT4(clk_100m), // OUT
  28. // Status and control signals
  29. .RESET(~ext_rst_n),// IN
  30. .LOCKED(sys_rst_n)); // OUT
  31. //-------------------------------------
  32. //25MHz时钟进行分频,产生一个100KHz频率的时钟使能信号
  33. wire clk_100khz_en; //100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
  34. clkdiv_generation uut_clkdiv_generation(
  35. .clk(clk_25m), //时钟信号
  36. .rst_n(sys_rst_n), //复位信号,低电平有效
  37. .clk_100khz_en(clk_100khz_en) //100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
  38. );
  39. //-------------------------------------
  40. //每100ms产生一个10us的高脉冲作为超声波测距模块的激励
  41. wire[15:0] echo_pulse_num; //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  42. wire echo_pulse_en; //超声波测距模块回响信号计数值有效信号
  43. ultrasound_controller uut_ultrasound_controller(
  44. .clk(clk_25m), //时钟信号
  45. .rst_n(sys_rst_n), //复位信号,低电平有效
  46. .clk_100khz_en(clk_100khz_en), //100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
  47. .ultrasound_trig(ultrasound_trig), //超声波测距模块脉冲激励信号,10us的高脉冲
  48. .ultrasound_echo(ultrasound_echo), //超声波测距模块回响信号
  49. .echo_pulse_en(echo_pulse_en), //超声波测距模块回响信号计数值有效信号
  50. .echo_pulse_num(echo_pulse_num) //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  51. );
  52. //-------------------------------------
  53. //缓存最近采集到的8组超声波测距回响脉冲计数值,对它们进行累加并求平均
  54. wire[15:0] echo_pulse_filter_num; //滤波处理后的超声波测距模块回响信号高脉冲计数值
  55. filter uut_filter(
  56. .clk(clk_25m), //时钟信号
  57. .rst_n(sys_rst_n), //复位信号,低电平有效
  58. .echo_pulse_en(echo_pulse_en), //超声波测距模块回响信号计数值有效信号
  59. .echo_pulse_num(echo_pulse_num), //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  60. .echo_pulse_filter_num(echo_pulse_filter_num) //滤波处理后的超声波测距模块回响信号高脉冲计数值
  61. );
  62. //-------------------------------------
  63. //换算出超声波测距的实际距离,并且以十进制,单位mm形式输出
  64. wire[15:0] echo_pulse_f_mul_num; //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值,换算为实际距离的10进制数据
  65. distance_compute uut_distance_compute(
  66. .clk(clk_25m), //时钟信号
  67. .rst_n(sys_rst_n), //复位信号,低电平有效
  68. .echo_pulse_filter_num(echo_pulse_filter_num), //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  69. .echo_pulse_f_mul_num(echo_pulse_f_mul_num) //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值,换算为实际距离的10进制数据
  70. );
  71. //-------------------------------------
  72. //4位数码管显示驱动
  73. seg7 uut_seg7(
  74. .clk(clk_25m), //时钟信号
  75. .rst_n(sys_rst_n), //复位信号,低电平有效
  76. .display_num(echo_pulse_f_mul_num), //显示数据
  77. .dtube_cs_n(dtube_cs_n), //7段数码管位选信号
  78. .dtube_data(dtube_data) //7段数码管段选信号(包括小数点为8段)
  79. );
  80. endmodule

子模块:


  
  1. /
  2. //工程硬件平台: Xilinx Spartan 6 FPGA
  3. //25°C时,声音在空气中传播的速度为346m/s
  4. //因此取距离s的单位是米(m),时间t的单位是秒(s),有 s = 346*t/2
  5. //若取距离s的单位是毫米(mm),时间t的单位是10微秒(10us),有s*0.001 = 346*t*0.00001/2,即s = 1.73*t
  6. //为了便于计算,取s = ((1.73*256)*t)/256 = (443*t)/256
  7. module distance_compute(
  8. input clk, //外部输入25MHz时钟信号
  9. input rst_n, //外部输入复位信号,低电平有效
  10. input[15:0] echo_pulse_filter_num, //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  11. output[15:0] echo_pulse_f_mul_num //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值,换算为实际距离的10进制数据
  12. );
  13. //-------------------------------------------------
  14. //距离换算
  15. wire[31:0] mul_out; //输出的乘法运算结果,取bit23-8为有效的16bit数据
  16. mul uut_mul (
  17. .clk(clk), // input clk
  18. .a(16'd443), // input [15 : 0] a
  19. .b(echo_pulse_filter_num), // input [15 : 0] b
  20. .p(mul_out) // output [31 : 0] p
  21. );
  22. //-------------------------------------------------
  23. //将16进制数据转换为10进制,由于我们已知有效的16bit数据的有效范围是0-4000mm
  24. wire[15:0] thousand_quotint,thousand_fractional; //千位除法运算结果与余数寄存器
  25. //千位运算
  26. div thousand_div (
  27. .clk(clk), // input clk
  28. .rfd(), // output rfd
  29. .dividend(mul_out[23:8]), // input [15 : 0] dividend
  30. .divisor(16'd1000), // input [15 : 0] divisor
  31. .quotient(thousand_quotint), // output [15 : 0] quotient
  32. .fractional(thousand_fractional) // output [15 : 0] fractional
  33. );
  34. wire[15:0] hundred_quotint,hundred_fractional; //百位除法运算结果与余数寄存器
  35. //百位运算
  36. div hundred_div (
  37. .clk(clk), // input clk
  38. .rfd(), // output rfd
  39. .dividend(thousand_fractional), // input [15 : 0] dividend
  40. .divisor(16'd100), // input [15 : 0] divisor
  41. .quotient(hundred_quotint), // output [15 : 0] quotient
  42. .fractional(hundred_fractional) // output [15 : 0] fractional
  43. );
  44. wire[15:0] ten_quotint,ten_fractional; //十位除法运算结果与余数寄存器
  45. //十位运算
  46. div ten_div (
  47. .clk(clk), // input clk
  48. .rfd(), // output rfd
  49. .dividend(hundred_fractional), // input [15 : 0] dividend
  50. .divisor(16'd10), // input [15 : 0] divisor
  51. .quotient(ten_quotint), // output [15 : 0] quotient
  52. .fractional(ten_fractional) // output [15 : 0] fractional
  53. );
  54. assign echo_pulse_f_mul_num = {thousand_quotint[3:0],hundred_quotint[3:0],ten_quotint[3:0],ten_fractional[3:0]};
  55. endmodule

  
  1. /
  2. //工程硬件平台: Xilinx Spartan 6 FPGA
  3. /
  4. module clkdiv_generation(
  5. input clk, //外部输入25MHz时钟信号
  6. input rst_n, //外部输入复位信号,低电平有效
  7. output clk_100khz_en //100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
  8. );
  9. //-------------------------------------------------
  10. //时钟分频产生
  11. reg[7:0] cnt; //时钟分频计数器,0-249
  12. //1s定时计数
  13. always @(posedge clk or negedge rst_n)
  14. if(!rst_n) cnt <= 8'd0;
  15. else if(cnt < 8'd249) cnt <= cnt+1'b1;
  16. else cnt <= 8'd0;
  17. assign clk_100khz_en = (cnt == 8'd249) ? 1'b1:1'b0; //每40us产生一个40ns的高脉冲
  18. endmodule

  
  1. /
  2. //工程硬件平台: Xilinx Spartan 6 FPGA
  3. /
  4. module ultrasound_controller(
  5. input clk, //外部输入25MHz时钟信号
  6. input rst_n, //外部输入复位信号,低电平有效
  7. input clk_100khz_en, //100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
  8. output ultrasound_trig, //超声波测距模块脉冲激励信号,10us的高脉冲
  9. input ultrasound_echo, //超声波测距模块回响信号
  10. output reg echo_pulse_en, //超声波测距模块回响信号计数值有效信号
  11. output reg[15:0] echo_pulse_num //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  12. );
  13. //-------------------------------------------------
  14. //1s定时产生逻辑
  15. reg[13:0] timer_cnt; //1s计数器,以100KHz(10us)为单位进行计数,计数100ms需要的计数范围是0~9999
  16. //1s定时计数
  17. always @(posedge clk or negedge rst_n)
  18. if(!rst_n) timer_cnt <= 14'd0;
  19. else if(clk_100khz_en) begin
  20. if(timer_cnt < 14'd9_999) timer_cnt <= timer_cnt+1'b1;
  21. else timer_cnt <= 14'd0;
  22. end
  23. else ;
  24. assign ultrasound_trig = (timer_cnt == 14'd1) ? 1'b1:1'b0; //10us高脉冲生成
  25. //-------------------------------------------------
  26. //超声波测距模块的回响信号echo打两拍,产生上升沿和下降沿标志位
  27. reg[1:0] ultrasound_echo_r;
  28. always @(posedge clk or negedge rst_n)
  29. if(!rst_n) ultrasound_echo_r <= 2'b00;
  30. else ultrasound_echo_r <= {ultrasound_echo_r[0],ultrasound_echo};
  31. wire pos_echo = ~ultrasound_echo_r[1] & ultrasound_echo_r[0]; //echo信号上升沿标志位,高电平有效一个时钟周期
  32. wire neg_echo = ultrasound_echo_r[1] & ~ultrasound_echo_r[0]; //echo信号下降沿标志位,高电平有效一个时钟周期
  33. //-------------------------------------------------
  34. //以10us为单位对超声波测距模块回响信号高脉冲进行计数
  35. reg[15:0] echo_cnt; //回响高脉冲计数器
  36. always @(posedge clk or negedge rst_n)
  37. if(!rst_n) echo_cnt <= 16'd0;
  38. else if(pos_echo) echo_cnt <= 16'd0; //计数清零
  39. else if(clk_100khz_en && ultrasound_echo_r[0]) echo_cnt <= echo_cnt+1'b1;
  40. else ;
  41. //计数脉冲数锁存
  42. always @(posedge clk or negedge rst_n)
  43. if(!rst_n) echo_pulse_num <= 16'd0;
  44. else if(neg_echo) echo_pulse_num <= echo_cnt;
  45. //计数脉冲有效使能信号锁存
  46. always @(posedge clk or negedge rst_n)
  47. if(!rst_n) echo_pulse_en <= 1'b0;
  48. else echo_pulse_en <= neg_echo;
  49. endmodule

  
  1. /
  2. //工程硬件平台: Xilinx Spartan 6 FPGA
  3. /
  4. //缓存最近采集到的8组超声波测距回响脉冲计数值,对它们进行累加并求平均
  5. module filter(
  6. input clk, //外部输入25MHz时钟信号
  7. input rst_n, //外部输入复位信号,低电平有效
  8. input echo_pulse_en, //超声波测距模块回响信号计数值有效信号
  9. input[15:0] echo_pulse_num, //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  10. output[15:0] echo_pulse_filter_num //滤波处理后的超声波测距模块回响信号高脉冲计数值
  11. );
  12. //-------------------------------------------------
  13. //echo_pulse_num信号缓存10拍
  14. reg[15:0] pulse_reg[7:0]; //echo_pulse_num信号缓存寄存器
  15. always @(posedge clk or negedge rst_n)
  16. if(!rst_n) begin
  17. pulse_reg[0] <= 16'd0;
  18. pulse_reg[1] <= 16'd0;
  19. pulse_reg[2] <= 16'd0;
  20. pulse_reg[3] <= 16'd0;
  21. pulse_reg[4] <= 16'd0;
  22. pulse_reg[5] <= 16'd0;
  23. pulse_reg[6] <= 16'd0;
  24. pulse_reg[7] <= 16'd0;
  25. end
  26. else if(echo_pulse_en) begin //缓存最新的数据,使用移位寄存器的方式推进最新数据,推出最老的数据
  27. pulse_reg[0] <= echo_pulse_num;
  28. pulse_reg[1] <= pulse_reg[0];
  29. pulse_reg[2] <= pulse_reg[1];
  30. pulse_reg[3] <= pulse_reg[2];
  31. pulse_reg[4] <= pulse_reg[3];
  32. pulse_reg[5] <= pulse_reg[4];
  33. pulse_reg[6] <= pulse_reg[5];
  34. pulse_reg[7] <= pulse_reg[6];
  35. end
  36. //-------------------------------------------------
  37. //对8个数据累加并输出平均值
  38. reg[15:0] sum_pulse_reg;
  39. always @(posedge clk or negedge rst_n)
  40. if(!rst_n) sum_pulse_reg <= 16'd0;
  41. 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];
  42. assign echo_pulse_filter_num = {3'b000,sum_pulse_reg[15:3]}; //右移3位,相当于除以8
  43. endmodule

  
  1. /
  2. //工程硬件平台: Xilinx Spartan 6 FPGA
  3. /
  4. module seg7(
  5. input clk, //时钟信号,25MHz
  6. input rst_n, //复位信号,低电平有效
  7. input[15:0] display_num, //数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
  8. output reg[3:0] dtube_cs_n, //7段数码管位选信号
  9. output reg[7:0] dtube_data //7段数码管段选信号(包括小数点为8段)
  10. );
  11. //-------------------------------------------------
  12. //参数定义
  13. //数码管显示 0~F 对应段选输出
  14. parameter NUM0 = 8'h3f,//c0,
  15. NUM1 = 8'h06,//f9,
  16. NUM2 = 8'h5b,//a4,
  17. NUM3 = 8'h4f,//b0,
  18. NUM4 = 8'h66,//99,
  19. NUM5 = 8'h6d,//92,
  20. NUM6 = 8'h7d,//82,
  21. NUM7 = 8'h07,//F8,
  22. NUM8 = 8'h7f,//80,
  23. NUM9 = 8'h6f,//90,
  24. NUMA = 8'h77,//88,
  25. NUMB = 8'h7c,//83,
  26. NUMC = 8'h39,//c6,
  27. NUMD = 8'h5e,//a1,
  28. NUME = 8'h79,//86,
  29. NUMF = 8'h71,//8e;
  30. NDOT = 8'h80; //小数点显示
  31. //数码管位选 0~3 对应输出
  32. parameter CSN = 4'b1111,
  33. CS0 = 4'b1110,
  34. CS1 = 4'b1101,
  35. CS2 = 4'b1011,
  36. CS3 = 4'b0111;
  37. //-------------------------------------------------
  38. //分时显示数据控制单元
  39. reg[3:0] current_display_num; //当前显示数据
  40. reg[7:0] div_cnt; //分时计数器
  41. //分时计数器
  42. always @(posedge clk or negedge rst_n)
  43. if(!rst_n) div_cnt <= 8'd0;
  44. else div_cnt <= div_cnt+1'b1;
  45. //显示数据
  46. always @(posedge clk or negedge rst_n)
  47. if(!rst_n) current_display_num <= 4'h0;
  48. else begin
  49. case(div_cnt)
  50. 8'hff: current_display_num <= display_num[3:0];
  51. 8'h3f: current_display_num <= display_num[7:4];
  52. 8'h7f: current_display_num <= display_num[11:8];
  53. 8'hbf: current_display_num <= display_num[15:12];
  54. default: ;
  55. endcase
  56. end
  57. //段选数据译码
  58. always @(posedge clk or negedge rst_n)
  59. if(!rst_n) dtube_data <= NUM0;
  60. else begin
  61. case(current_display_num)
  62. 4'h0: dtube_data <= NUM0;
  63. 4'h1: dtube_data <= NUM1;
  64. 4'h2: dtube_data <= NUM2;
  65. 4'h3: dtube_data <= NUM3;
  66. 4'h4: dtube_data <= NUM4;
  67. 4'h5: dtube_data <= NUM5;
  68. 4'h6: dtube_data <= NUM6;
  69. 4'h7: dtube_data <= NUM7;
  70. 4'h8: dtube_data <= NUM8;
  71. 4'h9: dtube_data <= NUM9;
  72. 4'ha: dtube_data <= NUMA;
  73. 4'hb: dtube_data <= NUMB;
  74. 4'hc: dtube_data <= NUMC;
  75. 4'hd: dtube_data <= NUMD;
  76. 4'he: dtube_data <= NUME;
  77. 4'hf: dtube_data <= NUMF;
  78. default: ;
  79. endcase
  80. end
  81. //位选译码
  82. always @(posedge clk or negedge rst_n)
  83. if(!rst_n) dtube_cs_n <= CSN;
  84. else begin
  85. case(div_cnt[7:6])
  86. 2'b00: dtube_cs_n <= CS0;
  87. 2'b01: dtube_cs_n <= CS1;
  88. 2'b10: dtube_cs_n <= CS2;
  89. 2'b11: dtube_cs_n <= CS3;
  90. default: dtube_cs_n <= CSN;
  91. endcase
  92. end
  93. 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]};
 

 

 

 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/86557104

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。