基于FPGA的矩阵键盘检测

项目简述

这是乐鑫提前批的一道笔试题,其实不算难,但是想在半小时内完成可能性非常小,所以乐鑫的笔试还是挺难的。

题目:请实现对4x4矩阵式键盘的按键识别,假设每次都是单按键输入,需要有去抖功能(持续20ms以上被认为是有效键值),模块时钟频率为1kHz,要求用状态机实现,定义状态,画出状态转移图,并用verilog完整描述该识别模块。矩阵式键盘电路结构参见下图,其中列线1-4由识别模块控制输出,行线5-8为识别模块的输入。
在这里插入图片描述
本次实验所用到的软硬件环境如下:
1、VIVADO 2019.1
2、Modelsim 10.7

求解过程

首先根据题意写出状态,然后画出状态转移图:
IDLE:自动跳转到P_FILTER状态;
P_FILTER:如果四条行线不全为1,代表有按键按下则跳转到DELAY状态,没有按键按下则停留在该状态;
DELAY:计数状态,计数20ms在这个过程中如果如果四条行线全为1,则跳转到P_FILTER状态,计数到20ms跳转到SCAN_C0状态,并将第 0 列拉低,进入 SCAN_C0 状态进行行扫描;
SCAN_C0:进行列扫描,来判断该列是否有按键被按下, 判断行输入是否等于 15,如果不等于则说明该列有按键被按下,否则将第 1 列拉低,状态跳转到 SCAN_C1状态进行行扫描;
SCAN_C1: 进行列扫描,来判断该列是否有按键被按下,判断行输入是否等于 15,如果不等于则说明该列有按键被按下,否则将第 2 列拉低,状态跳转到 SCAN_C3状态进行行扫描;
SCAN_C2: 进行列扫描,来判断该列是否有按键被按下, 判断行输入是否等于 15,如果不等于则说明该列有按键被按下,否则将第 3 列拉低,状态跳转到 SCAN_C3状态进行行扫描;SCAN_C3: 进行列扫描,来判断该列是否有按键被按下, 判断行输入是否等于 15,如果不等于则说明该列有按键被按下,状态跳转到WAIT_R状态;
WAIT_R:等待按键松开;
DELAY_P:等待按键松开过程稳定输出;

状态转移图如下:
在这里插入图片描述
经过上面的分析,我们便可以书写FPGA代码。

FPGA代码

