本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计
1.1.1 概述
学习了明德扬至简设计法和明德扬设计规范,本人用FPGA设计了一个测距系统。该系统采用超声波进行测量距离再在数码管上显示。在本案例的设计过程中包括了超声波的驱动、三线式数码管显示等技术。经过逐步改进、调试等一系列工作后,最终完成了此设计,并进行上板验证,下面将完整的设计记录与大家分享。
1.1.2 设计目标
此系统将实时显示前方障碍与装置之间的距离。
1.1.3 系统结构框图
系统结构框图如下所示:
1.1.4 模块功能
hc_sr04模块实现功能:
该模块通过控制触发信号trig(10us的TTL)使内部循环发出8个40KHZ脉冲即驱动超声波,接收回响信号echo,通过echo得到距离。
显示模块实现功能:
该模块完成了对所测距离通过数码管对其显示。
1.1.5顶层信号
1.1.6顶层代码
module top( clk , rst_n , echo , trig , sel, seg ); input clk ; input rst_n ; input echo ; output trig ; wire [3:0] s_g ; wire [3:0] s_s ; wire [3:0] s_b ; wire [3:0] s_q ; output [7:0] sel ; output [7:0] seg ; hc_sr04 hc_sr04_1( .clk (clk) , .rst_n (rst_n) , .echo (echo) , .trig (trig) , .s_g (s_g ), .s_s (s_s ), .s_b (s_b ), .s_q (s_q ) ); seg_disp u_seg_disp( .clk (clk ), .rst_n (rst_n), .segment_data({s_q,s_b,s_s,s_g}), .segment (seg ), .seg_sel (sel ) ); endmodule
1.2 hc_sr04模块设计
1.2.1 接口信号
1.2.2 设计思路
我们只需要提供一个短期的10uS脉冲触发信号trig,该模块内部将发出8个40kHz周期电平并检测回波,一旦检测到有回波信号则输出回响信号,回响信号echo是一个脉冲的宽度成正比的距离变量,可通过发射信号到收到的回响信号时间间隔可以计算得到距离。建议测量周期为60ms以上,以防止发射信号对回响信号的影响,这里我们采用的是1s测量一次。
时钟计数器cnt0:用于计算 1 秒的时钟个数,加一条件为1,表示一直计数;结束条件为数到 TIME_1S ,表示数到 1 秒就清零。
距离计数器 h_cnt:用于计算flag为高电平的宽度的时间,如果flag为1,h_cnt就加一;每完成1秒计数后h_cnt就变为0,此外h_cnt等于h_cnt。
模块时序图
1.2.3 参考代码
module hc_sr04( clk , rst_n , echo , trig , s_g , s_s , s_b , s_q ); parameter DATA_W = 14 ; parameter TIME_1S = 50_000_000; input clk ; input rst_n ; input echo ; output trig ; output[ 3:0] s_g ; output[ 3:0] s_s ; output[ 3:0] s_b ; output[ 3:0] s_q ; wire trig ; reg [ 3:0] s_g ; reg [ 3:0] s_s ; reg [ 3:0] s_b ; reg [ 3:0] s_q ; reg [DATA_W-1:0] distance; reg [25:0] cnt0 ; reg [20:0] h_cnt ; reg echo_2 ; reg echo_1 ; wire add_cnt0; wire end_cnt0; wire flag_h ; wire flag_l ; always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt0 <= 0; end else if(add_cnt0)begin if(end_cnt0) cnt0 <= 0; else cnt0 <= cnt0 + 1'b1; end end assign add_cnt0 = 1; assign end_cnt0 = add_cnt0 && cnt0 == TIME_1S - 1; assign trig = (cnt0>=500&&cnt0<1000)?1:0; always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin echo_1 <= 0; echo_2 <= 0; end else begin echo_1 <= echo ; echo_2 <= echo_1; end end always @(posedge clk or negedge rst_n)begin if(!rst_n)begin h_cnt <= 0; end else if(add_h_cnt)begin if(end_h_cnt) h_cnt <= 0; else h_cnt <= h_cnt + 1; end else if(end_cnt0)begin h_cnt <= 0; end end assign add_h_cnt = echo_2; assign end_h_cnt = 0 ; always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin distance <= 0; end else if(add_cnt0 && cnt0 == 45_000_000-1)begin distance <= h_cnt*34/10000; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_g <= 0; end else begin s_g <= distance%10; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_s <= 0; end else begin s_s <= (distance/10)%10; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_b <= 0; end else begin s_b <= (distance/100)%10; end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin s_q <= 0; end else begin s_q <= (distance/1000)%10; end end endmodule
1.3 显示模块设计
1.3.1接口信号
1.3.2设计思路
该模块对数码管的位选信号sel每隔1ms的时间移位一次,也就是1ms循环亮一个灯,由于1ms的频率肉眼观察不出,我们看到的就是4个灯全亮。
对输入距离distance进行求余处理,得到每一位的数据,通过case语句,让每一位数据形成段选信号,通过位选信号的控制显示在对应的数码管上。
1.3.3参考代码
module seg_disp( clk , rst_n , segment_data, segment , seg_sel ); parameter ZERO = 8'b1100_0000 ; parameter ONE = 8'b1111_1001 ; parameter TWO = 8'b1010_0100 ; parameter THREE = 8'b1011_0000 ; parameter FOUR = 8'b1001_1001 ; parameter FIVE = 8'b1001_0010 ; parameter SIX = 8'b1000_0010 ; parameter SEVEN = 8'b1111_1000 ; parameter EIGHT = 8'b1000_0000 ; parameter NINE = 8'b1001_0000 ; input clk ; input rst_n ; input [31:0] segment_data ; output [7:0 ] segment ; output [7:0 ] seg_sel ; reg [7:0 ] segment ; reg [7:0 ] seg_sel ; reg [10:0] delay ; reg [3:0 ] delay_time ; wire add_delay_time ; wire end_delay_time ; wire add_delay ; wire end_delay ; wire [3:0 ] segment_tmp ; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin delay <= 0; end else if(add_delay) begin if(end_delay) delay <= 0; else delay <= delay+1 ; end end assign add_delay = 1; assign end_delay = add_delay && delay == 2000-1 ; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin delay_time <= 0; end else if(add_delay_time) begin if(end_delay_time) delay_time <= 0; else delay_time <= delay_time+1 ; end end assign add_delay_time = end_delay; assign end_delay_time = add_delay_time && delay_time == 8-1 ; assign segment_tmp = segment_data[(1+delay_time)*4-1 -:4]; always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin segment <= ZERO; end else begin case(segment_tmp) 4'd0:segment <= ZERO; 4'd1:segment <= ONE ; 4'd2:segment <= TWO ; 4'd3:segment <= THREE; 4'd4:segment <= FOUR ; 4'd5:segment <= FIVE ; 4'd6:segment <= SIX ; 4'd7:segment <= SEVEN; 4'd8:segment <= EIGHT; 4'd9:segment <= NINE ; default:begin segment <= segment; end endcase end end always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin seg_sel <= 8'b1111_1111; end else begin seg_sel <= ~(8'b1<<delay_time); end end endmodule
1.4 效果和总结
上板验证效果
在这个设计中,使用明德扬的至简设计法,让我的思路非常清晰,逻辑非常严谨,虽然没有做到一遍成功,但在调试过程中我都比较快速的找到问题,并快速解决。对于学习FPGA的同学,我非常推荐使用明德扬至简设计法和明德扬模块进行学习和设计。
教学视频和工程源代码请移步明德扬论坛学习!
感兴趣的朋友也可以访问明德扬论坛(www.fpgabbs.cn)进行FPGA相关工程设计学习,也欢迎大家在评论与我进行讨论!
也可以看一下我们往期的文章:
《基于FPGA的密码锁设计》
《波形相位频率可调DDS信号发生器》
《基于FPGA的曼彻斯特编码解码设计》
《基于FPGA的出租车计费系统》
《数电基础与Verilog设计》《基于FPGA的频率、电压测量》
《基于FPGA的汉明码编码解码设计》
《关于锁存器问题的讨论》
《阻塞赋值与非阻塞赋值》
《参数例化时自动计算位宽的解决办法》
1.15公司简介
明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。
点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。
网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。
周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。
就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。
专题课程——高手修炼课:提升设计能力;
实用调试技巧课:提升定位和解决问题能力;
FIFO架构设计课:助你快速成为架构设计师;
时序约束、数字信号处理、PCIE、综合项目实践课等你来选。
项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。