news 2026/5/6 13:13:48

Verilog 超声波测距:从时序控制到距离计算的模块化设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Verilog 超声波测距:从时序控制到距离计算的模块化设计

1. 超声波测距原理与Verilog实现思路

超声波测距听起来很高科技,其实原理特别简单。想象一下你在山谷里大喊一声,然后听回声——超声波测距就是这个原理的电子版。模块发射超声波,遇到障碍物反射回来,我们只要计算声波往返时间,就能算出距离。我在做智能小车项目时,这个功能帮了大忙。

具体到Verilog实现,核心就四步:触发发射、捕获回波、计算时间、换算距离。这里有个坑要注意:声速会受温度影响,但一般室内应用可以忽略。我实测过,在20℃环境下,声速约343m/s,换算成us级时间就是每毫米往返需要5.8us。不过实际代码里我们用0.173这个魔术数字,这是把声速和单位换算都考虑进去的简化公式。

2. 模块化设计架构

2.1 时钟分频模块

先说说vlg_en这个模块。FPGA的时钟动不动就50MHz,但超声波测距需要的是us级精度。我常用的是把50MHz(20ns周期)分频成1MHz(1us周期)。代码里这个参数P_CLK_PERIORD就是输入时钟周期,单位是ns。这里有个小技巧:用parameter定义常量,后期修改特别方便。

module vlg_en #( parameter P_CLK_PERIORD = 20 //50MHz时钟 )( input clk, input rst_n, output reg clk_en ); //分频计数器最大值计算 localparam P_DIVCLK_MAX = 1000/P_CLK_PERIORD - 1; reg [7:0] r_divcnt; always @(posedge clk or negedge rst_n) begin if(!rst_n) r_divcnt <= 0; else if(r_divcnt < P_DIVCLK_MAX) r_divcnt <= r_divcnt + 1; else r_divcnt <= 0; end always @(posedge clk) begin clk_en <= (r_divcnt == P_DIVCLK_MAX); end endmodule

2.2 触发信号生成模块

vlg_tirg模块负责产生10us的TRIG脉冲,这个脉冲就像扣动扳机,让超声波模块开始工作。我建议把周期设为100ms,这样测距频率就是10Hz,既不会太频繁也不会太慢。调试时发现个有趣现象:如果TRIG脉冲太短(比如小于10us),模块可能不响应;太长又会降低刷新率。

module vlg_tirg ( input clk, input rst_n, input clk_en, //1MHz时钟使能 output reg trig ); //100ms=100_000us localparam P_TRIG_PERIORD_MAX = 100_000 - 1; localparam P_TRIG_HIGH_MAX = 10; //10us高电平 reg [16:0] tricnt; always @(posedge clk or negedge rst_n) begin if(!rst_n) tricnt <= 0; else if(clk_en) begin tricnt <= (tricnt < P_TRIG_PERIORD_MAX) ? tricnt + 1 : 0; end end always @(posedge clk) begin trig <= (tricnt > 0) && (tricnt <= P_TRIG_HIGH_MAX); end endmodule

3. 回波时间捕获技术

3.1 边沿检测技巧

vlg_echo模块最核心的技术就是边沿检测。我一般用两级寄存器做同步,既防亚稳态又能准确捕捉跳变。这里有个细节:echo信号来自外部模块,一定要先同步到FPGA时钟域!曾经有个项目因为没做同步,测距结果时不时抽风。

module vlg_echo ( input clk, input rst_n, input clk_en, input echo, output reg [15:0] t_us ); reg [1:0] r_echo; wire pos_echo = ~r_echo[1] & r_echo[0]; //上升沿 wire neg_echo = r_echo[1] & ~r_echo[0]; //下降沿 reg cnt_en; reg [15:0] echo_cnt; always @(posedge clk or negedge rst_n) begin if(!rst_n) r_echo <= 0; else r_echo <= {r_echo[0], echo}; end

3.2 高电平计时实现

计时逻辑要注意三点:1)只在echo高电平时计数 2)用clk_en控制计数精度 3)下降沿时锁存计数值。实测发现,用1MHz时钟时最大测距约5.6米(65535us),对小车够用了。如果要测更远,可以把计数器改成32位。

always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt_en <= 0; else if(pos_echo) cnt_en <= 1; else if(neg_echo) cnt_en <= 0; end always @(posedge clk or negedge rst_n) begin if(!rst_n) echo_cnt <= 0; else if(!cnt_en) echo_cnt <= 0; else if(clk_en) echo_cnt <= echo_cnt + 1; end always @(posedge clk or negedge rst_n) begin if(!rst_n) t_us <= 0; else if(neg_echo) t_us <= echo_cnt; end endmodule

4. 距离计算优化方案

4.1 定点数运算技巧

cal模块的s=0.173*t涉及浮点运算,但在FPGA里浮点计算太耗资源。我的解决方案是定点数运算:把0.173放大4096倍得到709,最后结果右移12位。这样既保证精度又节省资源。调试时发现,用移位相加代替乘法器可以进一步节省LUT。

module cal ( input clk, input rst_n, input [15:0] t_us, output [14:0] s_mm ); //709=512+128+64+4+1 wire [25:0] sum1 = t_us << 9; //512*t wire [25:0] sum2 = t_us << 7; //128*t wire [25:0] sum3 = t_us << 6; //64*t wire [25:0] sum4 = t_us << 2; //4*t wire [25:0] sum5 = t_us; //1*t wire [25:0] sum_total = sum1 + sum2 + sum3 + sum4 + sum5; assign s_mm = sum_total[25:12]; //右移12位 endmodule

4.2 乘法器IP核使用

