微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > FPGA,CPLD和ASIC > FPGA Verilog HDL 设计实例系列连载--------矩阵键盘接口

FPGA Verilog HDL 设计实例系列连载--------矩阵键盘接口

时间:10-02 整理:3721RD 点击:

矩阵键盘的原理

  矩阵键盘又叫行列式键盘。用带IO口的线组成行列结构,按键设置在行列的交点上。例如用4×4的行列式结构可以构成16个键的键盘。这样,当按键数量平方增长时,I/O口只是线性增长,这样就可以节省I/O口。矩阵键盘的原理图如图1.1所示:

        

                          图1.1  矩阵键盘的原理图

  按键设置在行列线交叉点,行列线分别连接到按键开关的两端。列线通过上拉电阻接3.3V电压,即列线的输出被钳位到高电平状态。  判断键盘中有无按键按下式通过行线送入扫描线好然后从列线读取状态得到的。其方法是依次给行线送低电平,检查列线的输入。如果列线全是高电平,则代表低电平信号所在的行中无按键按下;如果列线有输入为低电平,则代表低电平信号所在的行和出现低电平的列的交点处有按键按下。

2、一个完整的键盘控制程序应解决以下任务:

  (1)检测有无按键按下
  (2)有键按下,在无硬件去抖得情况下,应有软件延时除去抖动影响
  (3)键扫描程序
  (4)将键编码转换成相应建值
  整个设计程序包括三个模块:时钟分频、键盘扫描和键译码转换。为了显示,还必须在顶层添加显示部分。



3、时钟分频

  由于使用的外部时钟频率为50MHz,这个频率对扫描来说太高,所以这里需要一个分频器来分得适合键盘扫描使用的频率。

  1. //-------------------------------------------------------------------------------------------------
  2. // File        : scan_clk.v
  3. // Generated   : 2011-07-20
  4. // Author      : wangliang
  5. //-------------------------------------------------------------------------------------------------
  6. `timescale 1 ns / 1 ps

  7. module scan_clk ( clkout ,clk ,rst );
  8.      
  9. input rst ;
  10. input clk ;
  11. wire clk ;

  12. output clkout ;

  13. reg clkout_r ;

  14. parameter  period= 200000;   
  15. //parameter  period= 10;
  16. reg [31:0] cnt;  

  17. always @( posedge clk or negedge rst)                  //分频50Hz
  18.      begin
  19.          if ( !rst )
  20.          begin
  21.              cnt > 1) - 1)                   //设定周期时间的一半
  22.                  clkout_r <= #1 1'b1;
  23.          else if (cnt == period - 1)                    //设定的周期时间
  24.              begin
  25.                  clkout_r <= #1 1'b0;
  26.                   cnt <= #1 'b0;      
  27.              end
  28.          
  29.          end
  30.       end
  31. assign clkout = clkout_r ;   
  32.      
  33. endmodule

复制代码

  注:从上面可以看出,我们经过分频得到50HZ的时钟,是为下一模块键盘扫描提供的时钟,之所以是50HZ,而不是其他的数值,是因为,人在按键的时候,其停留时间大概是20ms左右,键盘扫描时,会发送固定的数值码,本例中是循环的发送4种数值,下面会提及到,50HZ转化为时间是0.02S=20ms。图1.2是分频模块。

        

         图1.2  分频模块

4、键盘扫描

  键盘扫描电路是用于产生keydrv3~ keydrv0 信号,其变化顺序是1110→1101→1011→0111→1110…周而复始地扫描。其停留时间大慨在20ms。更短的时间没有必要,因为人为按键的时间大慨为20ms,不可能产生有更快的动作;另外,更短的停留时间还容易采集到抖动信号,会干扰判断。而太长的时间容易丢失某些较快的按键动作。图1.3是键盘扫描模块。

  1. //-------------------------------------------------------------------------------------------------
  2. // File        : key_scan.v
  3. // Generated   : 2011-07-20
  4. // Author      : wangliang
  5. //-------------------------------------------------------------------------------------------------
  6. `timescale 1 ns / 1 ps

  7. module key_scan ( clk ,keydrv ,rst );

  8. input clk ;   
  9. input rst ;
  10. wire clk ;

  11. output [3:0] keydrv ;
  12. wire [3:0] keydrv ;

  13. parameter s1 = 4'b1110;
  14. parameter s2 = 4'b1101;
  15. parameter s3 = 4'b1011;
  16. parameter s4 = 4'b0111;     

  17. reg [3:0]current_state;
  18. reg [3:0]next_state;

  19. always @ ( posedge clk or negedge rst )
  20.      
  21.      begin
  22.          if ( !rst )
  23.              current_state <= s1    ;
  24.          else    current_state <= next_state       ;
  25.          end
  26.          
  27. always      @ ( current_state )
  28.      begin
  29.          case  ( current_state )
  30.              s1: next_state <=s2;     
  31.              s2: next_state <=s3;
  32.              s3: next_state <=s4;
  33.              s4: next_state <=s1;
  34.              default:  next_state <=s1;
  35.      endcase
  36.      end
  37.      
  38. assign    keydrv =  current_state      ;
  39.      
  40. endmodule

