一、超声波测距模块HC_SR04
HC-SR04是一种基于超声波的测距模块。该模块向前15度内发送超声波并接收回响,通过发出超声波到收到回响的这个时间间隔计算前方的障碍物距离,可以用来给智能小车做障碍物监测。可提供2cm- 400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。
该模块引脚图如下:
从上图我们可以看到超声波模块的4个引脚,它们的作用罗列如下:
-
VCC: 电源引脚,超声波模块工作电压为5伏。
-
Trig: 是Trigger(触发)这个单词的缩写,该引脚用于触发超声波脉冲。
-
Echo: 该引脚会在高电平和低电平之间转换,当检测到障碍物时,在高电平保持的时间就表示信号发射出去并反射回来的时间。
-
GND: 接地引脚。
该模块的时序图如下:
模块测距工作原理:
(1) 主控设备给 Trig 脚提供一个 10us 的脉冲信号。
(2) HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波。
(3) HC-SR04 接收到返回的超声波,把 Echo 置为低电平。
(4) Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。
我们知道距离=速度x时间,要求出超声波模块到障碍物之间的距离,我们就需要知道速度和时间。速度就是340m/s,也就是声音在空气中传播的速度,转换城cm/μs单位就是0.034 cm/μs。时间的话,由于我们得到的是信号从发射到返回的时间值,所以需要除以2。
最终得到的计算公式为:距离 = 0.034 cm/μs * 时间(μs) / 2
二、模块框图
我们需要设计以下模块:
1、测距信号源模块,输入时钟与复位,每隔300ms输出15us高电平。
2、距离计算模块,输入时钟、复位与回响信号echo,输出距离(cm)
3、数据显示模块,将得到的距离可视化,我用的是2个1位数码管模块(小脚丫自带两个1位共阴极数码管)
4、顶层模块
框图如下:
三、模块编写
3.1 测距信号源
在此模块,写一个计时周期为300us的循环计数器,当计数器值小于15时输出高电平,其他时候输出低电平即可,这里用锁相环生成了一个1MHZ(1us)的时钟,让它驱动计时:
module trigger_send #(
parameter TIME_1S = 12_000_000
) (
input clk ,
input rst_n ,
output trigger
);
//1us生成
wire clk_1us;
PLL UPLL(
.inclk0 (clk),
.c0 (clk_1us)
);
reg [25:0] cnt_1us;
always @(posedge clk_1us or negedge rst_n) begin
if (!rst_n) begin
cnt_1us <= 20'd0;
end
else if (cnt_1us == 20'd300_000 - 1)begin
cnt_1us <= 20'd0;
end
else begin
cnt_1us <= cnt_1us + 1'b1;
end
end
assign trigger = cnt_1us < 15 ? 1'b1 : 1'b0;
endmodule
3.2 距离计算
这个模块输入了HC_SR04的回响引脚,要做的就是计算它的高电平持续时间,并转化为距离数据输出。
这里生成一个1us时钟,用这个时钟监测回响信号的上升沿与下降沿。检测到上升沿,就每过一个时钟周期就把计时器数值加1;等检测到下降沿,就把计时器的数据保存起来,然后把计时器清空准备下次计时。
拿到保存好的计时后,使用上文提到的公式计算出距离输出。
module data_rec #(
parameter TIME_1S = 12_000_000
) (
input clk ,//系统时钟
input rst_n ,//复位
input rec_data ,//回响,早知道取名echo了
output [11:0] distance //计算好的距离
);
//锁相环生成1us周期时钟,因为后面的计时单位全是1us,这样方便
wire clk_1us;
PLL UPLL(
.inclk0 (clk),
.c0 (clk_1us)
);
//给回响信号打拍,检测上升沿下降沿
reg rec_data2;
reg rec_data3;
wire rec_negedge;
wire rec_posedge;
assign rec_negedge = (!rec_data2) && rec_data3;
assign rec_posedge = rec_data2 && (!rec_data3);
always @(posedge clk_1us or negedge rst_n) begin
if (!rst_n) begin
rec_data2 <= 1'b0;
end
else begin
rec_data2 <= rec_data;
end
end
always @(posedge clk_1us or negedge rst_n) begin
if (!rst_n) begin
rec_data3 <= 1'b0;
end
else begin
rec_data3 <= rec_data2;
end
end
//计时启动标志,上升沿启动,下降沿结束
reg flag;
always @(posedge clk_1us or negedge rst_n) begin
if (!rst_n) begin
flag <= 1'b0;
end
else if (rec_posedge)begin
flag <= 1'b1;
end
else if (rec_negedge)begin
flag <= 1'b0;
end
else begin
flag <= flag;
end
end
//计时器数值,flag期间计数,有下降沿就清0,急了多少数就是保持了多少us,因为是用1us时钟驱动的
reg [14:0] cnt_1us;
always @(posedge clk_1us or negedge rst_n) begin
if (!rst_n) begin
cnt_1us <= 15'd0;
end
else if (rec_negedge)begin
cnt_1us <= 15'd0;
end
else if (flag && cnt_1us < 15'd15_000)begin
cnt_1us <= cnt_1us + 1'b1;
end
else begin
cnt_1us <= cnt_1us;
end
end
//因为计时器结束计时会变0,因此要用另外的变量,在它清0的时候把值保存下来
reg [14:0] high_time;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
high_time <= 15'd0;
end
else if (rec_negedge)begin
high_time <= cnt_1us ;
end
else begin
high_time <= high_time;
end
end
//计算距离,Verilog不能直接用浮点数,就这样实现乘以0.017
//为什么不是0.034看前面
reg [11:0] distance_buf;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
distance_buf <= 12'd0;
end
else begin
distance_buf <= high_time * 17 / 1000;
end
end
assign distance = distance_buf;
endmodule
3.3 数码管模块
距离模块的distance就是测到的距离,我的小脚丫自带两个1位的共阴极数码管,就用它们来显示。
1位七段式共阴极数码管十进制显示模块如下:
module nixietube_1 (
input clk,
input rst_n,
input [3:0] din, //输入0-9
output drive_out, //使能
output [6:0] _dig, //输出数码管
output dot_out //小数点要亮吗
);
//dot小数点输出
assign dot_out = 1'b0;
//使能,阴极为0
assign drive_out = 1'b0;
//dig段选输出
parameter ZER = 7'b0111111,
ONE = 7'b0000110,
TWO = 7'b1011011,
THR = 7'b1001111,
FOU = 7'b1100110,
FIV = 7'b1101101,
SIX = 7'b1111101,
SEV = 7'b0000111,
EIG = 7'b1111111,
NIN = 7'b1101111;
reg [6:0] dig;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dig <= ZER;
end
else begin
case (din)
0 : dig <= ZER;
1 : dig <= ONE;
2 : dig <= TWO;
3 : dig <= THR;
4 : dig <= FOU;
5 : dig <= FIV;
6 : dig <= SIX;
7 : dig <= SEV;
8 : dig <= EIG;
9 : dig <= NIN;
default : dig <= ZER;
endcase
end
end
assign _dig = dig;
endmodule
3.4 顶层模块
代码如下:
module Ultrasound (
input clk,
input rst_n,
input rec_data ,
output trigger ,
output [6:0] dig1,
output [6:0] dig2,
output dot1,
output dot2,
output drive1,
output drive2
);
trigger_send u_trigger_send(
.clk (clk),
.rst_n (rst_n),
.trigger (trigger)
);
wire [11:0] distance;
data_rec u_data_rec(
.clk (clk),
.rst_n (rst_n),
.rec_data (rec_data),
.distance (distance)
);
nixietube_1 u_nixietube_1(
.clk (clk),
.rst_n (rst_n),
.din ((distance/10)%10),
._dig (dig1),
.dot_out (dot1),
.drive_out (drive1)
);
nixietube_1 u_nixietube_2(
.clk (clk),
.rst_n (rst_n),
.din ((distance/1)%10),
._dig (dig2),
.dot_out (dot2),
.drive_out (drive2)
);
endmodule
3.5 实验现象
引脚设置的时候,两个数码管,时钟按照小脚丫的原理图来设置,trig与echo自己设置,复位键可以用它自带的按钮。
四、总结
本次实验主要是了解到了超声波模块HC_SR04测距的原理,并用FPGA驱动了该模块,成功观察到了实验现象,收获很大。