一、矩阵键盘
由于最早的 MCU(即单片机) 其 IO 口相对较少,而且用到按键过多的话,就会占用过多的 IO。人们为了解决这个问题就引入了“矩阵键盘”。在矩阵键盘中,每条行线和列线在交叉处都不是直接连同,而是通过一个按键直接相连,这样以来一个 4×4 的矩阵键盘只需要 8 根控制线就可以完成16 个按键的控制。
二、矩阵键盘使用思路
1.检测思路:如,将COL0作为FPGA的输出管脚,当COL0输出低电平时,只需检测ROW0~4是否为低电平即可判断按键 0~4是否被按下。
1.COL = 1110,读ROW,可以确定知道第一列的4个按键是否有一个或多个被按下(0,4,8,C)。
2.COL = 1101,读ROW,可以确定知道第二列的4个按键是否有一个或多个被按下(1,5,9,D)。
……
2.按键扫描方案
通常我们检测矩阵键盘中某一按键是否被按下,采用的方法是列扫描法。图中一共有 8条控制线,4 条行控制线(ROW),4 条列控制线(COL),4 根行控制线(ROW)被通过上拉电阻拉到了高电平,因此,如果没有按键被按下的情况下,使用 FPGA 的 IO 坚持 ROW 信号上的电平,应该默认都是高电平。那我们如何去检测某一个按键被按下,并且还能找到其具体的位置呢?思路其实并不复杂,假如我们让 COL0=0,然后去读取 ROW 的值,如果 4 个 ROW信号的电平全部为高,则表明当前列并无按键按下,则切换到扫描下一列,再去读取 4 个 ROW信号的值,根据读到的 ROW 值判断当前是哪一个按键被按下。例如当扫描第三列的时候,即 COL=4’b1011 时,检测到 ROW=4’b1011,则说明“A”被按下。
这个思路看起来简单,实际上要用到的状态机数目可不少。
三、矩阵键盘控制代码
1.简易版
一次只能按下一个按键,若同时按下两个按键,则随机取其中一个按键的值。
module Matrix_Keyboard(
input Clk,
input Rst_n,
input [3:0] Row,
output reg [3:0] Col,
output reg [3:0] key_num
);
reg [3:0] Row_r [2:0];//定义3个4位宽的寄存器
reg [4:0] state;
reg [7:0] key_value_r;
reg [19:0] cnt;
reg delay_cnt_en;
localparam SC_C = 5'b00001,
RD_R = 5'b00010,
FILTER0 = 5'b00100,
WAIT_RELEASE = 5'b01000,
FILTER1 = 5'b10000,
T20MS = 20'd999_999;
//消除亚稳态,寄存两拍后再使用
always@(posedge Clk)begin
Row_r[2][3:0] <= Row_r[1][3:0];
Row_r[1][3:0] <= Row_r[0][3:0];
Row_r[0][3:0] <= Row;
end
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
Col <= 4'b1110;
state <= SC_C;
key_value_r <= 0;
end
else begin
case(state)
//扫描COL0~4
SC_C:
begin
state <= RD_R;
Col <= {
Col[2:0],Col[3]}; //循环左移扫描
end
//读ROW的状态
RD_R:
if(Row_r[2][3:0] != 4'b1111)begin //读取到的如果不是1111,则说明此刻某个按键正在被按下,进入消抖程序
delay_cnt_en <= 1'b1; //计时20ms使能
state <= FILTER0;
end
else //读取到的ROW值如果是1111,则该列没有按键按下,回到扫描状态,继续进行下一列的扫描
state <= SC_C;
FILTER0:
if(cnt >= T20MS)begin
if(Row_r[2][3:0] != 4'b1111)begin //延时20ms后,如果读到的ROW不是1111,说明按键已被稳定按下
key_value_r <= {
Col,Row_r[