复制代码

        

        图1.3 键盘扫描模块


5、键译码转换
  

  键盘译码电路是从keydrv3~keydrv0和keyin3~keyin0信号中译码出按键值的电路。clk是全局时钟,由外部晶振提供。clk在系统的频率是最高的,其他的时钟由分频产生。Keydrv表示键盘扫描信号,keyin为键盘输入信号,keyvalue为键值。其外部接口如图1.4所示。

  1. //-------------------------------------------------------------------------------------------------
  2. // File        : top.v
  3. // Generated   : 2011-07-20
  4. // Author      : wangliang
  5. //-------------------------------------------------------------------------------------------------
  6. `timescale 1 ns / 1 ps
  7.      
  8. module top ( KEYO ,KEYI ,clk ,Y , rst);

  9. input    [3:0]    KEYO ;               //与原理图一致,是键盘输出端口给FPGA
  10. input            clk ;
  11. input            rst ;

  12. output    [3:0]    KEYI ;                //与原理图一致,是FPGA输出给 键盘   
  13. output    [7:0]    Y ;

  14. wire            keypress;
  15. wire            scanclk;
  16. wire    [7:0]    temp ;
  17. wire    [3:0]    keydrv ;   
  18.    
  19. reg        [7:0]    temp_r ;
  20. reg        [7:0]    Y_r;   
  21. reg        [4:0]    keyvalue ;
  22.      
  23. reg        [3:0]    scankey_o;   
  24. reg        [3:0]    scankey_i;
  25. wire            dis;
  26. reg                dis_pre;   

  27. assign dis = &KEYO ;

  28. scan_clk key_clk(      
  29.      .clk ( clk),
  30.      .clkout ( scanclk) ,
  31.      .rst ( rst )
  32.      );

  33. key_scan key_scan(
  34.      .clk ( scanclk ) ,
  35.      .keydrv (keydrv) ,
  36.      .rst ( rst )
  37.      );

  38. always @ ( posedge clk or negedge rst ) begin
  39.      if ( rst==1'b0 ) begin
  40.          scankey_o <= 4'b0 ;
  41.          scankey_i <= 4'b0 ;
  42.          dis_pre <= dis;
  43.      end else if ( clk ==1'b1 ) begin
  44.          dis_pre <= dis;
  45.          if ( (dis == 1'b0)&&(dis_pre==1'b1) )  begin
  46.              scankey_o <= keydrv    ;  
  47.              scankey_i <= KEYO ;
  48.          end   
  49.      end
  50. end

  51. assign  KEYI = keydrv;

  52. assign temp = {scankey_o,scankey_i};

  53. always @ ( posedge clk or negedge rst ) begin  
  54.      if ( rst==1'b0 ) begin
  55.          temp_r <= 8'b0;
  56.      end else if ( clk == 1'b1 )
  57.          temp_r <= temp ;
  58. end   
  59.      
  60. always @( temp_r or rst ) begin  
  61.      if ( rst==1'b0 ) begin                //译码输出
  62.          keyvalue <= 5'b0;
  63.      end else
  64.          case ( temp_r )   
  65.              8'b0111_0111 :  keyvalue <= 5'hb;        //无用,仅作为复位
  66.              8'b1110_1110 :  keyvalue <= 5'h7;               
  67.              8'b1110_1101 :  keyvalue <= 5'h8;                 
  68.              8'b1110_1011 :  keyvalue <= 5'h9;               
  69.              8'b1101_1110 :  keyvalue <= 5'h4;               
  70.              8'b1101_1101 :  keyvalue <= 5'h5;               
  71.              8'b1101_1011 :  keyvalue <= 5'h6;               
  72.              8'b1011_1110 :  keyvalue <= 5'h1;               
  73.              8'b1011_1101 :  keyvalue <= 5'h2;               
  74.              8'b1011_1011 :  keyvalue <= 5'h3;               
  75.              8'b0111_1101 :  keyvalue <= 5'h0;                                 
  76.              8'b0111_1011 :  keyvalue <= 5'b1_0001;    //小数点
  77.              default:        keyvalue <= 5'h0;            
  78.          endcase   
  79. end     

  80. always @(keyvalue or rst ) begin  
  81.      if ( rst==1'b0 )            //译码输出
  82.          Y_r <= 8'b0000_0000;   
  83.      else begin
  84.          Y_r =8'b0000_0000;
  85.          case (keyvalue )
  86.                  5'h0: Y_r = 8'b0011_1111;         // 0
  87.                  5'h1: Y_r = 8'b0000_0110;         // 1
  88.                  5'h2: Y_r = 8'b0101_1011;         // 2
  89.                  5'h3: Y_r = 8'b0100_1111;         // 3
  90.                  5'h4: Y_r = 8'b0110_0110;         // 4
  91.                  5'h5: Y_r = 8'b0110_1101;         // 5
  92.                  5'h6: Y_r = 8'b0111_1101;         // 6
  93.                  5'h7: Y_r = 8'b0000_0111;         // 7
  94.                  5'h8: Y_r = 8'b0111_1111;         // 8
  95.                  5'h9: Y_r = 8'b0110_1111;         // 9
  96.                  5'b1_0001: Y_r = 8'b1000_0000;     //.
  97.                  default: Y_r = 8'b0000_0000;
  98.          endcase
  99.      end
  100. end

  101. assign Y =~Y_r;

  102. endmodule

复制代码

  注:其中第33行~第43行,是实例化上面2个时钟分频模块和键盘扫描模块。第61行的temp是根据键盘的行和键盘的列确立的一个序列。第70行~第89行是根据temp确定键值。第91行~第111行是根据键值确定数码管的显示,数码管由8位组成,最高位是小数点位。

      

      图1.4 键译码转换模块

分配引脚:

  按照图1.4的结构,将其对应的接口配以引脚,其中,Y[7..0]是七段数码管的引脚;clk是系统时钟;rst接复位信号;KEYO[3..0]接行扫描码;KEYI[3..0]接列的状态(可对应原理图)。

实验结果:

  

  我们用到的就是这种标准的数字键盘,已经上面2个七段数码管中的一个。当按下数字键的时候,可以在数码管中显示出来。好了,这样就完成了键盘的实验了。

这样子

很好

太给力了

好强大啊,谢谢了!

haohao..

很强大,学习了!

给力

谢谢小编很好的东西啊

为啥看不到代码的

好强大 学习了

小编给力,怎么做LCD1602的动态显示,可以边采样边显示的那种,用Verilog写的

你好,请问一下如何通过矩阵键盘依次输入数据,并显示在数码管上?刚接触FPGA,请大神指教

很好,谢谢小编分享了

非常实用!nice!

好东东了!我要好好学习。

好东西,好给力啊

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top