1、 实验目的
使用 DE2-115 开发板驱动 超声波检测模块(HC_SR04 ),并将所测得数据显示到开发板上的数码管上,或者通过用UART通信方式上传到笔记本串口助手上显示(可采用UART IP核)。
2、实验平台
DE2-E115 FPGA开发板 + Quartus + Modelsim
学习并掌握HC_SR04模块的使用
3. 实验原理
超声波原理:
HC-SR04超声波测距模块可提供 2cm-400cm的非接触式距离感测功能,测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。图1为HC-SR04外观,其基本工作原理为给予此超声波测距模块触发信号后模块发射超声波,当超声波投射到物体而反射回来时,模块输出回响信号,以触发信号和回响信号间的时间差,来判定物体的距离。
模块测距工作原理:
(1) 主控设备给 Trig 脚提供一个 10us 的脉冲信号。
(2) HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波。
(3) HC-SR04 接收到返回的超声波,把 Echo 置为低电平。
(4) Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。
产品实物图:
模块框图:
5、Verilog 实现数码管显视驱动模块
数码管:本质上为一组发光二极管按照一定顺序排列而成,其显示原理与LED无异。
根据硬件原理图所示,发光二极管,所有的阳极都接通3.3V的正电压,也即—高电平,所以如果我们想要
发光二极管导通的话,需要在阴极接通低电平,就可以让LED亮起来。
信号定义
设计文件
/*================================================*\
Filename ﹕data_gen.v
Author ﹕Adolph
Description ﹕产生数码管需要显示的数据. 32'h11111111~32'h33333333;
Called by ﹕seg_top.v
Revision History ﹕ 2022-6-6 15:49:24
Revision 1.0
Email﹕adolph1354238998@gmail.com
Company﹕
\*================================================*/
module data_gen(
input clk ,
input rst_n,
output reg [31:0] data_dis
);
parameter TIME_DELAY = 25'd2500;
reg [24:0] cnt_delay;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_delay <= 25'd0;
end
else if(cnt_delay >= TIME_DELAY - 25'd1)begin
cnt_delay <= 25'd0;
end
else begin
cnt_delay <= cnt_delay + 25'd1;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_dis <= 32'h1111_1111;
end
else if(data_dis == 32'h3333_3333)begin
data_dis <= 32'h1111_1111;
end
else if(cnt_delay >= TIME_DELAY - 25'd1)begin
data_dis <= data_dis + 32'h1;
end
else begin
data_dis <= data_dis;
end
end
endmodule
数码管驱动模块
信号定义
设计文件
/*================================================*\
Filename ﹕seg_driver.v
Author ﹕Adolph
Description ﹕对输入的数据译码,并驱动数码管显示对应数据
Called by ﹕seg_top.v
Revision History ﹕ 2022-5-30 14:27:22
Revision 1.0
Email﹕adolph1354238998@gmail.com
Company﹕
\*================================================*/
module seg_driver(
input clk ,
input rst_n ,
input [31:0]dis_data,//待显示的数据
output reg[7:0] dig_sel ,
output reg[7:0] dig_seg
);
//wire [31:0]dis_data;
// assign dig_seg = 8'd0;
// assign dig_sel = 1'b0;
localparam
NUM_0 = 8'hC0,
NUM_1 = 8'hF9,
NUM_2 = 8'hA4,
NUM_3 = 8'hB0,
NUM_4 = 8'h99,
NUM_5 = 8'h92,
NUM_6 = 8'h82,
NUM_7 = 8'hF8,
NUM_8 = 8'h80,
NUM_9 = 8'h90,
NUM_A = 8'h88,
NUM_B = 8'h83,
NUM_C = 8'hC6,
NUM_D = 8'hA1,
NUM_E = 8'h86,
NUM_F = 8'h8E,
LIT_ALL = 8'h00,
BLC_ALL = 8'hFF;
parameter CNT_REF = 25'd1000;
reg [9:0] cnt_20us; //20us计数器
reg [3:0] data_tmp; //用于取出不同位选的显示数据
// assign dis_data = 32'hABCD_4413;
//描述位选信号切换
//描述刷新计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20us <= 25'd0;
end
else if(cnt_20us >= CNT_REF - 25'd1)begin
cnt_20us <= 25'd0;
end
else begin
cnt_20us <= cnt_20us + 25'd1;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dig_sel <= 8'hfe;//8'b1111_1110
end
else if(cnt_20us >= CNT_REF - 25'd1)begin
dig_sel <= {dig_sel[6:0],dig_sel[7]};
end
else begin
dig_sel <= dig_sel;
end
end
//段选信号描述
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_tmp <= 4'd0;
end
else begin
case(dig_sel)
8'b1111_1110:data_tmp <= dis_data[ 3-:4];
8'b1111_1101:data_tmp <= dis_data[ 7-:4];
8'b1111_1011:data_tmp <= dis_data[11-:4];
8'b1111_0111:data_tmp <= dis_data[15-:4];
8'b1110_1111:data_tmp <= dis_data[19-:4];
8'b1101_1111:data_tmp <= dis_data[23-:4];
8'b1011_1111:data_tmp <= dis_data[27-:4];
8'b0111_1111:data_tmp <= dis_data[31-:4];
default: data_tmp <= 4'hF;
endcase
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dig_seg <= BLC_ALL;
end
else begin
case(data_tmp)
4'h0 : dig_seg <= NUM_0;
4'h1 : dig_seg <= NUM_1;
4'h2 : dig_seg <= NUM_2;
4'h3 : dig_seg <= NUM_3;
4'h4 : dig_seg <= NUM_4;
4'h5 : dig_seg <= NUM_5;
4'h6 : dig_seg <= NUM_6;
4'h7 : dig_seg <= NUM_7;
4'h8 : dig_seg <= NUM_8;
4'h9 : dig_seg <= NUM_9;
4'hA : dig_seg <= NUM_A;
4'hB : dig_seg <= NUM_B;
4'hC : dig_seg <= NUM_C;
4'hD : dig_seg <= NUM_D;
4'hE : dig_seg <= NUM_E;
4'hF : dig_seg <= NUM_F;
default: ;
endcase
end
end
endmodule
仿真验证
`timescale 1ns/1ns
module tb_dig;
reg clk;
reg rst_n;
wire [7:0] dig_sel;
wire [7:0] dig_seg;
reg [39:0] CHARAC;//1个 ASCII 码,需要8bit 二进制表示
defparam seg_driver.CNT_REF = 100;
localparam
NUM_0 = 8'hC0,
NUM_1 = 8'hF9,
NUM_2 = 8'hA4,
NUM_3 = 8'hB0,
NUM_4 = 8'h99,
NUM_5 = 8'h92,
NUM_6 = 8'h82,
NUM_7 = 8'hF8,
NUM_8 = 8'h80,
NUM_9 = 8'h90,
NUM_A = 8'h88,
NUM_B = 8'h83,
NUM_C = 8'hC6,
NUM_D = 8'hA1,
NUM_E = 8'h86,
NUM_F = 8'h8E;
always@(*)begin
case(dig_seg)
NUM_0 : CHARAC = "NUM_0";
NUM_1 : CHARAC = "NUM_1";
NUM_2 : CHARAC = "NUM_2";
NUM_3 : CHARAC = "NUM_3";
NUM_4 : CHARAC = "NUM_4";
NUM_5 : CHARAC = "NUM_5";
NUM_6 : CHARAC = "NUM_6";
NUM_7 : CHARAC = "NUM_7";
NUM_8 : CHARAC = "NUM_8";
NUM_9 : CHARAC = "NUM_9";
NUM_A : CHARAC = "NUM_A";
NUM_B : CHARAC = "NUM_B";
NUM_C : CHARAC = "NUM_C";
NUM_D : CHARAC = "NUM_D";
NUM_E : CHARAC = "NUM_E";
NUM_F : CHARAC = "NUM_F";
default : CHARAC = "NUM_0";
endcase
end
seg_driver seg_driver(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*output reg[7:0] */.dig_sel (dig_sel),
/*output reg[7:0] */.dig_seg (dig_seg)
);
initial clk = 1'b0;
always #10 clk = ~clk;
initial begin
rst_n = 1'b0;
#36 rst_n = 1'b1;
wait (seg_driver.lut == 15);
#(20 * seg_driver.CNT_REF * 3);
$stop(2);
end
endmodule
实验总结
经过此次实验大致了解了模块测距工作原理,但是由于时间关系还没有来得及上板测试。