FPGA初级课程第十一讲 键盘矩阵
FPGA初级课程第十一讲 键盘矩阵
Hi,大家好!我是至芯科技的李老师。
今天讲课的题目是:键盘矩阵。
本节课我先简要地介绍一下键盘矩阵的物理原理,然后实际演示一下键盘矩阵驱动逻辑电路的建模与仿真,我们还要结合第四讲的数码管一起编写一个完整的演示逻辑,并下板查看实际效果。
打开《ZX_NO3.pdf》文件,我们看一下矩阵键盘的电路图。
我们打开至芯科技编写的《40.炼狱传奇-矩阵键盘之战.pdf》文档。
在使用按键的时候,如果按键不多的话,我们可以直接按键与FPGA相连接,但是如果按键比较多的时候,如何还继续使用直接按键与FPGA相连接的话,所会大量增加FPGA端口的消耗,为了减少FPGA端口的消耗,我们可以把按键设计成矩阵的形式,就如下图所示:
由上图可以知道,矩阵键盘的行row(行)与col(列)的交点,都是通过一个按键来相连接。
传统的一个按键一个端口的方法,若要实现16个按键,则需要16个端口。
而现在这个矩阵键盘的设计,16个按键,仅仅需要8个端口,如果使用16个端口来做矩阵键盘的话,可以识别64个按键,端口的利用率远远比传统的设计好的多。
所以如果需要的按键少的话,可以选择传统的按键设计,如果需要的按键比较多的话,可以采用这种矩阵键盘的设计。
而我们现在就以扫描法为例来介绍矩阵键盘的工作原理。
首先col(列)是FPGA给矩阵键盘输出的扫描信号,而row(行)是矩阵键盘反馈给FPGA的输入信号,用于检测哪一个按键被按下来,如下图所示:
详细如上图所示,FPGA给出扫描信号COL,COL = 4’b0111,等下一个时钟周期COL = 4’b1011,再等下一个时钟周期COL =4’b1101,再等下一个时钟周期COL = 4’b1110,再等下一个时钟周期COL = 4’b0111,COL就是这样不断循环,给矩阵键盘一个低电平有效的扫描信号。
当FPGA给矩阵键盘COL扫描信号的同时,FPGA也要在检测矩阵键盘给FPGA的的反馈信号ROW。
举个例子,假若矩阵键盘中的9号按键被按下了:
当COL = 4’b0111,ROW = 4’b1111;
当COL = 4’b1011,ROW = 4’b1111;
当COL = 4’b1101,ROW = 4’b1011;
当COL = 4’b1110,ROW = 4’b1111;
有人问,为什么当COL = 4’b1101的时候,ROW = 4’b1011呢?
我们现在就以矩阵键盘的电路来分析一下这个原因,如下图所示:
当9号按键被按下的时候,9号按键的电路就会被导通,扫描电路COL开始扫描,当扫描到COL的时候,由于9号按键的电路被导通了,COL的电压等于ROW的电压。
所以会出现当COL = 4’b1101的时候ROW = 4’b1011。
然后我们就可以利用这一种现象,来设计一个识别按键的电路。
矩阵键盘的扫描原理既然已经清楚,我们下面来进行系统设计。
命名逻辑为key_pad。
架构图如下所示。
加上数码管数据预处理模块pre_seg7和数码管模块seg7后,架构图如下。
其中,数码管数据预处理模块pre_seg7将键盘矩阵模块key_pad输出的1位按键数补充到显示的6位数的低位上,高位左移。
比如,数码管原显示012345,如果键盘按下6,则数码管显示变为123456。
本帖最后由 lcytms 于 2016-11-11 16:56 编辑
先从键盘矩阵模块key_pad.v做起吧!
新建工程文件夹key_pad。
新建key_pad.v文件。
module key_pad (clk, rst_n, col, row, data, flag);
input clk, rst_n;
input row;
output reg col;
output reg data;
output reg flag;
reg count;
wire flag1ms;
parameter T1ms = 50_000; // 1ms/20ns =50_000
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
count <= 0;
end
else
begin
if (count < T1ms - 1)
begin
count <= count + 1;
end
else
begin
count <= 0;
end
end
end
assign flag1ms = (count == T1ms - 1) ? 1'b1 : 1'b0;
reg state;
reg cnt_key;
reg row_col;
parameter NUM_KEY = 20;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
col <= 4'b0000;
state <= 0;
cnt_key <= 0;
flag <= 0;
row_col <= 8'b1111_0000;
end
else
begin
case (state)
0 : begin
if (!flag1ms)
begin
state <= 0;
end
else
begin
if (row == 4'b1111) //no key pressed
begin
state <= 0;
end
else //key pressed
begin
if (cnt_key < NUM_KEY - 1)
cnt_key <= cnt_key + 8'b1;
else
begin
cnt_key <= 0;
col <= 4'b1110;
state <= 1;
end
end
end
end
1 : begin
if (!flag1ms)
begin
state = 1;
end
else
begin
if (row == 4'b1111) //no key pressed
begin
col <= {col, col};
end
else //key pressed
begin
row_col <= {row, col};
flag <= 1;
col <= 4'b0000;
state <= 2;
end
end
end
2 : begin
if (!flag1ms)
begin
flag <= 0;
state = 2;
end
else
begin
if (row != 4'b1111) //key unreleased
begin
state = 2;
end
else //key released
begin
if (cnt_key < NUM_KEY - 1)
cnt_key <= cnt_key + 8'b1;
else
begin
cnt_key <= 0;
col <= 4'b0000;
state <= 0;
end
end
end
end
default : state <= 0;
endcase
end
end
always @ (*)
begin
if (!rst_n)
begin
data <= 4'h0;
end
else
begin
case (row_col)
8'b1110_1110 : data <= 4'h0;
8'b1110_1101 : data <= 4'h1;
8'b1110_1011 : data <= 4'h2;
8'b1110_0111 : data <= 4'h3;
8'b1101_1110 : data <= 4'h4;
8'b1101_1101 : data <= 4'h5;
8'b1101_1011 : data <= 4'h6;
8'b1101_0111 : data <= 4'h7;
8'b1011_1110 : data <= 4'h8;
8'b1011_1101 : data <= 4'h9;
8'b1011_1011 : data <= 4'hA;
8'b1011_0111 : data <= 4'hB;
8'b0111_1110 : data <= 4'hC;
8'b0111_1101 : data <= 4'hD;
8'b0111_1011 : data <= 4'hE;
8'b0111_0111 : data <= 4'hF;
default : data <= 4'h0; //no key pressed
endcase
end
end
endmodule
本帖最后由 lcytms 于 2016-11-11 17:03 编辑
进行分析综合检查。
编写Testbench。
新建key_pad_tb.v文件。
`timescale 1ns/1ps
module key_pad_tb;
reg clk, rst_n;
reg row;
wire col;
wire data;
wire flag;
reg key;
key_pad #(.T1ms(50), .NUM_KEY(20))
dut (.clk(clk), .rst_n(rst_n), .col(col), .row(row), .data(data), .flag(flag));
initial
begin
clk = 1;
rst_n = 0;
key = 5'h10;
row = 4'b1111;
#200.1
rst_n = 1;
key = 5'h09;
#25_000
key = 5'h10;
#50_000
key = 5'h0a;
#30_000
key = 5'h10;
#50_000
key = 5'h0b;
#45_000
key = 5'h10;
#50_000
$stop;
end
always @ (*)
case (key)
5'h10 : row = 4'b1111; // no key pressed
5'h00 : row = {1'b1, 1'b1, 1'b1, col};
5'h01 : row = {1'b1, 1'b1, 1'b1, col};
5'h02 : row = {1'b1, 1'b1, 1'b1, col};
5'h03 : row = {1'b1, 1'b1, 1'b1, col};
5'h04 : row = {1'b1, 1'b1, col, 1'b1};
5'h05 : row = {1'b1, 1'b1, col, 1'b1};
5'h06 : row = {1'b1, 1'b1, col, 1'b1};
5'h07 : row = {1'b1, 1'b1, col, 1'b1};
5'h08 : row = {1'b1, col, 1'b1, 1'b1};
5'h09 : row = {1'b1, col, 1'b1, 1'b1};
5'h0a : row = {1'b1, col, 1'b1, 1'b1};
5'h0b : row = {1'b1, col, 1'b1, 1'b1};
5'h0c : row = {col, 1'b1, 1'b1, 1'b1};
5'h0d : row = {col, 1'b1, 1'b1, 1'b1};
5'h0e : row = {col, 1'b1, 1'b1, 1'b1};
5'h0f : row = {col, 1'b1, 1'b1, 1'b1};
default : row = 4'b1111; // no key pressed
endcase
always #10 clk = ~clk;
endmodule