矩阵键盘原理

在做矩阵键盘的驱动时,写了好多次都没有成功,出现了各种奇奇怪怪的错误。最后在网上看了无数篇的博客和讲解,终于搞懂了原理。
矩阵键盘的电路原理图如图所示(在网上看到别人的就随手down了下来):

矩阵键盘一共十六个按键,共接出八个引脚(四个行四个列),其中四个输入,四个输出。一般都把四个行作为输入的四条线,把四个列作为输出。
先将四条列线全部拉低,若有按键被按下,电路接通,则对应的行线也被拉低,输出低电平,其余行线为高电平。然后在依次拉低每一条列线,其余列线置高电平,来判断是哪一根列线使输出行线拉低,则跟据输入输出八根线的电位情况,就可以判断出哪个按键被按下。

程序编写

首先要明确程序要实现一个什么样的功能,这里我写的是一个矩阵键盘驱动然后输出键值并通过一位数码管进行显示。
在明确了程序功能后,就要确定功能引脚及接口。我一共使用了两个输入(一个是系统脉冲,一个是四位行输入接口),三个输出接口(一个是四位列输出接口,一个是数码管位接口,一个是数码管的八位段接口)

module key_board(input            clk,input      [3:0] row,                 // 矩阵键盘 行output reg [3:0] col,                 // 矩阵键盘 列output reg [7:0] seg,output reg sel);

接下来就是定义了各种中间信号还有定义的数码管常量(分别根据八段数码管值定义了从0~f的十六进制的数字显示):

reg [19:0] cnt;    //计数器
reg [3:0] key_out;  // 键盘值
reg [5:0] current_state, next_state;    // 现态、次态
reg       key_pressed_flag;             // 键盘按下标志
reg [3:0] col_val, row_val;             // 列行值寄存器reg key_clk;  //分频脉冲 //*************数码管数值定义******************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter dispa=8'b1000_1000;
parameter dispb=8'b1000_0011;
parameter dispc=8'b1010_0111;
parameter dispd=8'b1010_0001;
parameter dispe=8'b1000_0110;
parameter dispf=8'b1000_0011;

程序定义了六个状态的状态机,整个程序的重点就在于状态机的列状态切换。六个状态分别为 “没有按键按下”、“扫描第1列”、“扫描第2列”、“扫描第3列”、“扫描第4列”、“有按键按下”:

//*******************状态机定义*********************//
parameter IDLE           = 6'b000_001;  // 没有按键按下
parameter SCAN_COL0      = 6'b000_010;  // 扫描第0列
parameter SCAN_COL1      = 6'b000_100;  // 扫描第1列
parameter SCAN_COL2      = 6'b001_000;  // 扫描第2列
parameter SCAN_COL3      = 6'b010_000;  // 扫描第3列
parameter KEY_PRESSED    = 6'b100_000;  // 有按键按下

状态切换考虑到按键的消抖,所以通过分频产生21ms的脉冲来作为状态切换的时间间隔。分频模块如下:

//***************分频*******************//
always @ (posedge clk)begin if(cnt<=20'd1048_500)  //分频21mscnt <= cnt + 1'b1;else begincnt<=0;key_clk<=~key_clk;endend//****************状态切换***************//
always @ (posedge key_clk)current_state <= next_state;

当有按键被按下时,行输入值必定有一根线被拉低,所以输入不全为高电平,据此来判断是否有按键按下。当行输入值不全为1时,说明有按键按下,则将状态机切换到有按键被按下的状态,将此时的行列值都放到寄存器中:

//************状态机切换************************//
always @ *case (current_state)IDLE :                    // 没有按键按下if (row != 4'hF)next_state = SCAN_COL0;elsenext_state = IDLE;SCAN_COL0 :                         // 扫描第0列 if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL1;SCAN_COL1 :                         // 扫描第1列 if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL2;    SCAN_COL2 :                         // 扫描第2列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL3;SCAN_COL3 :                         // 扫描第3列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = IDLE;KEY_PRESSED :                       // 有按键按下if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = IDLE;                      endcase

行输入值不全为1 的前提一定是对应的列线有一根被拉低了,然后在扫描判断是哪根线被拉低。先将所有列线拉低,则一旦有按键被按下,行输入就不全为1,之后开始扫描每一列。扫描到对应列就将除该列的其他列都拉高,若行线仍全不为0则可判断是该列上的按键被按下,否则进行下一列的扫描:


//***************赋列值***********************//
always @ (posedge key_clk)case (next_state)IDLE         :                  // 没有按键按下begincol              <= 4'h0;key_pressed_flag <=    0;       // 清键盘按下标志endSCAN_COL0 :                       // 扫描第0列col <= 4'b1110;SCAN_COL1 :                       // 扫描第1列col <= 4'b1101;SCAN_COL2 :                       // 扫描第2列col <= 4'b1011;SCAN_COL3 :                       // 扫描第3列col <= 4'b0111;KEY_PRESSED :                     // 有按键按下begincol_val          <= col;        // 锁存列值row_val          <= row;        // 锁存行值key_pressed_flag <= 1;          // 置键盘按下标志  endendcase

在得到行列线的寄存值后,就可以对键值的输出做赋值:

//********************键值输出*************************//
always @ (posedge key_clk)if (key_pressed_flag)case ({col_val, row_val})8'b1110_1110 : key_out <= 4'h0;8'b1110_1101 : key_out <= 4'h4;8'b1110_1011 : key_out <= 4'h8;8'b1110_0111 : key_out <= 4'hC;8'b1101_1110 : key_out <= 4'h1;8'b1101_1101 : key_out <= 4'h5;8'b1101_1011 : key_out <= 4'h9;8'b1101_0111 : key_out <= 4'hD;8'b1011_1110 : key_out <= 4'h2;8'b1011_1101 : key_out <= 4'h6;8'b1011_1011 : key_out <= 4'hA;8'b1011_0111 : key_out <= 4'hE;8'b0111_1110 : key_out <= 4'h3; 8'b0111_1101 : key_out <= 4'h7;8'b0111_1011 : key_out <= 4'hB;8'b0111_0111 : key_out <= 4'hF;        endcase

根据输出键值,在对数码管显示的值进行定义:

always@(posedge clk)beginsel<=0;case(key_out)5'd0:seg<=disp0;5'd1:seg<=disp1;5'd2:seg<=disp2;5'd3:seg<=disp3;5'd4:seg<=disp4;5'd5:seg<=disp5;5'd6:seg<=disp6;5'd7:seg<=disp7;5'd8:seg<=disp8;5'd9:seg<=disp9;5'd10:seg<=dispa;5'd11:seg<=dispb;5'd12:seg<=dispc;5'd13:seg<=dispd;5'd14:seg<=dispe;5'd15:seg<=dispf;5'd16:seg<=disp0;endcaseend
endmodule

下面贴出完成代码:

完整代码:

module key_board(input            clk,input      [3:0] row,                 // 矩阵键盘 行output reg [3:0] col,                 // 矩阵键盘 列output reg [7:0] seg,output reg sel
);reg [19:0] cnt;    //计数器
reg [3:0] key_out;  // 键盘值 reg       key_pressed_flag;             // 键盘按下标志
reg [3:0] col_val, row_val;             // 列行值寄存器reg key_clk;  //分频脉冲 //*************数码管数值定义******************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter dispa=8'b1000_1000;
parameter dispb=8'b1000_0011;
parameter dispc=8'b1010_0111;
parameter dispd=8'b1010_0001;
parameter dispe=8'b1000_0110;
parameter dispf=8'b1000_0011;//***************分频*******************//
always @ (posedge clk)begin if(cnt<=20'd1048_500)  //分频21mscnt <= cnt + 1'b1;else begincnt<=0;key_clk<=~key_clk;endend//*******************状态机定义*********************//parameter IDLE                = 6'b000_001;  // 没有按键按下
parameter SCAN_COL0      = 6'b000_010;  // 扫描第0列
parameter SCAN_COL1      = 6'b000_100;  // 扫描第1列
parameter SCAN_COL2      = 6'b001_000;  // 扫描第2列
parameter SCAN_COL3      = 6'b010_000;  // 扫描第3列
parameter KEY_PRESSED    = 6'b100_000;  // 有按键按下reg [5:0] current_state, next_state;    // 现态、次态always @ (posedge key_clk)current_state <= next_state;//************状态机切换************************//
always @ *case (current_state)IDLE :                    // 没有按键按下if (row != 4'hF)next_state = SCAN_COL0;elsenext_state = IDLE;SCAN_COL0 :                         // 扫描第0列 if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL1;SCAN_COL1 :                         // 扫描第1列 if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL2;    SCAN_COL2 :                         // 扫描第2列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL3;SCAN_COL3 :                         // 扫描第3列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = IDLE;KEY_PRESSED :                       // 有按键按下if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = IDLE;                      endcase//***************赋列值***********************//
always @ (posedge key_clk)case (next_state)IDLE         :                  // 没有按键按下begincol              <= 4'h0;key_pressed_flag <=    0;       // 清键盘按下标志endSCAN_COL0 :                       // 扫描第0列col <= 4'b1110;SCAN_COL1 :                       // 扫描第1列col <= 4'b1101;SCAN_COL2 :                       // 扫描第2列col <= 4'b1011;SCAN_COL3 :                       // 扫描第3列col <= 4'b0111;KEY_PRESSED :                     // 有按键按下begincol_val          <= col;        // 锁存列值row_val          <= row;        // 锁存行值key_pressed_flag <= 1;          // 置键盘按下标志  endendcase//********************键值输出*************************//
always @ (posedge key_clk)if (key_pressed_flag)case ({col_val, row_val})8'b1110_1110 : key_out <= 4'h0;8'b1110_1101 : key_out <= 4'h4;8'b1110_1011 : key_out <= 4'h8;8'b1110_0111 : key_out <= 4'hC;8'b1101_1110 : key_out <= 4'h1;8'b1101_1101 : key_out <= 4'h5;8'b1101_1011 : key_out <= 4'h9;8'b1101_0111 : key_out <= 4'hD;8'b1011_1110 : key_out <= 4'h2;8'b1011_1101 : key_out <= 4'h6;8'b1011_1011 : key_out <= 4'hA;8'b1011_0111 : key_out <= 4'hE;8'b0111_1110 : key_out <= 4'h3; 8'b0111_1101 : key_out <= 4'h7;8'b0111_1011 : key_out <= 4'hB;8'b0111_0111 : key_out <= 4'hF;        endcasealways@(posedge clk)beginsel<=0;case(key_out)5'd0:seg<=disp0;5'd1:seg<=disp1;5'd2:seg<=disp2;5'd3:seg<=disp3;5'd4:seg<=disp4;5'd5:seg<=disp5;5'd6:seg<=disp6;5'd7:seg<=disp7;5'd8:seg<=disp8;5'd9:seg<=disp9;5'd10:seg<=dispa;5'd11:seg<=dispb;5'd12:seg<=dispc;5'd13:seg<=dispd;5'd14:seg<=dispe;5'd15:seg<=dispf;5'd16:seg<=disp0;endcaseend
endmodule

【注】:个人学习记录,如有错误,望不吝赐教

FPGA学习——矩阵键盘驱动及数码管显示键值相关推荐

  1. 4*4矩阵键盘扫描c语言,用扫描法读出4×4矩阵键盘,在数码管显示按键值

    原标题:用扫描法读出4×4矩阵键盘,在数码管显示按键值 //电路说明如下. //:使用51系列兼容的即可: //4×4:接在P1口: //两位数码显示器: P0口输出七段码,P2口输出位选码. //= ...

  2. FPGA的矩阵键盘驱动( 修正版)

    以前写的矩阵键盘的驱动是以单片机的思想来实现的,在FPGA上完全失败了,考虑的太简单了emmmmmm.所以在查了一些资料后,对过去写过的矩阵键盘驱动做个修正 旧版本代码 module keyboard ...

  3. FPGA学习汇总(六)----数码管显示(1)

    目录 概念 单个数码管显示单个数字 操作 代码 现象 分析 四个数码管定时单个显示数字 分析 代码 四个数码管同时显示 分析 代码 现象 四个数码管同时显示定时转换 分析 代码 概念 我们要搞懂数码管 ...

  4. 51单片机实现矩阵键盘密码锁,数码管显示

    板子是普中的,按键按下10为0,按键11为确认,按键12为取消. 输入密码正确时LED D8闪烁(正确密码为5555) #include <REGX52.H>//头文件定义 unsigne ...

  5. FPGA学习日志——74hc595驱动的数码管静态显示seg_595_static

    文章目录 数码管静态显示seg_595_static 实验原理 74HC595 实验框图.波形图与代码原理 数码管静态显示seg_595_static 数码管是一种半导体发光器件,其基本单元是发光二极 ...

  6. 矩阵键盘与六位数码管_[走近FPGA]之矩阵键盘

    注:由于新学期较为繁忙,本文由不愿透露姓名的 @Cardia 撰写.以下为正文. 在上一篇文章中,介绍了二进制转十进制电路的实现,其文章链接如下: 人生状态机:[走近FPGA]之二进制转BCD码​zh ...

  7. 基于FPGA的 矩阵键盘按键识别 【原理+源码】

    目录 引言 原理阐述 实现方法 源码分享 板级调试演示 引言 最近了解了矩阵键盘扫描的原理,动手实现了一下,在这里做一个简单的总结. 原理阐述 矩阵键盘典型电路: FPGA的应用电路: 其中,行信号为 ...

  8. linux下矩阵键盘设备名,Linux下矩阵键盘驱动分析与移植

    Post Views: 1,598 首先要介绍一下Linux中input子系统的模型,一图胜千言,所以直接上图. 上图一目了然,我们的键盘驱动就是工作在input子系统的最低层.单纯地从驱动角度讲,我 ...

  9. 矩阵键盘数 码管显示多位数 c语言,4×4矩阵键盘数码管显示按键值程序

    4×4矩阵键盘数码管显示按键值程序本文引用地址:http://www.eepw.com.cn/article/201607/294422.htm //电路说明如下. //单片机:使用51系列兼容的即可 ...

  10. Arduino与Proteus仿真实例-4x4矩阵键盘驱动仿真

    4x4矩阵键盘驱动仿真 键盘是广泛用于各种电子和嵌入式项目的输入设备. 它们用于以数字和字母的形式获取输入,并将其输入系统以进行进一步处理. 矩阵键盘由一组相互连接的按钮组成. 在本次实例中使用 4X ...

最新文章

  1. HDU1089-1096 A+B for Input-Output Practice 系列问题(输入输出格式练习)
  2. 15天Python入门-3-流程控制-选择结构
  3. A generic error occurred in GDI+的解决方案
  4. Linux内核网络数据包发送(一)
  5. sqlserver日志文件过大的处理方法
  6. 微信公众平台操作获取token类
  7. 电脑不能打字_电脑拼音打字快速入门秘籍
  8. Java讲课笔记35:初探泛型
  9. 【观察】嵌入式AI崛起,这里有一本通关“秘籍”
  10. SPSS回归分析案例
  11. 单机翻译软件android,计算机辅助翻译软件(Transmate)V7.3.0.1218 单机版
  12. Electronic Commerce 12th Gary Schneider
  13. 命令提示符命令(cmd)
  14. 基于MI的cfc(交叉频率耦合)分析
  15. 长大后自卑的孩子,大多出自这几种家庭,别不当回事(给已经做父母或未来即将为人父母的你)
  16. 传奇微端大带宽服务器如何选择
  17. 什么是Smalltalk
  18. SSM+老年人社区服务平台 毕业设计-附源码211711
  19. Hibernate_day01
  20. Arduino文档阅读笔记-4 WHEEL ROBOT CAR BASIC EXAMPLE

热门文章

  1. cad怎么画坐标系箭头_CAD中怎么画箭头啊 cad箭头
  2. Android笔记:多开/分身检测
  3. iphone计算机显示过程,iphone怎么同步显示到电脑
  4. 计算机存储容量的基本单位pb,pb存储单位是什么
  5. 单层决策树python_基于单层决策树的adaboost算法Python实现
  6. OGRE CG教程 (三): 渐隐效果
  7. 计算机逻辑与 或 非的表达式,逻辑表达式
  8. 去掉 新版GeForce Experience 桌面录制视频时的 右上角图标
  9. 什么是CBR,VBV和CPB
  10. 基于等离激元的空间微分