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

举报
李锐博恩 发表于 2021/07/15 05:53:34 2021/07/15
【摘要】 这篇博文快速完成,对上篇博文的一个延续,用动态扫描数码管显示模块来显示回响信号的脉冲长度,从而知道测距值。 上篇博文:超声波测距小实验(一) 关于数码管的动态扫描显示的博文之前也写了几篇,这里直接调用其模块即可:控制数码管动态扫描显示的小实验 功能框图: 同样为25MHz的时钟频率,所以段选,片选模块可以直接使用: ///工程硬件平台: Xilinx Spa...

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

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

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

功能框图:

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


  
  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. parameter N = 17;//高两位控制片选,其他位用于分频
  38. //-------------------------------------------------
  39. //分时显示数据控制单元
  40. reg[3:0] current_display_num; //当前显示数据
  41. reg[N-1:0] div_cnt; //分时计数器
  42. //分时计数器
  43. always @(posedge clk or negedge rst_n)
  44. if(!rst_n) div_cnt <= 0;
  45. else div_cnt <= div_cnt+1'b1;
  46. //显示数据
  47. always @(posedge clk or negedge rst_n)
  48. if(!rst_n) current_display_num <= 4'h0;
  49. else begin
  50. case(div_cnt[N-1:N-2])
  51. 2'b00: current_display_num <= display_num[3:0];
  52. 2'b01: current_display_num <= display_num[7:4];
  53. 2'b10: current_display_num <= display_num[11:8];
  54. 2'b11: current_display_num <= display_num[15:12];
  55. default: ;
  56. endcase
  57. end
  58. //段选数据译码
  59. always @(posedge clk or negedge rst_n)
  60. if(!rst_n) dtube_data <= NUM0;
  61. else begin
  62. case(current_display_num)
  63. 4'h0: dtube_data <= NUM0;
  64. 4'h1: dtube_data <= NUM1;
  65. 4'h2: dtube_data <= NUM2;
  66. 4'h3: dtube_data <= NUM3;
  67. 4'h4: dtube_data <= NUM4;
  68. 4'h5: dtube_data <= NUM5;
  69. 4'h6: dtube_data <= NUM6;
  70. 4'h7: dtube_data <= NUM7;
  71. 4'h8: dtube_data <= NUM8;
  72. 4'h9: dtube_data <= NUM9;
  73. 4'ha: dtube_data <= NUMA;
  74. 4'hb: dtube_data <= NUMB;
  75. 4'hc: dtube_data <= NUMC;
  76. 4'hd: dtube_data <= NUMD;
  77. 4'he: dtube_data <= NUME;
  78. 4'hf: dtube_data <= NUMF;
  79. default: ;
  80. endcase
  81. end
  82. //位选译码
  83. always @(posedge clk or negedge rst_n)
  84. if(!rst_n) dtube_cs_n <= CSN;
  85. else begin
  86. case(div_cnt[N-1:N-2])
  87. 2'b00: dtube_cs_n <= CS0;
  88. 2'b01: dtube_cs_n <= CS1;
  89. 2'b10: dtube_cs_n <= CS2;
  90. 2'b11: dtube_cs_n <= CS3;
  91. default: dtube_cs_n <= CSN;
  92. endcase
  93. end
  94. endmodule

