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

矩阵键盘原理

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1549729.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Linux-写USB键盘驱动(详解)

1.首先我们通过上节的代码中修改,来打印下键盘驱动的数据到底是怎样的 先来回忆下,我们之前写的鼠标驱动的id_table是这样: 所以我们要修改id_table,使这个驱动为键盘的驱动,如下图所示: 然后修改中断函数,通过printk()打印数据: 我们先按下按键A为例,打印出0x04,如下图: 我们…

基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v7.0版已发布

关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架&#xff0c;超轻量级、高度提炼&#xff0c;一套API优雅支持 UDP 、TCP 、WebSocket 三种协议&#xff0c;支持 iOS、Android、H5、标准Java、小程序、Uniapp&#xff0c;服务端基于Netty编写。 工程…

基于swing的教务管理系统java jsp学生教师信息mysql源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于swing的教务管理系统 系统有3权限&#xff1a;管…

推荐百度的八个不常用产品

大家最常用的百度产品就是搜索&#xff08;尤其是mp3搜索&#xff09;、贴吧&#xff08;我倒还没有用过&#xff09;、空间&#xff08;功能简单&#xff0c;但确实非常稳定&#xff09;。 其实百度还有一些不为人常用的产品&#xff0c;在这里。 如下图&#xff1a; 其中文档…

运行android项目时出现Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE错误!

[2011-12-14 11:10:00 - imusic] Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE [2011-12-14 11:10:00 - imusic] Please check logcat output for more details. [2011-12-14 11:10:02 - imusic] Launch canceled! 1: 我们再调试一个稍微比较大的应用&#xff0c;…

续一:《你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》

DDD领域驱动设计批评文集 “软件方法建模师”不再考查基础题 《软件方法》各章合集 我写了一篇文章&#xff0c;批评付施威的《DDD诊所——聚合过大综合症》&#xff08;以下简称《DDD诊所》&#xff09;&#xff0c;文章是《你的医书是假的&#xff01;批评付施威的《DDD诊…

qq windows版客户端0day复现——远程代码执行(七夕小礼物)

##ps&#xff1a;本文章仅用来分享&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff01;&#xff01;如有非法行为与本文章作者无任何关系。一切行为以遵守《中华人民共…

停车场收费软件兼容电脑操作系统问题如何搞定?

随着网络快速发展各式各样软件应运而生&#xff0c;同时操作系统对软件的要求也越来越高&#xff0c;兼容性问题越发重要。安装停车场收费软件&#xff0c;为了保障高效稳定的运行&#xff0c;前提也要兼容电脑的操作系统。 为此&#xff0c;易泊车牌识别停车场收费软件&#…

专访捷顺科技:“停车老司机”的智慧生态构想

本公众号已经改版&#xff0c;推出了线上线下课程&#xff0c;并且推出免费2个月广告服务业界优质产品。 实现智慧停车的过程中&#xff0c;互联网驱动下的模式创新是必要的&#xff0c;但有一个前提&#xff0c;即行业本身的技术创新。近日&#xff0c;捷顺科技总经理赵勇在接…

资本加速圈地,智慧停车战火越烧越旺

配图来自Canva 半年前&#xff0c;AIPARK爱泊车宣布完成B1和B2轮融资&#xff0c;投资方包括中美绿色一期基金、蔚来资本、中金资本、中关村启航基金等。 6月3日&#xff0c;城市级智慧云停车平台享停车宣布已完成数千万元融资&#xff0c;并获得了上亿元配套建设资金&#x…

SSM停车场管理系统-计算机毕设 附源码97557

SSM停车场管理系统 摘 要 21世纪时信息化的时代&#xff0c;几乎任何一个行业都离不开计算机&#xff0c;将计算机运用于停车场管理也是十分常见的。过去使用手工的管理方式对停车场进行管理&#xff0c;造成了管理繁琐、难以维护等问题&#xff0c;如今使用计算机对停车场的各…

django 停车场管理系统 计算机毕设源码19517

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…

道闸系统服务器内存不足,停车场智能道闸系统常见问题及解决方法

[导读]停车场智能道闸系统一般是口控制机、出口控制机、智能道闸、地感线圈、余位显示屏、图象识别设备、系统软件、管理工作站等这些设备组成的&#xff0c;所以哪方面出了问题&#xff0c;就可以从哪方面着手解决。 停车场智能道闸系统一般是口控制机、出口控制机、智能道闸、…

北安服务器维修,北安停车场收费系统企业

捷顺智慧停车场采用的ETC停车是基于车牌识别管理系统&#xff0c;将ETC技术运用于智能停车场中&#xff0c;采用ETC车牌识别&#xff0c;实现不停车收费和无人值守&#xff0c;使车辆进出停车识别率无限靠近一百&#xff0c;ETC采用国家交通部颁布的标准&#xff0c;实名制管理…

python+停车管理系统 毕业设计-附源码271400

Python停车管理系统 摘 要 21世纪时信息化的时代&#xff0c;几乎任何一个行业都离不开计算机&#xff0c;将计算机运用于停车场管理也是十分常见的。过去使用手工的管理方式对停车进行管理&#xff0c;造成了管理繁琐、难以维护等问题&#xff0c;如今使用计算机对停车场停车的…

uuid怎么获取_基于车位引导系统的捷顺室内导航项目怎么调试?

对于停车难&#xff0c;找车难&#xff0c;捷顺在几年前就已经推出成熟的解决方案&#xff0c;经过这几年&#xff0c;技术越发成熟、先进&#xff01;那么对于这款可以在室内蓝牙导航的先进系统要怎么调试呢&#xff1f;下面就让我们一起来学习下吧&#xff01; 一、介绍 基于…

《剑指Offer》模块2 二叉树【15道二叉树帮助你掌握二叉树】

二叉树 二叉树1. 树中两个结点的最低公共祖先方法一&#xff1a;公共路径方法二&#xff1a;递归 2. 重建二叉树根据前序遍历和中序遍历 得到树 补充题&#xff1a;树的遍历 3. 二叉树的下一个节点4. 树的子结构&#xff08; 递归中调用递归 &#xff09;5. 二叉树的镜像&#…

win7桌面图标突然消失,鼠标右键不管用―解决

win7电脑桌面图标突然消失&#xff0c;鼠标右键不管用–解决 我的电脑是win7&#xff0c;当时正在下软件&#xff0c;快下完的时候突然桌面上的图标都没了&#xff0c;只剩一张桌面壁纸&#xff0c;鼠标右键没用&#xff0c;关机重启也不行&#xff0c;去网上查了拼拼凑凑终于…

win7桌面图标不可读文件样式的东西遮挡

win7桌面图标不可读文件样式的东西遮挡 解决方法 1&#xff1a; 右击桌面空白处&#xff0c;打开“个性化”设置&#xff0c;更改Windows主题&#xff0c;让系统重新加载快捷方式图标&#xff1b; 2&#xff1a; 打开路径C:\Users\你的用户名\AppData\Local&#xff0c;找到其中…

简述计算机网络系统图标添加在桌面上的步骤,如何将网络图标添加到win7桌面上...

win7网上邻居在哪&#xff1f;相信对于一些刚从WinXP升级到win7的盆友来说&#xff0c;对win7这款操作系统都不是特别的了&#xff0c;以致许多的盆友都都无法找到网上邻居&#xff0c;那么那么网上邻居在哪呢&#xff0c;下面小编就来为大家揭晓一下哈~ 方法一、 1、最简单的方…