一.实验原理
1.模块介绍
- HC-SR04超声波测距模块可提供 2cm-400cm的非接触式距离感测功能,测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制电路。图1为HC-SR04外观,其基本工作原理为给予此超声波测距模块触发信号后模块发射超声波,当超声波投射到物体而反射回来时,模块输出回响信号,以触发信号和回响信号间的时间差,来判定物体的距离。
2.硬件时序图
二.代码设计
- clk_div模块
/*================================================*\
Filename ﹕
Author ﹕
Description ﹕产生周期为1us的时钟信号
Called by ﹕
Revision History ﹕ mm/dd/202x
Revision 1.0
Email﹕
Company﹕
\*================================================*/
module clk_div(
input wire Clk , //system clock 50MHz
input wire Rst_n , //reset ,low valid
output wire clk_us //
);
//Parameter Declarations
parameter CNT_MAX = 19'd50;//1us的计数值为 50 * Tclk(20ns)
//Interrnal wire/reg declarations
reg [5:00] cnt ; //Counter
wire add_cnt ; //Counter Enable
wire end_cnt ; //Counter Reset
//Logic Description
always @(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
cnt <= 'd0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
else begin
cnt <= cnt;
end
end
assign add_cnt = 1'b1;
assign end_cnt = add_cnt && cnt >= CNT_MAX - 19'd1;
assign clk_us = end_cnt;
endmodule
- hc_sr_echo
/*================================================*\
Filename ﹕
Author ﹕
Description ﹕超声波检测距离模块
本模块理论测试距离 2cm~510cm
输出结果保留两位小数
Called by ﹕
Revision History ﹕ mm/dd/202x
Revision 1.0
Email﹕
Company﹕
\*================================================*/
module hc_sr_echo(
input wire Clk , //clock 50MHz
input wire clk_us , //system clock 1MHz
input wire Rst_n , //reset ,low valid
input wire echo , //
output wire [18:00] data_o //检测距离,保留3位小数,*1000实现
);
/* S(um) = 17 * t --> x.abc cm */
//Parameter Declarations
parameter T_MAX = 16'd60_000;//510cm 对应计数值
//Interrnal wire/reg declarations
reg r1_echo,r2_echo; //边沿检测
wire echo_pos,echo_neg; //
reg [15:00] cnt ; //Counter
wire add_cnt ; //Counter Enable
wire end_cnt ; //Counter Reset
reg [18:00] data_r ;
//Logic Description
//如果使用clk_us 检测边沿,延时2us,差值过大
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 clk_us or negedge Rst_n)begin
if(!Rst_n)begin
cnt <= '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 <= '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 <= 'd2;
end
else if(echo_neg)begin
data_r <= (cnt << 4) + cnt;
end
else begin
data_r <= data_r;
end
end //always end
assign data_o = data_r >> 1;
endmodule
- hc_sr_trig
/*================================================*\
Filename ﹕
Author ﹕
Description ﹕超声波触发测距模块
波形周期300ms,前10us高电平
Called by ﹕
Revision History ﹕ mm/dd/202x
Revision 1.0
Email﹕
Company﹕
\*================================================*/
module hc_sr_trig(
input wire clk_us , //system clock 1MHz
input wire Rst_n , //reset ,low valid
output wire trig //触发测距信号
);
//Parameter Declarations
parameter CYCLE_MAX = 19'd300_000;
//Interrnal wire/reg declarations
reg [18:00] cnt ; //Counter
wire add_cnt ; //Counter Enable
wire end_cnt ; //Counter Reset
//Logic Description
always @(posedge clk_us or negedge Rst_n)begin
if(!Rst_n)begin
cnt <= 'd0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
else begin
cnt <= cnt;
end
end
assign add_cnt = 1'b1;
assign end_cnt = add_cnt && cnt >= CYCLE_MAX - 9'd1;
assign trig = cnt < 15 ? 1'b1 : 1'b0;
endmodule
- hc_sr_dirver(上述三个模块的顶层文件)
module hc_sr_driver (
input wire Clk ,
input wire Rst_n ,
input wire echo ,
output wire trig ,
output wire [18:0] data_o
);
wire clk_us;
hc_sr_echo u_hc_sr_echo(
.Clk (Clk ), //clock 50MHz
.clk_us (clk_us ), //system clock 1MHz
.Rst_n (Rst_n ), //reset ,low valid
.echo (echo ), //
.data_o (data_o ) //检测距离,保留3位小数,*1000实现
);
hc_sr_trig u_hc_sr_trig(
.clk_us (clk_us ), //system clock 1MHz
.Rst_n (Rst_n ), //reset ,low valid
.trig (trig )//触发测距信号
);
clk_div u_clk_div(
.Clk (Clk ), //system clock 50MHz
.Rst_n (Rst_n ), //reset ,low valid
.clk_us (clk_us )//
);
endmodule
- seg_dirver(数码管模块)
module seg_driver(
input wire Clk ,
input wire Rst_n ,
input wire [18:0] data_o ,
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 [6:0] hex6 ,
output wire [6:0] hex7 ,
output wire [6:0] hex8
);
parameter NOTION = 4'd10,
FUSHU = 4'd11;
parameter MAX20us = 10'd1000;
reg [9:0] cnt_20us;
reg [7:0] sel_r;
reg [3:0] number;
reg [6:0] seg_r;
reg [6:0] hex1_r;
reg [6:0] hex2_r;
reg [6:0] hex3_r;
reg [6:0] hex4_r;
reg [6:0] hex5_r;
reg [6:0] hex6_r;
reg [6:0] hex7_r;
reg [6:0] hex8_r;
//20微妙计数器
always @(posedge Clk or negedge Rst_n) begin
if (!Rst_n) begin
cnt_20us <= 10'd0;
end
else if (cnt_20us == MAX20us - 1'd1) begin
cnt_20us <= 10'd0;
end
else begin
cnt_20us <= cnt_20us + 1'd1;
end
end
//单个信号sel_r位拼接约束
always @(posedge Clk or negedge Rst_n) begin
if (!Rst_n) begin
sel_r <= 8'b11_11_11_10;
end
else if (cnt_20us == MAX20us - 1'd1) begin
sel_r <= {sel_r[6:0],sel_r[7]};
end
else begin
sel_r <= sel_r;
end
end
/*拿到数字*/
always @(*) begin
case (sel_r)
8'b11_11_11_10: number = NOTION ;
8'b11_11_11_01: number = data_o/10_0000 ;
8'b11_11_10_11: number = (data_o%10_0000)/1_0000 ;
8'b11_11_01_11: number = ((data_o%10_0000)%1_0000)/1000 ;
8'b11_10_11_11: number = FUSHU ;
8'b11_01_11_11: number = (((data_o%10_0000)%1_0000)%1000)/100 ;
8'b10_11_11_11: number = ((((data_o%10_0000)%1_0000)%1000)%100)/10 ;
8'b01_11_11_11: number = ((((data_o%10_0000)%1_0000)%1000)%100)%10 ;
default: number = 4'd0 ;
endcase
end
/*通过数字解析出seg值*/
always @(*) begin
case (number)
4'd0 : seg_r = 7'b100_0000;
4'd1 : seg_r = 7'b111_1001;
4'd2 : seg_r = 7'b010_0100;
4'd3 : seg_r = 7'b011_0000;
4'd4 : seg_r = 7'b001_1001;
4'd5 : seg_r = 7'b001_0010;
4'd6 : seg_r = 7'b000_0010;
4'd7 : seg_r = 7'b111_1000;
4'd8 : seg_r = 7'b000_0000;
4'd9 : seg_r = 7'b001_0000;
NOTION : seg_r = 7'b111_1111;
FUSHU : seg_r = 7'b011_1111;
default : seg_r = 7'b111_1111;
endcase
end
always @(*) begin
case (sel_r)
8'b11_11_11_10: hex1_r = seg_r;
8'b11_11_11_01: hex2_r = seg_r;
8'b11_11_10_11: hex3_r = seg_r;
8'b11_11_01_11: hex4_r = seg_r;
8'b11_10_11_11: hex5_r = seg_r;
8'b11_01_11_11: hex6_r = seg_r;
8'b10_11_11_11: hex7_r = seg_r;
8'b01_11_11_11: hex8_r = seg_r;
default: seg_r = seg_r;
endcase
end
assign hex1 = hex1_r;
assign hex2 = hex2_r;
assign hex3 = hex3_r;
assign hex4 = hex4_r;
assign hex5 = hex5_r;
assign hex6 = hex6_r;
assign hex7 = hex7_r;
assign hex8 = hex8_r;
endmodule
- beepled(报警模块包括蜂鸣器LED)
module beepled (
input wire Clk,
input wire Rst_n,
input wire [18:0] data_o,
output wire beep,
output wire [3:0] led
);
parameter MAX1S = 26'd2500_0000 ;
parameter MAX1_2S = 26'd1250_0000 ;
reg [25:0] cnt1s ;
reg [25:0] cnt1_2s ;
reg [3:0] led_r ;
reg beep_r ;
//1s计数器
always @(posedge Clk or negedge Rst_n) begin
if (!Rst_n) begin
cnt1s <= 26'd0; //复位,重新计数
end
else if (cnt1s == MAX1S - 1'd1) begin
cnt1s <= 26'd0; //记到最大数4999_9999后复位
end
else begin
cnt1s <= cnt1s + 1'd1; //其他情况+1
end
end
//0.5s计数器
always @(posedge Clk or negedge Rst_n) begin
if (!Rst_n) begin
cnt1_2s <= 26'd0; //复位,重新计数
end
else if (cnt1s == MAX1_2S - 1'd1) begin
cnt1_2s <= 26'd0; //记到最大数2499_9999后复位
end
else begin
cnt1_2s <= cnt1_2s + 1'd1; //其他情况+1
end
end
//led灯
always @(posedge Clk or negedge Rst_n) begin
if (!Rst_n) begin
led_r <= 4'b0000;
end
else if(data_o/1000 <= 20) begin
led_r <= 4'b1111;
end
else begin
led_r <= 4'b0000;
end
end
//蜂鸣器
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin//复位信号
beep_r <= 1'b0;//蜂鸣器默认设置为0
end
else if (data_o/1000 > 20)begin
beep_r <= 1'b0;
end
else if (data_o/1000 <= 20)begin
if (cnt1s == MAX1S - 1'd1)begin
beep_r <= ~beep_r;
end
else if (data_o/1000 <= 10 && cnt1s == MAX1_2S - 1'd1)begin
beep_r <= ~beep_r;
end
else begin
beep_r <= beep_r;
end
end
else begin
beep_r <= 1'b0;//否则不变
end
end
assign beep = beep_r;
assign led = led_r;
endmodule
- karman_filter(卡尔曼滤波)
module karman_filter (
input clk ,
input rst ,
input wire [18:0] data_o ,
output wire [18:0] data
);
/**
x_hat:状态估计量,表示系统状态的估计值。
p:协方差矩阵,表示状态估计量的不确定性。
kg:卡尔曼增益,用于调整测量值和状态估计值之间的权重。
q:过程噪声方差,表示系统模型中的噪声。
r:测量噪声方差,表示测量值中的噪声。
**/
reg [18:0] x_hat, p, kg;
reg [18:0] q, r;
always @(posedge clk or negedge rst) begin
if (!rst) begin
x_hat <= 0;
p <= 0;
end
else begin
// 预测步骤
x_hat <= x_hat;
p <= p + q;
// 更新步骤
kg <= p / (p + r);
x_hat <= x_hat + kg * (data_o - x_hat);
p <= (1 - kg) * p;
end
end
assign data = x_hat;
endmodule
- hc_sr_top(上述模块整体的顶层文件)
module hc_sr_top (
input wire Clk ,
input wire Rst_n ,
input wire echo ,
output wire trig ,
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 [6:0] hex6 ,
output wire [6:0] hex7 ,
output wire [6:0] hex8 ,
output wire beep ,
output wire [3:0] led
);
wire [18:0] data_o ;
wire [18:0] data ;
karman_filter u_karman_filter(
.clk (Clk ),
.rst (Rst_n ),
.data_o (data_o ),
.data (data )
);
hc_sr_driver u_hc_sr_driver(
.Clk (Clk ),
.Rst_n (Rst_n ),
.echo (echo ),
.trig (trig ),
.data_o (data_o )
);
seg_driver u_seg_driver(
.Clk (Clk ),
.Rst_n (Rst_n ),
.data_o (data ),
.hex1 (hex1 ),
.hex2 (hex2 ),
.hex3 (hex3 ),
.hex4 (hex4 ),
.hex5 (hex5 ),
.hex6 (hex6 ),
.hex7 (hex7 ),
.hex8 (hex8 )
);
beepled u_beepled(
.Clk (Clk ),
.Rst_n (Rst_n ),
.data_o (data_o ),
.beep (beep ),
.led (led )
);
endmodule
三.RTL viewer模块图
四.参考
https://blog.youkuaiyun.com/qq_43546203/article/details/125281386
五.总结
- 本次实验暂时完成了初步设计,实现了FPGA超声波测距功能。后续将完善串口及网络模块,可以期待一下后续博客的发布。