基于VHDL的键盘扫描及显示电路.docx
《基于VHDL的键盘扫描及显示电路.docx》由会员分享,可在线阅读,更多相关《基于VHDL的键盘扫描及显示电路.docx(14页珍藏版)》请在冰豆网上搜索。
基于VHDL的键盘扫描及显示电路
工学院
EDA课程设计
说明书
设计题目基于VHDL的键盘扫描
及显示电路
系别电控系
专业班级__________________
学生__________________
学号__________________
指导教师__________________
日期__________________
基于VHDL的键盘扫描及显示电路
一、工作原理:
可编程器件的KEY_HANG[3..0]行信号输出端不停循环输出“1110”“1101”“1011”“0111”。
当没有键按下时可编程器件的KEY_LIE[3..0]列信号输入端检测到的是“1111”。
当有按键按下时,如按下1,此时KEY_HANG[3..0]行信号输出为“0111”,即KEY_HANG[3..0]的3管脚为“0”,可由电路看出,此时输入端KEY_LIE[3..0]检测到的将是“0111”。
KEY_LIE[3..0]的3管脚为0,可以在编写程序时,将输出信号KEY_HANG[3..0]与输入信号KEY_LIE[3..0]同时判断,比如可以认为当数据“KEY_HANG&KEY_LIE”为“01110111”时,可译码成数据1,。
同理可得其他按键的编码。
根据不同数据的编码译成不同的数据。
名称
IO属性
描述
备注
clk
in
输入时钟,1K和40K频率
KEY_HANG[3..0]
out
矩阵键盘的扫描输入端口
KEY_LIE[3:
0]
in
矩阵键盘的扫描输出端口
START
out
数据输出标志
DISPDATA[6..0]
out
数码管译码显示译码输出
7bit
DASPSEL[1..0]
out
数码管扫描输出
2bit
CLK_1K
CLK_40K
二、设计思路:
1.循环输出行信号,检测列信号输入,将行列信号相并。
2.译键值。
3.去抖动。
在译没一个键值后,为了防止抖动,加了一个计算环节,一旦检测到列信号后,译码,紧跟着进入计数环节,此时键抖动不会进入其他环节,这样可以防止抖动。
4.数码管译码、循环显示。
电路的具体功能罗列如下:
1)采用4×4矩阵键盘作为操作数和操作符的输入设备。
2)采用2位8段数码管作为输出显示设备,显示按下的数字及简单的功能。
3)由于所有键盘在按下或者弹起的时候均有按键抖动,所以应该采用去抖电路,当检测到有按键按下去的时候,应该延时20ms后,再进行检测,如果仍有键盘按键被按下去的话,则进行键盘读值。
矩阵键盘模块key_4_4的RTL电路图如下所示。
当CLK_1K上升沿到来时状态转为state0,然后判断列与非后的值,看是否有按键按下,如果有输入数据,则自动启动20ms的计数器,当计满数后,产生一个指示信号,此信号为1bit,高电平有效。
当读到此指示信号后,便再次将row信号锁存至寄存器,便得到键盘的一个返回值。
如果row没有变化,则state转换为state2,对第二行进行按键扫描。
依此类推,扫描第三行与第四行。
因为普通的按键都是接触式的,当按键闭合或释放时,上下接触面都会产生一个很短暂的抖动,如图2.2所示,这个抖动时间一般都会持续5-10ms,虽然这个抖动时间很短,但对于FPGA工作在50M的高频率上的器件来说,还是可以捕捉的到的。
为了使CPU对于一次按键操作只处理一次,在软件中必须加入去除抖动处理。
图2.2按键闭合时产生的抖动
由图中可以看出,最简单的去抖方法就是每隔一段时间读一次键盘,时间间隔大于10ms即可。
如果连续两次检测都有按键被按下,则可以肯定有按键被按下,而且也进入闭合稳定期。
三、数码管显示译码模块设计
数码管显示译码电路主要用来对实际的二进制数据装换为8段数码管的实际显示控制码,采用两个2位的8段共阴极数码管,数码管的显示方式有两种:
静态显示和动态显示。
具体如下:
静态显示方式:
所谓静态显示就是指无论是多少位数码管,同时处于显示状态。
静态显示的优点是:
数码管显示无闪烁,亮度高,软件控制比较容易;缺点是:
需要的硬件电路较多(每一个数码管都需要一个锁存器),将造成很大的不便,同时由于所有数码管都处于被点亮状态,所以需要的电流很大,当数码管的数量增多时,对电源的要求也就随之增高。
所以,在大部分的硬件电路设计中,很少采用静态显示方式。
动态显示方式:
所谓动态显示,是指无论在任何时刻只有一个数码管处于显示状态,每个数码管轮流显示。
动态显示的优点是:
硬件电路简单(数码管越多,这个优势越明显),由于每个时刻只有一个数码管被点亮,所以所有数码管消耗的电流较小;缺点是:
数码管亮度不如静态显示时的亮度高,例如有8个数码管,以1秒为单位,每个数码管点亮的时间只有1/8秒,所以亮度较低;如果刷新率较低,会出现闪烁现象;如果数码管直接与单片机连接,软件控制上会比较麻烦等。
显示译码方式如下:
1)时钟上升沿到来时分别对位选和段选进行译码。
2)将输入的2bite位选数据译码成4比特数据控制数码管的2位,由于是共阴极数码管要选定相应的数码管则使该位位低电平,其它位为高电平即可,如:
0000译码为0111_1111,对应于实验板上的左边第一位数码管。
3)将输入的4bite段选数据译码为8比特数据控制8个LED的亮灭,最高位接A,最低位接小数点位DP。
若要显示0则对应的译码为8’b1111_1100。
四、仿真
没有键按下时行循环输出“1110”“1101”“1011”“0111”
当随机按下时行保持所按下的状态不变
五、结论
这次EDA课程设计历时十天,学到很多很多的东西。
同时不仅可以巩固以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。
通过这次设计,进一步加深了对EDA的了解,让我对它有了更加浓厚的兴趣。
特别是当每一个子模块编写调试成功时,都会很高兴。
在编写顶层文件的程序时,遇到了不少问题,特别是各元件之间的连接,以及信号的定义,总是有错误。
排除困难后,程序编译就通过了。
在波形仿真时,也遇到了一点困难,想要的结果不能在波形上得到正确的显示,后来,经过屡次的调试之后,才发现在写代码之前对信号的相位考虑不足。
通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。
在设计的过程中遇到问题,可以说得是困难重重,这毕竟第一次做的,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固。
总的来说,这次设计还是比较成功的,在设计中遇到了很多问题,最后在老师的辛勤指导下,终于迎刃而解,有点小小的成就感,终于觉得平时所学的知识有了实用的价值,达到了理论与实际相结合的目的,不仅学到了不少知识,而且锻炼了自己的能力,使自己对以后的路有了更加清楚的认识,同时,对未来有了更多的信心。
最后,对给过我帮助的老师和所有同学再次表示忠心的感!
6、程序
LIBRARYIEEE;
USEIEEE.STD_LOGIC_1164.ALL;
USEIEEE.STD_LOGIC_ARITH.ALL;
USEIEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITYHHIS
PORT(
CLK_1K:
INSTD_LOGIC;-- 时钟输入1KHZ
CLK_40K:
INSTD_LOGIC; --时钟输入40KHZ
KEY_LIE:
INSTD_LOGIC_VECTOR(3DOWNTO0);--列输入
START:
OUTSTD_LOGIC;--二-十进制数据输出标志
KEY_HANG:
OUTSTD_LOGIC_VECTOR(3DOWNTO0);--行输出
DATA_P:
OUTSTD_LOGIC_VECTOR(7DOWNTO0);--二-十进制数输出
DISP_DATA:
OUTSTD_LOGIC_VECTOR(6DOWNTO0);--数码管显示译码输出
DISP_SEL:
OUTSTD_LOGIC_VECTOR(1downto0));--数码管显示扫描输出
END;
ARCHITECTURERTLOFHHIS
SIGNALINT:
STD_LOGIC;--列与非信号
SIGNALCLK_SEL:
STD_LOGIC;--键值控制1khz的时钟信号
SIGNALSTART_REG:
STD_LOGIC;--数据输出标志信号
SIGNALDISP_SEL_REG:
STD_LOGIC_VECTOR(1DOWNTO0);--数码管显示扫描信号
SIGNALDATA_L,DATA_H:
STD_LOGIC_VECTOR(3DOWNTO0);--二-十进制低位、高位信号
SIGNALDATA_TMP:
STD_LOGIC_VECTOR(3DOWNTO0);--二-十进制低位高位暂存信号
SIGNALKEY_HANG_TMP:
STD_LOGIC_VECTOR(3DOWNTO0);--行输出信号
SIGNALDISP_DATA_REG:
STD_LOGIC_VECTOR(3DOWNTO0);--二-十进制低位、高位暂存信号(数码管用)
SIGNALKEY_CODE:
STD_LOGIC_VECTOR(7DOWNTO0);--行列相并信号
SIGNALDATA_P_REG:
STD_LOGIC_VECTOR(7DOWNTO0);--二-十进制数信号
BEGIN
KEY_CODE<=KEY_HANG_TMP&KEY_LIE;--行、列相并
DATA_P<=DATA_P_REG;
START<=START_REG;
KEY_HANG<=KEY_HANG_TMP;
DISP_SEL<=DISP_SEL_REG;
CLK_SEL<=CLK_1KAND(NOTINT);--无键按下时有CLK-SEL时钟信号输出
PROCESS(CLK_SEL,CLK_40K,INT)
VARIABLESTATE:
INTEGERRANGE0TO3;
BEGIN
IFRISING_EDGE(CLK_40K)THEN--一个40K的脉冲上升沿到来输入一次列状态以判断是否有按键按下
INT<=NOT(KEY_LIE(3)ANDKEY_LIE
(2)ANDKEY_LIE
(1)ANDKEY_LIE(0));
ENDIF;
IFRISING_EDGE(CLK_SEL)THEN--一个1K的脉冲的上升沿到来输出一个带表行的状态
CASESTATEIS--变量表示状态机,4循环
WHEN0=>
KEY_HANG_TMP<="1110";
STATE:
=1;
WHEN1=>
KEY_HANG_TMP<="1101";
STATE:
=2;
WHEN2=>
KEY_HANG_TMP<="1011";
STATE:
=3;
WHEN3=>
KEY_HANG_TMP<="0111";
STATE:
=0;
ENDCASE;
ENDIF;
ENDPROCESS;
PROCESS(CLK_40K,INT)--进程是并行的
VARIABLESTATE:
INTEGERRANGE0TO3;
VARIABLECOUNTER:
INTEGERRANGE0TO31;
BEGIN
IFINT='0'THEN
STATE:
=0;
COUNTER:
=0;
ELSIFRISING_EDGE(CLK_40K)THEN
CASESTATEIS
WHEN0=>
DATA_TMP<=DATA_L;--低四位放入暂存信号
STATE:
=1;
WHEN1=>--再嵌套一个CASE语句
CASEKEY_CODEIS--实现把像并数据译码十六进制的1到F
WHEN"01110111"=>
DATA_L<="0001";--把1放入低四位
DATA_H<=DATA_TMP;--把暂存信号的容放入高四位,相相当于向左移位
STATE:
=2;--跳出嵌套CASE语句,转向外CASE语句的状态2
WHEN"01111011"=>
DATA_L<="0010";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"01111101"=>
DATA_L<="0011";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"01111110"=>
DATA_L<="0100";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"10110111"=>
DATA_L<="0101";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"10111011"=>
DATA_L<="0110";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"10111101"=>
DATA_L<="0111";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"10111110"=>
DATA_L<="1000";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"11010111"=>--9键
DATA_L<="1001";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"11011011"=>--0键
DATA_L<="0000";
DATA_H<=DATA_TMP;
STATE:
=2;
WHEN"11011101"=>--A键,实现步进加一功能
IFDATA_H="1001"THEN
IFDATA_L="1001"THEN
DATA_H<="1001";
DATA_L<="1001";
ELSEDATA_L<=DATA_L+1;
ENDIF;
ELSIFDATA_L="1001"THEN
DATA_L<="0000";
DATA_H<=DATA_H+1;
ELSE
DATA_L<=DATA_L+1;
DATA_H<=DATA_H;
ENDIF;
STATE:
=2;
WHEN"11011110"=>--B键,实现步键加十
IFDATA_H="1001"THEN
DATA_H<="1001";
ELSE
DATA_L<=DATA_L;
DATA_H<=DATA_H+1;
ENDIF;
STATE:
=2;
WHEN"11100111"=>--C键,实现步进减一
IFDATA_L="0000"ANDDATA_H="0000"THEN
DATA_L<="0000";
DATA_H<="0000";
ELSIFDATA_L="0000"THEN
DATA_L<="1001";
DATA_H<=DATA_H-1;
ELSE
DATA_L<=DATA_L-1;
DATA_H<=DATA_H;
ENDIF;
STATE:
=2;
WHEN"11101011"=>--D键,实现步进减十
IFDATA_H="0000"THEN
DATA_H<="0000";
ELSE
DATA_L<=DATA_L;
DATA_H<=DATA_H-1;
ENDIF;
STATE:
=2;
WHEN"11101110"=>--E键,实现送数功能
DATA_L<=DATA_L;
DATA_H<=DATA_H;
DATA_P_REG<=DATA_H&DATA_L;
START_REG<='1';--数据输出标志信号置一
STATE:
=2;
WHEN"11101101"=>--F键,实现清零功能
DATA_L<="0000";
DATA_H<="0000";
STATE:
=2;
WHENOTHERS=>--不可缺少
STATE:
=2;
ENDCASE;
WHEN2=>--状态2实现去抖动功能
IFCOUNTER=31THEN--计数延时去抖
COUNTER:
=0;
STATE:
=3;
ELSE
COUNTER:
=COUNTER+1;
STATE:
=2;
ENDIF;
WHEN3=>--清除数据输出标志
START_REG<='0';
STATE:
=3;
ENDCASE;
ENDIF;
ENDPROCESS;
PROCESS(CLK_1K,DATA_L,DATA_H)--数码管循环显示
VARIABLESTATE:
INTEGERRANGE0TO1;
BEGIN
IFRISING_EDGE(CLK_1K)THEN
CASESTATEIS
WHEN0=>
DISP_SEL_REG<="10";--输入10到数码管显示扫描信号
DISP_DATA_REG<=DATA_L;--二-十进制低高位暂存节点
STATE:
=1;
WHEN1=>
DISP_SEL_REG<="01";
DISP_DATA_REG<=DATA_H;
STATE:
=0;
ENDCASE;
ENDIF;
ENDPROCESS;
PROCESS(CLK_1K,DISP_DATA_REG)--数码管译码
BEGIN
IFRISING_EDGE(CLK_1K)THEN
CASEDISP_DATA_REGIS
WHEN"0000"=>
DISP_DATA<="1111110";--0
WHEN"0001"=>
DISP_DATA<="0110000";--1
WHEN"0010"=>
DISP_DATA<="1101101";
WHEN"0011"=>
DISP_DATA<="1111001";
WHEN"0100"=>
DISP_DATA<="0110011";
WHEN"0101"=>
DISP_DATA<="1011011";
WHEN"0110"=>
DISP_DATA<="1011111";
WHEN"0111"=>
DISP_DATA<="1110000";
WHEN"1000"=>
DISP_DATA<="1111111";
WHEN"1001"=>
DISP_DATA<="1111011";
WHENOTHERS=>
DISP_DATA<="0000000";
ENDCASE;
ENDIF;
ENDPROCESS;
END;