1设计背景: 矩阵键盘在工程设计越来越多的被用到,已然成为了我们做开发接触到的不可缺少的小型项目,利于我们理解设计方向的原理为以后的强化学习打好了坚实的基础。 2设计原理: 在使用按键的时候,如果按键不多的话,我们可以直接按键与FPGA相连接,但是如果按键比较多的时候,如何还继续使用直接按键与FPGA相连接的话,所会大量增加FPGA端口的消耗,为了减少FPGA端口的消 耗,我们可以把按键设计成矩阵的形式,就如下图所示: 由上图可以知道,矩阵键盘的行row(行)与col(列)的交点,都是通过一个按键来相连接。 传统的一个按键一个端口的方法,若要实现16个按键,则需要16个端口,而现在这个矩阵键盘的设计,16个按键,仅仅需要8个端口,如果使用16个端口来做矩阵键盘的话,可以识别64个按键,端口的利用率远远比传统的设计好的多,所以如果需要的按键少的话,可以选择传统的按键设计,如果需要的按键比较多的话,可以采用这种矩阵键盘的设计。 而我们现在就以扫描法为例来介绍矩阵键盘的工作原理。 首先col(列)是FPGA给矩阵键盘输出的扫描信号,而row(行)是矩阵键盘反馈给FPGA的输入信号,用于检测哪一个按键被按下来,如下图所示: 详细如上图所示,FPGA给出扫描信号COL[3:0],COL = 4’b0111,等下一个时钟周期COL = 4’b1011,再等下一个时钟周期COL =4’b1101,再等下一个时钟周期COL = 4’b1110,再等下一个时钟周期COL = 4’b0111,COL就是这样不断循环,给矩阵键盘一个低电平有效的扫描信号,当FPGA给矩阵键盘COL扫描信号的同时,FPGA也要在检测矩阵键盘给FPGA的的反馈信号ROW,举个例子,假若矩阵键盘中的9号案件被按下了: 当 COL = 4’b1101,ROW =4’b1011 ; 当9号按键被按下的时候,9号按键的电路就会被导通,扫描电路COL开始扫描,当扫描到COL[1]的时候,由于9号按键的电路被导通了,COL[1]的电压等于ROW[2]的电压,所以会出现当COL = 4’b1101的时候ROW = 4’b1011;然后我们就可以利用这一种现象,来设计一个识别按键的电路。 3设计架构图: 4设计代码: 设计模块 0 module key_borad(clk,rst_n,row,col,key_num); 1 input clk; 2 input rst_n; 3 input [3:0] row; //输入反馈信号 4 5 6 output reg [3:0] col; //输出扫描信号 7 output reg [3:0] key_num; //按键值得输除 8 9 reg [15:0] count; 10 11 parameter T1ms = 50000; //扫描的时间间隔 50000 * 20ns 12 //parameter T1ms = 5; 13 14 reg flag; 15 always @ (posedge clk or negedge rst_n) 16 if(!rst_n) 17 begin 18 count <> 19 flag <> 20 end 21 else 22 begin 23 if(count < t1ms - 1) > t1ms - 1) > 24 begin 25 count <> 26 flag <> 27 end 28 else 29 begin 30 flag <= 1'b1; >= 1'b1; > 31 count <> 32 end 33 end 34 always @ (posedge clk or negedge rst_n) 35 if(!rst_n) 36 begin 37 col <> 38 end 39 else 40 begin 41 if(flag) 42 col <= {col[2:0],col[3]}; >= {col[2:0],col[3]}; > 43 else 44 col <> 45 end 46 47 //键值得翻译模块 48 always @ (posedge clk or negedge rst_n) 49 if(!rst_n) 50 key_num = 4'd0; 51 else 52 case ({row,col}) //位拼接行和列的信号,翻译出对应的键值 53 8'b0111_0111:key_num = 4'hf; 54 8'b0111_1011:key_num = 4'he; 55 8'b0111_1101:key_num = 4'hd; 56 8'b0111_1110:key_num = 4'hc; 57 58 8'b1011_0111:key_num = 4'hb; 59 8'b1011_1011:key_num = 4'ha; 60 8'b1011_1101:key_num = 4'h9; 61 8'b1011_1110:key_num = 4'h8; 62 63 8'b1101_0111:key_num = 4'h7; 64 8'b1101_1011:key_num = 4'h6; 65 8'b1101_1101:key_num = 4'h5; 66 8'b1101_1110:key_num = 4'h4; 67 68 8'b1110_0111:key_num = 4'h3; 69 8'b1110_1011:key_num = 4'h2; 70 8'b1110_1101:key_num = 4'h1; 71 8'b1110_1110:key_num = 4'h0; 72 default: ; 73 endcase 74 endmodule 测试模块 0 `timescale 1ns/1ps 1 2 module key_borad_tb(); 3 reg clk; 4 reg rst_n; 5 reg [4:0] pressnum; //按键的值 6 wire [3:0] row; 7 8 wire [3:0] col; 9 wire [3:0] key_num; //输出的值 10 11 initial begin 12 clk = 1'b1; 13 rst_n = 1'b0; 14 pressnum = 5'd16; 15 16 #200.1 17 rst_n = 1'b1; 18 #1000 19 pressnum = 5'd16; 20 21 #1000 22 pressnum = 5'd8; 23 24 #1000 25 pressnum = 5'd16; 26 27 #1000 28 pressnum = 5'd15; 29 #1000 30 pressnum = 5'd16; 31 #1000 32 $stop; 33 34 end 35 always #10 clk = ~clk; 36 //例化对应的模块 37 key_top borad_dut( 38 .clk(clk), 39 .rst_n(rst_n), 40 .row(row), 41 .col(col), 42 .key_num(key_num) 43 ); 44 yingjian yingjian_dut( //硬件检测电路 //此模块自己可以设计 45 .clk(clk), 46 .rst_n(rst_n), 47 .col(col), 48 .row(row), 49 .pressnum(pressnum) 50 ); 51 endmodule 仿真图: 在仿真图中可以清晰的看出当按键按下的时候为8,显示出来的键值也为8,当抬起的时候为16,那么键值就保持不变,在设置的时候我们设置的是按键抬起为16,通过验证我们得到我们的设计是正确的。 |
|