1、1FPGA 控制的 PS/2 接口电路设计摘要:PS2 作为键盘接口至今仍在广泛使用。本论文根据 PS2 键盘接口的协议,用 VHD语言编程,用 FPGA 控制 PS2,使其能够接收从键盘键入的信息,并把它显示在数码管和液晶显示频上。The Design of PS / 2 Interface Circuit by Control of FPGAAbstract:Today,PS2 keyboard interface is still being used widely.This report is based PS2 keyboard interface protocol, with VH
2、D language programming, using FPGA control PS2, so that it can receive information from the keyboard to type, and display it in the digital frequency of the tube and liquid crystal display.关键词:VHDL,FPGA , PS2, QuartusII引言:PS2 是一类应用广泛的键盘接口。本文论述了用 FPGA 控制 PS2 的设计思想及方法,并实现把按键的扫描码及其对应的 ASCII 码显示在 LED 及
3、LCD 上。设计原理通过分析,本次设计需要分四部分实现:1.接收从键盘发出的扫描码信息 2.把接收到的扫描码转换成 ASCII 码 3.把扫描码和 ASCII 码显示在数码管上 4.把扫描码和 ASCII 码显示在 LED 液晶上。1.PS2 键盘接口协议介绍PS2 协议和接口最初是由 IBM 公司开发并广泛使用的应用于键盘/ 鼠标的协议和接口。PS2 键盘接口是 6 芯的插头,各插头定义如下:1 DATA Key Data2 n/c Not connected3 GND Gnd4 VCC Power , +5 VDC5 CLK Clock6 n/c Not connected在 PS2 的
4、6 个芯中,2 和 6 未定义,3 和 4 为电源和地,需要用 FPGA 控制的只有 1(数据)和 5(时钟)。2.把从键盘发出的数据转换成扫描码2每当有按键按下或弹起时键盘都会发出一帧或几帧数据,每一帧数据一共都 12 位,其数据格式如下表所示:1 个起始位 总是逻辑 08 个数据位 (LSB)低位在前1 个奇偶校验位 奇校验1 个停止位 总是逻辑 11 个应答位 仅用在主机对设备的通讯中从键盘发出的数据是在时钟信号的下降沿的时候才被读取,FPGA 要想接收到键盘发出的数据就要配合 PS2 的时序。具体设计思路如下:首先我们来定义几个端口。首先要有数据输入端口 K_DATA 和键盘时钟K_C
5、LOCK。K_DATA 是从键盘发出的一位一位串行数据,我们要在K_CLOCK 的下降沿到来时接收这 12 位串行数据,并最终输出我们想要的中间1 至 8 位数据。这 8 位输出我们用 DATASCAN 来表示。另外再对整个系统设置一个复位信号 RST。为了方便接收数据,我们有必要定义一个计数器 COUNT,使它对K_CLOCK 键盘时钟计数,COUNT 的值在 0 到 11 之间变化,COUNT 等于 11时清零,其余自加 1。另外再定义一个接收键盘数据的中间变量 TMPDATA,每当检测到 K_CLOCK 下降沿到来时,就把 DATA 的值赋给 TMPDATA,这样,当 K_CLOCK 的
6、 12 个下降沿到来时,TMPDATA 就接收到了键盘发出的全部12 位数据。在程序中,我们还要定义一个串并转换结束的标志信号 FLAG,当 COUNT大于 8 时就可以把 FLAG 置为 1,当检测到 FLAG 变为 1 时,就把 TMPDATA的 1 至 8 位赋给输出端口 DATA,这样,我们就得到了想要的 8 位扫描码输出。为了和下一步的扫描码转换成 ASCII 码建立联系,我们还要定义一个触发中断信号 INTERRUPT1,当 COUNT 等于 11 的时候把 INTERRUPT1 置 1 触发中断,作为扫描码转换为 ASCII 码的使能信号。这部分电路的仿真结果如下(以输出 1C
7、为例):3.把扫描码转换成 ASCII 码把扫描码转换成 ASCII 码的目的是为了识别按键。现在,我们已经有了从键盘发出的扫描码信息,那么,如何根据这些扫描码信息来区分按下的是哪一个键呢?实际上,键盘接口中的微处理器负责扫描或监视按键的动作,如果发现有按键按下,按住或者释放,键盘将会发送扫描码的信息包。扫描码有通码和断3码两种,当一个按键按下或按住,键盘就向接口发送一个字节的键盘接通的扫描码,称为通码。当该按键弹起或释放时,键盘向接口发送两个字节的键盘断开的扫描码,称为断码。断码和通码有一定的联系,多数第二套断码有两字节长。它们的第一个字节是 F0 ,第二个字节是这个键的通码。扩展按键的断码
8、通常有三个字节,它们前两个字节是 E0h,F0h ,最后一个字节是这个按键通码的最后一个字节。键盘每个按键被分配了唯一的通码和断码。这样,我们通过查找唯一的通码和断码就可以确定按下的是哪一个键。每个键一整套的通断码组成了扫描码集。有三套标准的扫描码集,所有现代的键盘默认使用第二套扫描码。有了这个基础,我们就可以设计程序把扫描码转换成 ASCII 码了。这部分的核心是查表。需要注意的是当 Shift 键和 CapsLock 键按下时的不同结果。我们同样先来定义几个端口。用 scan 作为扫描码的输入端口,CLK 作为时钟信号,它与上一个扫描码输出电路的触发中断信号 INTERRUPT1 相连接。
9、定义DATAASCII 为最终的 ASCII 码输出。另外再定义一个 ASCII 码转换结束的中断信号 INTERRUPT2。整个转换的思路是,由接收过来的扫描码的值,通过查表判断这个扫描码对应的是哪一个按键,再在 ASCII 表中查找这个按键的 ASCII 值,然后把按键的 ASCII 值输出。这里面牵扯到组合键的问题。即 Shift 和 Capslock 这两个键有没有按下。为了判断组合键有没有按下,我们设计几个触发器来保存上一个时钟的特殊组合键的状态,以便后来的 ASCII 码转换。当检测到有这两个按键按下时,就把相应的 Shifted_D 或 Capslocked_D 置为 1,然后再
10、通过 D 触发器把这两个键的状态锁存起来。锁存后为 Shifted 和 Capslocked。判断这两个键按下的依据是判断扫描码的值,若扫描码的值对应的是这两个按键,就可以把相应的键的状态置 1。在转换时我们先不管组合键有没有按下,先根据扫描码的值通过查表找到它的 ASCII 值,最后再判断组合键有没有按下。具体操作是,当接收到扫描码时,先判断扫描码的值是否为 Shift 或 Capslock 的扫描码值,如果是的话就表示有组合键按下了,这时把 Shifted_D 或 Capslocked_D 置 1,否则置 0.转换成的 ASCII 码用 TMPASCII 表示。当这些工作完成以后,再判断
11、Shifted 或Capslocked 的值是否为 1,若这两个值都为 1 或 0,则应为小写,这时就需要把前得到的 TMPASCII 的值加上十六进制的“20”。当然,这些大小写转换是针对ASCII 值在“A”和“a”之间而言的。另外,有些键上面有上下两排字符,当Shift 键按下时用到的是上面一排,这个应该注意。解决这个问题的方法很简单,由于在前面我们已经把 Shift 键前一个状态锁存起来,在进行扫描码到ASCII 码转换的时候,若遇到有两排字符的键时,先判断 Shifted 是否为 1,若为 1 则转换上面字符的 ASCII 值就行了。最后我们把转换得到的值赋给一个信号 ASCII。若
12、没有按键按下或扫描码对应的不是键盘的 126 个键,则把 ASCII赋成“10000000”。在转换中我们还应该考虑一个问题。比如我们要显示小写的 a 这个字母,当按下 a 再弹起时我们接收到的一串扫描码应该为 1C,F0,1C。再比如我们要显示大写的字母 A,应该是先按 Shift 键,再按 A 键,然后弹起 A 键,再弹起Shift 键。那么我们接收到的扫描码就该为:12,1C,F0,1C,F0,12。我们接收到这么些扫描码,而我们要输出的 ASCII 码应该为 61(A 为小写时)和 41(A 为大写时)。如果我们都一一转换的话会出错。注意到如果我们在“F0”扫描码4到来时才进行转换就可
13、以解决问题了。因此,在转换前先判断扫描码是不是为F0,若是的话就代表断码到来了,这时把定义好的 DUANMA_D 置 1,否则置 0,再把它锁入锁存器,输出为 DUANMA,在进行 ASCII 码转换时,我们都把 DUANMA等于 1 作为转换的一个条件,这样就可以得到正确的 ASCII 码值了。接下来的问题是,我们应该在什么时候把转换成的 ASCII 码输出呢?在前面,我们把没有转换的 ASCII 信号赋为“10000000”,所以,每当 CLK 下降沿到来时,我们去检测 ASCII 的后 7 位是否为“0000000”,若不是则说明已经转换结束了,这时我们给程序一个转换结束标志 FLAG,
14、当检测到 FLAG 上升沿到来时,把 ASCII 的值传递给最终的 ASCII 码输出端口 DATAASCII,同时触发中断 INTERRUPT2。这部分电路的仿真结果如下(以输出大写和小写字母 A 的 ASSCII 码值为例):4.把扫描码及 ASCII 码值显示在数码管上经过前两部分的设计分析,我们已经得到了按键的扫描码值和 ASCII 码值。我们要用动态扫描的方法把这两个值显示在数码管上。显示这两个键值需要点亮 4 个数码管。实现动态扫描的原理就是使这 4 个数码管依次点亮,由于依次点亮的时间间隔很短,而人都有一定时间的视觉暂留,所以就会看到 4 个数码管同时点亮。为了依次点亮 4 个数
15、码管,我们定义一个计数器 CNT,CNT 在“00”到“11”之间变化。CNT 每变化依次,我们把相应的段选信号输出到对应的数码管,那么,当 CNT 连续变化很快时,我们就可以看到四个数码管点亮了。接下来的问题是,段选信号如何获得?容易想到,段选信号就是扫描码的高四位、低四位和 ASCII 码的高四位、低四位所对应的数字的段选信号。所以我们要把扫描码和 ASCII 码的高四位、低四位分别求出来。而扫描码和 ASCII码的值我们提前是不知道的,所以我们要建立一个对照表,我们先把扫描码和ASCII 码的高四位和低四位放在四个变量中,然后分别判断这四位对应的是 0到 F 之间的哪一个数,然后求出这个
16、数对应的段选信号,并把它放在信号中,等到相应的 CNT 到来时,把这个段选信号输出到数码管就行了。5.把扫描码及 ASCII 码显示在 LCD 液晶屏上在这里要用到 LCD1602。LCD1602 为一个 16 位,两行的液晶显示频,我们要想把键值显示在 LCD 上,只要按照它的协议来做就行了。LCD 接收数据有它对应的时钟,这个我们可以通过分频产生。当时钟到来时,我们要决定是送指令还是数据,是读还是写,还要送入相应的指令或数据信息。根据要求,我们要在上面一行显示“Scancode:*” ,第二行显示:5“ASCIIcode:*” 。在显示这些字符之前,必须先做以下准备:1.开显示 2.使指针
17、自加 1 3.清屏 4.设置显示模式(这里用 5*7 显示模式)。做完这几步工作才可以传递数据。首先给 LCD 一个指令,让指针指到第一行的第一个点阵(当然,如果不想从第一个点阵开始显示,也可以把指针指到其它地方)。在下一个时钟到来时,就要依次把“Scancode : ”这几个字符的 ASCII 值赋给数据端口,接下来就该显示扫描码值了。显示扫描码的值比较麻烦,因为扫描码是提前未知的,所以我们还得先解码。做这部分的工作和 LED 显示部分一样,也是要提前先建立一个对照表,求出扫描码和 ASCII 码的高四位和低四位对应的是 0 到 F 之间的哪一个数据,不同的是不再把数据对应的段选信号赋给对应
18、的变量了,而是把数据所对应的 ASCII 码值赋给变量。因为 LCD 要接收 ASCII 码值然后显示 ASCII值对应的字符。有了这部分工作,当轮到显示扫描码时,就要把前面得到的扫描码高、低四位对应数据的 ASCII 码送到数据端口就行了。扫描码显示完后,就该显示 ASCII 码了,这时指针不能自加 1 了,而应该跳到下一行显示。所以应该把下一行第一个点阵的地址赋给数据端口,然后再依次把“ASCIIcode:”这几个字符的 ASCII 码值赋给数据端口,然后再把ASCII 码高、低四位对应数据的 ASCII 码值送到数据端口,这样就可以显示出ASCII 码的数据了。6.设计顶层文件当前面四个
19、模块都设计完成以后,就可以设计顶层文件了,也就是把相应的模块连接起来。在这里我们用端口映射的方法。具体链接在这里就不再赘述了。顶层文件设计完成后,整体编译一遍,然后进行 RTL 级仿真,便得到如下所示的硬件连接电路:结束语本次的 PS2 键盘接口设计,总的来说,实现了 4 大功能: 1,用 FPGA 控制PS2 键盘接口,接收键盘发出的扫描码数据 2.把接收到的扫描码转换成 ASCII码 3.把扫描码和 ASCII 码显示在数码管上 4.把扫描码和 ASCII 码显示在 LCD液晶上。这四个功能的设计,基本上涵盖了平时讲课的大部分内容。对我们来说这是一次很好的锻炼机会,也使我们对实际系统的设计
20、掌握了一定的经验,提升了我们对这门课的理解,同时培养了我们编程和写论文的能力。6回想自己所设计的程序,里面还有很多瑕疵,比如显示扫描码时部分,每显示一个键需要接收至少 3 个字节(按下时键盘发出一个字节的通码,弹起时发送两个字节的断码)的数据,比如显示大写字母 A 的一串扫描码为12,1C ,F0,1C,F0,12。这 5 个扫描码将会分别显示在数码管和液晶上,而其实我们更希望只显示 1C。想改进的话可以在扫描码转换为 ASCII 码部分,把最后得到的 ASCII 码再反过来求出按键的扫描码,这样又要建立一个大的对照表,但目前也只能想到这个方法,而且比较麻烦,所以不再设计。另外,根据所设计的程
21、序的功能,只有当按键弹起时才能把按键的信息显示出来,而实际上我们希望当按住键不放时也能显示键值信息。更多需要改进的地方这里就不再列举了。总之,这次的设计真的使我受益匪浅。参考文献1 侯伯亨,刘凯,顾新. VHD L硬件描述语言与数字逻辑电路设计【M】.西安:西安电子 科技大学出版社,2 0092 刘勇 , 王 玉晶, 曲斌, 于洋.1种嵌入式 PC 非标准键盘的设计【M】. 微计 算机信息, 2007,23-1 : 8788。3 苗新法,王秀华.PS2键盘在嵌入式系统中的应用研究【M】.兰州:兰州交通大学学报(自然科学版), 2007(1).4 徐晓 , 汪道辉 .标准PS2键盘与单片机的接口
22、设计【J】.中国测试技术, 2005(1).5 王勇.电子设计自动化实验指导书【M】.河南科技大学:电子信息工程学院,2009.电路设计源程序:-接收键盘发出的扫描码library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;use ieee.std_logic_arith.all;entity keyboard isport(k_data:in std_logic;k_clock:in std_logic;rst:in std_logic;datascan:out std_logic_vector(7 do
23、wnto 0);interrupt1:out std_logic);end keyboard;architecture one of keyboard issignal tmpdata:std_logic_vector(11 downto 0):=“000000000000“;signal flag:std_logic;signal count:integer range 0 to 11;beginprocess(rst,k_clock)beginif(rst=1)then count16#40#) and (tmpAsciiaaaaaaaaaaaaaaaanull;end case;case
24、 data_scan_l iswhen “0000“ =bbbbbbbbbbbbbbbbnull;end case;case data_ascii_h iswhen “0000“ =ccccccccccccccccnull;end case;case data_ascii_l iswhen “0000“ =ddddddddddddddddnull;end case;end process;process(enabl,clk)beginif(rst=1)then cntsgsgsgsgnull;end case;end if;end process;end;-把扫描码和ASCII码显示在LED液
25、晶上library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;use ieee.std_logic_arith.all;entity lcddisp isport(rst:in std_logic;clk:in std_logic;enabl:in std_logic;data_scan:in std_logic_vector(7 downto 0);data_ascii:in std_logic_vector(7 downto 0);data:out std_logic_vector(7 downto 0
26、);rs,rw,en:out std_logic);end lcddisp;architecture one of lcddisp issignal tmp:integer range 0 to 25000000;signal cp50:std_logic;signal data_scan_h:std_logic_vector(3 downto 0);signal data_scan_l:std_logic_vector(3 downto 0);signal data_ascii_h:std_logic_vector(3 downto 0);signal data_ascii_l:std_lo
27、gic_vector(3 downto 0);signal cnt_disp:integer range 0 to 50;signal cnt_init:integer range 0 to 5;signal a,b,c,d:std_logic_vector(7 downto 0);beginprocess(rst,enabl)beginif(rst=0)thenif(enabl=1)thendata_scan_haaaaaaaaaaaaaaaanull;end case;case data_scan_l iswhen “0000“ =bbbbbbbbbbbbbbbbnull;end case
28、;case data_ascii_h iswhen “0000“ =ccccccccccccccccnull;end case;case data_ascii_l iswhen “0000“ =ddddddddddddddddnull;end case;end process;process(clk)beginif (clkevent and clk=1) thenif tmp=499999 thentmp=0;cp50=not cp50;else tmp=tmp+1;end if;end if;17end process;process(cp50)beginif cp50event and
29、cp50=1 thenif cnt_disp=28 thencnt_disp=0;else cnt_disp=cnt_disp+1;end if;end if;if cp50event and cp50=1 thenif cnt_init=3 thencnt_init=0;else cnt_init=cnt_init+1;end if;end if;end process;process(enabl,cnt_disp,cnt_init)beginif (enabl=1) thenif cnt_init=0 thenif cp50=0 thenrs=0;rw=0;data=x“0e“;end i
30、f;elsif cnt_init=1 thenif cp50=0 thenrs=0;rw=0;data=x“06“;end if;elsif cnt_init=2 thenif cp50=0 thenrs=0;rw=0;data=x“01“;end if;elsif cnt_init=3 thenif cp50=0 thenrs=0;rw=0;data=x“38“;end if;end if;elseif cnt_disp=0 thenif cp50=0 thenrs=0;rw=0;data=x“80“;end if;18elsif cnt_disp=1 thenif cp50=0 thenr
31、s=1;rw=0;data=x“53“;-Send if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“63“;-cend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“61“;-aend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“6E“;-nend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“63“;-cend if;elsif cnt_d
32、isp=1 thenif cp50=0 thenrs=1;rw=0;data=x“6F“;-oend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“64“;-dend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“65“;-eend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“3A“;-:end if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=a
33、;end if;elsif cnt_disp=2 thenif cp50=0 thenrs=1;rw=0;data=b;end if;19elsif cnt_disp=17 thenif cp50=0 thenrs=0;rw=0;data=x“c0“;-line2end if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“41“;-Aend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“53“;-Send if;elsif cnt_disp=1 thenif cp50=0
34、thenrs=1;rw=0;data=x“43“;-Cend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“49“;-Iend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“49“;-Iend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“63“;-cend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“6F“;-oend if;elsif
35、cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“64“;-dend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“65“;-eend if;elsif cnt_disp=1 thenif cp50=0 thenrs=1;rw=0;data=x“3A“;-:20end if;elsif cnt_disp=18 thenif cp50=0 thenrs=1;rw=0;data=c;end if;elsif cnt_disp=19 thenif cp50=0 thenrs=1;rw=0;data=d;end if;end if;end if;end process;en=cp50;end;