矩阵键盘键信号检测电路设计-EDA课程设计说明书
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
课程设计说明书
题目EDA技术与应用系(部)
专业(班级)
姓名
学号
指导教师
起止日期
EDA技术课程设计任务书系(部):专业:指导教师:
目录
引言 (5)
一、绪论 (5)
1.1 FPGA概况 (5)
1.2 此课题的研究意义 (6)
二、矩阵键盘接口电路的原理与总体设计 (6)
2.1 矩阵键盘接口电路的原理 (6)
2.2 总体设计 (8)
三、各模块的设计及仿真 (8)
3.1 键盘扫描电路 (8)
3.2 键盘译码电路和按键标志位产生电路 (11)
3.3 时钟产生模块 (16)
3.4 键盘接口电路顶层电路实现 (18)
四、硬件测试 (19)
五、实验设备 (19)
六、总结 (20)
参考文献 (20)
矩阵键盘键信号检测电路设计
引言
人类文明已进入到高度发达的信息化社会。
信息化社会的发展离不开电子信息产品开发技术、产品品质的提高和进步。
电子信息产品随着科学技术的进步,其电子器件和设计方法更新换代的速度日新月异。
实现这种进步的主要原因就是电子设计技术和电子制造技术的发展,其核心就是电子设计自动化(EDA,Electronic Design Automation)技术,EDA技术的发展和推广应用又极大地推动了电子信息产业的发展。
为保证电子系统设计的速度和质量,适应“第一时间推出产品”的设计要求,EDA技术正逐渐成为不可缺少的一项先进技术和重要工具。
目前,在国内电子技术教学和产业界的技术推广中已形成“EDA热”,完全可以说,掌握EDA技术是电子信息类专业学生、工程技术人员所必备的基本能力和技能。
此设计主要利用VHDL硬件描述语言在EDA平台Quartus II上设计一个4×4阵列键盘扫描电路,将行扫描信号输入阵列键盘,读取列信号的值,输出按键编码,从而判断出按键按下的位置。
并且进行模拟仿真,下载到EDA实验箱进行硬件验证。
一、绪论
1.1 FPGA概况
早期的可编程逻辑器件只有可编程只读存储器(PROM)、紫外线可擦除只读存储器(EPROM)和电可擦除只读存储器(E2PROM)三种。
由于结构的限制,它们只能完成简单的数字逻辑功能。
其后出现了一类结构上稍复杂的可编程芯片,即可编程逻辑器件(PLD),它能够完成各种数字逻辑功能。
典型的PLD由一个“与”门和一个“或”门阵列组成,而任意一个组合逻辑都可以用“与—或”表达式来描述,所以PLD能以乘积和的形式完成大量的组合逻辑功能。
这一阶段的产品主要有PAL(可编程阵列逻辑)和GAL(通用阵列逻辑)。
PAL由一个可编程的“与”平面和一个固定的“或”平面构成,或门的输出可以通过触发器有选择地被置为寄存状态。
PAL器件是现场可编程的,它的实现工艺有反熔丝技术、EPROM技术和E2PROM技术。
还有一类结构更为灵活的逻辑器件是可编程逻辑阵列(PLA),它也由一个“与”平面和一个“或”平面构成,但是这两个平面的连接关系是可编程的。
PLA器件既有现场可编程的,也有掩膜可编程的。
在PAL的基础上又发展了一种通用阵列逻辑(GAL、Generic ArrayLogic),如GAL16V8、GAL22V10等。
它采用了EPROM工艺,实现了电可擦除、电可改写,其输出结构是可编程的逻辑宏单元,因而它的设计具有很强的灵活性,至今仍有许多人使用。
这些早期的PLD器件的一个共同特点是可以实现速度特性较好的逻辑功能,但其过于简单的结构也使它们只能实现规模较小的电路。
为了弥补这一缺陷,20世纪80年代中期,Altera和Xilinx分别推出了类似于PAL结构的扩展型
CPLD(Complex Programmable Logic Dvice)和与标准门阵列类似的FPGA(FieldProgrammable Gate Array),它们都具有体系结构和逻辑单元灵活、集成度高以及适用范围宽等特点。
这两种器件兼容了PLD和通用门阵列的优点,可实现较大规模的电路,编程也很灵活。
与门阵列等其他ASIC(Application Specific IC)相比,它们又具有设计开发周期短、设计制造成本低、开发工具先进、标准产品不需测试、质量稳定以及可实时在线检验等优点,因此被广泛应用于产品的原型设计和产品生产(一般在10 000件以下)之中。
几乎所有应用门阵列、PLD和中小规模通用数字集成电路的场合均可应用FPGA和CPLD 器件。
1.2 此课题的研究意义
近年来EDA技术在电子领域引发的技术革命,推动着电子技术的迅猛发展,为世人所瞩目,而FPGA 为代表的可编程逻辑器件的应用,更是受到业内人士的普遍关注。
伴随着大规模集成电路和计算机技术的高速发展,在设计工业自动化,仪器仪表,计算机设计与应用、通信、国防等领域的电子系统中,FPGA 技术的含量正以惊人的速度提升。
将尽可能大的完整的电子系统在单一FPGA芯片中实现已成为现实,电子类新技术项目的开发也更多地依赖于FPGA技术的应用。
作为FPGA研究课题之一的矩阵键盘控制接口电路的设计,在FPGA设计中是一个经常被提到的话题,就像是利用PFGA设计数字中一样,虽然简单,但是却是一个很有研究意义的话题,涉及到怎么样才能是FPGA资源更加充分利用,现在很多电子产品都涉及到按键,小的有独立按键,大的有N*N的矩阵键盘,独立按键由于案件的个数少,也就没必要考虑资源的利用问题了。
而矩阵键盘,由于按键多,对整个系统的影响大,所以肯定要考虑资源的利用问题,而且还要考虑一下电路里面的时序问题。
本次设计要求设计一个4*4矩阵键盘,也就是行为4,列为4,一共可以设计16个按键。
其中设计方法为:一般判断键盘中有没有按键按下是通过行线送入扫描信号,然后从列线中读取状态得到的,其方法是依次给行线送入低电平,检查列线的输入。
如果列线信号高电平,则代表低电平信号所在的行中无按键按下,反之,则有,则在低电平信号所在的行和出现低电平的交叉处有按键按下。
二、矩阵键盘接口电路的原理与总体设计
2.1 矩阵键盘接口电路的原理
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式。
在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。
这样,一个端口就可以构成4*4=16个按键,比之直接将端口线用于键盘多出了一倍,而且线数越多,区别越明显,比如再多加一条线就可以构成20键的键盘,而直接用端口线则只能多出一键(9键)。
由此可见,在需要的键数比较多时,采用矩阵法来做键盘是合理的。
矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,列线通过电阻接正电源,并将行线所接的FPGA的I/O口作为输出端,而列线所接的I/O口则作为输入。
这样,当按键没有按下时,所有的输出端都是高电平,代表无键按下。
行线输出是低电平,一旦有键按下,则输入线就会被拉低,这样,通过读入输入线的状态就可得知是否有键按下了。
行列式键盘的电路原理如图2.1.1所示:
+5
v
+5V
图2.1.1 行列式键盘的电路原理图
设置扫描信号为keydrv3~keydrv0,列线按键输入信号keyin3~keyin0与按键位置的关系如表2.1.1所示:
2.2总体设计
本次设计在EDA开发平台QUARTUSⅡ9.0上利用VHDL语言设计矩阵键盘控制接口电路。
由行列式键盘原理就可以知道,要正确的完成键盘输入工作必须有按键扫描电路产生keydrv3~keydrv0信号,同时还必须有按键译码电路从keydrv3~keydrv0信号和keyin3~keyin0信号中译码出按键的值。
此外,一般还需要一个按键发生信号用于和其他模块接口,通知其他模块键盘上有按键动作发生,并可以从键盘上读取按键的键值。
由于各个模块需要的时钟频率是不一样的,因此时钟产生模块就是用于产生各个模块需要的时钟信号。
此课题的实验一共有三个模块,分别为:扫描电路模块、时钟产生模块、键盘译码电路和按键标志位产生电路。
扫描模块中是为了产生扫描信号,来利用扫描信号来扫描键盘中中是否有按键按下。
键盘译码电路和按键标志位产生电路是为了配合扫描模块来扫描电路中是否有按键按下,而且还要求它来产生按键标志信号,以便和外部电路握手。
时钟产生电路是为了产生不同频率的信号,来驱动上面两个电路的运转。
三、各模块的设计及仿真
3.1 键盘扫描电路
键盘扫描电路是用于产生keydrv3~keydrv0信号,其变化的顺序依次是1110---1101---1011---0111---……周而复始地扫描。
其停留在某个状态的时间大约为10ms。
更短的停留时间是没有必要的,因为人按键的时间大约为10ms,不可能有更快的按键动作发生;另外,更短的停留时间还容易采集到抖动信号,会干扰判断,而太长的停留时间则会使某些较快的按键东走丢失。
键盘扫描电路的外部接口电路如图3.1.1所示,其中clk_scan是周期为10ms的扫描时钟,keydrv 为输出到键盘的扫描信号,宽度为4位。
图3.1.1 键盘扫描电路的外部接口电路图
其VHDL描述如下:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY key_scan IS
-- {{ALTERA_IO_BEGIN}} DO NOT REMOVE THIS LINE!
PORT
(
clk_scan : IN STD_LOGIC; --扫描时钟,周期10ms
keydrv : OUT STD_LOGIC_VECTOR(3 DOWNTO 0) --输出扫描信号
);
-- {{ALTERA_IO_END}} DO NOT REMOVE THIS LINE!
END key_scan;
-- Architecture Body
ARCHITECTURE key_scan_architecture OF key_scan IS
CONSTANT s0 :STD_LOGIC_VECTOR (3 DOWNTO 0):="1110" ;
--定义状态机编码
CONSTANT s1 :STD_LOGIC_VECTOR (3 DOWNTO 0):="1101" ;
CONSTANT s2 :STD_LOGIC_VECTOR (3 DOWNTO 0):="1011" ;
CONSTANT s3 :STD_LOGIC_VECTOR (3 DOWNTO 0):="0111" ;
SIGNAL present_state:STD_LOGIC_VECTOR(3 DOWNTO 0);
--状态机现态
SIGNAL next_state:STD_LOGIC_VECTOR(3 DOWNTO 0);
--状态机次态
BEGIN
--状态更新进程
PROCESS(clk_scan)
BEGIN
IF(clk_scan'event and clk_scan='1') then
present_state<=next_state;
END IF;
END PROCESS;
--状态译码
PROCESS (present_state)
BEGIN
CASE present_state IS
WHEN s0=>next_state<=s1;
WHEN s1=>next_state<=s2;
WHEN s2=>next_state<=s3;
WHEN s3=>next_state<=s0;
--多余状态处理
WHEN OTHERS=>next_state<=s0;
END CASE;
END PROCESS;
--输出译码
keydrv<=present_state;
END key_scan_architecture;
以上程序采用一个状态机来实现扫描电路。
该状态机是一个one-hot状态机,并且输出值就是状态机的状态,没有通过一个逻辑电路来做输出译码。
这样的好处是得到的输出信号比较“干净”,没有毛刺。
其仿真波形如图3.1.2所示:
图3.1.2 键盘扫描电路的仿真图
从图4.1.2中很容易发现present_state的值的变化是随着扫描信号key_scan的上升沿的到来而变化的,也就是key_scan每来一个脉冲,相应的present_state的值就变化一次。
很容易发现keydrv 的值的变化顺序为1110---1101---1011---0111,也就是每个key_scan来一个脉冲时,保证keydrv相邻的值只有一个变化,这样为了防止产生不必要的毛刺。
present_state值和keydrv值是相同的,只不过一个用的是二进制,一个用的是十进制,所以它的变化为14—13—11—7。
3.2 键盘译码电路和按键标志位产生电路
键盘译码电路是从keydrv3~keydrv0和keyin3~keyin0信号中译码出按键的键值的电路,它的真值表就是以前行扫描信号、列扫描与按键位置的关系图。
按键标志位产生电路是产生按键标志位信号keypressed的电路。
由于这两个电路关系紧密,因此放入同一个模块中实现,其外部接口图如图3.2.1所示。
其中clk为局信号,它是由FPGA芯片的外部晶振给出的。
clk在系统中的频率是最高,其他时钟都是它的分频产生。
keydrv为键盘扫描信号,keyin为键盘输入信号,keyvalue为键值(代表按键所在的位置),keypressed表示有一个按键被按下,每发生一次按键动作,keypressed就输出一个宽度为全局时钟周期的正脉冲。
该信号用于与其他模块握手,负责通知其他模块键盘是否有按键发生。
其他模块在keypressed有效时,可以读取键值。
图3.2.1 键盘译码电路的外部接口
其VHDL实现如下:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
ENTITY keydecoder IS
PORT(
clk : IN STD_LOGIC; --全局时钟
clk_scan : IN STD_LOGIC; --扫描时钟
keyin : IN STD_LOGIC_VECTOR(8 DOWNTO 0); --键盘输入
keydrv : IN STD_LOGIC_VECTOR(3 DOWNTO 0); --扫描信号
keyvalue : OUT STD_LOGIC_VECTOR(8 DOWNTO 0);--键值
keypressed : OUT STD_LOGIC --有按键按下
);
END keydecoder;
ARCHITECTURE keydecoder_architecture OF keydecoder IS
--TEMP <=keyin & keydrv;
SIGNAL temp:STD_LOGIC_VECTOR(12 DOWNTO 0) ;
SIGNAL temp_pressed: STD_LOGIC;
SIGNAL keypressed_asy: STD_LOGIC;
SIGNAL q1,q2,q3,q4,q5,q6 :STD_LOGIC;
BEGIN
temp<=keyin & keydrv;
--译码进程
PROCESS (temp)
BEGIN
case temp is
when "1111111101110"=>keyvalue<=conv_std_logic_vector(1,9);
temp_pressed <='1';
when "1111111101101"=>keyvalue<=conv_std_logic_vector(2,9);
temp_pressed <='1';
when "1111111101011"=>keyvalue<=conv_std_logic_vector(3,9);
temp_pressed <='1';
when "1111111100111"=>keyvalue<=conv_std_logic_vector(4,9);
temp_pressed <='1';
when "1111111011110"=>keyvalue<=conv_std_logic_vector(5,9);
temp_pressed <='1';
when "1111111011101"=>keyvalue<=conv_std_logic_vector(6,9);
temp_pressed <='1';
when "1111111011011"=>keyvalue<=conv_std_logic_vector(7,9);
temp_pressed <='1';
when "1111111010111"=>keyvalue<=conv_std_logic_vector(8,9);
temp_pressed <='1';
when "1111110111110"=>keyvalue<=conv_std_logic_vector(9,9);
temp_pressed <='1';
when "1111110111101"=>keyvalue<=conv_std_logic_vector(10,9);
when "1111110111011"=>keyvalue<=conv_std_logic_vector(11,9);
temp_pressed <='1';
when "1111110110111"=>keyvalue<=conv_std_logic_vector(12,9);
temp_pressed <='1';
when "1111101111110"=>keyvalue<=conv_std_logic_vector(13,9);
temp_pressed <='1';
when "1111101111101"=>keyvalue<=conv_std_logic_vector(14,9);
temp_pressed <='1';
when "1111101111011"=>keyvalue<=conv_std_logic_vector(15,9);
temp_pressed <='1';
when "1111101110111"=>keyvalue<=conv_std_logic_vector(16,9);
temp_pressed <='1';
when "1111011111110"=>keyvalue<=conv_std_logic_vector(17,9);
temp_pressed <='1';
when "1111011111101"=>keyvalue<=conv_std_logic_vector(18,9);
temp_pressed <='1';
when "1111011111011"=>keyvalue<=conv_std_logic_vector(19,9);
temp_pressed <='1';
when "1111011110111"=>keyvalue<=conv_std_logic_vector(20,9);
temp_pressed <='1';
when "1110111111110"=>keyvalue<=conv_std_logic_vector(21,9);
temp_pressed <='1';
when "1110111111101"=>keyvalue<=conv_std_logic_vector(22,9);
temp_pressed <='1';
when "1110111111011"=>keyvalue<=conv_std_logic_vector(23,9);
temp_pressed <='1';
when "1110111110111"=>keyvalue<=conv_std_logic_vector(24,9);
temp_pressed <='1';
when "1101111111110"=>keyvalue<=conv_std_logic_vector(25,9);
when "1101111111101"=>keyvalue<=conv_std_logic_vector(26,9);
temp_pressed <='1';
when "1101111111011"=>keyvalue<=conv_std_logic_vector(27,9);
temp_pressed <='1';
when "1101111110111"=>keyvalue<=conv_std_logic_vector(28,9);
temp_pressed <='1';
when "1011111111110"=>keyvalue<=conv_std_logic_vector(29,9);
temp_pressed <='1';
when "1011111111101"=>keyvalue<=conv_std_logic_vector(30,9);
temp_pressed <='1';
when "1011111111011"=>keyvalue<=conv_std_logic_vector(31,9);
temp_pressed <='1';
when "1011111110111"=>keyvalue<=conv_std_logic_vector(32,9);
temp_pressed <='1';
when "0111111111110"=>keyvalue<=conv_std_logic_vector(33,9);
temp_pressed <='1';
when "0111111111101"=>keyvalue<=conv_std_logic_vector(34,9);
temp_pressed <='1';
when "0111111111011"=>keyvalue<=conv_std_logic_vector(35,9);
temp_pressed <='1';
when "0111111110111"=>keyvalue<=conv_std_logic_vector(36,9);
temp_pressed <='1';
when others =>
temp_pressed<='0';
end case;
end process;
--按键标志产生电路
process (clk_scan)
begin
if (clk_scan'event and clk_scan='1') then
q1<=temp_pressed;
q2<=q1;
q3<=q2;
q4<=q1;
end if;
keypressed_asy<=q1 or q2 or q3 or q4 ;
end process;
--同步化keypressed_asy
process(clk)
begin
if(clk'event and clk='1') then
q5<=keypressed_asy;
q6<=q5;
end if;
keypressed<=q5 and not(q6) ;
end process;
END keydecoder_architecture;
上面程序中有两个进程。
第一个进程负责译码,值得注意的是WHEN OTHEN语句有没有对temp_pressed和keyvalue信号赋值,这相当于不改变temp_presed和keyvalue信号的值,即实现了锁存输出。
在不需要锁存输出时,在WHEN O-THEN语句中,一定要对所有的case语句中出现的信号逐一赋值,以免产生意想不到的结果。
第二个进程负责把按键同步信号同步化与全局时钟同步的并且脉宽为一个周期的脉冲。
重新编写按键发生标志电路,该电路不仅要解决按键抖动导致一次按键被当成多次的问题,同时还要解决按键太长导致一次按键被当成多次的问题。
考虑这些因素之后,编写的程序应该是阴影部分的程序。
该电路的仿真结果如图3.2.2所示:
图3.2.2 键盘译码电路仿真图
通过仿真波形图可以看出,按键的抖动不会影响输出结果;无论一个按键动作持续时间有多久,但仍然认为是一个按键。
3.3 时钟产生模块
时钟产生模块是用于扫描时钟的,它的输出供给给键盘扫描模块和按键标志位的产生模块,其外部接口图如图3.3.1所示:
图3.3.1 时钟产生模块外部接口电路
其VHDL实现如下:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_ARITH.all;
ENTITY clk_gen IS
PORT
(clk : IN STD_LOGIC; --全局时钟
clk_scan : OUT STD_LOGIC --扫描时钟
);
END clk_gen;
ARCHITECTURE clk_gen_architecture OF clk_gen IS
signal cnt :integer range 0 to 119999;
begin
--计数模块
process(clk)
begin
if(clk'event and clk ='1') then
if (cnt=cnt'high) then
cnt<=0;
else
cnt<=cnt+1;
end if;
end if;
end process;
--译码输出
process(cnt,clk)
begin
if(clk'event and clk='1') then
if cnt>=cnt'high/2 then
clk_scan<='1';
else
clk_scan<='0';
end if;
end if;
end process;
END clk_gen_architecture;
该程序主要包含有一个计数器模块和一个译码输出模块,该程序是通过计数器模块实现分频的。
要从12MHZ全局时钟得到100HZ的时钟,必须进行120000倍的分频。
这么大的分频需要仿真时间很久。
仿真时一般采用小分频来代替,(在本程序中采用150分频来仿真)以便快速观察到结果。
等到真正下
载到电路上时,就需要采用120000分频了。
该电路的仿真结果如图3.3.2所示:
图3.3.2 时钟产生模块的仿真图
3.4 键盘接口电路顶层电路实现
键盘接口电路的顶层电路只是把键盘扫描模块、键盘译码且按键标志位产生模块和时钟产生模块连接起来,其结构如图3.4.1所示:
图3.4.1 键盘接口电路的顶层电路
该电路的仿真波形如图3.4.2所示:
图3.4.2 键盘接口电路的顶层电路仿真图
四、硬件测试
五、实验设备
本次实验采用的是Create-SOPC实验平台,使用Cyclone系列EP1C20F324C8芯片
六、总结
矩阵式键盘是一种常见的输入装置,在日常的生活中,矩阵式键盘在计算机、电话、手机、微波炉等电子产品上已被广泛应用,各式各样的矩阵键盘控制着各种各样的功能。
矩阵键盘控制接口电路是最基本的输入电路,我们利用软件Quartus II自行设计并修改的键盘输入显示电路是通过VHDL语言的编写并实现了键码的对应显示,在设计中发现了许多问题,比如去按键抖动,这个过程很有意思但却很繁琐,在利用Quartus II进行仿真的时候,要细心地对参数进行设置,最后才能够仿真出结果。
本次设计中最为突出的是对键盘译码电路和按键标志位产生电路的设计,主要是去抖动,其中重要一点是按键去抖动电路不仅要解决按键抖动导致一次按键被当成多次的问题,同时还要解决按键太长导致一次按键被当成多次的问题。
否则,当设计的键盘在实际中应用的时候,就会出现错误,所以应该把之前的程序重新编写。
在新程序中:
1.多了一个输入信号clk_scan,它是用于产生扫描信号的时钟,周期为10ms。
2.按键信号tepm_pressed首先通过clk_scan信号的上升沿采样,通过采样后,抖动噪声被消除。
3.采样后得到信号被分别延迟1~4个clk_csan周期得到4个信号q1、q2、q3、q4。
这四个信号
进行或运算得到一个宽度约为80ms并且与全局时钟异步的按键信号keypressed_asy。
原来程序中一个长时间的按键过程有可能被认为是多次按键,通过这种方法使得一个长时间的按键仍然是一次按键。
4.把keypressed_asy同步化。
5.消除了按键抖动。
参考文献
[1] 潘松、黄继业,EDA技术与VHDL[M],北京:清华大学出版社,2005
[2] 谭京生,EDA技术及应用[M],西安:西安电子科技大学出版社,2001
[3] 徐志军,CPLD/FPGA的开发与应用[M],北京:电子工业出版社,2001
[4] 朱正伟,EDA技术与应用[M],北京:清华大学出版社,2005
[5] 潘松,VHDL实用教程[M],成都:电子科技大学出版社,2001
[6] 周祖成,电子设计硬件描述语言VHDL [M],北京:北京学苑出版社,2000
[7] 侯伯亭,VHDL硬件描述语言与数字逻辑电路设计[M],西安:西安电子科技大学出版社,2001。