矩阵键盘在嵌入式系统中非常常见,它能有效减少I/O引脚占用。想象一下,如果每个按键都单独占用一个引脚,16个按键就需要16个引脚,而采用4x4矩阵排列只需要8个引脚(4行+4列)。这种设计原理就像城市交通网的十字路口,行线和列线的交叉点就是按键位置。
实际硬件连接时,行线通常由FPGA驱动输出,列线通过上拉电阻连接到高电平。当没有按键按下时,所有列线都保持高电平;当某个按键按下时,对应的行和列就会导通。这里有个关键点:行线需要设置为开漏输出模式,这样多行同时输出低电平时才不会产生短路。
我曾在项目中遇到过行线驱动能力不足的问题,表现为按键检测不稳定。后来发现是忘记在行线添加缓冲器,导致驱动电流不够。建议在设计初期就考虑以下硬件参数:
状态机是矩阵键盘驱动的灵魂。经过多次实践验证,我将扫描过程划分为四个关键状态:
状态转换的Verilog代码片段如下:
verilog复制always@(*) begin
case(state_c)
CHK_COL: state_n = (key_col_ff1!=4'hf) ? CHK_ROW : CHK_COL;
CHK_ROW: state_n = (end_row_index) ? DELAY : CHK_ROW;
DELAY: state_n = (end_row_cnt) ? WAIT_END : DELAY;
WAIT_END:state_n = (key_col_ff1==4'hf) ? CHK_COL : WAIT_END;
endcase
end
行扫描需要精确的时序控制。我的经验是:
这里有个容易踩坑的地方:状态转换条件判断必须用寄存后的列信号(key_col_ff1),而不是直接使用key_col,否则会出现亚稳态问题。我曾经因为这个问题调试了整整两天!
机械按键在闭合时会产生5-20ms的抖动,就像老式日光灯开启时的闪烁。传统软件消抖是通过延时采样,但会占用CPU资源。我们的FPGA方案采用硬件消抖计数器,具体实现如下:
verilog复制// 消抖计数器
always@(posedge clk) begin
if(key_col_ff1 != 4'hf) begin
if(shake_cnt < TIME_20MS_NUM)
shake_cnt <= shake_cnt + 1;
end else begin
shake_cnt <= 0;
end
end
为了适应不同时钟频率,我采用了参数化设计:
verilog复制parameter TCLK = 20; // 时钟周期(ns)
parameter TIME_20MS = 20000000; // 20ms对应的纳秒数
localparam TIME_20MS_NUM = TIME_20MS/TCLK;
实测发现,对于工业环境中的按键,建议将消抖时间延长到30-50ms。曾经有个工厂设备因为振动较大,20ms消抖仍会出现误触发,调整后问题解决。
按键位置编码采用行列组合方式,4位输出可表示16个键:
verilog复制always@(posedge clk) begin
if(state_c==CHK_ROW && end_row_cnt) begin
key_out <= {row_index, key_col_get}; // 高2位行号,低2位列号
key_vld <= 1'b1;
end else begin
key_vld <= 1'b0;
end
end
编写Testbench时,我创建了自动化测试任务来模拟按键抖动:
verilog复制task key_task(input [1:0] row, col);
begin
// 模拟前抖动
repeat(20) begin
#({$random}%100);
key_col_r[col] = ~key_col_r[col];
end
// 稳定按下阶段
key_col_r[col] = 0;
#(TIME_20MS*2);
// 释放抖动
repeat(20) begin
#({$random}%100);
key_col_r[col] = ~key_col_r[col];
end
end
endtask
仿真波形中要重点观察三个信号:
记得第一次调试时,我发现key_vld信号出现了多个脉冲,原来是WAIT_END状态没有等到按键完全释放就提前转换了。这个教训让我深刻理解了状态机严格性的重要。
默认扫描周期约128个时钟周期(4行×16周期+状态转换)。对于50MHz时钟,这相当于2.56μs,远快于人工操作速度。但在某些低功耗场景,可以通过以下方式优化:
对于需要更多按键的场景,可以采用矩阵扩展方案:
曾为某MIDI控制器设计过级联键盘,采用方案2实现了128键输入,关键是要处理好片选信号的时序同步。
根据多年经验,矩阵键盘的典型问题包括:
最近帮客户调试的一个案例特别典型:按键响应时快时慢。最后发现是状态机中缺少DELAY状态的超时退出机制,导致在某些干扰情况下会长时间卡在消抖状态。