logo资料库

基于FPGA的PS2接口设计.pdf

第1页 / 共22页
第2页 / 共22页
第3页 / 共22页
第4页 / 共22页
第5页 / 共22页
第6页 / 共22页
第7页 / 共22页
第8页 / 共22页
资料共22页,剩余部分请下载后查看
基于 FPGA 的 PS2 接口设计 1. 实验引言 在电子系统设计中,当需要大量的外部按键输入时,普通的独立按键和 矩阵键盘已经无法满足我们的需求,那么此时,我们就需要一种功能更加强 大的键盘,来帮助我们输入更多的信息。在 PC 机上我们常使用 104 键的键 盘,那么本实验我们就使用 FPGA 来解码驱动一个采用 PS2 接口的 PC 机键盘, 这个键盘可以用来扩展我们 FPGA 的输入系统,以使我们能够方便的输入更 多信息。 2. 实验目的 实现采用 PS2 协议的 PC 机键盘的解码,最终将 PS2 键盘作为 FPGA 系统 的一个准输入设备。 2.实验内容 本实验的内容是学习 PS2 键盘的解码,并将解码的结果通过数码管显示 出来,同时,为了能够让大家更加直观的感受到按键被按下和释放,我们在 这里增加了一个蜂鸣器,当有按键按下或释放(包括长按时每次接收到的通 码)时,蜂鸣器会发出短暂的响声,及时地提醒操作者 FPGA 解码到的按键信 息。 3. 实验原理 PS2 键盘的内部结构我们不需要过多的去关注,我们只需要关心其接口 协议,能够正确的解码其发送过来的按键信息即可,至于解码到的按键信息 可以作何用途,可谓是见仁见智,这里不做过多的介绍。PS2 协议的简单描述 如下: PS2 协议总共有两根线组成,一根时钟线和一根数据线,这里我们将采用 PS2 的键盘成为从机,将控制和解码 PS2 协议的一方称为主机,生活中最常 见的主机便是我们的 PC 机。PS2 总线协议的时钟线始终是由从机(即键盘) 产生的。PS2 协议发送一个字节数据共有 11 位,分别为: 1 Bit 起始位,总是 0 8 Bit 数据位,低位在前
1 Bit 校验位,奇校验 1 Bit 停止位,总是 1 PS2 从机发送一个完整数据包的时序如图 1 所示 图 1 PS2 时序 相信熟悉 UART 协议的同学一看便知,PS2 协议和 UART 协议非常相似,那 么我们的工作好像一下子就简单了,既然键盘按照这个时序发送数据,那么 FPGA 作为主机、作为数据接收方,只需要正确实现该协议的解码,将其中的 8 Bit 数 据位提取出来即可。由图 1 可知,数据在 PS2 时钟的下降沿处是保持稳定的,因 此我们只需要在检测到 PS2 时钟出现下降沿以后,马上去读取数据线上的电平, 就能够得到正确的数据。 通过以上介绍,我们只是了解了 PS2 从机到主机的通信协议,但是通信过程 中从机发送过来的每一个字节代表什么内容呢?我们还需要对照键盘编码表来 查看。 CLKDAT01D0D1D2D3D4D5D6D7P
表 1 键码对照表 键盘扫描码分为第一套扫描码、第二套扫描码和第三套扫描码,由于我们常 见的扫描码绝大多数采用第二套扫描码,因此这里我们就只附上第二套扫描码的 内容。
实际一个按键由按下到释放时键盘将按照如下的规定向主机发送数据: 只要一个按键被按下,这个键的通码(MAKE)就会被发送到主机。通码只 表示键盘上的一个按键,它不表示印刷在按键上的那个字符。这就意味着在通码 和 ASCII 码之间没有已定义好的关联,直到主机把扫描码翻译成一个字符或命令。 虽然多数第二套扫描码通码只有一个字节,但也有少数扩展按键的通码是两 字节或四字节,这类的通码第一个字节总是 E0H(如“END”、“HOME”……)。正如 按键被按下通码就被发往主机一样,只要按键一被释放,断码也会被发送。每个 键都有自己唯一的通码和断码,幸运的是,我们并不用总是通过查表来找出所有 按键的断码—在通码和断码之间存在着必然的联系,多数第二套断码有两个字节, 他们的第一个字节是 F0H,第二个字节则是这个键的通码。 例如,当我们按下键盘上的”A”键时,键盘就会向主机发送”A”键对应的通码 (MAKE)”1C”,如果一直按着这个按键不释放,那么在一个短暂的延时之后,键 盘会再次开始以一定的速率持续向主机发送”1C“,直到该按键被释放。在该按键被 释放后,键盘将会向主机发送”A“的断码(BREAK),即首先发送”F0“,然后下一个字 节再马上发送”1C“。 如果我们按下的是”END”、”PAGE UP“等扩展按键时,会怎么样呢?这里 以”END“键举例。当”END“键被按下后,键盘会首先向主机发送”E0”,然后发送”F0”, 最后再发送”69“。 由上述分析可知,我们 FPGA 在解码到一次数据后,还需要对这个数据进行 分析判断,判断该数据是否为断码标志”F0“以及扩展码标志”E0”。 4. 系统结构框图
系统端口及其意义如下: 图 2 系统架构框图 表 2 系统端口及其意义 端口说明 端口功能或意义 全局复位(低电平有效) 系统时钟输入 PS2 数据线 PS2 时钟线 数码管位选输出,接三八译码器 数码管段选输出,接数码管八个段 蜂鸣器驱动端口 端口名 Rst_n Clk PS2_Din PS2_Clk Dig_sel Dig_seg Beep PS2_Key_Board_DriverBeepDIG_LED_DRIVEPS2_DinPS2_Clk{14'd0,Key_Value[9:0]}Beep_EnKey_FlagClkRst_nClkRst_nClkRst_nBeepDig_led_sel[2:0]Dig_led_seg[7:0]Key_Disp
内部信号及其意义如下: 表 3 内部信号及其意义 内部信号说明 内部信号名 Data Key_Value Beep_En Key_Flag 5. 代码解释 内部信号功能或意义 数码管待显示数据,共 24 位,每四位 组成一个 BCD 码,对应一个数码管需 要显示的数据内容。这里高 14 位接 0, 低 10 位接到了 Key_Value 上,以显示 Key_Value 的值 按键检测结果输出,共 10 位,其中最 高位为通/断码标志位,为 0 表示通码, 为 1 表示断码;次高位为短码和长码 (扩展码)标志位,为 0 表示短码,为 1 表示长码;低八位为数据位 蜂鸣器鸣叫使能信号,为高则使能一 次鸣叫,这里接到了 Key_Flag 上,因 此,只要成功解码到一次按键动作(按 下、释放、长按时的每一次发送数据), 都会使能一次蜂鸣器鸣叫 按键检测成功标志信号 这里解码的关键是 PS2 接口的时钟信号,该时钟为异步时钟,我们需要通过 边沿检测的方式来检测其下降沿,以便根据下降沿的个数来确定每个时钟我们应 该做什么。 程序清单 边沿检测电路 01 reg PS2_Clk_Tmp0,PS2_Clk_Tmp1,PS2_Clk_Tmp2,PS2_Clk_Tmp3; 02 03 wire nedge_PS2_Clk; /*PS2 从机时钟下降沿检测标志信号*/ 04 05 always @ (posedge Clk or negedge Rst_n)
06 if(!Rst_n) begin 07 PS2_Clk_Tmp0 <= 1'b0; 08 PS2_Clk_Tmp1 <= 1'b0; 09 PS2_Clk_Tmp2 <= 1'b0; 10 PS2_Clk_Tmp3 <= 1'b0; 11 end 12 else begin 13 PS2_Clk_Tmp0 <= PS2_Clk; 14 PS2_Clk_Tmp1 <= PS2_Clk_Tmp0; 15 PS2_Clk_Tmp2 <= PS2_Clk_Tmp1; 16 PS2_Clk_Tmp3 <= PS2_Clk_Tmp2; 17 end 18 19 /*-------获取 PS2 时钟信号的下降沿-------------*/ 20 assign nedge_PS2_Clk = !PS2_Clk_Tmp0 & !PS2_Clk_Tmp1 & PS2_Clk_Tmp2 & PS2_Clk_Tmp3; 一个 PS2 的数据包共有 11 位组成,因此会有 11 个时钟下降沿,因此我们必 须对下降沿的个数准确计数,才能保证我们能够解码得到正确的数据,这里,使 用我们的 PS2 时钟下降沿的标志信号来使能我们的计数器自加,当计数器加到 11 后,表示一个数据包接收完成,将计数器清零,等待下一个下降沿的到来,相 关代码如下: 程序清单 PS2 时钟下降沿计数器 01 /*------------PS2 时钟下降沿个数计数器-----------------------*/ 02 always @(posedge Clk or negedge Rst_n) 03 if(!Rst_n) 04 Cnt1 <= 4'd0; 05 else if(Cnt1 == 4'd11) 06 Cnt1 <= 4'd0; 07 else if(nedge_PS2_Clk) 08 Cnt1 <= Cnt1 + 1'b1; 接下来就是根据时钟下降沿的计数个数来读取对应数据位的数据了。因为采 用了非阻塞赋值的方式,因此,PS2 时钟下降沿到来时,cnt1 会执行自加 1 操作, 但同时如果马上用 cnt1 的值来确定数据位数,就一定会造成错误,因为此时 cnt1 加 1 的操作并没有马上执行,而是会在下一个系统时钟上升沿到来之时才会改 变。因此,为了保证我们所使用的 cnt1 的数据已经是最新的,我们需要在 cnt1
的值变化稳定以后才能使用。即在检测到 PS2 时钟下降沿之后,滞后一个系统时 钟周期再来读取 PS2_Din 上的值,比较简单的操作方式就是将 PS2 时钟下降沿检 测标志信号再用寄存器打一拍,对应代码如下: always @(posedge Clk) nedge_PS2_Clk_Shift <= nedge_PS2_Clk; 程序清单 寄存器延时 接下来就是根据 cnt1 的计数值来读取每一位的数据了,这部分代码很简单, 如下所示: 程序清单 PS2 数据存取 01 /*--------------读取 8 位数据位---------------*/ 02 always @ (posedge Clk or negedge Rst_n) 03 if(!Rst_n) 04 Data_tmp <= 8'd0; 05 else if(nedge_PS2_Clk_Shift) begin 06 case(Cnt1) 07 4'd2:Data_tmp[0] <= PS2_Din; 08 4'd3:Data_tmp[1] <= PS2_Din; 09 4'd4:Data_tmp[2] <= PS2_Din; 10 4'd5:Data_tmp[3] <= PS2_Din; 11 4'd6:Data_tmp[4] <= PS2_Din; 12 4'd7:Data_tmp[5] <= PS2_Din; 13 4'd8:Data_tmp[6] <= PS2_Din; 14 4'd9:Data_tmp[7] <= PS2_Din; 15 default:Data_tmp <= Data_tmp; 16 endcase 17 end 18 else 19 Data_tmp <= Data_tmp; 通过以上操作,我们就能正确的解码 PS2 键盘发送过来的每一个字节的数据 了,但是,这些数据代表什么呢?如果是断码标志,或者是长码标志,我们又该 如何操作呢?这里,我们先附代码如下: 01 always @ (posedge Clk or negedge Rst_n) 程序清单 PS2 键值解码 02 if(!Rst_n) begin 03 Break_r <= 1'b0;
分享到:
收藏