前言
本篇博客主要是实现使用 DE2-115 开发板驱动 超声波检测模块(HC_SR04 ),并将所测得数据显示到开发板上的数码管上。
一、实验设备
DE2-E115 FPGA开发板 + Quartus + HC_SR04超声波检测模块
二、实验原理
1、超声波原理
HC-SR04超声波测距模块可提供 2cm-400cm的非接触式距离感测功能,测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。图1为HC-SR04外观,其基本工作原理为给予此超声波测距模块触发信号后模块发射超声波,当超声波投射到物体而反射回来时,模块输出回响信号,以触发信号和回响信号间的时间差,来判定物体的距离。
HC-SR04超声波测距模块实物图:
2、超声波测距模块时序图
以上时序图表明你只需要提供一个10uS 以上脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。
三、系统结构设计
四、代码实现
module HC_SR04_TOP(
input clk ,
input rstn ,
input echo , // 距离信号
output trig , // 触发测距信号
output [6:0] hex1 , // -共阳极,低电平有效
output [6:0] hex2 , // -
output [6:0] hex3 , // -
output [6:0] hex4 , //连接符
output [6:0] hex5 , //cm -
output [6:0] hex6 , //cm -
output [6:0] hex7 , //cm -
output [6:0] hex8 //熄
);
wire [18:00] data_o ;
wire clk_us ;
seg_driver u_seg_driver(
.clk (clk ),
.rstn (rstn ),
.data_in (data_o ), //待显示数据
.hex1 (hex1 ), // -共阳极,低电平有效
.hex2 (hex2 ), // -
.hex3 (hex3 ), // -
.hex4 (hex4 ), //连接符
.hex5 (hex5 ), //cm -
.hex6 (hex6 ), //cm -
.hex7 (hex7 ), //cm -
.hex8 (hex8 ) //熄灭
);
clk_div u_clk_div(
.clk (clk ),
.rstn (rstn ),
.clk_us (clk_us )
);
trig_driver u_trig_driver(
.clk_us (clk_us ),
.rstn (rstn ),
.trig (trig )
);
echo_driver u_echo_driver(
.clk (clk ),
.clk_us (clk_us ),
.rstn (rstn ),
.echo (echo ),
.data_o (data_o )
);
endmodule
module trig_driver(
input wire clk_us ,
input wire rstn ,
output wire trig //触发测距信号
);
parameter CYCLE_MAX = 19'd29_9999;
reg [18:00] cnt ;
// 10毫秒持续电平输出
always @(posedge clk_us or negedge rstn) begin
if(!rstn) begin
cnt <= 19'd0;
end
else if(cnt == CYCLE_MAX) begin
cnt <= 19'd0;
end
else begin
cnt <= cnt + 19'd1;
end
end
assign trig = cnt < 15 ? 1'b1 : 1'b0;
endmodule
module echo_driver(
input wire clk ,
input wire clk_us ,
input wire rstn ,
input wire echo ,
output wire [18:00] data_o //检测距离,保留3位小数,*1000实现
);
parameter T_MAX = 16'd5_9999;//510cm 对应计数值
reg r1_echo,r2_echo; //边沿检测
wire echo_pos,echo_neg; //
reg [15:00] cnt ;
reg [18:00] data_r ;
//如果使用clk_us 检测边沿,延时2us,差值过大
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
r1_echo <= 1'b0;
r2_echo <= 1'b0;
end
else begin
r1_echo <= echo;
r2_echo <= r1_echo;
end
end
assign echo_pos = r1_echo & ~r2_echo;
assign echo_neg = ~r1_echo & r2_echo;
always @(posedge clk_us or negedge rstn) begin
if(!rstn) begin
cnt <= 16'd0;
end
else if(echo) begin
if(cnt == T_MAX) begin
cnt <= 16'd0;
end
else begin
cnt <= cnt + 16'd1;
end
end
else begin
cnt <= 16'd0;
end
end
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
data_r <= 'd2;
end
else if(echo_neg)begin
data_r <= (cnt << 4) + cnt;
end
else begin
data_r <= data_r;
end
end
assign data_o = data_r >> 1;
endmodule
module clk_div(
input wire clk ,
input wire rstn ,
output wire clk_us //
);
parameter CNT_MAX = 19'd49;//1us的计数值为 50 * Tclk(20ns)
reg [5:0] cnt ;
wire add_cnt ;
wire end_cnt ;
// 时钟分频
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
cnt <= 6'd0;
end
else if(cnt == CNT_MAX) begin
cnt <= 6'd0;
end
else begin
cnt <= cnt + 6'd1;
end
end
assign clk_us = cnt >= CNT_MAX ;
endmodule
module seg_driver(
input wire clk , //50MHz
input wire rstn , //low valid
input wire [18:0] data_in , //待显示数据
output reg [6:0] hex1 , // -共阳极,低电平有效
output reg [6:0] hex2 , // -
output reg [6:0] hex3 , // -
output reg [6:0] hex4 , //连接符
output reg [6:0] hex5 , //cm -
output reg [6:0] hex6 , //cm -
output reg [6:0] hex7 , //cm -
output reg [6:0] hex8 //熄灭
);
//parameter define
localparam NUM_0 = 8'b1100_0000,
NUM_1 = 8'b1111_1001,
NUM_2 = 8'b1010_0100,
NUM_3 = 8'b1011_0000,
NUM_4 = 8'b1001_1001,
NUM_5 = 8'b1001_0010,
NUM_6 = 8'b1000_0010,
NUM_7 = 8'b1111_1000,
NUM_8 = 8'b1000_0000,
NUM_9 = 8'b1001_0000,
NUM_A = 8'b1000_1000,
NUM_B = 8'b1000_0011,
NUM_C = 8'b1100_0110,
NUM_D = 8'b1010_0001,
NUM_E = 8'b1000_0110,
NUM_F = 8'b1000_1110,
ALL_LIGHT = 8'b0000_0000,
LIT_OUT = 8'b1111_1111;
//reg 、wire define
reg [3:0] cm_hund ;//100cm
reg [3:0] cm_ten ;//10cm
reg [3:0] cm_unit ;//1cm
reg [3:0] point_1 ;//1mm
reg [3:0] point_2 ;//0.1mm
reg [3:0] point_3 ;//0.01mm
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
cm_hund <= 'd0;
cm_ten <= 'd0;
cm_unit <= 'd0;
point_1 <= 'd0;
point_2 <= 'd0;
point_3 <= 'd0;
end
else begin
cm_hund <= data_in / 10 ** 5;
cm_ten <= data_in / 10 ** 4 % 10;
cm_unit <= data_in / 10 ** 3 % 10;
point_1 <= data_in / 10 ** 2 % 10;
point_2 <= data_in / 10 ** 1 % 10;
point_3 <= data_in / 10 ** 0 % 10;
end
end
always @(posedge clk or negedge rstn)begin
if(!rstn)begin
hex1 <= ALL_LIGHT;
hex2 <= ALL_LIGHT;
hex3 <= ALL_LIGHT;
hex4 <= ALL_LIGHT;
hex5 <= ALL_LIGHT;
hex6 <= ALL_LIGHT;
hex7 <= ALL_LIGHT;
hex8 <= ALL_LIGHT;
end
else begin
hex1 <= hex_data(point_3);
hex2 <= hex_data(point_2);
hex3 <= hex_data(point_1);
hex4 <= 7'b011_1111;
hex5 <= hex_data(cm_unit);
hex6 <= hex_data(cm_ten);
hex7 <= hex_data(cm_hund);
hex8 <= LIT_OUT;
end
end //always end
function [6:0] hex_data; //函数不含时序逻辑相关
input [03:00] data_i;//至少一个输入
begin
case(data_i)
'd0:hex_data = NUM_0;
'd1:hex_data = NUM_1;
'd2:hex_data = NUM_2;
'd3:hex_data = NUM_3;
'd4:hex_data = NUM_4;
'd5:hex_data = NUM_5;
'd6:hex_data = NUM_6;
'd7:hex_data = NUM_7;
'd8:hex_data = NUM_8;
'd9:hex_data = NUM_9;
default:hex_data = ALL_LIGHT;
endcase
end
endfunction
endmodule
引脚配置图:
五、RTL视图
六、实验结果
七、总结
通过本次实验,了解了HC-SR04超声波测距模块,懂得了如何通过超声波进行测距的原理,同时将测距模块与之前学习的FPGA联系起来,对数码管这些模块也更加熟练,让我能够将FPGA与实际的项目联系起来,收获很大。