ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

FPGA基础——矩阵键盘(FSM)

2021-08-04 11:04:01  阅读:280  来源: 互联网

标签:FPGA FSM col 键盘 key 按键 低电平 reg


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

确认矩阵键盘上哪个按键被按下有多同方法,其中行扫描法又称为逐行(或列)扫描查询法,是一种最常用的按键识别方法。

1. 判断键盘中有无键按下:将全部行线 KEY_R1~KEY_R4 置低电平,然后检测列线 KEY_C1~KEY_C4 的状态。只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与 4 根行线相交叉的 4 个按键之中。若所有列线均为高电平,则键盘中无键按下。

2. 判断闭合键所在的位置:在确认有键按下后,即可进入确定具体闭合键的过程。其方法是:

依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。

 ➢ 打拍操作
输入的 key_col 是异步信号,通常要进行打两拍操作,将异步信号 key_col 同步化,并防止亚稳态。

按键消抖

软件方法消抖,即检测出键闭合后执行一个延时程序,抖动时间的长短由按键的机械特性决定,一般为 5ms~20ms, 让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认按下按键操作有效。当检测到按键释放后,也要给 5ms~20ms 的延时,待后沿抖动消失后才能转入该键的处理程序。由于按键按下去的时间一般都会大于 20ms,为了达到不管按键按下多久,都视为按下一次的效果,提出以下计数器架构,如下图所示:

 

消抖计数器 shake_cnt:用于计算 20ms 的时间,加一条件为 key_col_ff1 != 4'hf && flag_key==0,表示有某个按键按下并且之前没有按键按下;数到 1,000,000 下,表示数到 20ms 就结束。

行扫描计数器 row_index:用于区分扫描的行,加一条件为 key_row_check && end_shake_cnt,表示当处于行扫描状态并且每行消抖 20ms 后,开始扫描下一行;数到 4 下,表示 4 行按键扫描完了。

按键:表示有无按键按下,没被按下时为高电平,按下后为低电平。

按键指示信号 flag_key:该信号为低电平,指示之前没有按键按下;否则,指示有按键按下并且按键已消抖。

行扫描指示信号 key_row_check:该信号为高电平,指示当前处于行扫描状态。

矩阵键盘列信号 key_col_ff1:4bit 位宽的矩阵键盘列信号,最高位表示矩阵键盘往右数第四列,默认信号为 key_col_ff1 = 4'hf,否则表示该信号低电平对应位的列有按键按下。

矩阵键盘扫描代码如下(MDY):

module key_scan(
        input  clk ,
        input  rst_n ,
        input  [3:0] key_col,
        output reg [3:0] key_row,
        output reg [3:0] key_out,
        output reg key_vld
        );
parameter TIME_20MS = 1_000_000 ;

reg [3:0] key_col_ff0 ;
reg [3:0] key_col_ff1 ;
reg key_col_check;
reg [ 21:0] shake ;
wire add_shake ;
wire end_shake ;
reg [1:0] key_col_get ;
reg key_row_check;
reg [1:0] row_index ;
wire add_row_index;
wire end_row_index;
wire flag ;
reg flag_add ;

always @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_col_ff0 <= 4'b1111;
        key_col_ff1 <= 4'b1111;
    end
    else begin
        key_col_ff0 <= key_col ;
        key_col_ff1 <= key_col_ff0;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_col_check <= 1'b0;
    end
    else if(key_col_ff1 !=4'hf && end_shake)begin
        key_col_check <= 1'b1;
    end
    else if(key_col_ff1==4'hf)begin
        key_col_check <= 1'b0;
    end
end

always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
        shake <= 0;
    end
    else if(add_shake) begin
        if(end_shake)
            shake <= 0;
        else
            shake <= shake+1 ;
    end
end

assign add_shake = (key_col_ff1 !=4'hf && flag_add==0);
assign end_shake = add_shake && shake == TIME_20MS-1 ;

always @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        flag_add <= 0;
    end
    else if(end_shake)begin
        flag_add <= 1;
    end
    else if(key_col_ff1 == 4'hf)begin
        flag_add <= 0;
    end
end


always @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_col_get <= 0;
    end
    else if(key_col_check) begin
        if(key_col_ff1==4'b1110)
            key_col_get <= 0;
        else if(key_col_ff1==4'b1101)
            key_col_get <= 1;
        else if(key_col_ff1==4'b1011)
            key_col_get <= 2;
        else if(key_col_ff1==4'b0111)
            key_col_get <= 3;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_row_check <= 0;
    end
    else if(key_col_check)begin
        key_row_check <= 1;
    end
    else if(flag)begin
        key_row_check <= 0;
    end
end

always @(posedge clk or negedge rst_n) begin
    if (rst_n==0) begin
        row_index <= 0;
    end
    else if(add_row_index) begin
        if(end_row_index)
            row_index <= 0;
        else
            row_index <= row_index+1 ;
    end
end

assign add_row_index = key_row_check && end_shake;
assign end_row_index = add_row_index && row_index == 4-1 ;

always @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_row = 4'b0;
    end
    else if(key_row_check)begin
        key_row = ~(4'b0001 << row_index);
    end
    else begin
        key_row = 4'b0;
    end
end

assign flag = key_row_check && key_col_ff1[key_col_get]==1'b0 && key_col_check==0;

always @(*)begin
    if(rst_n==1'b0)begin
        key_vld = 1'b0;
    end
    else if(flag )begin
        key_vld = 1'b1;
    end
    else begin
        key_vld = 1'b0;
    end
end


always @(*)begin
    if(rst_n==1'b0)begin
        key_out = 4'd0;
    end
    else if(flag )begin
        key_out = {row_index,key_col_get};
    end
    else begin
        key_out = 4'd0;
    end
end

endmodule

 

用状态机实现:

对于列扫描法(列线置低电平,检测行线状态),其状态如下:

 

  1 module matrixKeyboard(
  2   input            clk,
  3   input            rst_n,
  4   input      [3:0] row,                 // 矩阵键盘 行
  5   output reg [3:0] col,                 // 矩阵键盘 列
  6   output reg [3:0] keyboard_val         // 键盘值     
  7 );
  8  
  9 //++++++++++++++++++++++++++++++++++++++
 10 // 分频部分 开始
 11 //++++++++++++++++++++++++++++++++++++++
 12 reg [19:0] cnt;                         // 去抖动计数器
 13  
 14 always @ (posedge clk, negedge rst_n)
 15   if (!rst_n)
 16     cnt <= 0;
 17   else
 18     cnt <= cnt + 1'b1;
 19  
 20 wire key_clk = cnt[19];                 //T =(2^20/50M = 20.97152)ms 
 21 //--------------------------------------
 22 // 分频部分 结束
 23 //--------------------------------------
 24  
 25  
 26 //++++++++++++++++++++++++++++++++++++++
 27 // 状态机部分 开始
 28 //++++++++++++++++++++++++++++++++++++++
 29 // 状态数较少,独热码编码
 30 parameter NO_KEY_PRESSED = 6'b000_001;  // 没有按键按下  
 31 parameter SCAN_COL0      = 6'b000_010;  // 扫描第0列 
 32 parameter SCAN_COL1      = 6'b000_100;  // 扫描第1列 
 33 parameter SCAN_COL2      = 6'b001_000;  // 扫描第2列 
 34 parameter SCAN_COL3      = 6'b010_000;  // 扫描第3列 
 35 parameter KEY_PRESSED    = 6'b100_000;  // 有按键按下
 36  
 37 reg [5:0] current_state, next_state;    // 现态、次态
 38  
 39 always @ (posedge key_clk, negedge rst_n)
 40   if (!rst_n)
 41     current_state <= NO_KEY_PRESSED;
 42   else
 43     current_state <= next_state;
 44  
 45 // 根据条件转移状态
 46 always @(*)
 47   case (current_state)
 48     NO_KEY_PRESSED :                    // 没有按键按下
 49         if (row != 4'hF)
 50           next_state = SCAN_COL0;
 51         else
 52           next_state = NO_KEY_PRESSED;
 53     SCAN_COL0 :                         // 扫描第0列 
 54         if (row != 4'hF)
 55           next_state = KEY_PRESSED;
 56         else
 57           next_state = SCAN_COL1;
 58     SCAN_COL1 :                         // 扫描第1列 
 59         if (row != 4'hF)
 60           next_state = KEY_PRESSED;
 61         else
 62           next_state = SCAN_COL2;    
 63     SCAN_COL2 :                         // 扫描第2列
 64         if (row != 4'hF)
 65           next_state = KEY_PRESSED;
 66         else
 67           next_state = SCAN_COL3;
 68     SCAN_COL3 :                         // 扫描第3列
 69         if (row != 4'hF)
 70           next_state = KEY_PRESSED;
 71         else
 72           next_state = NO_KEY_PRESSED;
 73     KEY_PRESSED :                       // 有按键按下
 74         if (row != 4'hF)
 75           next_state = KEY_PRESSED;
 76         else
 77           next_state = NO_KEY_PRESSED;                      
 78   endcase
 79  
 80 reg       key_pressed_flag;             // 键盘按下标志
 81 reg [3:0] col_val, row_val;             // 列值、行值
 82  
 83 // 根据次态,给相应寄存器赋值
 84 always @ (posedge key_clk, negedge rst_n)
 85   if (!rst_n)
 86   begin
 87     col              <= 4'h0;
 88     key_pressed_flag <=    0;
 89   end
 90   else
 91     case (next_state)
 92       NO_KEY_PRESSED :                  // 没有按键按下
 93       begin
 94         col              <= 4'h0;
 95         key_pressed_flag <=    0;       // 清键盘按下标志
 96       end
 97       SCAN_COL0 :                       // 扫描第0列
 98         col <= 4'b1110;
 99       SCAN_COL1 :                       // 扫描第1列
100         col <= 4'b1101;
101       SCAN_COL2 :                       // 扫描第2列
102         col <= 4'b1011;
103       SCAN_COL3 :                       // 扫描第3列
104         col <= 4'b0111;
105       KEY_PRESSED :                     // 有按键按下
106       begin
107         col_val          <= col;        // 锁存列值
108         row_val          <= row;        // 锁存行值
109         key_pressed_flag <= 1;          // 置键盘按下标志  
110       end
111     endcase
112 //--------------------------------------
113 // 状态机部分 结束
114 //--------------------------------------
115  
116  
117 //++++++++++++++++++++++++++++++++++++++
118 // 扫描行列值部分 开始
119 //++++++++++++++++++++++++++++++++++++++
120 always @ (posedge key_clk, negedge rst_n)
121   if (!rst_n)
122     keyboard_val <= 4'h0;
123   else
124     if (key_pressed_flag)
125       case ({col_val, row_val})
126         8'b1110_1110 : keyboard_val <= 4'h0;
127         8'b1110_1101 : keyboard_val <= 4'h4;
128         8'b1110_1011 : keyboard_val <= 4'h8;
129         8'b1110_0111 : keyboard_val <= 4'hC;
130          
131         8'b1101_1110 : keyboard_val <= 4'h1;
132         8'b1101_1101 : keyboard_val <= 4'h5;
133         8'b1101_1011 : keyboard_val <= 4'h9;
134         8'b1101_0111 : keyboard_val <= 4'hD;
135          
136         8'b1011_1110 : keyboard_val <= 4'h2;
137         8'b1011_1101 : keyboard_val <= 4'h6;
138         8'b1011_1011 : keyboard_val <= 4'hA;
139         8'b1011_0111 : keyboard_val <= 4'hE;
140          
141         8'b0111_1110 : keyboard_val <= 4'h3; 
142         8'b0111_1101 : keyboard_val <= 4'h7;
143         8'b0111_1011 : keyboard_val <= 4'hB;
144         8'b0111_0111 : keyboard_val <= 4'hF;        
145       endcase
146 //--------------------------------------
147 //  扫描行列值部分 结束
148 //--------------------------------------
149        
150 endmodule

 

 

参考资料:明德杨至简设计法

参考资料:驱动4x4矩阵键盘的思路

标签:FPGA,FSM,col,键盘,key,按键,低电平,reg
来源: https://www.cnblogs.com/yiquwange/p/15096703.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有