一天一个设计实例-矩阵开关的应用

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

图5‑18 矩阵键盘的原理图

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

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

(1)检测有无按键按下

(2)有键按下,在无硬件去抖得情况下,应有软件延时除去抖动影响

(3)键扫描程序

(4)将键编码转换成相应建值

接下来看下整个模块结构:

图5‑19 矩阵键盘模块结构

表5‑9 矩阵键盘(Key4x4_module.v)模块间信号定义

模块间信号

管脚

传输方向

作用/说明

Pin_Out[3:0]

key4x4funcmod_module.v模块传输给smg_interface.v

Pin_Out值0~15代表16个按键KEY0~KEY15。

时序

没有特殊要求。

表5‑10 矩阵键盘(Key4x4_module.v)顶层模块信号定义

顶层模块

输入管脚

作用/说明

输出管脚

作用/说明

key_in_y

key_out_x

key_in_y

输入矩阵键盘的列信号(即带上拉信号的列信号)

key_out_x,        输出矩阵键盘的行信号(不带上拉信号列)

数码管

数码管显示键值

时序

没有特殊要求。

图5‑19上是一个简单的点击与长按模块。设计的方法主要是由“按键功能模块”和“LED显示模块”组合合成,“按键功能模块”主要做“点击”和“长按击”检测。

“按键功能模块”中电平检测思路详见2.3.4实用UART传输FPGA实现节介绍。设计的思路如图所示:

图5‑20 矩阵键盘设计框图

整个模块设计思路如下:

先第一行输出 0,检查列线是否非为高;

再第二行输出 0,检查列线是否非为高;

再第三行输出 0,检查列线是否非为高;

再第三行输出 0,检查列线是否非为高;

如果某行输出 0 时,查到列线非全高,则该行有按键按下;

根据第几行线输出 0 与第几列线读入为 0,即可判断在具体什么位置的按键按下。

整个代码如下:

代码5‑6 矩阵键盘设计代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-06-09 03:52:48

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-06-10 19:40:30

7.//# Description:

