文章目录
一、使用 DE2-115 开发板驱动 超声波检测模块(HC_SR04 ),并将所测得数据显示到开发板上的数码管上,或者通过用UART通信方式上传到笔记本串口助手上显示(可采用UART IP核)。
HC-SR04 超声波测距模块可提供 2cm-400cm 的非接触式距离感测功能,测
距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。
实验平台
DE2-E115 FPGA开发板 + Quartus + Modelsim
实验要求
使用DE2开发板驱动HC_SR04模块,并将所测得数据显示到开发板上的数码管。
1.基本工作原理
(1)采用 IO 口 TRIG 触发测距,给最少 10us 的高电平信呈。
(2)模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
(3)有信号返回,通过 IO 口 ECHO 输出一个高电平,高电平持续的时间就是超声
波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;
2.硬件模块时序图
以上时序图表明你只需要提供一个 10uS 以上脉冲触发信号,该模块内部将
发出 8 个 40kHz 周期电平并检测回波。一旦检测到有回波信号则输出回响信号。
回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号
时间间隔可以计算得到距离。公式:uS/58=厘米或者 uS/148=英寸;或是:距离= 高电平时间*声速(340M/S)/2;建议测量周期为 60ms 以上,以防止发射信号对回响信号的影响。
3.设计文件
我这里设计的测量周期是60ms,可以稍微调高点
然后TTL是15us脉冲触发信号
distance.v代码如下:
module distance (
input wire clk ,
input wire rst_n ,
input wire echo ,
output wire trig ,
output wire [19:0] distance_data
);
parameter MAX1_us = 6'd50 ;
parameter MAX60_ms = 16'd60_000;
parameter T_MAX = 15'd20_000;
reg [5:0] cnt_1us ;
reg [15:0] cnt_60ms ;
wire addcnt ;
wire endcnt ;
wire echo_pos,echo_neg ;
reg r1_echo,r2_echo ;
reg [14:0] cnt ;
reg [15:0] data_r ;
wire add_cnt ;
wire end_cnt ;
reg us_clk ;
assign distance_data[3:0] =data_r%10;
assign distance_data[7:4] =data_r/10%10;
assign distance_data[11:8] =data_r/100%10;
assign distance_data[15:12]=data_r/1000%10;
assign distance_data[19:16]=data_r/10000%10;
//1us计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_1us <= 1'd0;
end
else if(endcnt)begin
cnt_1us <= 1'd0;
end
else begin
cnt_1us <= cnt_1us + 1'd1;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
us_clk <= 1'b0 ;
end
else if(endcnt)begin
us_clk <= ~us_clk ;
end
else begin
us_clk <= us_clk ;
end
end
assign endcnt = (cnt_1us==MAX1_us-1'd1);
//60ms计数器
always @(posedge us_clk or negedge rst_n) begin
if(!rst_n)begin
cnt_60ms <= 1'd0;
end
else if(cnt_60ms==MAX60_ms - 16'd1)begin
cnt_60ms <= 1'd0;
end
else begin
cnt_60ms <= cnt_60ms + 1'b1;
end
end
assign trig = cnt_60ms < 15 ? 1'b1 : 1'b0;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)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 us_clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 1'd0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= cnt;
end
else begin
cnt <= cnt + 1'b1;
end
end
else begin //echo 低电平 归零
cnt <= 1'd0;
end
end
assign add_cnt = echo;
assign end_cnt = add_cnt && cnt >= T_MAX - 1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_r <= 1'd0;
end
else if(echo_neg)begin
data_r <= cnt*340/200;
end
else begin
data_r <= data_r;
end
end
endmodule
数码管segdrive.v代码如下:
module segdrive (input wire clk ,
input wire rst_n ,
input wire [ 23:0 ] distance_data ,
output reg [ 6:0 ] hex0 ,
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
);
localparam ZERO = 7'b100_0000;
localparam ONE = 7'b111_1001;
localparam TWO = 7'b010_0100;
localparam THREE = 7'b011_0000;
localparam FOUR = 7'b001_1001;
localparam FIVE = 7'b001_0010;
localparam SIX = 7'b000_0010;
localparam SEVEN = 7'b111_1000;
localparam EIGHT = 7'b000_0000;
localparam NINE = 7'b001_0000;
localparam N = 7'b010_1011;
localparam P = 7'b000_1111;
reg dot;
localparam state0 = 3'd0;
localparam state1 = 3'd1;
localparam state2 = 3'd2;
localparam state3 = 3'd3;
localparam state4 = 3'd4;
localparam state5 = 3'd5;
reg flag;
reg [20:0] cnt;
parameter MAX_NUM = 1_000;
reg [ 3:0 ] num1;
reg [ 3:0 ] num2;
reg [ 3:0 ] num3;
reg [ 3:0 ] num4;
reg [ 3:0 ] num5;
reg [ 3:0 ] num6;
reg [2:0] current_state;
reg [2:0] next_state;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
current_state <= state0;
end
else if(flag) begin
current_state <= next_state;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 1'b0;
cnt <= 0;
end
else if(cnt == 0)begin//一轮计数完毕
flag <= 1'b1;
cnt <= 1;
end
else begin
flag <= 1'b0;
cnt <= (cnt + 1'b1) % MAX_NUM;//循环+1
end
end
always @(*) begin
if(!rst_n) begin
next_state <= state0;
end
else if(flag) begin
case(current_state)
state0: begin
next_state <= state1;
end
state1: begin
next_state <= state2;
end
state2: begin
next_state <= state3;
end
state3: begin
next_state <= state4;
end
state4: begin
next_state <= state5;
end
state5: begin
next_state <= state0;
end
default:begin
next_state <= state0;
end
endcase
end
else begin
next_state <= next_state;
end
end
//状态输出
always@(current_state) begin
case (current_state)
state0: begin
num1 = distance_data[3 :0];
num2=num2;
num3=num3;
num4=num4;
num5=num5;
num6=num6;
dot = 1;
end
state1: begin
num1=num1;
num2 = distance_data[7 :4];
num3=num3;
num4=num4;
num5=num5;
num6=num6;
dot = 1;
end
state2: begin
num1=num1;
num2=num2;
num3 = distance_data[11 :8];
num4=num4;
num5=num5;
num6=num6;
dot = 0;
end
state3: begin
num1=num1;
num2=num2;
num3=num3;
num4 = distance_data[15:12];
num5=num5;
num6=num6;
dot = 1;
end
state4: begin
num1=num1;
num2=num2;
num3=num3;
num4=num4;
num5 = distance_data[19 :16];
num6=num6;
dot = 1;
end
state5: begin
num1=num1;
num2=num2;
num3=num3;
num4=num4;
num5=num5;
num6 = distance_data[23:20];
dot = 1;
end
default:begin
num1=num1;
num2=num2;
num3=num3;
num4=num4;
num5=num5;
num6=num6;
dot=dot;
end
endcase
end
always @ ( * ) begin
case( num1 )
4'd0: hex0 = {ZERO}; // 匹配到后参考共阳极真值表
4'd1: hex0 = {ONE};
4'd2: hex0 = {TWO};
4'd3: hex0 = {THREE};
4'd4: hex0 = {FOUR};
4'd5: hex0 = {FIVE};
4'd6: hex0 = {SIX};
4'd7: hex0 = {SEVEN};
4'd8: hex0 = {EIGHT};
4'd9: hex0 = {NINE};
default : hex0 = {ZERO};
endcase
end
always @ ( * ) begin
case( num2 )
4'd0: hex1 = {ZERO}; // 匹配到后参考共阳极真值表
4'd1: hex1 = {ONE};
4'd2: hex1 = {TWO};
4'd3: hex1 = {THREE};
4'd4: hex1 = {FOUR};
4'd5: hex1 = {FIVE};
4'd6: hex1 = {SIX};
4'd7: hex1 = {SEVEN};
4'd8: hex1 = {EIGHT};
4'd9: hex1 = {NINE};
default : hex1 = {ZERO};
endcase
end
always @ ( * ) begin
case( num3 )
4'd0: hex2 = {ZERO}; // 匹配到后参考共阳极真值表
4'd1: hex2 = {ONE};
4'd2: hex2 = {TWO};
4'd3: hex2 = {THREE};
4'd4: hex2 = {FOUR};
4'd5: hex2 = {FIVE};
4'd6: hex2 = {SIX};
4'd7: hex2 = {SEVEN};
4'd8: hex2 = {EIGHT};
4'd9: hex2 = {NINE};
default : hex2 = {ZERO};
endcase
end
always @ ( * ) begin
case( num4 )
4'd0: hex3 = {ZERO}; // 匹配到后参考共阳极真值表
4'd1: hex3 = {ONE};
4'd2: hex3 = {TWO};
4'd3: hex3 = {THREE};
4'd4: hex3 = {FOUR};
4'd5: hex3 = {FIVE};
4'd6: hex3 = {SIX};
4'd7: hex3 = {SEVEN};
4'd8: hex3 = {EIGHT};
4'd9: hex3 = {NINE};
default : hex3 = {ZERO};
endcase
end
always @ ( * ) begin
case( num5 )
4'd0: hex4 = {ZERO}; // 匹配到后参考共阳极真值表
4'd1: hex4 = {ONE};
4'd2: hex4 = {TWO};
4'd3: hex4 = {THREE};
4'd4: hex4 = {FOUR};
4'd5: hex4 = {FIVE};
4'd6: hex4 = {SIX};
4'd7: hex4 = {SEVEN};
4'd8: hex4 = {EIGHT};
4'd9: hex4 = {NINE};
default : hex4 = {ZERO};
endcase
end
always @ ( * ) begin
case( num6 )
4'd0: hex5 = {ZERO}; // 匹配到后参考共阳极真值表
4'd1: hex5 = {ONE};
4'd2: hex5 = {TWO};
4'd3: hex5 = {THREE};
4'd4: hex5 = {FOUR};
4'd5: hex5 = {FIVE};
4'd6: hex5 = {SIX};
4'd7: hex5 = {SEVEN};
4'd8: hex5 = {EIGHT};
4'd9: hex5 = {NINE};
default : hex5 = {ZERO};
endcase
end
e![请添加图片描述](https://img-blog.csdnimg.cn/6ca96bbfe0f94d389349fa3b8eb1caad.png)
ndmodule
4.顶层文件设计:
module distance_top (
input wire clk ,
input wire rst_n ,
input wire echo ,
output wire [ 6:0 ] hex0 ,
output wire [ 6:0 ] hex1 ,
output wire [ 6:0 ] hex2 ,
output wire [ 6:0 ] hex3 ,
output wire [ 6:0 ] hex4 ,
output wire [ 6:0 ] hex5 ,
output wire trig
);
wire [19:0 ] distance_data;
distance distance_inst(
.clk (clk ),
.rst_n (rst_n ),
.echo (echo ),
.trig (trig ),
.distance_data(distance_data)
);
segdrive segdrive_inst(
.clk (clk ) ,
.rst_n (rst_n ) ,
.distance_data(distance_data),
.hex0 (hex0) ,
.hex1 (hex1) ,
.hex2 (hex2) ,
.hex3 (hex3) ,
.hex4 (hex4) ,
.hex5 (hex5)
);
endmodule
5.仿真文件设计
`timescale 1ns/1ns //仿真系统时间尺度定义
module tb_distance();
//激励信号定义
reg clk ;
reg rst_n ;
reg echo ; //
wire trig ;
wire [19:0] distance_data;
wire[ 6:0 ] hex0;
wire[ 6:0 ] hex1;
wire[ 6:0 ] hex2;
wire[ 6:0 ] hex3;
wire[ 6:0 ] hex4;
wire[ 6:0 ] hex5;
//响应信号定义
distance i1(
.clk (clk ),
.rst_n(rst_n),
.echo (echo ),
.trig (trig ),
.distance_data (distance_data)
);
segdrive i3(
.clk (clk ),
.rst_n (rst_n ),
.distance_data(distance_data),
.hex0 (hex0) ,
.hex1 (hex1) ,
.hex2 (hex2) ,
.hex3 (hex3) ,
.hex4 (hex4) ,
.hex5 (hex5)
);
localparam CLK_PERIOD = 2;
always #(CLK_PERIOD/2) clk=~clk;
initial begin
rst_n<=1'b0;
clk<=1'b0;
# CLK_PERIOD;
rst_n<=1;
echo <= 0;
# (CLK_PERIOD * 10000);
echo <= 1;
# (CLK_PERIOD * 20000);
echo <= 0;
# (CLK_PERIOD * 30000);
echo <= 1;
# (CLK_PERIOD * 40000);
echo <= 0;
# (CLK_PERIOD *10000);
$stop;
end
endmodule
仿真波形图:
num1-6分别对应数码管hex0-hex5
二、总结
这个实验可以帮助了我更加深入地了解硬件模块的时序图和基本工作原理,并锻炼了我使用FPGA开发板进行硬件设计和驱动的能力。此实验,需要我掌握使用IO口触发测距、发送方波和检测回波等方法,以及通过高电平持续时间计算距离的公式。此外,我还需要掌握如何将所测得数据显示到开发板上的数码管上。细致认真地完成该实验提高了我对FPGA开发板的理解,并培养了我实际操作的能力。