一、实验准备
软件:Quartus + Modelsim
硬件:DE2-E115 FPGA开发板+HC_SR04模块
二、实验要求
使用DE2开发板驱动HC_SR04模块,并将所测得数据显示到开发板上的数码管。
三、实验原理
模块实物图:
模块引脚介绍:
从上图我们可以看到超声波模块的4个引脚,它们的作用罗列如下:
-
VCC: 电源引脚,超声波模块工作电压为5伏。
-
Trig: 是Trigger(触发)这个单词的缩写,该引脚用于触发超声波脉冲。
-
Echo: 该引脚会在高电平和低电平之间转换,当检测到障碍物时,在高电平保持的时间就表示信号发射出去并反射回来的时间。
-
GND: 接地引脚。
模块测距工作原理:
-
主控设备给 Trig 脚提供一个 10us 的脉冲信号。
-
HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波。
-
HC-SR04 接收到返回的超声波,把 Echo 置为低电平。
-
Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。
超声波时序图:
四、模块实现
hc_sr_trig
模块
module hc_sr_trig(
input wire clk ,
input wire rst_n ,
output wire trig //触发测距信号
);
//trig:超声波触发信号,高电平至少为10us,同时考虑到信号不要重叠,所以当距离为5000mm时的来回时间做为超声波触发信号的周期T
//T=(5/340) * 1000ms = 14.7ms,来回就是29.4ms,所以就让触发脉冲的周期至少为30ms,高电平时间为10us
//为了防止发射信号对回响信号产生影响,这里直接设定为100ms的周期,高电平为10us
parameter CNT_10US_MAX = 9'd500;
parameter CNT_100MS_MAX = 23'd5000_000;
reg [22:0] cnt_100ms ;
wire cnt_100ms_flag ;
reg trig_r;
//cnt_100ms and cnt_100ms_flag
assign cnt_100ms_flag = cnt_100ms==CNT_100MS_MAX - 1'b1;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_100ms<=23'd0;
end
else if(cnt_100ms_flag) begin
cnt_100ms<=23'd0;
end
else begin
cnt_100ms<=cnt_100ms+1'b1;
end
end
//trig
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
trig_r<=1'b0;
end
else if(cnt_100ms<CNT_10US_MAX) begin
trig_r<=1'b1;
end
else begin
trig_r<=1'b0;
end
end
assign trig = trig_r;
endmodule
hc_sr_echo
模块
module hc_sr_echo(
input wire clk ,
input wire rst_n ,
input wire echo ,
output reg fall_flag_r1, //下降沿标志的打拍信号
output wire [12:0] data_o //检测距离,保留3位小数,*1000实现
);
/*计算:s=340*t/2 m = 340_000 mm * t /2
记脉冲个数:N
s=N*20*340_000/1000_000_000/2 mm = N*0.0034 mm = N*34/10000 ;
5000/0.0034=1470588 21bit ; 34 6bit
*/
reg echo_r1 ;
reg echo_r2 ;
wire raise_flag ;//上升沿标志信号
wire fall_flag ;//下降沿标志信号
reg [20:0] cnt_pulse ;//考虑的范围25~4000mm
reg [20:0] temp_pulse ;
reg [12:0] data_bin ;
//raise_flag
assign raise_flag = ((~echo_r2) && (echo_r1)) ? 1'b1 : 1'b0 ;
assign fall_flag = ((echo_r2) && (~echo_r1)) ? 1'b1 : 1'b0 ;
//beat time
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
echo_r1<=1'b0;
echo_r2<=1'b0;
end
else begin
echo_r1<=echo;
echo_r2<=echo_r1;
end
end
//cnt_pulse
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_pulse<=21'd0;
end
else if(raise_flag) begin
cnt_pulse<=21'd1;//直接从1开始计数,计算的时候就不需要再进行加1操作了
end
else if(echo_r2) begin
cnt_pulse<=cnt_pulse+1'b1;
end
else begin
cnt_pulse<=cnt_pulse;
end
end
//temp_pulse
always @ (posedge clk or negedge rst_n ) begin
if(!rst_n) begin
temp_pulse<=21'd0;
end
else if(fall_flag) begin
temp_pulse<=cnt_pulse;
end
else begin
temp_pulse<=temp_pulse;
end
end
//计算距离,以mm为单位,s=N*34/10000
always @ (posedge clk or negedge rst_n ) begin
if(!rst_n) begin
fall_flag_r1<=1'b0;
end
else begin
fall_flag_r1<=fall_flag;
end
end
//data_bin
always @ (posedge clk or negedge rst_n ) begin
if(!rst_n) begin
data_bin<=1'b0;
end
else if(fall_flag_r1) begin
data_bin<=temp_pulse*34/10000;
end
else begin
data_bin<=data_bin;
end
end
assign data_o = data_bin;
endmodule
均值滤波
//均值滤波算法
module filter
(
input wire clk ,
input wire rst_n ,
input wire [12:0] data_bin ,
input wire fall_flag_r1 ,
output wire [12:0] data_ave
);
reg [12:0] data_temp [7:0];
reg [15:0] data_sum ;
//data_temp:移位暂存8次测距的数值
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_temp[0]<=13'd0;
data_temp[1]<=13'd0;
data_temp[2]<=13'd0;
data_temp[3]<=13'd0;
data_temp[4]<=13'd0;
data_temp[5]<=13'd0;
data_temp[6]<=13'd0;
data_temp[7]<=13'd0;
end
else if(fall_flag_r1) begin
data_temp[0]<=data_bin ;
data_temp[1]<=data_temp[0] ;
data_temp[2]<=data_temp[1] ;
data_temp[3]<=data_temp[2] ;
data_temp[4]<=data_temp[3] ;
data_temp[5]<=data_temp[4] ;
data_temp[6]<=data_temp[5] ;
data_temp[7]<=data_temp[6] ;
end
end
//data_sum:暂存的8个数据之和
always @ (posedge clk or negedge rst_n ) begin
if(!rst_n) begin
data_sum<=1'd0;
end
else begin
data_sum<=data_temp[0]+data_temp[1]+data_temp[2]+data_temp[3]+data_temp[4]+data_temp[5]+data_temp[6]+data_temp[7];
end
end
//data_ave
assign data_ave = data_sum[15:3] ;//截取高13位,相当于右移3位,即除以8的效果。
endmodule
二进制码转化为8421BCD码
//二进制码转化为8421BCD码
module bin_to_bcd
(
input wire clk ,//The onboard clock frequency is 50MHz.
input wire rst_n ,
input wire [12:0] data_ave ,
output reg [15:0] data_bcd
);
parameter SHIFT_CNT_MAX = 5'd14 ;//输入的二进制数有多少位就进行多少次比较移位操作
reg shift_flag ;
reg [4:0] shift_cnt ;
reg [28:0] data_shift ;
wire complete_flag ;
wire shift_cnt_flag ;
//shift_flag
always @ (posedge clk or negedge rst_n ) begin
if(!rst_n) begin
shift_flag <= 1'b0;
end
else begin
shift_flag <= ~shift_flag;
end
end
//shift_cnt
assign shift_cnt_flag = shift_cnt==SHIFT_CNT_MAX ;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
shift_cnt <= 5'd0;
end
else if(shift_flag) begin
if(shift_cnt_flag) begin
shift_cnt <= 5'd0;
end
else begin
shift_cnt <= shift_cnt+1'b1;
end
end
else begin
shift_cnt <= shift_cnt;
end
end
//data_shift
always @ (posedge clk or negedge rst_n ) begin
if(!rst_n) begin
data_shift <= 29'd0;
end
else if((!shift_cnt) && (shift_flag)) begin
data_shift <= {16'b0,data_ave};
end
else if((shift_cnt>=1'b1) && (shift_cnt <= 5'd13)) begin
if(!shift_flag) begin //shift_flag==0,比较
data_shift[16:13] <= (data_shift[16:13] > 4'd4) ? (data_shift[16:13]+4'd3) : data_shift[16:13];
data_shift[20:17] <= (data_shift[20:17] > 4'd4) ? (data_shift[20:17]+4'd3) : data_shift[20:17];
data_shift[24:21] <= (data_shift[24:21] > 4'd4) ? (data_shift[24:21]+4'd3) : data_shift[24:21];
data_shift[28:25] <= (data_shift[28:25] > 4'd4) ? (data_shift[28:25]+4'd3) : data_shift[28:25];
end
else begin//shift_flag==1,移位
data_shift <= data_shift<<1'b1;
end
end
else begin
data_shift <= data_shift;
end
end
//complete_flag
assign complete_flag = (shift_cnt_flag && shift_flag) ? 1'b1: 1'b0 ;
//data_bcd
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_bcd <= 16'd0;
end
else if(complete_flag) begin
data_bcd <= data_shift[28:13];
end
else begin
data_bcd <= data_bcd;
end
end
endmodule
RS232 UART
module FPGA_UART (
input wire clk ,
input wire rst_n ,
input wire uart_rx ,
input wire [16:00] data_o ,
output wire uart_tx
);
parameter state_wait = 4'd0;
parameter state_send = 4'd1;
reg [7:0] uart_send_data = 8'd0;
reg uart_send_valid;
wire uart_send_ready;
reg [31:0] counter = 32'd0;
reg [3:0] state = state_wait;
reg send_flag = 0; // 这个标志位的作用应该是表示移出数据标志位,这里懒得改了
reg end_flag = 0;
wire [15:00] data_buf; // 测试数据
assign data_buf = data_o[15:0];
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
uart_send_valid <= 0;
counter <= 32'd0;
uart_send_data <= 8'd0;
state <= state_wait;
send_flag = 0;
end
else begin
case (state)
state_wait: begin
counter <= counter + 32'd1;
if(counter >= 32'd24999999) begin
counter <= 32'd0;
state <= state_send;
uart_send_data <= data_buf[15:8]; // 提前移出第一个字节
end
end
state_send: begin
if(end_flag) begin
end_flag <= 0;
uart_send_valid <= 0;
send_flag <= 0;
state <= state_wait;
end
else begin
uart_send_valid <= 1;
send_flag <= 1;
if(send_flag) begin // 这里是为了让uart_send_valid维持一个周期后等第一个字节发送完成后移出后面的字节
uart_send_data <= data_buf[7:0]; // 模拟字节移出
end_flag = 1; // 强行退出发送状态,实际运用中会判断数据长度
end
end
end
endcase
end
end
uart u0 (
.clk_clk (clk), // clk.clk
.reset_reset_n (rst_n), // reset.reset_n
// .rs232_0_from_uart_ready (<connected-to-rs232_0_from_uart_ready>), // rs232_0_avalon_data_receive_source.ready
// .rs232_0_from_uart_data (<connected-to-rs232_0_from_uart_data>), // .data
// .rs232_0_from_uart_error (<connected-to-rs232_0_from_uart_error>), // .error
// .rs232_0_from_uart_valid (<connected-to-rs232_0_from_uart_valid>), // .valid
.rs232_0_to_uart_data (uart_send_data), // rs232_0_avalon_data_transmit_sink.data
// .rs232_0_to_uart_error (<connected-to-rs232_0_to_uart_error>), // .error
.rs232_0_to_uart_valid (uart_send_valid), // .valid
.rs232_0_to_uart_ready (uart_send_ready), // .ready
.rs232_0_UART_RXD (uart_rx), // rs232_0_external_interface.RXD
.rs232_0_UART_TXD (uart_tx) // .TXD
);
endmodule
顶层文件
module hc_sr_top (
input wire clk ,
input wire rst_n ,
input wire echo ,
input wire uart_rx ,
output wire trig ,
output wire uart_tx ,
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 [6:0] hex6
);
wire fall_flag_r1 ;
wire [12:0] data_o ;//得到的超声波测得的距离值,以mm为单位
wire [12:0] data_ave ;//均值滤波后的数据
wire [15:0] data_bcd ;//显示的四位BCD码数据
hc_sr_trig u_hc_sr_trig(
.clk (clk ),
.rst_n (rst_n ),
.trig (trig )
);
hc_sr_echo u_hc_sr_echo(
.clk (clk ),
.rst_n (rst_n ),
.echo (echo ),
.fall_flag_r1 (fall_flag_r1 ),
.data_o (data_o )
);
filter u_filter
(
.clk (clk ),
.rst_n (rst_n ),
.data_bin (data_o ),
.fall_flag_r1 (fall_flag_r1 ),
.data_ave (data_ave )
);
//二进制码转化为8421BCD码
bin_to_bcd u_bin_to_bcd
(
.clk (clk ),//The onboard clock frequency is 50MHz.
.rst_n (rst_n ),
.data_ave (data_ave ),
.data_bcd (data_bcd )
);
FPGA_UART u_FPGA_UART(
.clk (clk ),
.rst_n (rst_n ),
.uart_rx (uart_rx ),
.data_o (data_o ),
.uart_tx (uart_tx )
);
endmodule
五、参考与总结
参考:https://blog.csdn.net/jynyyhd/article/details/130899810
总结:
模块功能基本实现,能够测量距离
存在距离和延时问题