8.//# @Modification History: 2019-06-10 19:40:30

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2019-06-10 19:40:30

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.`timescale 1ns / 1ps

17.module key4x4funcmod_module

18.(

19.   CLOCK,

20.    RST_n,            // 开发板上复位按键

21.    key_in_y,         // 输入矩阵键盘的列信号(KEY0~KEY3)

22.    key_out_x,        // 输出矩阵键盘的行信号(KEY4~KEY7)

23.   LED,

24.    Pin_Out           // 输出LED灯,用于矩阵键盘板上16个LED(LED1~LED16)

25.

26.);

27.

28.//========================================================

29.// PORT declarations

30.//========================================================

31.input        CLOCK;

32.input        RST_n;

33.input  [3:0] key_in_y;

34.output reg [3:0] key_out_x;

35.output [3:0] Pin_Out;

36.output reg[3:0] LED;

37.

38.//寄存器定义

39.reg [19:0] count;

40.

41.//==============================================

42.// 输出矩阵键盘的行信号,20ms扫描矩阵键盘一次,采样频率小于按键毛刺频率,相当于滤除掉了高频毛刺信号。

43.//==============================================

44.always @(posedge CLOCK or negedge RST_n)     //检测时钟的上升沿和复位的下降沿

45.begin

46.   if(!RST_n) begin               //复位信号低有效

47.      count <= 20'd0;        //计数器清0

48.      key_out_x <= 4'b1111;

49.   end

50.   else begin

51.           if(count == 20'd0)           //0ms扫描第一行矩阵键盘

52.            begin

53.               key_out_x <= 4'b1110;   //开始扫描第一行矩阵键盘,第一行输出0

54.                    count <= count + 20'b1; //计数器加1

55.            end

56.         else if(count == 20'd249_999) //5ms扫描第二行矩阵键盘,5ms计数(50M/200-1=249_999)

57.            begin

58.               key_out_x <= 4'b1101;   //开始扫描第二行矩阵键盘,第二行输出0

59.                    count <= count + 20'b1; //计数器加1

60.            end

61.            else if(count ==20'd499_999)   //10ms扫描第三行矩阵键盘,10ms计数(50M/100-1=499_999)

62.            begin

63.               key_out_x <= 4'b1011;   //扫描第三行矩阵键盘,第三行输出0

64.                    count <= count + 20'b1; //计数器加1

65.            end

66.            else if(count ==20'd749_999)   //15ms扫描第四行矩阵键盘,15ms计数(50M/67.7-1=749_999)

67.            begin

68.               key_out_x <= 4'b0111;   //扫描第四行矩阵键盘,第四行输出0

69.                    count <= count + 20'b1; //计数器加1

70.            end

71.         else if(count ==20'd999_999)  //20ms计数(50M/50-1=999_999)

72.               begin

73.               count <= 0;             //计数器为0

74.            end

75.          else

76.                count <= count + 20'b1;    //计数器加1

77.

78.     end

79.end

80.//====================================================

81.// 采样列的按键信号

82.//====================================================

83.reg [3:0] key_h1_scan;    //第一行按键扫描值KEY

84.reg [3:0] key_h1_scan_r;  //第一行按键扫描值寄存器KEY

85.reg [3:0] key_h2_scan;    //第二行按键扫描值KEY

86.reg [3:0] key_h2_scan_r;  //第二行按键扫描值寄存器KEY

87.reg [3:0] key_h3_scan;    //第三行按键扫描值KEY

88.reg [3:0] key_h3_scan_r;  //第三行按键扫描值寄存器KEY

89.reg [3:0] key_h4_scan;    //第四行按键扫描值KEY

90.reg [3:0] key_h4_scan_r;  //第四行按键扫描值寄存器KEY

91.always @(posedge CLOCK)

92.    begin

93.        if(!RST_n) begin               //复位信号低有效

94.            key_h1_scan <= 4'b1111;

95.            key_h2_scan <= 4'b1111;

96.            key_h3_scan <= 4'b1111;

97.            key_h4_scan <= 4'b1111;

98.        end

99.        else begin

100.          if(count == 20'd124_999)           //2.5ms扫描第一行矩阵键盘值

101.               key_h1_scan<=key_in_y;         //扫描第一行的矩阵键盘值

102.          else if(count == 20'd374_999)      //7.5ms扫描第二行矩阵键盘值

103.               key_h2_scan<=key_in_y;         //扫描第二行的矩阵键盘值

104.          else if(count == 20'd624_999)      //12.5ms扫描第三行矩阵键盘值

105.               key_h3_scan<=key_in_y;         //扫描第三行的矩阵键盘值

106.          else if(count == 20'd874_999)      //17.5ms扫描第四行矩阵键盘值

107.               key_h4_scan<=key_in_y;         //扫描第四行的矩阵键盘值

108.        end

109.end

110.

111.//====================================================

112.// 按键信号锁存一个时钟节拍

113.//====================================================

114.always @(posedge CLOCK)

115.   begin

116.         key_h1_scan_r <= key_h1_scan;

117.         key_h2_scan_r <= key_h2_scan;

118.         key_h3_scan_r <= key_h3_scan;

119.         key_h4_scan_r <= key_h4_scan;

120.    end

121.

122.wire [3:0] flag_h1_key = key_h1_scan_r[3:0] & (~key_h1_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效

123.wire [3:0] flag_h2_key = key_h2_scan_r[3:0] & (~key_h2_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效

124.wire [3:0] flag_h3_key = key_h3_scan_r[3:0] & (~key_h3_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效

125.wire [3:0] flag_h4_key = key_h4_scan_r[3:0] & (~key_h4_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效

126.//=====================================================

127.// 方便封装,将按键值进行赋值

128.//=====================================================

129.reg [3:0] rPin_Out;

130.always @ (posedge CLOCK or negedge RST_n)      //检测时钟的上升沿和复位的下降沿

131.begin

132.    if (!RST_n)                 //复位信号低有效

133.         rPin_Out <= 4'd0;     //

134.    else

135.         begin

136.             if ( flag_h1_key[0] ) begin LED<=4< span="">'b0000;rPin_Out <= 4'd0;  end  //按键第一行的KEY1值变化时,LED1将做亮灭翻转

137.             if ( flag_h1_key[1] ) begin LED<=4< span="">'b0001;rPin_Out <= 4'd1; end   //按键第一行的KEY2值变化时,LED2将做亮灭翻转

138.             if ( flag_h1_key[2] ) begin LED<=4< span="">'b0010;rPin_Out <= 4'd2;  end  //按键第一行的KEY3值变化时,LED3将做亮灭翻转

139.             if ( flag_h1_key[3] ) begin LED<=4< span="">'b0011;rPin_Out <= 4'd3;  end  //按键第一行的KEY4值变化时,LED4将做亮灭翻转

140.

141.             if ( flag_h2_key[0] ) begin LED<=4< span="">'b0100;rPin_Out <= 4'd4; end   //按键第二行的KEY5值变化时,LED5做亮灭翻转

142.             if ( flag_h2_key[1] ) begin LED<=4< span="">'b0101;rPin_Out <= 4'd5;  end  //按键第二行的KEY6值变化时,LED6将做亮灭翻转

143.             if ( flag_h2_key[2] ) begin LED<=4< span="">'b0110;rPin_Out <= 4'd6;  end  //按键第二行的KEY7值变化时,LED7将做亮灭翻转

144.             if ( flag_h2_key[3] ) begin LED<=4< span="">'b0111;rPin_Out <= 4'd7;  end  //按键第二行的KEY8值变化时,LED8将做亮灭翻转

145.

146.             if ( flag_h3_key[0] ) begin LED<=4< span="">'b1000;rPin_Out <= 4'd8;  end  //按键第三行的KEY9值变化时,LED9将做亮灭翻转

147.             if ( flag_h3_key[1] ) begin LED<=4< span="">'b1001; rPin_Out <= 4'd9;  end  //按键第三行的KEY10值变化时,LED10将做亮灭翻转

148.             if ( flag_h3_key[2] ) begin LED<=4< span="">'b1010;rPin_Out <= 4'd10; end   //按键第三行的KEY11值变化时,LED11将做亮灭翻转

149.             if ( flag_h3_key[3] ) begin LED<=4< span="">'b1011;rPin_Out <= 4'd11; end   //按键第三行的KEY12值变化时,LED12将做亮灭翻转

150.

151.             if ( flag_h4_key[0] ) begin LED<=4< span="">'b1100;rPin_Out <= 4'd12;   end //按键第四行的KEY13值变化时,LED13将做亮灭翻转

152.             if ( flag_h4_key[1] ) begin LED<=4< span="">'b1101;rPin_Out <= 4'd13;  end  //按键第四行的KEY14值变化时,LED14将做亮灭翻转

153.             if ( flag_h4_key[2] ) begin LED<=4< span="">'b1110;rPin_Out <= 4'd14;  end  //按键第四行的KEY15值变化时,LED15将做亮灭翻转

154.             if ( flag_h4_key[3] ) begin LED<=4< span="">'b1111;rPin_Out <= 4'd15; end   //按键第四行的KEY16值变化时,LED16将做亮灭翻转

155.         end

156.end

157.//====================================================

158.// 输出引脚

159.//====================================================

160.

161.assign Pin_Out = rPin_Out;

162.

163.endmodule

行130~156,将检测到的按键值赋值给4位LED,分别代表0~15的键值,同时将键值赋值给Pin_Out。

数码管模块见1.8节,在此不再赘述。

最后按照图5‑20进行顶层模块的设计,详细的代码如下:

代码5‑7 矩阵键盘顶层模块代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-06-05 21:05:29

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-06-09 04:34:28

7.//# Description:

8.//# @Modification History: 2010-08-07 14:36:26

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2010-08-07 14:36:26

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.module Key4x4_module

17.(

18.    CLOCK, RST_n,

19.    key_in_y,         // 输入矩阵键盘的列信号(KEY0~KEY3)

20.    key_out_x,        // 输出矩阵键盘的行信号(KEY4~KEY7)

21.    LED,

22.    SMG_Data,

23.    Scan_Sig

24.);

25.

26.    input CLOCK;

27.    input RST_n;

28.   input  [3:0] key_in_y;

29.    output [3:0] key_out_x;

30.    output [3:0] LED;

31.    output [7:0]SMG_Data;

32.    output [5:0]Scan_Sig;

33.

34.    /**************************/

35.

36.    wire [3:0]Pin_Out;

37.

38.    key4x4funcmod_module  U1

39.     (

40.        .CLOCK( CLOCK ),

41.        .RST_n( RST_n ),

42.        .key_in_y( key_in_y ),   // input - from top

43.        .key_out_x(key_out_x),   // output - to top

44.        .LED(LED),

45.        .Pin_Out( Pin_Out )      // < core

46.     );

47.

48.     /**************************/

49.     smg_interface U2

50.     (

51.         .CLOCK( CLOCK ),

52.         .RST_n( RST_n ),

53.         .SMG_Data( SMG_Data ),          // output - to top

54.         .Scan_Sig( Scan_Sig ),          // output - to top

55.         .Number_Sig( Number_Sig )          // < core

56.     );

57.

58.    reg [23:0]Number_Sig;

59.

60.    always @ ( posedge CLOCK or negedge RST_n )

61.        if( !RST_n )

62.             begin

63.                Number_Sig[7:0] <= 8'h16;

64.              end

65.         else

66.             case( Pin_Out )

67.                0:

68.                    Number_Sig[7:0] <= 8'h00;

69.                 1:

70.                    Number_Sig[7:0] <= 8'h01;

71.                 2:

72.                    Number_Sig[7:0] <= 8'h02;

73.                 3:

74.                    Number_Sig[7:0] <= 8'h03;

75.                 4:

76.                    Number_Sig[7:0] <= 8'h04;

77.                 5:

78.                    Number_Sig[7:0] <= 8'h05;

79.                 6:

80.                    Number_Sig[7:0] <= 8'h06;

81.                 7:

82.                    Number_Sig[7:0] <= 8'h07;

83.                 8:

84.                    Number_Sig[7:0] <= 8'h08;

85.                 9:

86.                    Number_Sig[7:0] <= 8'h09;

87.                 10:

88.                    Number_Sig[7:0] <= 8'h10;

89.                 11:

90.                    Number_Sig[7:0] <= 8'h11;

91.                 12:

92.                    Number_Sig[7:0] <= 8'h12;

93.                 13:

94.                    Number_Sig[7:0] <= 8'h13;

95.                 14:

96.                    Number_Sig[7:0] <= 8'h14;

97.                 15:

98.                    Number_Sig[7:0] <= 8'h15;

99.

100.                default:

101.                   Number_Sig[7:0]<=8'h16;

102.

103.              endcase

104.

105.endmodule

行60~103是顶层模块的核心操作,主要将Pin_Out值进行解析显示到数码管上。

将上述代码综合后下载到板子上:

矩阵键盘 K0 按一下-----------LED[3:0]显示0000,同时二位数码管显示00;

矩阵键盘 K1 按一下-----------LED[3:0]显示0001,同时二位数码管显示01;

矩阵键盘 K2 按一下-----------LED[3:0]显示0010,同时二位数码管显示02;

矩阵键盘 K3 按一下-----------LED[3:0]显示0011,同时二位数码管显示03;

矩阵键盘 K4 按一下-----------LED[3:0]显示0100,同时二位数码管显示04;

……………

整个模块的RTL如下所示:

图5‑21 整个模块综合后RTL

(0)

相关推荐

  • 凔海笔记之单片机(五):给按键编个码

    按键通常是一种长常开型按钮开关,例如下图,如果不按动得话它就是断开状态的. 它作为单片机的附属小鬼,主要就是为了让单片机检测出它的状态,然后单片机根据这个状态来干一些事.例如,当按键按下灯亮,当按键按 ...

  • 一天一个设计实例-基于FPGA的模数、数模转换器应用设计

    基于FPGA的模数.数模转换器应用设计 本节设计采用黑金ADDA模块,模块硬件结构如下: 图7‑32 硬件结构 数模转换( DA) 电路 如硬件结构图所示, DA 电路由高速 DA 芯片. 7 阶巴特 ...

  • 一天一个设计实例-基于FPGA的数模转换器应用设计

    基于FPGA的数模转换器应用设计 1.1.1带 EEPROM 存储器的 12 位MCP4725应用设计 7.4.1.1 MCP4725简介 MCP4725 是低功耗.高精度.单通道的 12 位缓冲电压 ...

  • 一天一个设计实例-基于FPGA的模数转换器应用设计

    基于FPGA的模数转换器应用设计 1.1.1八通道AD7606应用设计 7.3.1.1 AD7606简介 AD7606 是一款集成式 8 通道数据采集系统,片内集成输入放大器.过压保护电路.二阶模拟抗 ...

  • 一天一个设计实例-AD转换器原理

    AD转换器原理 A/D 转换器的基本原理 图7‑2 A/D转换器功能示意图 A/D转换器(Analog-to-Digital Converter)又叫模/数转换器,即是将模拟信号(电压或是电流的形式) ...

  • 一天一个设计实例-LCD12864的应用设计

    LCD12864的应用设计 LCD12864的应用基本和LCD1602的自定义字库非常类似,下面就简单介绍下LCD12864. 12864 中文 汉字图形点阵液晶显示模块,可显示汉字及图形,内置 81 ...

  • 一天一个设计实例-​LCD1602的应用设计

    LCD1602的应用设计 1.1.1LCD1602的简介 工业字符型液晶,能够同时显示16x02即32个字符.(16列2行) 图6‑16 LCD1602实物图 注:为了表示的方便 ,后文皆以1表示高电 ...

  • 一天一个设计实例-LED显示模块设计

    LED显示模块设计 LED点阵模块指的是利用封装8*8的模块组合点元板形成模块,而LED模组应用中一般指两类产品:一种是用插灯或表贴封装做成的单元板,常用户外门头单红屏.户外全彩屏,室内全彩屏等:另外 ...

  • 一天一个设计实例-GPIO PWM应用

    GPIO PWM 1.1.1PWM简介 脉冲宽度调制脉冲宽度调制(PWM),是英文"Pulse Width Modulation"的缩写,简称脉宽调制,是利用微处理器的数字输出来对 ...

  • 一天一个设计实例-PS/2键盘及鼠标的应用设计

    PS/2键盘及鼠标的应用设计 1.1.1键盘与单次操作 PS/2接口用于许多现代的鼠标和键盘,由IBM最初开发和使用.物理上的PS/2接口有两种类型的连接器:5脚的DIN和6脚的mini-DIN.图1 ...