【原创】FPGA开发手记(三) PS/2键盘
2013-09-01 12:07
169 查看
[align=center]以下内容均以Xilinx的Nexys3作为开发板[/align]
[align=center] [/align]
[align=center]1.PS/2键盘简介[/align]
虽然Nexys3开发板是利用USB接口搭载键盘,但是其原理与PS/2键盘完全相同,现在就仅以PS/2键盘为例讲解如何将键盘搭载在开发板上。代码程序均在Nexys3上经过测试。
PS/2标准键盘使用6个接口,各个接口定义如下:
[align=center] [/align]
[align=center]1:DATA,数据信号[/align]
[align=center]2:N.C.,不连接[/align]
[align=center]3:GND,地[/align]
[align=center]4:VCC,+5V电源[/align]
[align=center]5:CLK,时钟[/align]
[align=center]6:N.C.,不连接[/align]
而对于USB键盘,有用的接口只有两个CLK以及DATA,同时需要+5V供电。这点很重要,但是FPGA的CMOS最大供电是3.3V,如何解决这个问题笔者也是花费了一番周折,文章末会提到。
PS/2标准的数据帧格式与串口通讯很相似,USB就是串口通讯的格式。起始为低电平,停止位为高电平。如果数据位中1的个数是偶数,校验位就为1;如果数据位中1的个数是奇数,校验位就为0,通常自己使用我们都不管校验位。其实就是奇校验。
[align=center] 图1 数据帧格式[/align]
[align=center] [/align]
[align=center]2.键盘扫描码[/align]
[align=center] [/align]
当PC通过PS/2接口与从设备通信时,总是利用下降沿读取数据。键盘仅仅在数据线和时钟线都为高电平(即空闲态)时可以把数据传送给主机,一旦主机使用了总线,键盘在驱动总线前必须检查主机当前是否仍在发送数据。为了促进这一核查,时钟线将暂时作为发送完成信号。一旦主机把时钟线拉低,那么键盘将使时钟线变成高电平才可以传输数据。
PS/2型键盘主要使用扫描码来进行数据传输。每个按键被按下时都会被分配固定的扫描码,如果按键一直被按下,那么该按键码值将每隔100ms重复发送一次。当按键释放后,一个关键码F0发送后,该扫描码才会被发送。当使用上档键进行组合键时,也会产生上档键的码值,并且由主机决定到底是哪一个ASCII值。当组合键被释放后,一个E0和F0关键码发送后,按键扫描码才被发送。。大多数按键的扫描码如下图所示。
[align=center] 图2 键盘扫描码[/align]
[align=center] [/align]
主机也会向键盘发送数据。下面是主机可能发送的常用命令的清单。
ED 使NumLock,CapsLock和ScrollLock LED灯发光。在接收到ED后键盘反馈FA码,然后主机发送一个用来设置LED显示状态的字节:第0位控制ScrollLock置位,第1位控制NumLock置位,第2位控制CapsLock置位;
EE 用来进行键盘测试,当键盘接受EE后将反馈EE;
F3 设置扫描码重复频率,当键盘接受FA反馈F3后,主机将发送第二个字符用来设置重复频率。
FE 重新发送。FE将使键盘重新发送最近的扫描码。
FF 重置。重置键盘。
3.源程序简介
PS/2键盘模块接口定义:
[align=center] [/align]
[align=center]首先,还是利用移位寄存器的方式捕捉PS/2键盘时钟线的下降沿。[/align]
neg_ps2_clk即为PS/2时钟信号下降沿的有效信号。然后,利用一个寄存器和计数器,存储8位有效数据帧。
通过当前按键状态,判断通码还是断码,并保存在ps2_byte_r的8位寄存器内。
利用case判断不同的通码,来决定字符的ASCII。
以上就是程序代码的主要部分。最重要的还是管脚分配文件如何解决FPGA供电不足的问题。其实也很简单,只是我们平时不常用罢了。而且这也是官方标准ucf文件的一个错误。只需要将PS/2键盘的时钟线与数据线上拉即可,详细内容如下(只列举PS/2键盘部分):
NET "ps2_clk" LOC = L12 | IOSTANDARD = LVCMOS33 | PULLUP;
NET "ps2_data" LOC = J13 | IOSTANDARD = LVCMOS33 | PULLUP;
至此,笔者也只是将PS/2键盘搭载到了开发板上。但是如何更加灵活的利用键盘以及VGA,还需要仔细的设计各个寄存器的功能以及用法。
以下Demo内容需要连接VGA接口,按动键盘的A~Z的任意键,LED灯会显示对应的ASCII值。若按动A键,屏幕会显示字符a。(因为没有时间折腾字符提取软件,rom比较小只存了a的一个字符。喜欢的朋友也可以增加很多其他的字符。)若按动其他键,屏幕只显示一个白色矩形。
4.源程序
PS2键盘源程序.rar
以上内容均可以在百度网盘下载http://pan.baidu.com/share/link?shareid=940132753&uk=1092766566。
[align=center] [/align]
[align=center] [/align]
[align=center]1.PS/2键盘简介[/align]
虽然Nexys3开发板是利用USB接口搭载键盘,但是其原理与PS/2键盘完全相同,现在就仅以PS/2键盘为例讲解如何将键盘搭载在开发板上。代码程序均在Nexys3上经过测试。
PS/2标准键盘使用6个接口,各个接口定义如下:
[align=center] [/align]
[align=center]1:DATA,数据信号[/align]
[align=center]2:N.C.,不连接[/align]
[align=center]3:GND,地[/align]
[align=center]4:VCC,+5V电源[/align]
[align=center]5:CLK,时钟[/align]
[align=center]6:N.C.,不连接[/align]
而对于USB键盘,有用的接口只有两个CLK以及DATA,同时需要+5V供电。这点很重要,但是FPGA的CMOS最大供电是3.3V,如何解决这个问题笔者也是花费了一番周折,文章末会提到。
PS/2标准的数据帧格式与串口通讯很相似,USB就是串口通讯的格式。起始为低电平,停止位为高电平。如果数据位中1的个数是偶数,校验位就为1;如果数据位中1的个数是奇数,校验位就为0,通常自己使用我们都不管校验位。其实就是奇校验。
[align=center] 图1 数据帧格式[/align]
[align=center] [/align]
[align=center]2.键盘扫描码[/align]
[align=center] [/align]
当PC通过PS/2接口与从设备通信时,总是利用下降沿读取数据。键盘仅仅在数据线和时钟线都为高电平(即空闲态)时可以把数据传送给主机,一旦主机使用了总线,键盘在驱动总线前必须检查主机当前是否仍在发送数据。为了促进这一核查,时钟线将暂时作为发送完成信号。一旦主机把时钟线拉低,那么键盘将使时钟线变成高电平才可以传输数据。
PS/2型键盘主要使用扫描码来进行数据传输。每个按键被按下时都会被分配固定的扫描码,如果按键一直被按下,那么该按键码值将每隔100ms重复发送一次。当按键释放后,一个关键码F0发送后,该扫描码才会被发送。当使用上档键进行组合键时,也会产生上档键的码值,并且由主机决定到底是哪一个ASCII值。当组合键被释放后,一个E0和F0关键码发送后,按键扫描码才被发送。。大多数按键的扫描码如下图所示。
[align=center] 图2 键盘扫描码[/align]
[align=center] [/align]
主机也会向键盘发送数据。下面是主机可能发送的常用命令的清单。
ED 使NumLock,CapsLock和ScrollLock LED灯发光。在接收到ED后键盘反馈FA码,然后主机发送一个用来设置LED显示状态的字节:第0位控制ScrollLock置位,第1位控制NumLock置位,第2位控制CapsLock置位;
EE 用来进行键盘测试,当键盘接受EE后将反馈EE;
F3 设置扫描码重复频率,当键盘接受FA反馈F3后,主机将发送第二个字符用来设置重复频率。
FE 重新发送。FE将使键盘重新发送最近的扫描码。
FF 重置。重置键盘。
3.源程序简介
PS/2键盘模块接口定义:
[align=center] [/align]
[align=center]首先,还是利用移位寄存器的方式捕捉PS/2键盘时钟线的下降沿。[/align]
reg ps2_clk_r0,ps2_clk_r1,ps2_clk_r2; wire neg_ps2_clk; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin ps2_clk_r0 <= 1'b0; ps2_clk_r1 <= 1'b0; ps2_clk_r2 <= 1'b0; end else begin ps2_clk_r0 <= ps2_clk; ps2_clk_r1 <= ps2_clk_r0; ps2_clk_r2 <= ps2_clk_r1; end end // end always assign neg_ps2_clk = ps2_clk_r2 & (~ps2_clk_r1);
neg_ps2_clk即为PS/2时钟信号下降沿的有效信号。然后,利用一个寄存器和计数器,存储8位有效数据帧。
//------------------------------------------------------ // 接受来自PS/2键盘的数据存储器 reg [7:0] ps2_byte_r; // 来自PS/2的数据寄存器 reg [7:0] temp_data; // 当前接受数据寄存器 reg [3:0] num; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin num <= 4'd0; temp_data <= 8'd0; end else if (neg_ps2_clk) begin case (num) 4'd0: begin num <= num + 1'b1; end 4'd1: begin num <= num + 1'b1; temp_data[0] <= ps2_data; end 4'd2: begin num <= num + 1'b1; temp_data[1] <= ps2_data; end 4'd3: begin num <= num + 1'b1; temp_data[2] <= ps2_data; end 4'd4: begin num <= num + 1'b1; temp_data[3] <= ps2_data; end 4'd5: begin num <= num + 1'b1; temp_data[4] <= ps2_data; end 4'd6: begin num <= num + 1'b1; temp_data[5] <= ps2_data; end 4'd7: begin num <= num + 1'b1; temp_data[6] <= ps2_data; end 4'd8: begin num <= num + 1'b1; temp_data[7] <= ps2_data; end 4'd9: begin num <= num + 1'b1; end 4'd10: begin num <= 4'b0; end default: num <= 4'd0; endcase end end // end always
通过当前按键状态,判断通码还是断码,并保存在ps2_byte_r的8位寄存器内。
//------------------------------------- reg key_f0; // 松键标志位 reg ps2_state_r; // 当前状态,高电平表示有键按下 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin key_f0 <= 1'b0; ps2_state_r <= 1'b0; ps2_byte_r <= 8'd0; end else if (num == 4'd10) begin // 刚传输一个字节 if (temp_data == 8'hf0) begin // 断码的第一个字节 key_f0 <= 1'b1; end else begin if (key_f0) begin // 存储通码 ps2_state_r <= 1'b1; ps2_byte_r <= temp_data; key_f0 <= 1'b0; end else begin ps2_state_r <= 1'b0; key_f0 <= 1'b0; end end end else ps2_state_r <= 1'b0; end // end always
利用case判断不同的通码,来决定字符的ASCII。
//--------------------------------------- reg [7:0] ps2_ascii; //接受相应的asciiI always @(posedge clk) begin case (ps2_byte_r) 8'h15: ps2_ascii = 8'h51; //Q 8'h1d: ps2_ascii = 8'h57; //W 8'h24: ps2_ascii = 8'h45; //E 8'h2d: ps2_ascii = 8'h52; //R 8'h2c: ps2_ascii = 8'h54; //T 8'h35: ps2_ascii = 8'h59; //Y 8'h3c: ps2_ascii = 8'h55; //U 8'h43: ps2_ascii = 8'h49; //I 8'h44: ps2_ascii = 8'h4f; //O 8'h4d: ps2_ascii = 8'h50; //P 8'h1c: ps2_ascii = 8'h41; //A 8'h1b: ps2_ascii = 8'h53; //S 8'h23: ps2_ascii = 8'h44; //D 8'h2b: ps2_ascii = 8'h46; //F 8'h34: ps2_ascii = 8'h47; //G 8'h33: ps2_ascii = 8'h48; //H 8'h3b: ps2_ascii = 8'h4a; //J 8'h42: ps2_ascii = 8'h4b; //K 8'h4b: ps2_ascii = 8'h4c; //L 8'h1a: ps2_ascii = 8'h5a; //Z 8'h22: ps2_ascii = 8'h58; //X 8'h21: ps2_ascii = 8'h43; //C 8'h2a: ps2_ascii = 8'h56; //V 8'h32: ps2_ascii = 8'h42; //B 8'h31: ps2_ascii = 8'h4e; //N 8'h3a: ps2_ascii = 8'h4d; //M default ps2_ascii = 8'hfe; endcase end assign ps2_byte = ps2_ascii; assign ps2_state = ps2_state_r;
以上就是程序代码的主要部分。最重要的还是管脚分配文件如何解决FPGA供电不足的问题。其实也很简单,只是我们平时不常用罢了。而且这也是官方标准ucf文件的一个错误。只需要将PS/2键盘的时钟线与数据线上拉即可,详细内容如下(只列举PS/2键盘部分):
NET "ps2_clk" LOC = L12 | IOSTANDARD = LVCMOS33 | PULLUP;
NET "ps2_data" LOC = J13 | IOSTANDARD = LVCMOS33 | PULLUP;
至此,笔者也只是将PS/2键盘搭载到了开发板上。但是如何更加灵活的利用键盘以及VGA,还需要仔细的设计各个寄存器的功能以及用法。
以下Demo内容需要连接VGA接口,按动键盘的A~Z的任意键,LED灯会显示对应的ASCII值。若按动A键,屏幕会显示字符a。(因为没有时间折腾字符提取软件,rom比较小只存了a的一个字符。喜欢的朋友也可以增加很多其他的字符。)若按动其他键,屏幕只显示一个白色矩形。
4.源程序
PS2键盘源程序.rar
以上内容均可以在百度网盘下载http://pan.baidu.com/share/link?shareid=940132753&uk=1092766566。
[align=center] [/align]
相关文章推荐
- 【原创】FPGA开发手记(一) UART接口
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验九:PS/2模块③ — 键盘与多组合键
- 【原创】FPGA开发手记(二) VGA接口
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验八:PS/2模块② — 键盘与组合键
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验七:PS/2模块① — 键盘
- 【搬家】FPGA学习手记(一) FPGA入门及建立FPGA开发环境
- 原创:wicket开发手记(二)
- 原创:iReport&KasperReport开发手记(一)
- HoloLens开发手记 - Unity之Keyboard input 键盘输入
- 【原创】Crystal report 实现打印-网站开发手记
- FPGA 实现PS/2键盘控制LED
- FPGA学习手记(一) FPGA入门及建立FPGA开发环境
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十:PS/2模块④ — 普通鼠标
- [原创]Wicket 1.4 开发手记(一) helloworld与环境配置
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十一:PS/2模块⑤ — 扩展鼠标
- IOCP(完成端口)开发手记(3)
- 单片机系统中PS/2键盘驱动程序的设计
- 嵌入式开发中DSP与FPGA的关系
- M8系统开发手记(1)
- iPhone开发笔记 退回输入键盘:自定义颜色:隐藏状态栏: