FPGA|CPLD|ASIC论坛
直播中

uuyuanuu

9年用户 47经验值
擅长:可编程逻辑 EDA/IC设计
私信 关注
[经验]

【AC620 FPGA试用体验】+矩阵按键

在矩阵键盘中,每条行线和列线在交叉处都不是直接连同,而是通过一个按键直接相连,这样以来一个4*4的矩阵键盘只需要8根控制线就可以完成16个按键的控制。分类主要分为PCBA型以及薄膜型,AC620配套赠送的为PCBA型,手感更好,毕竟自带哒哒哒答的BGM。

2.png

4*4矩阵按键与AC620连接示意图

3.png

4*4矩阵按键电路

  矩阵按键扫描原理

检测矩阵按键中某一按键是否被按下,常用的方法是行扫描法。图中一共有8条控制线,4条行控制线(ROW),4条列控制线(COL),而且可以看到8条控制线的初始状态都位高电平。那我们如何去检测某一个按键被按下,并且还能找到其具体的位置?假如我们让COL0=0,然后去逐行的进行扫描每一行,如果ROW0=0出现低电平通过20ms的延迟再次判断该行是否仍然为零,如果仍然为零,那么说明按键0被按下。其它按键的检测类似这种方法。
1.扫描COL0列:
a)将COL0-COL3输出为0111。
b)读ROW0- ROW3这4个输入口的电平。如果048c都没被按下,则ROW0-ROW3由于都是上拉到VCC的原因,全部都为1;如果有1个按键被按下比如是0,则ROW0-ROW3输出应为0111,以此类推若8被按下ROW0-ROW3输出应为1101。若都没被按下则开始第二列扫描。
2.扫描COL1列:
a) 将COL0-COL3输出为1011。
b) 读ROW0- ROW3这4个输入口的电平。如果159D都没被按下,则ROW0-ROW3由于都是上拉到VCC的原因,全部都为1;如果有1个被按下比如是5,则ROW0-ROW3输出应为1011,可依此类推其他按键被按下情况。若都没被按下则开始第三列扫描。
3.扫描COL2列:
a) 将COL0-COL3输出为1101。
b) 读ROW0- ROW3这4个输入口的电平。如果26AE都没被按下,则ROW0-ROW3由于都是上拉到VCC的原因,全部都为1;如果有1个被按下比如是A,则ROW0-ROW3输出应为1101,可依此类推其他按键被按下情况。若都没被按下则开始第四列扫描。
4.扫描COL3列:
a) 将COL0-COL3输出为1110。
b) 读ROW0- ROW3这4个输入口的电平。如果37BF都没被按下,则ROW0-ROW3由于都是上拉到VCC的原因,全部都为1;如果有1个被按下比如是F,则ROW0-ROW3输出应为1110,可依此类推其他按键被按下情况。若都没被按下则开始下一轮扫描,即开始第一列扫描。
通过这4列的扫描,就已经能够确定S这16个按键当前的状态了。
矩阵按键检测模块接口设计
经过以上原理分析确立以下模块接口示意图

4.png


图2 矩阵按键模块框图
  
信号名称
  
  
I/O
  
  
功能描述
  
  Clk
  
  I
  
  模块时钟,50MHz
  
  Rst_n
  
  I
  
  模块复位,低电平有效
  
  Key_Board_row_i
  
  I
  
  矩阵按键行输入信号
  
  Key_Flag
  
  O
  
  按键检测完成标志信号
  
  Key_Value
  
  O
  
  按键值
  
  Key_Board_col_o
  
  O
  
  矩阵按键输出列信号
  
表2 矩阵按键模块接口列表

状态机及功能描述

  状态机编码
  
  状态机名称
  
  功能描述
  
  1
  
  IDLE
  
  检测按键是否按下,若按下则开始按键消抖
  
  2
  
  P_FILTER
  
  按键消抖检测,消抖完成开始读取行状态
  
  3
  
  READ_ROW_P
  
  读取行状态信号值,并开始列扫描
  
  4
  
  SCAN_C0
  
  扫描第一列,并判断按下按键是否在当前列,若否则开始扫描下一列
  
  5
  
  SCAN_C1
  
  扫描第二列,并判断按下按键是否在当前列,若否则开始扫描下一列
  
  6
  
  SCAN_C2
  
  扫描第三列,并判断按下按键是否在当前列,若否则开始扫描下一列
  
  7
  
  SCAN_C3
  
  扫描第三列,并输出按键值
  
  8
  
  PRESS_RESULT
  
  输出扫描结果
  
  9
  
  WAIT_R
  
  等待按键释放
  
  10
  
  R_FILTER
  
  释放检测
  
  11
  
  READ_ROW_R
  
  释放结束后,回到初始态
  

先编写一个20ms的延时信号
  
    reg [19:0] counter;
  
reg En_Cnt;
  
    reg Cnt_Done;
  
    always@(posedge Clk or negedge Rst_n)
  
    if(!Rst_n)
  
        counter <= 20'd0;
  
    else if(En_Cnt)begin
  
        if(counter == 20'd999999)
  
            counter <= 20'd0;
  
        else
  
            counter <= counter + 1'b1;
  
    end
  
    else
  
        counter <= 20'd0;
  
        
  
    always@(posedge Clk or negedge Rst_n)
  
    if(!Rst_n)
  
        Cnt_Done <= 1'b0;
  
    else if(counter == 20'd999999)
  
        Cnt_Done <= 1'b1;
  
    else
  
        Cnt_Done <= 1'b0;
  
  

    开始状态机的编写,这里使用一段
  
    reg [3:0]Col_Tmp;
  
    reg [3:0]Key_Value;
  
    reg [7:0]Key_Value_tmp;
  
reg Key_Flag_r;
  
    reg [10:0]state;
  
  
    always@(posedge Clk or negedge Rst_n)
  
    if(!Rst_n)begin
  
        En_Cnt <= 1'b0;
  
        state <= IDEL;
  
        Key_Board_Col_o <= 4'b0000;
  
        Col_Tmp <= 4'd0;
  
        Key_Flag_r <= 1'b0;
  
        Key_Value_tmp <= 8'd0;
  
        Key_Board_Row_r <= 4'b1111;
  
    end
  
    else begin
  
        case(state)
  
//////此部分见下
  
            default:;
  
        endcase
  
    end
  
  

    检测按键是否按下,若按下则开始按键消抖并使能20MS计数器。
  IDEL:
      if(Key_Board_Row_i != 4'b1111)begin
          En_Cnt <= 1'b1;
          state <= P_FILTER;         
      end
      else begin
          En_Cnt <= 1'b0;
          state <= IDEL;         
  
    end
  
  

按键消抖检测,消抖完成开始读取行状态
  
P_FILTER:
  
    if(Cnt_Done) begin
  
        En_Cnt <= 1'b0;
  
        state <= READ_ROW_P;
  
    end
  
    else begin
  
        En_Cnt <= 1'b1;
  
        state <= P_FILTER;              
  
    end
  
  

    按键仍被按下(真实按下非抖动),暂存行状态值,并开始列扫描
  
READ_ROW_P:
  
    if(Key_Board_Row_i != 4'b1111)begin
  
        Key_Board_Row_r <= Key_Board_Row_i;
  
        state <= SCAN_C0;   
  
        Key_Board_Col_o <= 4'b1110;
  
    end
  
    else begin
  
        state <= IDEL;
  
        Key_Board_Col_o <= 4'b0000;
  
    end
  
  

    扫描第0列,若按键在在第0列被按下,则寄存此时列值到列寄存器中。
  
SCAN_C0:
  
    begin
  
        state <= SCAN_C1;   
  
        Key_Board_Col_o <= 4'b1101;
  
        if(Key_Board_Row_i != 4'b1111)                     
  
            Col_Tmp <= 4'b0001;
  
        else
  
            Col_Tmp <= 4'b0000;
  
    end
  
  

    扫描第1列,若按键在在第0列被按下,则寄存此时列值到列寄存器中。
  
SCAN_C1:
  
    begin
  
        state <= SCAN_C2;   
  
        Key_Board_Col_o <= 4'b1011;
  
        if(Key_Board_Row_i != 4'b1111)                     
  
            Col_Tmp <= Col_Tmp | 4'b0010;
  
        else
  
            Col_Tmp <= Col_Tmp;            
  
    end
  
  

扫描第2列,若按键在在第2列被按下,则寄存此时列值到列寄存器中。
  
SCAN_C2:
  
    begin
  
        state <= SCAN_C3;   
  
        Key_Board_Col_o <= 4'b0111;
  
        if(Key_Board_Row_i != 4'b1111)
  
            Col_Tmp <= Col_Tmp | 4'b0100;
  
        else
  
            Col_Tmp <= Col_Tmp;            
  
    end
  
  

扫描第3列,若按键在在第3列被按下,则寄存此时列值到列寄存器中。
  
SCAN_C3:
  
    begin
  
        state <= PRESS_RESULT;  
  
        if(Key_Board_Row_i != 4'b1111)
  
            Col_Tmp <= Col_Tmp | 4'b1000;
  
        else
  
            Col_Tmp <= Col_Tmp;            
  
    end
  
  
    这样四列扫描结束即可得到一个值,并产生一次检测结束标志信号,信号最终输出组成为列值、行值。其中if中判断语句是为了 /*4位行输入值相加为3,表明有且只有一个行输入为0,既保证只有一行中有按键被按下*4位列扫描结果相加为1,表明有且只有列中有按键被按下,通过这两个条件保证了一次*按键中只有一个按键被按下时才检测有效*/

  
PRESS_RESULT:
  
    begin
  
        state <= WAIT_R;
  
        Key_Board_Col_o <= 4'b0000;
  
        if(((Key_Board_Row_r[0] + Key_Board_Row_r[1] + Key_Board_Row_r[2] + Key_Board_Row_r[3]) == 4'd3) ||
  
            ((Col_Tmp[0] + Col_Tmp[1] + Col_Tmp[2] + Col_Tmp[3]) == 4'd1))begin
  
            Key_Flag_r <= 1'b1;
  
            Key_Value_tmp <= {Key_Board_Row_r,Col_Tmp};
  
        end
  
        else begin
  
            Key_Flag_r <= 1'b0;
  
            Key_Value_tmp <= Key_Value_tmp;
  
        end
  
    end
  
  

    等待按键释放,若检测到行信号全为1,开始释放抖动检测
  
WAIT_R:
  
    begin
  
        Key_Flag_r <= 1'b0;
  
        if(Key_Board_Row_i == 4'b1111)begin
  
            En_Cnt <= 1'b1;
  
            state <= R_FILTER;
  
        end
  
        else begin
  
            state <= WAIT_R;
  
            En_Cnt <= 1'b0;
  
        end
  
    end
  
  

    释放抖动检测,
  
R_FILTER:                    
  
        if(Cnt_Done) begin
  
            En_Cnt <= 1'b0;
  
            state <= READ_ROW_R;
  
        end
  
        else begin
  
            En_Cnt <= 1'b1;
  
            state <= R_FILTER;              
  
        end
  
  

    是否完全释放,如是则回到初始态,如不是则继续等待释放完成。
  
READ_ROW_R:
  
    if(Key_Board_Row_i == 4'b1111)
  
        state <= IDEL;
  
    else begin
  
        En_Cnt <= 1'b1;
  
        state <= R_FILTER;              
  
    end
  
  

在得到行值与列值后需要译码出实际按键所代表的含义
  
    always@(posedge Clk or negedge Rst_n)
  
    if(!Rst_n)begin
  
        Key_Flag <= 1'd0;
  
        Key_Value <= 4'd0;
  
    end
  
    else begin
  
        Key_Flag <= Key_Flag_r;
  
        case(Key_Value_tmp)
  
            8'b1110_0001 : Key_Value = 4'h0;
  
            8'b1110_0010 : Key_Value = 4'h1;
  
            8'b1110_0100 : Key_Value = 4'h2;
  
            8'b1110_1000 : Key_Value = 4'h3;
  
            
  
            8'b1101_0001 : Key_Value = 4'h4;
  
            8'b1101_0010 : Key_Value = 4'h5;
  
            8'b1101_0100 : Key_Value = 4'h6;
  
            8'b1101_1000 : Key_Value = 4'h7;
  
            
  
            8'b1011_0001 : Key_Value = 4'h8;
  
            8'b1011_0010 : Key_Value = 4'h9;
  
            8'b1011_0100 : Key_Value = 4'hA;
  
            8'b1011_1000 : Key_Value = 4'hB;
  
            
  
            8'b0111_0001 : Key_Value = 4'hC;
  
            8'b0111_0010 : Key_Value = 4'hD;
  
            8'b0111_0100 : Key_Value = 4'hE;
  
            8'b0111_1000 : Key_Value = 4'hF;
  
            default:begin Key_Value = Key_Value;Key_Flag <= Key_Flag;end
  
        endcase
  
    end
  
  
验证
5.png

板级测试

另外,在之前介绍矩阵键盘检测原理的时候讲解过,对于ROW信号,其与FPGA的连接管脚上默认是连接了有上拉电阻的,但是市面上很多矩阵键盘默认都是没有提供上拉电阻的,我们开发板选配的矩阵键盘默认也是没有提供上拉电阻的,那么是不是没有上拉电阻就不能完成实验了呢?实际上,我们可以使用FPGA片上的IO弱上拉电阻来代替外部的上拉电阻。
受限于篇幅问题,具体操作可以在芯航线手册中找到。

下面我们再通过另开一种方法也是工作中比较常用的一种验证方法,嵌入式逻辑分析仪,来检测本设计在板上运行时的工作情况。

经过上述描述,硬件的环境搭建完毕,然后使用Quartusii里面SignalTap进行在线调试,抓取实时数据分析波形。
14.png
观察逻辑分析仪抓取到的信号,可以见到当完成一次检测时,Key_Board_Row_r寄存器中行值为0010b,此时的列值Col_Tmp为0010b,检测结果Key_Value_tmp为0010_0010b,通过译码结果Key_Value为第一行第一列所以键值为“0101”,同理可以分析其他键值情况。


  • 1.png
  • 6.png
  • 7.png
  • 8.png
  • 9.png
  • 10.png
  • 11.png
  • 12.png
  • 13.png

回帖(2)

Harvestlamb

2017-8-24 00:43:20
呵呵哒
举报

h1654156195.4410

2018-10-18 08:13:55
据说回复可以加积分 不知道管不管用啊
举报

更多回帖

发帖
×
20
完善资料,
赚取积分