当前位置:网站首页>基于FPGA的超声波测距
基于FPGA的超声波测距
2022-07-05 18:40:00 【醉意丶千层梦】
文章目录
一、项目框架
distance超声波测距模块负责数据的采集,vga、uart、beep、数码管根据采集到的数据分别进行vga的屏幕打点、串口输出到上位机、蜂鸣器根据数据大小进行鸣叫以及数码管显示采集到的数据。
RTL视图
二、超声波测距模块
代码
module distance_drive (
input wire clk,
input wire clk_1,
input wire rst_n,
input wire echo,
output reg trig,
output wire data_out_vld,
output wire [ 23:0 ] distance_data
);
localparam MAX_DISTANCE = 117647; //最大距离 4m
parameter s_idle = 0;//空闲状态
parameter s_send = 1;//发送触发信号
parameter s_wait = 2;//等待内部发送脉冲
parameter s_accept = 3;//检测回响信号
parameter s_accept_wait = 4;//延时等待
reg echo_r0 ;
reg echo_r1 ;
reg [ 2:0 ] s_current ;
reg [ 2:0 ] s_next ;
reg [ 22 :0 ] cnt ;
reg [ 22:0 ] cnt_wait ;
reg [ 22:0 ] cnt_max ;
reg [ 16:0 ] cnt_distance ;
// reg [ 25:0 ] cnt_distance_r1 ;
// reg [ 19:0 ] cnt_distance_r2 ;
wire accept_start_sig ;
wire accept_stop_sig ;
wire idle_sig ;
wire send_sig ;
// wire wait_sig ;
wire flag_clear_cnt ;
wire flag_clear_cnt_wait ;
reg [ 19:0 ] distance_data_r ;
wire [ 23:0 ] distance_data_r1 ;
assign idle_sig = s_current == s_idle;
assign send_sig = s_current == s_send && flag_clear_cnt;
// assign wait_sig = s_current == s_wait && flag_clear_cnt_wait;
assign accept_wait_sig = s_current == s_accept_wait && flag_clear_cnt_wait;
assign accept_start_sig = s_current == s_wait && echo_r0 && ~echo_r1;
assign accept_stop_sig = s_current == s_accept && (~echo_r0 && echo_r1);
// always @(posedge clk or negedge rst_n) begin
// if(!rst_n) begin
// cnt_distance_r1 <= 0;
// // cnt_distance_r2 <= 0;
// end
// else begin
// cnt_distance_r1 <= cnt_distance * 340 / 100;
// // cnt_distance_r2 <= cnt_distance_r1 >> 4;
// end
// end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
echo_r0 <= 0;
echo_r1 <= 0;
end
else begin
echo_r0 <= echo;
echo_r1 <= echo_r0;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
s_current <= s_idle;
end
else begin
s_current <= s_next;
end
end
always @(*) begin
case (s_current)
s_idle : begin
if(idle_sig) begin
s_next = s_send;
end
else begin
s_next = s_idle;
end
end
s_send : begin
if(send_sig) begin
s_next = s_wait;
end
else begin
s_next = s_send;
end
end
s_wait : begin
if(accept_start_sig) begin
s_next = s_accept;
end
else begin
s_next = s_wait;
end
end
s_accept : begin
if(accept_stop_sig) begin
s_next = s_accept_wait;
end
else begin
s_next = s_accept;
end
end
s_accept_wait : begin
if(accept_wait_sig) begin
s_next <= s_idle;
end
else begin
s_next <= s_accept_wait;
end
end
default: s_next = s_idle;
endcase
end
//距离
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
distance_data_r <= 0;
end
else if(accept_stop_sig) begin
distance_data_r <= cnt_distance * 340 / 200;
end
end
//转BCD码
assign distance_data_r1[3:0] = distance_data_r % 10;
assign distance_data_r1[7:4] = distance_data_r / 10 % 10;
assign distance_data_r1[11:8] = distance_data_r / 100 % 10;
assign distance_data_r1[15:12] = distance_data_r / 1000 % 10;
assign distance_data_r1[19:16] = distance_data_r / 10000 % 10;
assign distance_data_r1[23:20] = distance_data_r / 100000 % 10;
assign data_out_vld = accept_wait_sig;
assign distance_data = distance_data_r1;
//回响信号计数器
always @(posedge clk_1 or negedge rst_n) begin
if(!rst_n) begin
cnt_distance <= 0;
end
else if(accept_start_sig) begin
cnt_distance <= 0;
end
else if(s_current == s_accept) begin
cnt_distance <= cnt_distance + 1;
end
else begin
cnt_distance <= 0;
end
end
//发送触发信号
always @(posedge clk_1 or negedge rst_n) begin
case (s_current)
s_idle : begin
trig <= 0;
end
s_send : begin
trig <= 1;
end
s_wait : begin
trig <= 0;
end
s_accept : begin
trig <= 0;
end
s_accept_wait : begin
trig <= 0;
end
default: begin
trig <= 0;
end
endcase
end
//等待发送玩脉冲
always @( posedge clk_1 or negedge rst_n ) begin
if ( !rst_n ) begin
cnt <= 0;
end
else if ( s_current == s_send ) begin
if ( flag_clear_cnt == 9 ) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
else begin
cnt <= 0;
end
end
assign flag_clear_cnt = cnt == 9;
//延时计数器
always @( posedge clk_1 or negedge rst_n ) begin
if ( !rst_n ) begin
cnt_wait <= 0;
end
else if ( s_current == s_accept_wait ) begin
if ( flag_clear_cnt_wait ) begin
cnt_wait <= 0;
end
else begin
cnt_wait <= cnt_wait + 1;
end
end
else begin
cnt_wait <= 0;
end
end
assign flag_clear_cnt_wait = cnt_wait == 250_000;
endmodule //distance
三、串口模块
1.串口发送模块
module uart_tx(input wire clk,
input wire rst_n,
input wire tx_enable, // 发送使能
input wire [ 07:0 ] data_in, // 需要发送的数据
input wire [ 19:0 ] tx_bps, // 发送的波特率
output wire data, // 发送的数据
output wire tx_done);
localparam MAX_BIT = 10;
reg [ 09:0 ] data_r ; // 数据寄存器
reg [ 12:0 ] cnt_bps ; // 波特率计数器
reg [ 03:0 ] cnt_bit ; // 数据位计数器
wire [ 12:0 ] max_bps ; // 波特率对应频率
wire flag_clear_cnt_bps ; // 计数器归零
wire flag_add_cnt_bit ; // 计数器+1
wire flag_clear_cnt_bit ;
reg flag_send_data ; //发送数据标志
//输入数据寄存
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_r <= 10'b0;
end
else if(tx_enable) begin
data_r <={
1'b1, data_in, 1'b0};
end
end
// 波特率计数器
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt_bps <= 0;
end
else if ( flag_send_data ) begin
if ( flag_clear_cnt_bps ) begin
cnt_bps <= 0;
end
else begin
cnt_bps <= cnt_bps + 1;
end
end
else begin
cnt_bps <= 0;
end
end
assign flag_clear_cnt_bps = cnt_bps >= max_bps -1;
assign max_bps = 50_000_000 / tx_bps;
// 数据位计数器
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt_bit <= 0;
end
else if ( flag_send_data ) begin
if ( flag_clear_cnt_bit ) begin
cnt_bit <= 0;
end
else if ( flag_add_cnt_bit )begin
cnt_bit <= cnt_bit + 1;
end
else begin
cnt_bit <= cnt_bit;
end
end
else begin
cnt_bit <= 0;
end
end
assign flag_add_cnt_bit = flag_clear_cnt_bps;
assign flag_clear_cnt_bit = cnt_bit >= MAX_BIT - 1 && flag_add_cnt_bit ;
//发送数据标志
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
flag_send_data <= 0;
end
else if(tx_enable) begin
flag_send_data <= 1;
end
else if(flag_clear_cnt_bit) begin
flag_send_data <= 0;
end
else begin
flag_send_data <= flag_send_data;
end
end
//发送数据
assign data = flag_send_data ? data_r[cnt_bit]:1;
assign tx_done = ~flag_send_data ;
endmodule
2.串口发送控制模块
串口控制模块把接受到的24位BCD码转换成ASSIC码,并且清除高位零位和添加单位和小数点。把处理好的数据加入FIFO中,再通过串口发送模块进行发送。
module uart_drive (
input wire clk,
input wire rst_n,
input wire [ 23:0 ] distance_data,
input wire data_vld,
output wire rx_data,
output wire tx_data
);
reg [ 23:0 ] distance_data_r ;
reg [ 7:0 ] data ;
reg [ 3:0 ] cnt_byte ;
reg send_flag ;
wire [ 7:0 ] distance ;
wire rdreq ;
wire wrreq ;
wire empty ;
wire full ;
wire [ 7:0 ] data_in ;
reg flag ;
//串口
uart_tx u_uart_tx(
.clk ( clk ),
.rst_n ( rst_n ),
.tx_enable ( rdreq ),
.data_in ( data_in ),
.tx_bps ( 115200 ),
.data ( tx_data ),
.tx_done ( tx_done )
);
assign rdreq = tx_done && ~empty;
assign wrreq = ~full && send_flag && (cnt_byte > 0) && flag;
assign distance = data;
tx_fifo tx_fifo_inst (
.aclr ( ~rst_n ),
.clock ( clk ),
.data ( distance ),
.rdreq ( rdreq ),
.wrreq ( wrreq ),
.empty ( empty ),
.full ( full ),
.q ( data_in ),
.usedw ( usedw_sig )
);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
send_flag <= 0;
end
else if(cnt_byte == 9) begin
send_flag <= 0;
end
else if(data_vld) begin
send_flag <= 1;
end
end
//数据计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_byte <=0;
end
else if(cnt_byte == 9) begin
cnt_byte <= 0;
end
else if(send_flag) begin
cnt_byte <= cnt_byte + 1;
end
end
//寄存数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
distance_data_r <=0;
end
else if(data_vld) begin
distance_data_r <= distance_data;
end
end
//去除前面的不必要的0
always @(*) begin
if(!rst_n) begin
flag = 0;
end
else if(!send_flag) begin
flag <= 0;
end
else if(cnt_byte > 3 || data> 48) begin
flag = 1;
end
else begin
flag <= flag;
end
end
//发送距离
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data <=0;
end
else if(send_flag) begin
case (cnt_byte)
0 : data <= distance_data_r[23:20] + 48;
1 : data <= distance_data_r[19:19] + 48;
2 : data <= distance_data_r[15:12] + 48;
3 : data <= distance_data_r[11:8 ] + 48;
4 : data <= 46; // .
5 : data <= distance_data_r[7 : 4] + 48;
6 : data <= distance_data_r[3 : 0] + 48;
7 : data <= 99 ; //c
8 : data <= 109; //m
default: data <=0;
endcase
// data <= distance_data_r[(4 * (6-cnt_byte) -1) -:4] + 48;
end
end
endmodule //uart_drive
四、蜂鸣器模块
蜂鸣器模块把接受到的数据去掉低两位,也就是精度变成厘米级别。当处理完后的数据在MAX_DISTANCE和MIN_DISTANCE之间,则会根据数据的大小调整蜂鸣器鸣叫间隔,使得蜂鸣器的鸣叫频率随着距离的减少越来越高,当数据小于MIN_DISTANCE时则会一直处在鸣叫。
module beep_dirve (
input wire clk,
input wire rst_n,
input wire beep_vld,
input wire data_vld,
input wire [ 23:0 ] distance_data,
output reg beep
);
parameter MAX_DISTANCE = 20;
parameter MIN_DISTANCE = 10;
parameter MAX_TIME = 50_000_000;
reg [ 27:0 ] cnt ;
wire [ 27:0 ] delay ;
wire [ 19:0 ] distance ;
reg [ 23:0 ] distance_data_r ;
// wire [ 19:0 ] distance_r ;
//寄存数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
distance_data_r <= 0;
end
else if(data_vld) begin
distance_data_r <= distance_data;
end
end
// 根据距离设置翻转频率
assign distance = distance_data_r[11:8] + distance_data_r[15:12] * 10 + distance_data_r[19:16] * 100 + distance_data_r[23:20] *1000;
assign delay = ((distance ) + 1) * 200_000;
// // led
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt <= 0;
end
else if ( cnt >= delay ) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
beep <= 1;
end
else if(~beep_vld) begin
beep <= 1;
end
else if(distance <= MAX_DISTANCE && distance >= MIN_DISTANCE && cnt == 1 && beep_vld) begin
beep <= ~ beep;
end
else if(distance < MIN_DISTANCE && beep_vld) begin
beep <= 0;
end
else if(distance > MAX_DISTANCE) begin
beep <= 1;
end
else begin
beep <= beep;
end
end
endmodule //beep_dirve
五、vga显示模块
1.vga协议驱动代码
module vga_dirve (input wire clk, //系统时钟
input wire rst_n, //复位
input wire [ 15:0 ] rgb_data, //16位RGB对应值
output wire vga_clk, //vga时钟 25M
output reg h_sync, //行同步信号
output reg v_sync, //场同步信号
output reg [ 11:0 ] addr_h, //行地址
output reg [ 11:0 ] addr_v, //列地址
output wire [ 4:0 ] rgb_r, //红基色
output wire [ 5:0 ] rgb_g, //绿基色
output wire [ 4:0 ] rgb_b //蓝基色
);
// 640 * 480 60HZ
localparam H_FRONT = 16; // 行同步前沿信号周期长
localparam H_SYNC = 96; // 行同步信号周期长
localparam H_BLACK = 48; // 行同步后沿信号周期长
localparam H_ACT = 640; // 行显示周期长
localparam V_FRONT = 11; // 场同步前沿信号周期长
localparam V_SYNC = 2; // 场同步信号周期长
localparam V_BLACK = 31; // 场同步后沿信号周期长
localparam V_ACT = 480; // 场显示周期长
// 800 * 600 72HZ
// localparam H_FRONT = 40; // 行同步前沿信号周期长
// localparam H_SYNC = 120; // 行同步信号周期长
// localparam H_BLACK = 88; // 行同步后沿信号周期长
// localparam H_ACT = 800; // 行显示周期长
// localparam V_FRONT = 37; // 场同步前沿信号周期长
// localparam V_SYNC = 6; // 场同步信号周期长
// localparam V_BLACK = 23; // 场同步后沿信号周期长
// localparam V_ACT = 600; // 场显示周期长
localparam H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期
localparam V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期
reg [ 11:0 ] cnt_h ; // 行计数器
reg [ 11:0 ] cnt_v ; // 场计数器
reg [ 15:0 ] rgb ; // 对应显示颜色值
// 对应计数器开始、结束、计数信号
wire flag_enable_cnt_h ;
wire flag_clear_cnt_h ;
wire flag_enable_cnt_v ;
wire flag_clear_cnt_v ;
wire flag_add_cnt_v ;
wire valid_area ;
// 25M时钟 行周期*场周期*刷新率 = 800 * 525 * 60
reg clk_25 ;
// 50M时钟 1040 * 666 * 72
wire clk_50 ;
//PLL
// pll pll_inst (
// .areset ( ~rst_n ),
// .inclk0 ( clk ),
// .c0 ( clk_50 ), //50M
// .c1 ( clk_25 ) //25M
// );
//根据不同分配率选择不同频率时钟
assign vga_clk = clk;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
clk_25 <= 0;
end
else begin
clk_25 <= ~clk_25;
end
end
// 行计数
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt_h <= 0;
end
else if ( flag_enable_cnt_h ) begin
if ( flag_clear_cnt_h ) begin
cnt_h <= 0;
end
else begin
cnt_h <= cnt_h + 1;
end
end
else begin
cnt_h <= 0;
end
end
assign flag_enable_cnt_h = 1;
assign flag_clear_cnt_h = cnt_h == H_TOTAL - 1;
// 行同步信号
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
h_sync <= 1;
end
else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1
h_sync <= 0;
end
else if ( flag_clear_cnt_h ) begin // 其余为0
h_sync <= 1;
end
else begin
h_sync <= h_sync;
end
end
// 场计数
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt_v <= 0;
end
else if ( flag_enable_cnt_v ) begin
if ( flag_clear_cnt_v ) begin
cnt_v <= 0;
end
else if ( flag_add_cnt_v ) begin
cnt_v <= cnt_v + 1;
end
else begin
cnt_v <= cnt_v;
end
end
else begin
cnt_v <= 0;
end
end
assign flag_enable_cnt_v = flag_enable_cnt_h;
assign flag_clear_cnt_v = cnt_v == V_TOTAL - 1;
assign flag_add_cnt_v = flag_clear_cnt_h;
// 场同步信号
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
v_sync <= 1;
end
else if ( cnt_v == V_SYNC - 1 ) begin
v_sync <= 0;
end
else if ( flag_clear_cnt_v ) begin
v_sync <= 1;
end
else begin
v_sync <= v_sync;
end
end
// 对应有效区域行地址 1-640
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
addr_h <= 0;
end
else if ( valid_area ) begin
addr_h <= cnt_h - H_SYNC - H_BLACK + 1;
end
else begin
addr_h <= 0;
end
end
// 对应有效区域列地址 1-480
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
addr_v <= 0;
end
else if ( valid_area ) begin
addr_v <= cnt_v -V_SYNC - V_BLACK + 1;
end
else begin
addr_v <= 0;
end
end
// 有效显示区域
assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h <= H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v <= V_SYNC + V_BLACK + V_ACT;
// 显示颜色
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
rgb <= 16'h0;
end
else if ( valid_area ) begin
rgb <= rgb_data;
end
else begin
rgb <= 16'b0;
end
end
assign rgb_r = rgb[ 15:11 ];
assign rgb_g = rgb[ 10:5 ];
assign rgb_b = rgb[ 4:0 ];
endmodule // vga_dirve
2.vga数据控制模块
横坐标为时间,纵坐标为距离大小。距离大小决定场坐标,第几个距离决定行坐标。
module data_drive (input wire clk,
input wire vga_clk,
input wire rst_n,
input wire [ 11:0 ] addr_h,
input wire [ 11:0 ] addr_v,
input wire data_vld,
input wire [ 23:0 ] distance_data,
output wire [ 15:0 ] rgb_data);
localparam red = 16'd63488;
localparam orange = 16'd64384;
localparam yellow = 16'd65472;
localparam green = 16'd1024;
localparam blue = 16'd31;
localparam indigo = 16'd18448;
localparam purple = 16'd32784;
localparam white = 16'd65503;
localparam black = 16'd0;
parameter NUM = 100;
reg [ 19:0 ] distance_data_r ;
reg [ 15:0 ] rgb_data_r ;
reg [ 10:0 ] data_r[NUM -1:0] ;
integer j;
integer i;
//寄存数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
distance_data_r <= 0;
end
else if(data_vld) begin
distance_data_r <=distance_data[7:4]+ distance_data[11:8]*10 + distance_data[15:12] * 100 ;
end
end
//数据打拍
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
for (i = 0; i<NUM - 1;i=i+1 ) begin
data_r[i] <= 0;
end
end
else if(data_vld) begin
data_r[0] <= distance_data_r;
for (i = 1; i<NUM - 1;i=i+1 ) begin
data_r[i] <= data_r[i-1];
end
end
end
reg [ 10:0 ] cnt ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rgb_data_r = 0;
cnt = 0;
end
else if(cnt == NUM) begin
rgb_data_r = black;
cnt = 0;
end
else if(addr_v > 470 && addr_v < 476 && addr_h >9 && addr_h < 625 ) begin //横坐标
rgb_data_r = white;
cnt = cnt;
end
else if(addr_h > 9 && addr_h <15 && addr_v >9 && addr_v <= 470) begin //纵坐标
rgb_data_r = white;
cnt = cnt;
end
else if(addr_h >20 && addr_h < 620 && addr_v >10 && addr_v < 470) begin //打点
if ( (cnt+1) * 3 == addr_h -20) begin
if(data_r[cnt] == 470 - addr_v)begin
rgb_data_r = red;
cnt = cnt + 1;
end
else begin
rgb_data_r = black;
cnt = cnt + 1;
end
end
else begin
rgb_data_r = black;
cnt = cnt;
end
end
else begin
rgb_data_r = black;
cnt = cnt;
end
end
assign rgb_data = rgb_data_r;
endmodule
六、数码管
1.数码管段选控制
根据位选信号来显示对应位置的数字
module seg_drive(input wire clk,
input wire rst_n,
input wire data_vld,
input wire [ 23:0 ] display_data,
input wire [ 5:0 ] sel,
output reg [ 7:0 ] seg);
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 [ 23:0 ] display_data_r ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
display_data_r <= 0;
end
else if(data_vld) begin
display_data_r <= display_data;
end
end
reg dot;
reg [ 3:0 ] num;
[email protected]( * ) begin
case( sel )
6'b111_110: begin
// num = display_data / 100000 % 10;
num = display_data_r[23 :20];
dot = 1;
end
6'b111_101: begin
// num = display_data / 10000 % 10;
num = display_data_r[19 : 16];
dot = 1;
end
6'b111_011: begin
// num = display_data / 1000 % 10;
num = display_data_r[15 : 12];
dot = 1;
end
6'b110_111: begin
// num = display_data / 100 % 10;
num = display_data_r[11 :8];
dot = 0;
end
6'b101_111: begin
//num = display_data / 10 % 10;
num = display_data_r[7 :4];
dot = 1;
end
6'b011_111: begin
//num = display_data % 10;
num = display_data_r[3 :0];
dot = 1;
end
default num = 4'hf;
endcase
end
always @ ( * ) begin
case( num )
4'd0: seg = {
dot,ZERO}; // 匹配到后参考共阳极真值表
4'd1: seg = {
dot,ONE};
4'd2: seg = {
dot,TWO};
4'd3: seg = {
dot,THREE};
4'd4: seg = {
dot,FOUR};
4'd5: seg = {
dot,FIVE};
4'd6: seg = {
dot,SIX};
4'd7: seg = {
dot,SEVEN};
4'd8: seg = {
dot,EIGHT};
4'd9: seg = {
dot,NINE};
default : seg = {
1'b0,ZERO};
endcase
end
endmodule
2.数码管位选
每20000ns刷新一次数码管
module sel_drive(
input clk,
input rst_n,
output reg [5:0] sel
);
localparam state0 = 3'd0;
localparam state1 = 3'd1;
localparam state2 = 3'd2;
localparam state3 = 3'd3;
localparam state4 = 3'd4;
localparam state5 = 3'd5;
parameter MAX_NUM = 1_000;
reg [2:0] current_state;
reg [2:0] next_state;
reg [20:0] cnt; //时钟分频计数器
reg flag;
//计数器
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 @(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 @(*) 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
//状态输出
[email protected](current_state) begin
case (current_state)
state0: begin
sel <= 6'b011111;
end
state1: begin
sel <= 6'b101111;
end
state2: begin
sel <= 6'b110111;
end
state3: begin
sel <= 6'b111011;
end
state4: begin
sel <= 6'b111101;
end
state5: begin
sel <= 6'b111110;
end
default:begin
sel <= 6'b111111;
end
endcase
end
endmodule
七、顶层文件
由于数码管需要25M时钟,超声波测距需要1M时钟,通过PLL分频得到对应的时钟。
module distance_top (
input wire clk,
input wire rst_n,
input wire echo,
output wire trig,
output wire [ 5:0 ] sel,
output wire [ 7:0 ] seg,
output reg [ 3:0 ] led,
output wire beep,
input wire key,
output wire h_sync,
output wire v_sync,
output wire [ 4:0 ] rgb_r,
output wire [ 5:0 ] rgb_g,
output wire [ 4:0 ] rgb_b,
input wire rx_data,
output wire tx_data
);
wire clk_50 ;
wire clk_1 ;
wire clk_25 ;
wire [ 23:0 ] distance_data ;
wire data_out_vld ;
reg beep_vld ;
wire key_out ;
wire [ 15:0 ] rgb_data ;
wire [ 11:0 ] addr_h;
wire [ 11:0 ] addr_v;
pll pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( clk_50 ),
.c1 ( clk_1 ),
.c2 ( clk_25 )
);
//vga
vga_dirve u_vga_dirve(
.clk ( clk_25 ),
.rst_n ( rst_n ),
.rgb_data ( rgb_data ),
.vga_clk ( vga_clk ),
.h_sync ( h_sync ),
.v_sync ( v_sync ),
.addr_h ( addr_h ),
.addr_v ( addr_v ),
.rgb_r ( rgb_r ),
.rgb_g ( rgb_g ),
.rgb_b ( rgb_b )
);
//vag数据
data_drive u_data_drive(
.clk (clk),
.vga_clk ( vga_clk ),
.rst_n ( rst_n ),
.addr_h ( addr_h ),
.addr_v ( addr_v ),
.data_vld ( data_out_vld ),
.distance_data ( distance_data ),
.rgb_data ( rgb_data )
);
//数码管
seg_drive u_seg_drive(
.clk ( clk ),
.rst_n ( rst_n ),
.data_vld ( data_out_vld ),
.display_data ( distance_data),
.sel ( sel ),
.seg ( seg )
);
sel_drive u_sel_drive(
.clk ( clk_50 ),
.rst_n ( rst_n ),
.sel ( sel )
);
//测距
distance_drive u_distance(
.clk ( clk ),
.clk_1 (clk_1),
.rst_n ( rst_n ),
.echo ( echo ),
.trig ( trig ),
.data_out_vld ( data_out_vld ),
.distance_data ( distance_data )
);
//串口
uart_drive u_uart_drive(
.clk ( clk ),
.rst_n ( rst_n ),
.distance_data ( distance_data ),
.data_vld ( data_out_vld ),
.rx_data ( rx_data ),
.tx_data ( tx_data )
);
//蜂鸣器
beep_dirve u_beep_dirve(
.clk ( clk ),
.rst_n ( rst_n ),
.beep_vld ( beep_vld ),
.data_vld ( data_out_vld ),
.distance_data ( distance_data ),
.beep ( beep )
);
//按键消抖
key_debounce#(.KEY_W ( 1 )) u_key_debounce(
.clk ( clk ),
.rst_n ( rst_n ),
.key_in ( key ),
.key_out ( key_out )
);
//控制蜂鸣器使能
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
beep_vld <= 0;
end
else if(key_out) begin
beep_vld <= ~beep_vld;
end
end
reg [ 27:0 ] cnt ;
// led
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
cnt <= 0;
end
else if ( cnt == 50_000_000 - 1 ) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
always @( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
led <= 4'b0000;
end
else if ( cnt == 50_000_000 -1 )begin
led <= ~led;
end
else begin
led <= led;
end
end
endmodule //distance_top
八、源代码
边栏推荐
- golang通过指针for...range实现切片中元素的值的更改
- Tupu software digital twin | visual management system based on BIM Technology
- Analysis of postman core functions - parameterization and test report
- The worse the AI performance, the higher the bonus? Doctor of New York University offered a reward for the task of making the big model perform poorly
- The easycvr authorization expiration page cannot be logged in. How to solve it?
- C language makes it easy to add, delete, modify and check the linked list "suggested collection"
- Use QT to traverse JSON documents and search sub objects
- Oracle date format conversion to_ date,to_ char,to_ Timestamp mutual conversion
- XML basic knowledge concept
- c期末复习
猜你喜欢
The worse the AI performance, the higher the bonus? Doctor of New York University offered a reward for the task of making the big model perform poorly
websocket 工具的使用
Low code practice of xtransfer, a cross-border payment platform: how to integrate with other medium-sized platforms is the core
2022最新大厂Android面试真题解析,Android开发必会技术
《ClickHouse原理解析与应用实践》读书笔记(5)
Oracle日期格式转换 to_date,to_char,to_timetamp 相互转换
AI表现越差,获得奖金越高?纽约大学博士拿出百万重金,悬赏让大模型表现差劲的任务
Tupu software digital twin | visual management system based on BIM Technology
Oracle date format conversion to_ date,to_ char,to_ Timestamp mutual conversion
vs2017 qt的各种坑
随机推荐
1亿单身男女撑起一个IPO,估值130亿
蚂蚁集团开源可信隐私计算框架「隐语」:开放、通用
Technology sharing | interface testing value and system
Quickly generate IPA package
Low code practice of xtransfer, a cross-border payment platform: how to integrate with other medium-sized platforms is the core
2022最新Android面试笔试,一个安卓程序员的面试心得
公司破产后,黑石们来了
R language Visual scatter plot graph, add labels to some data points in the graph, and always display all labels, even if they have too much overlap. Ggrep package helps
Oracle date format conversion to_ date,to_ char,to_ Timestamp mutual conversion
图扑软件数字孪生 | 基于 BIM 技术的可视化管理系统
EMQX 5.0 正式发布:单集群支持 1 亿 MQTT 连接
The era of Web3.0 is coming. See how Tianyi cloud storage resources revitalize the system to enable new infrastructure (Part 2)
Oracle日期格式转换 to_date,to_char,to_timetamp 相互转换
Video fusion cloud platform easycvr adds multi-level grouping, which can flexibly manage access devices
跨境支付平台 XTransfer 的低代码实践:如何与其他中台融合是核心
MYSQL中 find_in_set() 函数用法详解
视频自监督学习综述
2022 latest intermediate and advanced Android interview questions, [principle + practice + Video + source code]
面试官:Redis 过期删除策略和内存淘汰策略有什么区别?
Emqx 5.0 officially released: a single cluster supports 100million mqtt connections