一天一个设计实例-矩阵开关的应用
矩阵键盘又叫行列式键盘。用带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