VHDL键盘扫描.docx
《VHDL键盘扫描.docx》由会员分享,可在线阅读,更多相关《VHDL键盘扫描.docx(48页珍藏版)》请在冰点文库上搜索。
VHDL键盘扫描
VHDL键盘扫描
一、设计目的
巩固和运用所学课程,理论联系实际,提高分析、解决计算机技术实际问题的独立工作能力,通过对FPGA几个简单的模块设计,进一步加深对计算机原理以及数字电路应用技术方面的了解与认识,进一步熟悉数字电路系统设计、制作与调试的方法和步骤。
二、设计内容
(1)设计一个4×4的键盘扫描模块,要求有防抖动功能。
(2)用键盘控制16×16点阵LED显示各种字符。
(3)用键盘控制蜂鸣器发声,能有各种音调。
三、实现方法
本系统的系统组成及连接原理如图3.1所示,主系统由四个功能模块组成:
分频模块(为键盘扫描模块和提供时钟)、键盘扫描模块、LED点阵模块、发声模块。
时钟输入
接蜂鸣器
接LED点阵
接键盘
图3.1整体模块划分
1、分频模块
分频模块的作用是把2MHZ的时钟分成1kHZ和500HZ,为键盘扫描模块和LED点阵模块提供时钟。
主要方法是通过计数器实现分频。
分频模块如图3.2,clk是输入时钟,clkout_kb是输入给键盘扫描模块使用,clkout_LED是输入给LED点阵模块使用。
图3.2分频模块视图
其仿真结果如图3.3
图3.3分频模块仿真
2、键盘扫描模块
2.1行列式键盘原理
行列式键盘又叫做矩阵式键盘。
用带有I/O口的线组成行列结构,按键设置在行列的交点上。
本次试验用到的是4×4的行列结构键盘,可以构成16个按键。
这样,当按键数量平方增长是,I/O口只是线性增长,这样就可以节省I/O口。
行列式键盘的原理如图3.4所示。
keyin上拉电阻
keydrv
图3.4行列式键盘的原理
按键设置在行列线交叉点,行、列线分别连接到按键开关的两端。
列线通过上拉电阻接+5V的电压,即列线的输出被嵌位在高电平状态。
判断按键中有无键按下时通过行线输入扫描信号,然后从列线读取到状态实现的。
其方法是依次给行线送低电平,检查列线的输入。
如果列线信号全为高电平,则代表低电平所在的行中无按键按下;如果列线有输入为低电平,则低电平信号所在的行和出现低电平的列的交点处有按键按下。
设行扫描信号为keydrv3~keydrv0,列线按键输入信号keyin3~keyin0与按键位置的关系如表3.1所示
表3.1行扫描信号、列线按键输入信号与按键位置的关系
keydrv3~keydrv0
keyin3~keyin0
对应的按键
1110
1110
0
1101
1
1011
2
0111
3
1101
1110
4
1101
5
1011
6
0111
7
1011
1110
8
1101
9
1011
A
0111
B
0111
1110
C
1101
D
1011
E
0111
F
2.2键盘扫描系统模块划分
图3.5键盘扫描系统模块划分
键盘扫描电路时用于产生keydrv3-keydrv0信号,其变化顺序为1110→1101→1011→0111……周而复始的扫描。
其停留在每个状态的时间大约有10ms。
扫描电路的模型如下:
图3.6扫描电路外观接口
扫描电路的仿真如图3.7所示:
图3.7扫描电路仿真
键盘译码电路是从keydrv3~keydrv0和keyin3~keyin0译出按键值的电路,它的真值表如表1所示。
键盘译码电路的模型如图3.8所示:
图3.8键盘译码电路外观
其中键值keycode用5位编码,keycode为00000~01111表示键值0~F,keycode为11111时表示无按键按下。
译码电路的仿真如图3.9所示:
图3.9译码电路仿真
图3.9仿真的是扫描信号在keydrv=1011时有按键按下,并且keyin=0111时的键值,由表1可知这时的键值因该是B,而由图中可以看出仿真结果是5’h0B,即预测结果与仿真结果一致。
2.3按键去抖动电路实现
由于机械触点的弹性作用,触点在闭合和断开瞬间的电接触情况不稳定,造成了电压信号的抖动现象,如图3.10所示。
键的抖动时间一般为5~10ms。
这种现象会引起FPGA对于一次键操作进行多次处理,因此须设法消除键接通或断时的抖动现象。
去抖动的方法有硬件和软件两种:
硬件去抖动和软件去抖动。
图3.10按键抖动现象
去抖动模块采用多次连续检测输入键值的原理防抖。
当检测到有键值输入时并不马上输出,而是当16次输入的键值都一样时才输出,其中只要有一次键值不同的话计数器都会置零,重新记数。
这样就能消除按键的抖动影响。
其模块外观如图3.11所示。
图3.11去抖动模块外观
按键去抖动仿真:
图3.12按键去抖动仿真
由仿真图形可以看出,输入keyvaluein开始和结束时都产生抖动,但经过去抖模块后抖动消失了。
起到了去抖动的功能。
3、16×16LED点阵显示屏的设计
3.116x16LED点阵的工作原理及连接组成方式:
16x16LED点阵的工作原理同8x8LED点阵的工作原理,图3.13是8x8的LED点阵单色行共阳模块的内部结构图,其单点工作电压Uf为1.8V,正向电流IF为8~10mA。
当某一行线为高电平而某一列线为低时,其行列交叉的点就被点亮;而当其某一列线为高时,其行列交叉的点为暗;当某一行线为低电平时,无论列线如何,对应这一行的点全部为暗。
图3.13:
8x8的LED点阵单色行共阳模块的内部结构图
本实验采用的16x16LED点阵用四个8x8点阵显示构成,其连接方法如图3.14所示。
图中,将(A)和(B)的8列、(C)和(D)的8列分别对应相连,同时将(A)和(C)的8行、(B)和(D)的8行分别对应相连。
即可形成一个16行(每一行有16个LED)、16列(每一列也有16个LED)的16x16点阵显示器,可将这256个点称为一页,这样,显示字符时。
只要对一页中对应的亮灭进行控制即可。
图3.14:
四个8x8点阵显示构成的16x16LED点阵
3.216x16LED点阵的驱动方式
本实验采用动态扫描型驱动方式显示,即显示屏上的16行发光二极管共用一组列驱动寄存器,然后通过行驱动管的分时工作,来使每行LED的点亮时间占总时间的1/16。
只要每行的刷新速率大于50Hz,利用人眼的视觉暂留效应,人们就可以看到一幅完整的文字或画面。
扫描驱动分行扫描驱动和列扫描驱动。
由于16x16点阵显示器有16行,故使用行扫描信号,本电路中加入了一个4-16线译码器,其输入是一个频率为1000Hz的时钟信号,内部采用4位二进制加法器,分别为0000~1111,然后使每种状态只控制一路输出,解码输出为低态扫描信号,即会有16路输出。
本实验的列信号由事先存于寄存器中的16进制码构成,每个矩阵表示一个字符的列扫描信号,伴随行扫描信号进行交替的扫描,其设计图见附录中16x16LED点阵扫描设计草图。
本实验所显示的字符由键盘编码所确定,其编码与显示字符对应的关系如表3.2所示:
表3.2:
键盘编码与对应显示字符
键盘编码
00000
00001
00010
00011
显示字符
0
1
2
3
键盘编码
00100
00101
00110
00111
显示字符
4
5
6
7
键盘编码
01000
01001
01010
01011
显示字符
8
9
A
B
键盘编码
01100
01101
01110
01111
显示字符
C
D
E
F
键盘编码
11111
显示字符
音符图形
3.316x16LED点阵驱动电路
16x16LED点阵驱动外观电路如图3.15所示:
图3.1516x16LED点阵驱动电路外观
16x16LED点阵驱动电路原理图如图3.16所示:
图3.16:
16x16LED点阵驱动电路原理图
驱动行信号仿真如图3.17所示:
图3.17驱动行信号
4、发声模块
4.1音调的产生
音调的产生是通过分频实现的。
设计要求产生不同的音调,就需要不同的分频器。
可以先构造一个分频器,然后通过VHDL中的参数传递语句构造出十二个不同的分频器,其分频数见表3.2(输入时钟是2MHz)。
表3.3音符频率对照表
键值
音符
频率
分频数
键值
音符
频率
分频数
0
低5
392
5102.0408
7
中5
784
2551.0204
1
低6
440
4545.4545
8
中6
880
2272.7273
2
低7
494
4048.583
9
中7
988
2024.2915
3
中1
523
3824.0918
A
高1
1046
1912.0459
4
中2
578
3460.2076
B
高2
1175
1702.1277
5
中3
659
3034.9014
C
高3
1318
1517.4507
6
中4
698
2865.3295
D
高4
1397
1431.6392
4.2电路实现
发声模块其内部是一个有一个输入,十二个输出的分频器,用于产生十二种音调。
然后根据键值判断哪个口输出。
其顶层视图如图3.18所示。
图3.18发声模块外观
发声模块的仿真见图3.19
图3.19发声模块仿真
仿真波形中l5~h4是分频器的十二个输出口,当键值是C时speaker输入的是h3的值,达到了要求。
5、顶层模块实现
顶层电路只是把分频模块、键盘扫描模块、LED点阵模块和发声模块连接起来,其外观和内部结构如图3.20和3.21所示。
图3.20顶层模块外观
图3.21顶层模块内部结构
6、管脚的定义
管脚的定义如表3.4所示
表3.4管脚的定义
I/OName
I/ODirection
Loc
Bank
clk
Input
P3
BANK7
Speaker
Output
P95
BANK4
keydrv<0>
Output
P120
BANK3
keydrv<1>
Output
P121
BANK3
keydrv<2>
Output
P122
BANK3
keydrv<3>
Output
P123
BANK3
keyin<0>
Input
P125
BANK3
keyin<1>
Input
P127
BANK3
keyin<2>
Input
P129
BANK3
keyin<3>
Input
P132
BANK2
LEDcolumn<0>
Output
P133
BANK2
LEDcolumn<1>
Output
P134
BANK2
LEDcolumn<2>
Output
P136
BANK2
LEDcolumn<3>
Output
P138
BANK2
LEDcolumn<4>
Output
P139
BANK2
LEDcolumn<5>
Output
P140
BANK2
LEDcolumn<6>
Output
P141
BANK2
LEDcolumn<7>
Output
P147
BANK2
LEDcolumn<8>
Output
P148
BANK2
LEDcolumn<9>
Output
P149
BANK2
LEDcolumn<10>
Output
P150
BANK2
LEDcolumn<11>
Output
P151
BANK2
LEDcolumn<12>
Output
P152
BANK2
LEDcolumn<13>
Output
P162
BANK1
LEDcolumn<14>
Output
P163
BANK1
LEDcolumn<15>
Output
P164
BANK1
LEDrow<0>
Output
P165
BANK1
LEDrow<1>
Output
P167
BANK1
LEDrow<2>
Output
P73
BANK5
LEDrow<3>
Output
P74
BANK5
LEDrow<4>
Output
P70
BANK5
LEDrow<5>
Output
P71
BANK5
LEDrow<6>
Output
P68
BANK5
LEDrow<7>
Output
P69
BANK5
LEDrow<8>
Output
P63
BANK5
LEDrow<9>
Output
P67
BANK5
LEDrow<10>
Output
P61
BANK5
LEDrow<11>
Output
P62
BANK5
LEDrow<12>
Output
P59
BANK5
LEDrow<13>
Output
P60
BANK5
LEDrow<14>
Output
P57
BANK5
LEDrow<15>
Output
P58
BANK5
附录Ⅰ程序代码
1、顶层模块
libraryIEEE;
useIEEE.STD_LOGIC_1164.ALL;
useIEEE.STD_LOGIC_ARITH.ALL;
useIEEE.STD_LOGIC_UNSIGNED.ALL;
----Uncommentthefollowinglibrarydeclarationifinstantiating
----anyXilinxprimitivesinthiscode.
--libraryUNISIM;
--useUNISIM.VComponents.all;
entityplay_showis
Port(clk:
inSTD_LOGIC;
keyin:
inSTD_LOGIC_VECTOR(3downto0);
keydrv:
outSTD_LOGIC_VECTOR(3downto0);
LEDrow:
outSTD_LOGIC_VECTOR(15downto0);--LED行扫描信号
LEDcolumn:
outSTD_LOGIC_VECTOR(15downto0);
Displayshow:
outSTD_LOGIC_VECTOR(6downto0);
Displayselect:
outSTD_LOGIC_VECTOR(3downto0);
speaker:
outSTD_LOGIC);
endplay_show;
architecturertlofplay_showis
componentfreq_division
Port(clk:
inSTD_LOGIC;
clkout_kb:
outSTD_LOGIC;
clkout_LED:
outSTD_LOGIC;
clkout_display:
outstd_logic);
endcomponent;
componentkeyboard
Port(clk:
inSTD_LOGIC;--扫描时钟,周期10ms
keyin:
inSTD_LOGIC_VECTOR(3downto0);
keydrv:
outSTD_LOGIC_VECTOR(3downto0);
keyvalue:
outSTD_LOGIC_VECTOR(4downto0));--输出扫描信号
endcomponent;
componentLEDArray
Port(clk:
inSTD_LOGIC;--时钟信号,经分灯鞯玫1ms脉冲
keycode:
inSTD_LOGIC_VECTOR(4downto0);--键盘给的编码
LEDrow:
outSTD_LOGIC_VECTOR(15downto0);--LED行扫描信号
LEDcolumn:
outSTD_LOGIC_VECTOR(15downto0)--LED列信号
);
endcomponent;
componentplay
port(clk:
inSTD_LOGIC;
keyvalue:
inSTD_LOGIC_VECTOR(4downto0);
speaker:
outSTD_LOGIC);
endcomponent;
componentIBUF
port(I:
instd_logic;O:
outstd_logic);
endcomponent;
componentDisplay
Port(Keyvalue:
inSTD_LOGIC_VECTOR(4downto0);
clk:
inSTD_LOGIC;
Display:
outSTD_LOGIC_VECTOR(6downto0);
Diselect:
outSTD_LOGIC_VECTOR(3downto0));
endcomponent;
--signalclkin:
STD_LOGIC;
signalclkout_kb,clkout_LED,clkout_display:
STD_LOGIC;
--signalkeydrv1:
STD_LOGIC_VECTOR(3downto0);
signalkeyvalue1:
STD_LOGIC_VECTOR(4downto0);
signalCLK_SIG:
std_logic;
begin
--keydrv<=keydrv1;
U1:
IBUFportmap(I=>clk,O=>CLK_SIG);
U2freq_division:
freq_division
portmap(CLK_SIG,clkout_kb,clkout_LED,clkout_display);
U3keyboard:
keyboard
portmap(clkout_kb,keyin,keydrv,keyvalue1);
U4LEDArray1616:
LEDArray
portmap(clkout_LED,keyvalue1,LEDrow,LEDcolumn);
U5Uplay:
play
portmap(CLK_SIG,keyvalue1,speaker);
U6Display:
Display
portmap(keyvalue1,clkout_display,Displayshow,Displayselect);
endrtl;
2、分频器
entityfredivnis
generic(N:
integer:
=2);
Port(clkin:
inSTD_LOGIC;
clkout:
outSTD_LOGIC);
endfredivn;
architectureBehavioraloffredivnis
signalclk1:
std_logic:
='0';
signalcounter:
integerrange0ton;
begin
process(clkin)
begin
ifrising_edge(clkin)then
ifcounter=(n-1)/2then
clk1<=notclk1;
counter<=0;
else
counter<=counter+1;
endif;
endif;
endprocess;
clkout<=clk1;
endBehavioral;
3、总分频器
entityfreq_divisionis
Port(clk:
inSTD_LOGIC;
clkout_kb:
outSTD_LOGIC;
clkout_LED:
outSTD_LOGIC;
clkout_display:
outSTD_LOGIC);
endfreq_division;
architectureRTLoffreq_divisionis
componentfredivnis
generic(N:
positive);
Port(clkin:
inSTD_LOGIC;
clkout:
outSTD_LOGIC);
endcomponent;
begin
U1:
fredivn
genericmap(N=>kb_N)
portmap(clk,clkout_kb);
U2:
fredivn
genericmap(N=>LED_N)
portmap(clk,clkout_LED);
U3:
fredivn
genericmap(N=>display_N)
portmap(clk,clkout_display);
endRTL;
4、键盘扫描驱动
entitykeyscanis
Port(clkin:
inSTD_LOGIC;--扫描时钟,周期20ms
keydrv:
outSTD_LOGIC_VECTOR(3downto0));--输出扫描信号
endkeyscan;
architecturebehavioralofkeyscanis
signalcount:
std_logic_vector(1downto0):
="00";--计数器信号
begin
process(clkin)
begin
ifrising_edge(clkin)then
ifcount="11"then
count<="00";
else
count<=count+1;
endif;
endif;
endprocess;
process(count)
begin
casecountis
when"00"=>keydrv<="1110";
when"01"=>keydrv<="1101";
when"10"=>keydrv<="1011";
when"11"=>keydrv<="0111";
whenothers=>keydrv<="0000";
endcase;
endprocess;
endbehavioral;
5、键盘编码
entitykeydecoderis
Port(clkin:
instd_logic;
keyin:
inSTD_LOGIC_VECTOR(3downto0);
keydrv:
inSTD_LOGIC_VECTOR(3downto0);