这里给出主模块:


  
  1. /
  2. //工程硬件平台: Xilinx Spartan 6 FPGA
  3. /
  4. //每秒产生1个超声波测距模块所需的10us高脉冲激励,并用数码管以16进制数据显示回响信号的高脉冲计数值(以10us为单位)
  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. //每秒产生一个10us的高脉冲作为超声波测距模块的激励
  41. wire[15:0] echo_pulse_num; //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  42. ultrasound_controller uut_ultrasound_controller(
  43. .clk(clk_25m), //时钟信号
  44. .rst_n(sys_rst_n), //复位信号,低电平有效
  45. .clk_100khz_en(clk_100khz_en), //100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
  46. .ultrasound_trig(ultrasound_trig), //超声波测距模块脉冲激励信号,10us的高脉冲
  47. .ultrasound_echo(ultrasound_echo), //超声波测距模块回响信号
  48. .echo_pulse_num(echo_pulse_num) //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  49. );
  50. //-------------------------------------
  51. //4位数码管显示驱动
  52. seg7 uut_seg7(
  53. .clk(clk_25m), //时钟信号
  54. .rst_n(sys_rst_n), //复位信号,低电平有效
  55. .display_num(echo_pulse_num), //显示数据
  56. .dtube_cs_n(dtube_cs_n), //7段数码管位选信号
  57. .dtube_data(dtube_data) //7段数码管段选信号(包括小数点为8段)
  58. );
  59. 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[15:0] echo_pulse_num //以10us为单位对超声波测距模块回响信号高脉冲进行计数的最终值
  11. );
  12. //-------------------------------------------------
  13. //1s定时产生逻辑
  14. reg[16:0] timer_cnt; //1s计数器,以100KHz(10us)为单位进行计数,计数1s需要的计数范围是0~99999
  15. //1s定时计数
  16. always @(posedge clk or negedge rst_n)
  17. if(!rst_n) timer_cnt <= 17'd0;
  18. else if(clk_100khz_en) begin
  19. if(timer_cnt < 17'd99_999) timer_cnt <= timer_cnt+1'b1;
  20. else timer_cnt <= 17'd0;
  21. end
  22. else ;
  23. assign ultrasound_trig = (timer_cnt == 17'd1) ? 1'b1:1'b0; //10us高脉冲生成
  24. //-------------------------------------------------
  25. //超声波测距模块的回响信号echo打两拍,产生上升沿和下降沿标志位
  26. reg[1:0] ultrasound_echo_r;
  27. always @(posedge clk or negedge rst_n)
  28. if(!rst_n) ultrasound_echo_r <= 2'b00;
  29. else ultrasound_echo_r <= {ultrasound_echo_r[0],ultrasound_echo};
  30. wire pos_echo = ~ultrasound_echo_r[1] & ultrasound_echo_r[0]; //echo信号上升沿标志位,高电平有效一个时钟周期
  31. wire neg_echo = ultrasound_echo_r[1] & ~ultrasound_echo_r[0]; //echo信号下降沿标志位,高电平有效一个时钟周期
  32. //-------------------------------------------------
  33. //以10us为单位对超声波测距模块回响信号高脉冲进行计数
  34. reg[15:0] echo_cnt; //回响高脉冲计数器
  35. always @(posedge clk or negedge rst_n)
  36. if(!rst_n) echo_cnt <= 16'd0;
  37. else if(pos_echo) echo_cnt <= 16'd0; //计数清零
  38. else if(clk_100khz_en && ultrasound_echo_r[0]) echo_cnt <= echo_cnt+1'b1;
  39. else ;
  40. always @(posedge clk or negedge rst_n)
  41. if(!rst_n) echo_pulse_num <= 16'd0;
  42. else if(neg_echo) echo_pulse_num <= echo_cnt;
  43. endmodule

  
  1. module clkdiv_generation(
  2. input clk, //外部输入25MHz时钟信号
  3. input rst_n, //外部输入复位信号,低电平有效
  4. output clk_100khz_en //100KHz频率的一个时钟使能信号,即每10us产生一个时钟脉冲
  5. );
  6. //-------------------------------------------------
  7. //时钟分频产生
  8. reg[7:0] cnt; //时钟分频计数器,0-249
  9. //1s定时计数
  10. always @(posedge clk or negedge rst_n)
  11. if(!rst_n) cnt <= 8'd0;
  12. else if(cnt < 8'd249) cnt <= cnt+1'b1;
  13. else cnt <= 8'd0;
  14. assign clk_100khz_en = (cnt == 8'd249) ? 1'b1:1'b0; //每40us产生一个40ns的高脉冲
  15. 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进制还是有点障碍的,不过别急,后面的博文会解决此问题。

 

 

 

 

 

 

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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