最近闲着没啥事,用FPGA做一个计算器,练练手。其中用到了4*4的矩阵按键,在这里详细的记录一下矩阵按键的代码以及仿真代码。
代码如下:
/***************************************************
* key_flag delay 1 clock of key_value
***************************************************/
module key_4(
clk,
rst_n,
col,
row,
key_flag,
key_value
);
input clk;
input rst_n;
input [3:0]row;
output reg key_flag;
output reg [3:0]key_value;
output reg [3:0]col;
reg [3:0]row_r;
reg [4:0]c_state,n_state;
reg [7:0]key_value_r;
reg en_delay;
reg [19:0]delay;
parameter CNT_MAX = 999_999;
localparam
scan = 5'b00001,
judge = 5'b00010,
filter0 = 5'b00100,
down = 5'b01000,
filter1 = 5'b10000;
//寄存行的值
always@(posedge clk or negedge rst_n)
if(!rst_n)
row_r <= 0;
else
row_r <= row;
//状态机第一段
always@(posedge clk or negedge rst_n)
if(!rst_n)
c_state <= scan;
else
c_state <= n_state;
//状态机第二段
always@(*)
begin
n_state = 5'bxxxxx;
case(c_state)
scan: n_state = judge;
judge:
if(row_r != 4'b1111)
n_state = filter0;
else
n_state = scan;
filter0:
if(delay == CNT_MAX)
begin
if(row_r != 4'b1111)
n_state = down;
else
n_state = scan;
end
else
n_state = c_state;
down:
if(row_r == 4'b1111)
n_state = filter1;
else
n_state = c_state;
filter1:
if(delay == CNT_MAX)
begin
if(row_r != 4'b1111)
n_state = down;
else
n_state = scan;
end
else
n_state = c_state;
default:n_state = scan;
endcase
end
//状态机第三段
always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
en_delay <= 0;
col <= 4'b1110;
key_flag <= 0;
key_value_r <= 0;
end
else
begin
case(n_state)
scan:
begin
en_delay <= 0;
col <= {col[2:0],col[3]};
end
judge: key_flag <= 0;
filter0:
begin
en_delay <= 1;
if(delay == CNT_MAX-1)
begin
key_flag <= 1;
key_value_r <= {row_r,col};
end
else
begin
key_flag <= 0;
key_value_r <= 0;
end
end
down:
begin
key_flag <= 0;
en_delay <= 0;
key_value_r <= 0;
end
filter1:en_delay <= 1;
default:
begin
en_delay <= 0;
col <= 4'b1110;
key_flag <= 0;
end
endcase
end
//抖动20ms的时间计数
always@(posedge clk or negedge rst_n)
if(!rst_n)
delay <= 0;
else if(en_delay) begin
if(delay == CNT_MAX)
delay <= 0;
else
delay <= delay + 1'b1;
end
else
delay <= 0;
//根据行和列的值确定输出按键值
always@(posedge clk or negedge rst_n)
if(!rst_n)
key_value <= 0;
else
begin
case(key_value_r)
8'b1110_1110: key_value <= 4'd0;
8'b1110_1101: key_value <= 4'd1;
8'b1110_1011: key_value <= 4'd2;
8'b1110_0111: key_value <= 4'd3;
8'b1101_1110: key_value <= 4'd4;
8'b1101_1101: key_value <= 4'd5;
8'b1101_1011: key_value <= 4'd6;
8'b1101_0111: key_value <= 4'd7;
8'b1011_1110: key_value <= 4'd8;
8'b1011_1101: key_value <= 4'd9;
8'b1011_1011: key_value <= 4'd10;
8'b1011_0111: key_value <= 4'd11;
8'b0111_1110: key_value <= 4'd12;
8'b0111_1101: key_value <= 4'd13;
8'b0111_1011: key_value <= 4'd14;
8'b0111_0111: key_value <= 4'd15;
default:;
endcase
end
endmodule
仿真代码如下:
`timescale 1ns/1ns
module key_4_tb;
reg clk;
reg rst_n;
reg [3:0]row;
wire key_flag;
wire [3:0]key_value;
wire [3:0]col;
key_4 key_4(
.clk(clk),
.rst_n(rst_n),
.col(col),
.row(row),
.key_flag(key_flag),
.key_value(key_value)
);
initial clk = 1;
always#10 clk = ~clk;
initial begin
rst_n = 0;
row = 4'b1111;
#201;
rst_n = 1;
#300;
press;
#1000;
press;
#1000;
press;
#1000;
press;
#2000;
$stop;
end
reg [15:0]myrand;
task press;
begin
repeat(30)
begin
myrand = {$random}%65535;
#myrand row[1] = ~row[1];
end
row = 4'b1101;
#21_000_000;
repeat(30)
begin
myrand = {$random}%65535;
#myrand row[1] = ~row[1];
end
row = 4'b1111;
#21_000_000;
end
endtask
endmodule
注意:
在做这个项目的过程中,遇到了一个很严重的问题,就是按键检测不稳定,这个问题困扰了我很久,我反复的看了好多遍代码,感觉没问题,仿真也没问题,然后我就使用SignalTap II进行在线仿真调试,发现key_value信号一直在跳变。我感觉应该不是软件问题,而是硬件问题,回想起之前在51单片机上做矩阵按键实验时,按键是有上拉电阻的,而我现在所用的按键是没有上拉电阻的,终于找到问题了,key_value值的跳变,是因为小信号干扰的原因。我打算给矩阵按键焊接一个上拉电阻,但又感觉太麻烦了,我在想既然FPGA的电路都可以自己设置,那么给IO口设置成上拉电阻应该也没问题,于是百度了一下,果然发现了IO口设置成上拉电阻的方法,在这里附上链接https://www.cnblogs.com/chengqi521/p/8375955.html ,设置完以后果然发现,按键的功能正常了。开心!!!