当需要更高性能时,可以用Xilinx的乘法器IP核。在Vivado里创建IP时,选择Multiplier类型,配置为16bit x 10bit无符号乘法。记得把Pipeline Stages设为2,这样时序更稳定。我在Artix-7上实测,用IP核比纯LUT实现节省30%资源。

5. 系统集成与调试

5.1 顶层模块设计

vlg_top就像乐高底座,把所有模块插在一起。接口设计有个原则:控制信号用寄存器输出,状态信号用wire输入。我习惯把参数集中定义在顶层,这样修改起来一目了然。特别注意跨时钟域信号要加同步器,特别是echo信号。

module vlg_top( input clk, input rst_n, input echo, output trig, output [14:0] distance_mm ); parameter P_CLK_PERIORD = 20; //50MHz wire clk_en; wire [15:0] t_us; vlg_en #(.P_CLK_PERIORD(P_CLK_PERIORD)) u_en( .clk(clk), .rst_n(rst_n), .clk_en(clk_en)); vlg_tirg u_trig( .clk(clk), .rst_n(rst_n), .clk_en(clk_en), .trig(trig)); vlg_echo u_echo( .clk(clk), .rst_n(rst_n), .clk_en(clk_en), .echo(echo), .t_us(t_us)); cal u_cal( .clk(clk), .rst_n(rst_n), .t_us(t_us), .s_mm(distance_mm)); endmodule

5.2 测试平台搭建

好的TB文件能事半功倍。我用$random生成随机延时,模拟不同距离。注意给echo信号加500ns延迟,模拟硬件响应时间。波形查看重点:trig脉冲宽度、echo响应延迟、计数器清零时机。

module tb_top(); reg clk; reg rst_n; reg echo; wire trig; wire [14:0] s_mm; vlg_top uut(.*); initial begin clk = 1; forever #10 clk = ~clk; end initial begin rst_n = 0; echo = 0; #200 rst_n = 1; repeat(5) begin @(posedge trig); #5000 echo = 1; #($urandom_range(26000, 11)*1000); echo = 0; end $finish; end endmodule

6. 常见问题与解决方案

6.1 信号抖动处理

实际项目中echo信号常有毛刺。我的应对方案:1)硬件上加RC滤波 2)Verilog里用计数器去抖。比如连续3个周期高电平才认为有效。在低速应用(如10Hz刷新)时,这个方案特别有效。

6.2 温度补偿方案

虽然我们忽略了温度影响,但对精度要求高的场合可以加DS18B20温度传感器。实测温度每升高1℃,声速增加0.6m/s。可以在cal模块里动态调整乘法系数,公式变为:s = (331.4 + 0.6*T)*t/2,其中T是摄氏温度。

6.3 多模块协同工作

当系统中有多个超声波模块时,要分时复用触发信号。我的方案是用状态机轮询,每个模块间隔100ms触发。关键是要确保前一个模块的echo信号结束再触发下一个,否则会互相干扰。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/6 13:13:15

从递归到迭代:深入理解栈在算法中的角色,以快排和二叉树遍历为例

从递归到迭代&#xff1a;深入理解栈在算法中的角色&#xff0c;以快排和二叉树遍历为例 在计算机科学中&#xff0c;递归和迭代是解决问题的两种基本范式。递归以其优雅简洁著称&#xff0c;但当问题规模增大时&#xff0c;系统调用栈的限制往往成为性能瓶颈。理解如何将递归算…

作者头像 李华
网站建设 2026/4/15 18:45:45

15MW海上风电标杆:IEA-15-240-RWT开源模型全解析与实践指南

15MW海上风电标杆&#xff1a;IEA-15-240-RWT开源模型全解析与实践指南 【免费下载链接】IEA-15-240-RWT 15MW reference wind turbine repository developed in conjunction with IEA Wind 项目地址: https://gitcode.com/gh_mirrors/ie/IEA-15-240-RWT 国际能源署&…

作者头像 李华
网站建设 2026/4/15 18:45:42

ARM64环境下Harbor镜像仓库高可用实战:从Helm部署到避坑指南

ARM64环境下Harbor镜像仓库高可用实战&#xff1a;从Helm部署到避坑指南 在云原生技术快速发展的今天&#xff0c;容器镜像仓库作为DevOps流程中的核心基础设施&#xff0c;其稳定性和性能直接影响着整个交付链路的效率。而随着ARM架构在服务器领域的崛起&#xff0c;越来越多的…

作者头像 李华
网站建设 2026/4/15 18:45:34

WaveTools开源工具箱:重构《鸣潮》游戏体验的技术架构与实战应用

WaveTools开源工具箱&#xff1a;重构《鸣潮》游戏体验的技术架构与实战应用 【免费下载链接】WaveTools &#x1f9f0;鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools WaveTools鸣潮工具箱是一款专为《鸣潮》玩家设计的开源游戏优化工具&#xff0…

作者头像 李华
网站建设 2026/4/15 18:44:52

从OJ题解到实战:Boyer-Moore-Horspool算法核心原理与高效实现

1. 从OJ题解看BMH算法的实战价值 第一次在SWUST OJ上遇到572号题目时&#xff0c;我完全被Boyer-Moore-Horspool这个拗口的名字唬住了。直到亲手用这个算法AC了那道超时已久的字符串匹配题&#xff0c;才发现它就像个精明的快递员——总能找到最短的配送路径。这个由Nigel Hors…

作者头像 李华
网站建设 2026/4/15 18:44:28

基于QT Creator与C++的串口上位机开发实战指南

1. 为什么选择QT Creator开发串口上位机 我第一次接触串口通信是在大学做嵌入式项目时&#xff0c;当时用C#写了个简陋的串口调试工具。后来转用QT Creator后才发现&#xff0c;这才是嵌入式开发者的"瑞士军刀"。QT Creator配合C开发上位机有三大不可替代的优势&…

作者头像 李华