key模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : https://blog.csdn.net/zhangningning1996
// Module Name  : key.v
// Create Time  : 2020-06-16 14:39:12
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module key(
    input                   sclk            ,
    input                   rst_n           ,
    input           [ 3:0]  key_row         ,
    output  reg     [ 3:0]  key_col         ,
    output  reg     [ 3:0]  key             ,
    output  reg             key_en
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter   DELAY_20MS      =   20000-1     ;
parameter   IDLE            =   9'b000000001;
parameter   P_FILTER        =   9'b000000010;
parameter   DELAY           =   9'b000000100;
parameter   SCAN_C0         =   9'b000001000;
parameter   SCAN_C1         =   9'b000010000;
parameter   SCAN_C2         =   9'b000100000;
parameter   SCAN_C3         =   9'b001000000;
parameter   WAIT_R          =   9'b010000000;
parameter   DELAY_P         =   9'b100000000;


reg                 [ 8:0]  state           ;
reg                 [20:0]  cnt_20ms        ;
reg                 [ 3:0]  key_row_c0      ;
reg                 [ 3:0]  key_row_c1      ;
reg                 [ 3:0]  key_row_c2      ;
reg                 [ 3:0]  key_row_c3      ;
wire                [15:0]  key_row_c       ;


 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign      key_row_c       =               {key_row_c3,key_row_c2,key_row_c1,key_row_c0};       

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state               <=              IDLE;
    else case(state)
        IDLE    :       state           <=          P_FILTER;
        P_FILTER:       if(key_row != 4'hf)
                            state       <=          DELAY;
                        else
                            state       <=          state;
        DELAY   :       if(key_row == 4'hf)
                            state       <=          P_FILTER;
                        else if(cnt_20ms == DELAY_20MS)
                            state       <=          SCAN_C0;
                        else
                            state       <=          state;
        SCAN_C0 :       state           <=          SCAN_C1;
        SCAN_C1 :       state           <=          SCAN_C2;
        SCAN_C2 :       state           <=          SCAN_C3;
        SCAN_C3 :       state           <=          WAIT_R;
        WAIT_R  :       if(key_row == 4'hf)
                            state       <=          DELAY_P;
                        else
                            state       <=          state;
        DELAY_P :       if(cnt_20ms == DELAY_20MS)
                            state       <=          P_FILTER;
                        else if(key_row != 4'hf)
                            state       <=          WAIT_R;
                        else
                            state       <=          state;
        default :       state           <=          IDLE;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_20ms            <=              21'd0;                      
    else if(state == DELAY || state == DELAY_P)begin
        if(cnt_20ms == DELAY_20MS)
            cnt_20ms        <=              DELAY_20MS;
        else 
            cnt_20ms        <=              cnt_20ms + 1'b1;
    end else
        cnt_20ms            <=              21'd0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_row_c0          <=              4'hf;
    else if(state == SCAN_C0)
        key_row_c0          <=              key_row;
    else
        key_row_c0          <=              key_row_c0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_row_c1          <=              4'hf;
    else if(state == SCAN_C1)
        key_row_c1          <=              key_row;
    else
        key_row_c1          <=              key_row_c1;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_row_c2          <=              4'hf;
    else if(state == SCAN_C2)
        key_row_c2          <=              key_row;
    else
        key_row_c2         <=              key_row_c2;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_row_c3          <=              4'hf;
    else if(state == SCAN_C3)
        key_row_c3          <=              key_row;
    else
        key_row_c3          <=              key_row_c3;
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key                 <=              4'd0;  
    else case(key_row_c)
        16'b1111_1111_1111_1110     :   key         <=          4'd0;
        16'b1111_1111_1111_1101     :   key         <=          4'd4; 
        16'b1111_1111_1111_1011     :   key         <=          4'd8;
        16'b1111_1111_1111_0111     :   key         <=          4'd12;
        16'b1111_1111_1110_1111     :   key         <=          4'd1;
        16'b1111_1111_1101_1111     :   key         <=          4'd5;
        16'b1111_1111_1011_1111     :   key         <=          4'd9;
        16'b1111_1111_0111_1111     :   key         <=          4'd13;
        16'b1111_1110_1111_1111     :   key         <=          4'd2;
        16'b1111_1101_1111_1111     :   key         <=          4'd6;
        16'b1111_1011_1111_1111     :   key         <=          4'd10;
        16'b1111_0111_1111_1111     :   key         <=          4'd14;
        16'b1110_1111_1111_1111     :   key         <=          4'd3;
        16'b1101_1111_1111_1111     :   key         <=          4'd7;
        16'b1011_1111_1111_1111     :   key         <=          4'd11; 
        16'b0111_1111_1111_1111     :   key         <=          4'd15;
        default                     :   key         <=          4'd0;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_en              <=          1'b0;  
    else if(state == DELAY_P && cnt_20ms == DELAY_20MS)
        key_en              <=          1'b1;
    else
        key_en              <=          1'b0;   

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_col             <=          4'd0;
    else case(state)
        DELAY   :       if(cnt_20ms == DELAY_20MS)
                            key_col     <=          4'b1110;
                        else
                            key_col     <=          key_col;
        SCAN_C0 :       key_col         <=          4'b1101;
        SCAN_C1 :       key_col         <=          4'b1011;
        SCAN_C2 :       key_col         <=          4'b0111;
        default :       key_col         <=          4'd0;
    endcase


endmodule

测试模块代码

key_tb模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : nnzhang1996@foxmail.com
// Website      : https://blog.csdn.net/zhangningning1996
// Module Name  : key_tb.v
// Create Time  : 2020-06-16 15:47:58
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************
module key_tb();
reg                    sclk                 ;
reg                    rst_n                ;
reg            [ 3:0]  Key_Row              ;
reg            [ 3:0]  Key_Row_r            ;
reg            [15:0]  myrand               ;
reg            [ 1:0]  now_col,now_row      ; //当前行与当前列
reg                    key_row_sel          ; //行选通信号
wire           [ 3:0]  Key_Col              ;
wire           [ 3:0]  key                  ;
wire                   key_en               ;

initial begin
    sclk                =           1'b0;
    key_row_sel         <=          1'b0;
    rst_n               <=          1'b0;
    Key_Row             <=          4'hf;
    #(1000)
    rst_n               <=          1'b1;
    #(10000)
    press_key(0,0);
    press_key(0,1);
    press_key(0,2);
    press_key(0,3);
    press_key(1,0);
    press_key(1,1);
    press_key(1,2);
    press_key(1,3);
    press_key(2,0);
    press_key(2,1);
    press_key(2,2);
    press_key(2,3);
    press_key(3,0);
    press_key(3,1);
    press_key(3,2);
    press_key(3,3);
end
always      #5        sclk        =       ~sclk;


key key_inst(
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    .key_row                (Key_Row                ),
    .key_col                (Key_Col                ),
    .key                    (key                    ),
    .key_en                 (key_en                 )
);

task press_key;
    input [1:0]row,col;
    begin
        key_row_sel = 0;
        Key_Row_r = 4'b1111;
        Key_Row_r[row] = 0;
        now_row = row;
        repeat(10)begin //重复 20 次,随机产生按键按下时 20 个不同的行状态
            myrand = {$random} % 6556;
            #myrand Key_Row_r[row] = ~Key_Row_r[row];
        end
        key_row_sel = 1; //将行选择信号设置为有效状态
        now_col = col; //获取当前列状态
        #22000000;
        key_row_sel = 0;
        Key_Row_r = 4'b1111;
        repeat(20)begin //重复 20 次,随机产生按键松开时 20 个不同行状态
            myrand = {$random} % 6556;
            #myrand Key_Row_r[row] = ~Key_Row_r[row];
        end
        Key_Row_r = 4'b1111;
        #22000000;
        end
endtask

always@(*)
    if(key_row_sel) //行选择信号有效
        case(now_row) //根据当前行输出,键盘的行信号
            2'd0:Key_Row = {1'b1,1'b1,1'b1,Key_Col[now_col]};
            2'd1:Key_Row = {1'b1,1'b1,Key_Col[now_col],1'b1};
            2'd2:Key_Row = {1'b1,Key_Col[now_col],1'b1,1'b1};
            2'd3:Key_Row = {Key_Col[now_col],1'b1,1'b1,1'b1};
        endcase
    else //保持上一个行状态
        Key_Row = Key_Row_r;

endmodule

上面的测试代码写起来还是挺有难度的大家可以好好学习一下。

仿真结果

仿真结果如下:
在这里插入图片描述

总结

创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

  • 10
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值