在单片机原理与应用的课程我们学习了矩阵键盘的原理并且在考试中有做了相应的试题。今天一起了解一下在FPGA 中如何使用矩阵键盘。在键盘中按键数量较多时,为了减少 I/O 口的占用,通常将按键排列成矩阵形式。在矩阵式键盘 中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。这样,一个端口(如 P 1 口)就可以构成 4*4=16 个按键。、
设计目标
1运用逐行扫描的方法进行按键检测;
2检测到有按键按下时,消抖时间20ms
3输出信号key-vld持续一拍即可;
4输出信号key_out表示16个按键,并且在数码管上显示对应的数值。
首先要了解整体的结构
矩阵键盘模块:@1 将外来异步信号打两拍子处理,将异步信号同步化;
@2 实现20ms消抖功能
@ 实现矩阵键盘的按键检测功能
输入的 key_col 是异步信号,所以要进行打两拍操作,将异步信号 key_col 同步化,并防止亚稳 态。
代码
input [3:0] key_col ;
reg [3:0] key_col_ff0 ;
reg [3:0] key_col_ff1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_col_ff0 <= 4'b1111;
key_col_ff1 <= 4'b1111;
end
else begin
key_col_ff0 <= key_col ;
key_col_ff1 <= key_col_ff0;
end
end
按键消抖 对于按键和触摸屏等机械设备来说,都存在一个固有问题,那就是“抖动”,按键从最初接通到 稳定接通要经过数毫秒,其间可能发生多次“接通-断开”这样的毛刺。如果不进行处理,会使系统识 别到抖动信号而进行不必要的反应,导致模块功能不正常,为了避免这种现象的产生,需要进行按键 消抖的操作。 软件方法消抖,即检测出键闭合后执行一个延时程序,抖动时间的长短由按键的机械特性决定, 一般为 5ms~20ms,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认按 下按键操作有效。当检测到按键释放后,也要给 5ms~20ms 的延时,待后沿抖动消失后才能转入该 键的处理程序。
数码管显示模块设计
module seg_disp(
input wire clk,
input wire rst_n,
input [3:0] data_in,
input wire key_en,
output reg [7:0 ] segment,
output reg [1:0 ] seg_sel
);
reg [10:0] delay ;
reg [1:0 ] delay_time ;
wire add_delay_time ;
wire end_delay_time ;
wire add_delay ;
wire end_delay ;
reg [4:0 ] segment_tmp ;
reg [3:0 ] segment_data ;
parameter ZERO = 8'b0000_0011 ;
parameter ONE = 8'b1001_1111 ;
parameter TWO = 8'b0010_0101 ;
parameter THREE = 8'b0000_1101 ;
parameter FOUR = 8'b1001_1001 ;
parameter FIVE = 8'b0100_1001 ;
parameter SIX = 8'b0100_0001 ;
parameter SEVEN = 8'b0001_1111 ;
parameter EIGHT = 8'b0000_0001 ;
parameter NINE = 8'b0000_1001 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
delay <= 0;
end
else if(add_delay) begin
if(end_delay)
delay <= 0;
else
delay <= delay+1 ;
end
end
assign add_delay = 1;
assign end_delay = add_delay && delay == 2000-1 ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
delay_time <= 0;
end
else if(add_delay_time) begin
if(end_delay_time)
delay_time <= 0;
else
delay_time <= delay_time+1 ;
end
end
assign add_delay_time = end_delay;
assign end_delay_time = add_delay_time && delay_time == 2-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment_data <= 4'd0;
end
else if(key_en)begin
segment_data <= data_in;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment_tmp <= 4'd0;
end
else if(add_delay_time && delay_time == 1-1)begin
segment_tmp <= (segment_data+1)%10;
end
else if(end_delay_time)begin
segment_tmp <= (segment_data+1)/10;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment <= ZERO;
end
else begin
case(segment_tmp)
4'd0:segment <= ZERO;
4'd1:segment <= ONE ;
4'd2:segment <= TWO ;
4'd3:segment <= THREE;
4'd4:segment <= FOUR ;
4'd5:segment <= FIVE ;
4'd6:segment <= SIX ;
4'd7:segment <= SEVEN;
4'd8:segment <= EIGHT;
4'd9:segment <= NINE ;
default:begin
segment <= segment;
end
endcase
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
seg_sel <= 2'b11;
end
else begin
seg_sel <= ~(2'b1<<delay_time);
end
end
endmodule
按键模块
module key_scan(
input wire clk,
input wire rst_n,
input [3:0] key_col ,
output reg[3:0] key_row,
output reg key_vld ,
output reg [DATA_W-1:0] key_out
);
parameter DATA_W = 4 ;
parameter TIME_20MS = 1_000_000 ;
reg [3:0] key_col_ff0 ;
reg [3:0] key_col_ff1 ;
reg key_col_check;
reg [ 21:0] shake ;
wire add_shake ;
wire end_shake ;
reg [1:0] key_col_get ;
reg key_row_check;
reg [1:0] row_index ;
wire add_row_index;
wire end_row_index;
wire flag ;
reg flag_add ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_col_ff0 <= 4'b1111;
key_col_ff1 <= 4'b1111;
end
else begin
key_col_ff0 <= key_col ;
key_col_ff1 <= key_col_ff0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_col_check <= 1'b0;
end
else if(key_col_ff1 !=4'hf && end_shake)begin
key_col_check <= 1'b1;
end
else if(key_col_ff1==4'hf)begin
key_col_check <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
shake <= 0;
end
else if(add_shake) begin
if(end_shake)
shake <= 0;
else
shake <= shake+1 ;
end
end
assign add_shake = (key_col_ff1 !=4'hf && flag_add==0);
assign end_shake = add_shake && shake == TIME_20MS-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_add <= 0;
end
else if(end_shake)begin
flag_add <= 1;
end
else if(key_col_ff1 == 4'hf)begin
flag_add <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_col_get <= 0;
end
else if(key_col_check) begin
if(key_col_ff1==4'b1110)
key_col_get <= 0;
else if(key_col_ff1==4'b1101)
key_col_get <= 1;
else if(key_col_ff1==4'b1011)
key_col_get <= 2;
else if(key_col_ff1==4'b0111)
key_col_get <= 3;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_row_check <= 0;
end
else if(key_col_check)begin
key_row_check <= 1;
end
else if(flag)begin
key_row_check <= 0;
end
end
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
row_index <= 0;
end
else if(add_row_index) begin
if(end_row_index)
row_index <= 0;
else
row_index <= row_index+1 ;
end
end
assign add_row_index = key_row_check && end_shake;
assign end_row_index = add_row_index && row_index == 4-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_row = 4'b0;
end
else if(key_row_check)begin
key_row = ~(4'b0001 << row_index);
end
else begin
key_row = 4'b0;
end
end
assign flag = key_row_check && key_col_ff1[key_col_get]==1'b0 &&
key_col_check==0;
always @(*)begin
if(rst_n==1'b0)begin
key_vld = 1'b0;
end
else if(flag )begin
key_vld = 1'b1;
end
else begin
key_vld = 1'b0;
end
end
always @(*)begin
if(rst_n==1'b0)begin
key_out = 4'd0;
end
else if(flag )begin
key_out = {row_index,key_col_get};
end
else begin
key_out = 4'd0;
end
end
endmodule
顶层模块
module top(
input wire clk,
input wire rst_n,
input [3:0] key_col,
output wire[3:0] key_row,
output wire[7:0] segment,
output wire [1:0] seg_sel
);
parameter DATA_W= 4;
wire[DFATA_W-1:0] key_out;
wire key_vld;
wire[DFATA_W-1:0] segmenmt_data;
key_scan key_scan_u(
.clk (clk)
.rst_n ( rst_n)
.key_col (key_col)
.key_row (key_row)
. key_out (key_out)
.key_vld (key_vld)
);
seg_disp seg_disp_u(
clk ( rst_n)
rst_n (rst_n)
data_in (key_out)
key_en (key_vld)
segment (segment)
seg_sel (seg_sel)
);
endmodule