在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,使用行线和列线分别连接到按键开关的两端,这样我们就可以通过4根行线和4根列线(共8个I/O口)连接16个按键,而且按键数量越多优势越明显。
FPGA驱动矩阵按键模块,首先我们来了解矩阵按键的硬件连接:
上图为4×4矩阵按键的硬件电路图,可以看到4根行线(ROW1、ROW2、ROW3、ROW4)和4根列线(COL1、COL2、COL3、COL4),同时列线通过上拉电阻连接到VCC电压(3.3V),对于矩阵按键来讲:
4根行线是输入的,是由FPGA控制拉高或拉低,
4根列线数输出的,是由4根行线的输入及按键的状态决定,输出给FPGA
当某一时刻,FPGA控制4根行线分别为ROW1=0、ROW2=1、ROW3=1、ROW4=1时,
对于K1、K2、K3、K4按键:按下时对应4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,不按时对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1,
对于K5~~~K16之间的按键:无论按下与否,对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1,
通过上面的描述:在这一时刻只有K1、K2、K3、K4按键被按下,才会导致4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,否则COL1=1、COL2=1、COL3=1、COL4=1,反之当FPGA检测到列线(COL1、COL2、COL3、COL4)中有低电平信号时,对应的K1、K2、K3、K4按键应该是被按下了。
按照扫描的方式,一共分为4个时刻,分别对应4根行线中的一根拉低,4个时刻依次循环,这样就完成了矩阵按键的全部扫描检测,我们在程序中以这4个时刻对应状态机的4个状态。 至于循环的周期,根据我们基础教程里可知,按键抖动的不稳定时间在10ms以内,所以对同一个按键采样的周期大于10ms,这同样取20ms时间。20ms时间对应4个状态,每5分钟进行一次状态转换。
// -------------------------------------------------------------------- // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // -------------------------------------------------------------------- // Module: Array_KeyBoard // // Author: Step// // Description: Array_KeyBoard // // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2015/11/11 |Initial ver // -------------------------------------------------------------------- module Array_KeyBoard #( parameter NUM_FOR_200HZ = 60000 //定义计数器cnt的计数范围,例化时可更改)( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 input [3:0] col, //矩阵按键列接口 output reg [3:0] row, //矩阵按键行接口 output reg [15:0] key_out //消抖后的信号);/* 因使用4x4矩阵按键,通过扫描方法实现,所以这里使用状态机实现,共分为4种状态 在其中的某一状态时间里,对应的4个按键相当于独立按键,可按独立按键的周期采样法采样 周期采样时每隔20ms采样一次,对应这里状态机每隔20ms循环一次,每个状态对应5ms时间 对矩阵按键实现原理不明白的,请去了解矩阵按键实现原理 */ localparam STATE0 = 2'b00; localparam STATE1 = 2'b01; localparam STATE2 = 2'b10; localparam STATE3 = 2'b11; //计数器计数分频实现5ms周期信号clk_200hz reg [15:0] cnt; reg clk_200hz; always@(posedge clk_in or negedge rst_n_in) begin if(!rst_n_in) begin //复位时计数器cnt清零,clk_200hz信号起始电平为低电平 cnt <= 16'd0; clk_200hz <= 1'b0; end else begin if(cnt >= ((NUM_FOR_200HZ>>1) - 1)) begin //数字逻辑中右移1位相当于除2 cnt <= 16'd0; clk_200hz <= ~clk_200hz; //clk_200hz信号取反 end else begin cnt <= cnt + 1'b1; clk_200hz <= clk_200hz; end end end reg [1:0] c_state; //状态机根据clk_200hz信号在4个状态间循环,每个状态对矩阵按键的行接口单行有效 always@(posedge clk_200hz or negedge rst_n_in) begin if(!rst_n_in) begin c_state <= STATE0; row <= 4'b1110; end else begin case(c_state) STATE0: begin c_state <= STATE1; row <= 4'b1101; end //状态c_state跳转及对应状态下矩阵按键的row输出 STATE1: begin c_state <= STATE2; row <= 4'b1011; end STATE2: begin c_state <= STATE3; row <= 4'b0111; end STATE3: begin c_state <= STATE0; row <= 4'b1110; end default:begin c_state <= STATE0; row <= 4'b1110; end endcase end end //因为每个状态中单行有效,通过对列接口的电平状态采样得到对应4个按键的状态,依次循环 always@(negedge clk_200hz or negedge rst_n_in) begin if(!rst_n_in) begin key_out <= 16'hffff; end else begin case(c_state) STATE0:key_out[3:0] <= col; //采集当前状态的列数据赋值给对应的寄存器位 STATE1:key_out[7:4] <= col; STATE2:key_out[11:8] <= col; STATE3:key_out[15:12] <= col; default:key_out <= 16'hffff; endcase end end endmodule
本节主要为大家讲解了矩阵按键的工作原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。