矩阵键盘的试验,采用三段式状态机处理方法。
难点在于检测状态机中:按键消抖以后逐列检测。
电路图:
代码
/********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :keyboard.v ** CreateDate :2015.04 ** Funtions :4x4矩阵键盘 ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: 2015.6 *******************************************************************************/ module keyboard( clk, rst_n, l_in, h_out, test_1, test_2, key_val, key_val_flag ); input clk; /* clk = 24M */ input rst_n; input [3:0] l_in; //列输入,一般接上拉,为高电平 output test_1; output test_2; output [3:0] h_out; //行输出信号,低有效 output [3:0] key_val; //按键值,输出 output key_val_flag; wire [3:0] key_val; reg test_1; reg test_2; //************************************* /* 分频Ƶ*20ms,用于消抖 .状态机直接用clk_20ms,则可以跳过消抖*/ // `define CLK_24M `define CLK_20M `ifdef CLK_24M parameter t_20ms = 20'd479999; `endif `ifdef CLK_20M parameter t_20ms = 20'd399999; `endif reg [19:0] cnt; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 'd0; end else begin if(cnt == t_20ms) cnt <= 'd0; else cnt <= cnt + 'd1; end end wire shake_over = (cnt == t_20ms); //******************状态机****************** localparam NO_KEY_pressed = 3'd0; /* 初始化 */ localparam key_shake_1 = 3'd1; /* 消抖1 */ localparam KEY_h_1 = 3'd2; /* 检测第一列 */ localparam KEY_h_2 = 3'd3; /* 检测第二列 */ localparam KEY_h_3 = 3'd4; /* 检测第三列 */ localparam KEY_h_4 = 3'd5; /* 检测第四列 */ localparam KEY_pressed = 3'd6; /* 按键值输出*/ localparam key_shake_2 = 3'd7; /* 消抖2 */ /* 3-1 */ reg [2:0] current_state; reg [2:0] next_state; reg key_pressed_flag; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin current_state <= 0; end else if(shake_over) begin current_state <= next_state; end else current_state <= current_state ; end /* 3-2 */ always @(*) begin next_state = NO_KEY_pressed; case(current_state) NO_KEY_pressed: begin if(l_in != 4'hf) next_state = key_shake_1; else next_state = NO_KEY_pressed; end key_shake_1: begin if(l_in != 4'hf) next_state = KEY_h_1; else next_state = NO_KEY_pressed; end KEY_h_1: begin if(l_in != 4'hf) next_state = KEY_pressed; else next_state = KEY_h_2; end KEY_h_2: begin if(l_in != 4'hf) next_state = KEY_pressed; else next_state = KEY_h_3; end KEY_h_3: begin if(l_in != 4'hf) next_state = KEY_pressed; else next_state = KEY_h_4; end KEY_h_4: begin if(l_in != 4'hf) next_state = KEY_pressed; else next_state = NO_KEY_pressed; end KEY_pressed: begin if(l_in != 4'hf) next_state = key_shake_2; else next_state = NO_KEY_pressed; end key_shake_2: begin if(l_in != 4'hf) next_state = key_shake_2; else next_state = NO_KEY_pressed; end default:; endcase end /* 3-3 */ reg [3:0] l_in_reg; reg [3:0] h_out_reg; reg [3:0] h_out; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin l_in_reg <= 4'd0; h_out_reg<= 4'd0; h_out <= 4'd0; key_pressed_flag <= 0; end else if(shake_over) begin case(next_state) NO_KEY_pressed: begin l_in_reg <= l_in_reg; h_out_reg<= h_out_reg; h_out <= 4'd0; key_pressed_flag <= 0; end KEY_h_1: begin h_out <= 4'b1110; end KEY_h_2: begin h_out <= 4'b1101; end KEY_h_3: begin h_out <= 4'b1011; end KEY_h_4: begin h_out <= 4'b0111; end KEY_pressed: begin l_in_reg <= l_in; h_out_reg<= h_out; end key_shake_2: begin key_pressed_flag <= 1; end default:; endcase end end reg [3:0] temp_key_val; always @(posedge clk or negedge rst_n) begin if(!rst_n) temp_key_val <= 4'd0; else begin if(key_pressed_flag) begin case ({h_out_reg,l_in_reg}) 8'b1110_1110 : temp_key_val <= 4'd0; 8'b1110_1101 : temp_key_val <= 4'd1; 8'b1110_1011 : temp_key_val <= 4'd2; 8'b1110_0111 : temp_key_val <= 4'd3; 8'b1101_1110 : temp_key_val <= 4'd4; 8'b1101_1101 : temp_key_val <= 4'd5; 8'b1101_1011 : temp_key_val <= 4'd6; 8'b1101_0111 : temp_key_val <= 4'd7; 8'b1011_1110 : temp_key_val <= 4'd8; 8'b1011_1101 : temp_key_val <= 4'd9; 8'b1011_1011 : temp_key_val <= 4'd10; 8'b1011_0111 : temp_key_val <= 4'd11; 8'b0111_1110 : temp_key_val <= 4'd12; 8'b0111_1101 : temp_key_val <= 4'd13; 8'b0111_1011 : temp_key_val <= 4'd14; 8'b0111_0111 : temp_key_val <= 4'd15; default: temp_key_val <= 4'd0; endcase end end end assign key_val = ~temp_key_val; assign key_val_flag = ~key_pressed_flag; endmodule
测试代码:
module testbench; reg clk; /* clk = 24M */ reg rst_n; reg [3:0] l_in; //列输入,一般接上拉,为高电平 wire test_1; wire test_2; wire [3:0] h_out; //行输出信号,低有效 wire [3:0] key_val; //按键值,输出 wire key_val_flag; keyboard u1( .clk, .rst_n, .l_in, .h_out, .test_1, .test_2, .key_val, .key_val_flag ); defparam u1. t_20ms = 399; parameter tck = 100; parameter t = 1000/tck; always #(t/2) clk = ~clk; task key_test; input [3:0] data_key; begin #(3*t) l_in = data_key; #(5000*t) l_in = 4'b1111; #(2500*t); end endtask initial begin clk = 0; rst_n = 0; l_in = 4'd0; #(6*t) rst_n = 1; #(60*t); key_test(4'B1101); key_test(4'B1011); end endmodule
仿真波形: