基于FPGA的矩阵按键检测

在单片机原理与应用的课程我们学习了矩阵键盘的原理并且在考试中有做了相应的试题。今天一起了解一下在FPGA 中如何使用矩阵键盘。在键盘中按键数量较多时,为了减少 I/O 口的占用,通常将按键排列成矩阵形式。在矩阵式键盘 中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。这样,一个端口(如 P 1 口)就可以构成 4*4=16 个按键。、

设计目标

1运用逐行扫描的方法进行按键检测;

2检测到有按键按下时,消抖时间20ms

3输出信号key-vld持续一拍即可;

4输出信号key_out表示16个按键,并且在数码管上显示对应的数值。

首先要了解整体的结构

矩阵键盘模块:@1 将外来异步信号打两拍子处理,将异步信号同步化;

@2 实现20ms消抖功能

@ 实现矩阵键盘的按键检测功能

这里要明白为什么要打两拍,主要是防止亚稳态的产生,这里可以参考以下博主对于亚稳态和打一拍打两拍的理解http://(15条消息) FPGA设计的“打拍(寄存)”和“亚稳态” 到底是什么?_wuzhikaidetb的博客-CSDN博客_fpga 打一拍 https://blog.csdn.net/wuzhikaidetb/article/details/119619162?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164700735416780269812763%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164700735416780269812763&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-119619162.pc_search_insert_es_download&utm_term=%E6%89%93%E4%B8%A4%E6%8B%8D%E6%B6%88%E9%99%A4%E4%BA%9A%E7%A8%B3%E6%80%81&spm=1018.2226.3001.4187

输入的 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

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旭旭宝宝和车友车行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值