ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA数字接口设计实战:基于Verilog的PS/2键盘鼠标协议解析与实现

FPGA数字接口设计实战:基于Verilog的PS/2键盘鼠标协议解析与实现 简介本资源是一套基于Xilinx FPGA平台实现PS/2键盘与鼠标控制器的Verilog源码工程面向FPGA初学者及数字系统设计学习者解决外设接口协议解析、时序控制与硬件中断生成等典型实践问题。压缩包共115个文件涵盖9个核心Verilog源文件.v、4个ISE/Vivado工程文件.prj、2个配置约束文件.ucf/.pcf、2个比特流文件.bit及多个综合、布局布线与日志文件.ngc/.ncd/.log等完整覆盖从代码编写、仿真、综合到FPGA下载验证的全流程。资源包大小仅1.5MB结构清晰含keyboard_test1_top等可直接上板运行的顶层模块已通过真实Xilinx开发板验证支持PS/2协议扫描、按键去抖、扫描码解析与LED状态反馈等功能。目前已有142人学习下载适合作为FPGA课程实验、毕业设计参考或嵌入式外设接口开发的入门范例。1. 项目概述一个FPGA验证过的键盘鼠标接口设计最近在整理硬盘里的老项目翻出来一个压箱底的宝贝——一个用Verilog写的、在Xilinx FPGA上验证通过的键盘和鼠标接口源码。这玩意儿是我当年刚入行FPGA时为了彻底搞懂PS/2协议硬着头皮从零开始撸出来的。现在回头看代码风格可能有点“稚嫩”但整个设计思路清晰模块划分明确最关键的是它真的能跑起来键盘按键和鼠标移动都能被FPGA正确识别。对于正在学习数字电路设计、Verilog语言特别是想亲手实现一个外设接口的FPGA新手来说这个项目是个非常不错的“麻雀”五脏俱全值得拆开细细研究。它不涉及复杂的算法核心就是状态机、时序逻辑和协议解析能把书本上那些枯燥的理论瞬间变成板子上闪烁的LED和串口打印出来的键值这种成就感是看多少遍教程都换不来的。2. 核心设计思路与协议解析2.1 为什么选择PS/2接口现在市面上USB一统天下为什么这个源码还是基于PS/2协议这里有几个很实际的考虑。首先协议简单。PS/2是典型的同步串行协议时钟线和数据线分开通信速率不高通常10-20kHz时序要求相对宽松非常适合作为FPGA初学者的第一个通信协议实践项目。你不需要处理USB那种复杂的包结构、CRC校验和复杂的枚举过程。其次便于调试。你可以用逻辑分析仪甚至FPGA上的ILA集成逻辑分析仪轻松抓取时钟和数据信号直观地看到每一位的传输过程对理解通信原理有巨大帮助。最后资源占用极低。整个接口逻辑可能只需要几十个Slice在任何一款入门级FPGA比如Spartan-6、Artix-7上都能轻松实现让你专注于逻辑设计本身而不是为资源不够而发愁。2.2 PS/2协议核心要点拆解PS/2协议的精髓可以概括为“设备产生时钟主机读取数据”。但作为主机端的FPGA我们需要模拟这个“读取”的过程。电气与连接键盘或鼠标通过一根6针的Mini-DIN接口与主机连接。我们真正关心的只有四根线VCC5V、GND、CLK时钟和DATA数据。在FPGA端CLK和DATA需要被设置为双向口inout但通常我们初始化为输入以监听设备发送的数据。这里有一个关键细节PS/2总线是“线与”的空闲时为高电平。设备通过将时钟线或数据线拉低来发起通信。数据帧格式每一帧数据包含11位。起始位总是为0低电平。8位数据位从最低位LSB开始发送。奇偶校验位奇校验。即数据位中“1”的个数加上校验位本身“1”的总数应为奇数。这是一个简单的错误检测机制。停止位总是为1高电平。应答位在设备到主机的通信中主机在收到数据后需要在第11个时钟周期将数据线拉低作为应答。但很多设计包括本例中FPGA作为主机只负责接收不发送应答设备也能正常工作。不过一个健壮的设计应该实现应答。时序是关键设备在CLK的下降沿高电平到低电平的变化改变DATA的数据。而主机我们的FPGA应该在CLK的上升沿低电平到高电平的变化采样DATA数据以确保数据稳定。这个“设备下降沿输出主机上升沿采样”的规则必须严格遵守。注意虽然协议规定设备驱动时钟但在实际接收时FPGA内部的逻辑必须用时钟的上升沿来触发采样。这个时钟是外部输入的所以你的采样逻辑本质上是一个对输入时钟敏感的同步逻辑。处理好这个外部异步时钟与FPGA内部全局时钟的关系是避免亚稳态问题的关键。3. 源码模块深度解析与实操要点拿到keyboard_mouse_interface.v这个源码文件我们不要急着看整体先把它拆成几个功能明确的模块来理解。一个结构清晰的设计通常包含以下部分3.1 顶层模块Top Module系统的指挥官顶层模块叫ps2_host它就像项目的总接线图。它的主要任务有三个端口定义声明与外部世界连接的所有信号。包括FPGA的全局时钟clk、复位rst_n、PS/2的时钟和数据线ps2_clk、ps2_data以及输出给其他模块使用的键盘扫描码key_code、鼠标数据包mouse_data和相应的数据有效标志key_valid、mouse_valid。模块实例化它内部会实例化两个核心子模块——ps2_keyboard和ps2_mouse分别处理键盘和鼠标的数据流。这里体现了模块化设计的思想功能分离各司其职。时钟域处理关键这是最容易出问题的地方。PS/2的时钟~20kHz和FPGA的系统时钟比如50MHz是不同源的。顶层模块需要负责将ps2_clk这个慢速的异步信号安全地引入到FPGA的时钟系统中。常见的做法是使用两级D触发器进行同步也就是所谓的“同步器”来大幅降低亚稳态传播的风险。代码里可能会有一个ps2_clk_sync的信号它就是经过同步化处理后的“干净”时钟供后续逻辑使用。3.2 键盘接口模块ps2_keyboard解码按键语言这个模块是解析键盘扫描码的核心。它的状态机是设计的灵魂。状态机设计通常是一个有限状态机FSM包含以下几个状态IDLE空闲状态等待时钟线被设备拉低起始位开始。START检测到起始位准备接收数据。DATA循环接收8个数据位。这里会用一个计数器bit_cnt来控制。PARITY接收奇偶校验位并进行校验计算。STOP接收停止位。如果一切正常将接收到的8位数据扫描码输出并拉高key_valid信号一个时钟周期。ERROR如果校验失败或帧格式错误进入错误状态丢弃本帧数据并等待重新开始。扫描码处理键盘的扫描码分为通码Make和断码Break。按下按键时发送通码释放时先发送0xF0再发送该键的通码。因此一个完整的按键事件需要解析多帧数据。这个模块需要包含一个小的缓存逻辑来组合判断是按下事件还是释放事件并输出一个统一的、易于上层应用的键值代码。消抖处理虽然PS/2协议层面已经比较稳定但按键的机械抖动可能仍然存在。一个更稳健的设计会在收到有效键值后进行一个短时间的延时例如10-20ms再确认或者使用简单的滤波逻辑避免一次按键被误识别为多次。3.3 鼠标接口模块ps2_mouse追踪光标移动鼠标协议比键盘稍复杂一点因为它默认工作在流模式会持续不断地发送数据包。数据包结构一个标准的PS/2鼠标数据包包含3个字节连续发送。Byte 1状态字节。包含按键信息左、右、中键按下状态和溢出标志以及最重要的——X、Y位移量的符号位第4、5位。Byte 2X方向位移量。是一个9位有符号数的低8位最高位符号位在状态字节中。正值表示向右移动。Byte 3Y方向位移量。同样是一个9位有符号数的低8位最高位在状态字节中。注意Y轴方向与屏幕通常相反正值表示向下移动但鼠标移动时是向上所以实际坐标累加时可能需要取反。模块工作流程该模块也需要一个状态机但它的状态循环是等待包起始 - 接收字节1 - 接收字节2 - 接收字节3 - 输出完整数据包 - 继续等待下一个包。因为鼠标是连续发送的所以它的IDLE状态可能只是在等待一个包内字节间的短暂间隔。坐标累加器一个实用的鼠标接口模块不会只输出原始的位移字节。它内部通常会维护两个寄存器x_pos和y_pos。每当收到一个完整的3字节包就将有符号的位移量累加到当前位置寄存器上并将累加后的坐标值输出。这样上层应用拿到的是绝对的坐标信息而不是需要自己处理的相对位移。3.4 测试与调试模块Simulation/Testbench源码包里很可能还包含一个tb_ps2_host.v的文件这是用于在Modelsim或Vivado Simulator中进行仿真的测试平台。学习看测试平台和看设计代码一样重要。如何模拟PS/2设备测试平台里会有一个task或initial块来模拟键盘或鼠标的行为。例如它会按照PS/2时序精确地在时钟下降沿改变数据线的值发送一帧预设的扫描码比如字母‘A’的通码0x1C。查看波形通过仿真你可以在波形图中清晰地看到ps2_clk和ps2_data上的模拟波形。状态机state是如何一步步跳转的。bit_cnt如何从0计数到10。最终key_code和key_valid信号是否在正确的时间点输出预期的值。自我验证尝试修改测试平台发送一个错误的校验位数据包观察你的设计是否能跳转到ERROR状态并妥善处理。4. 基于源码的FPGA上板验证全流程有了源码和仿真信心下一步就是让它在一块真实的FPGA开发板上跑起来。这里以常见的Xilinx Artix-7开发板如Basys3、Nexys4为例。4.1 硬件连接与约束文件.xdc这是从代码到硬件的桥梁一步错满盘输。物理连接你需要一根PS/2连接线通常是母头将其时钟和数据线通过杜邦线连接到FPGA的普通IO引脚上。务必查阅开发板原理图确认你连接的引脚没有其他特殊功能如专用时钟输入。VCC和GND也需要接好为键盘/鼠标供电。编写XDC约束这是最关键的一步。你需要告诉Vivado工具每个端口对应FPGA的哪个物理引脚以及其电气标准。# 示例假设将ps2_clk连接到开发板上的JXADC接口的某个引脚具体查原理图 set_property PACKAGE_PIN “J15” [get_ports ps2_clk] set_property IOSTANDARD LVCMOS33 [get_ports ps2_clk] set_property PACKAGE_PIN “H15” [get_ports ps2_data] set_property IOSTANDARD LVCMOS33 [get_ports ps2_data] # 系统时钟和复位引脚 set_property PACKAGE_PIN “W5” [get_ports clk] # 假设是100MHz时钟 set_property IOSTANDARD LVCMOS33 [get_ports clk] create_clock -period 10.000 -name sys_clk -waveform {0.000 5.000} [get_ports clk]踩坑记录IOSTANDARD一定要设对3.3V的开发板就设LVCMOS331.8V的就设LVCMOS18。设错了要么检测不到信号要么可能损坏设备。PACKAGE_PIN的编号必须百分百准确一个数字错了就连不上。4.2 Vivado中的综合、实现与下载创建工程与添加源码在Vivado中新建项目选择正确的FPGA型号例如xc7a35tcsg324-1。将所有的.v源文件、测试文件以及刚写好的.xdc约束文件添加到工程中。综合Synthesis点击Run Synthesis。这个过程将你的Verilog代码转换成门级网表。重点关注“Messages”窗口不要有“Critical Warning”或“Error”。常见的警告如未连接的端口需要根据情况处理。实现Implementation综合成功后运行Run Implementation。这一步包括布局布线Place Route将逻辑门映射到FPGA的实际资源上。完成后可以打开“Implemented Design”查看资源利用率报告。我们这个设计应该只占用极少的LUT和FF。生成比特流Generate Bitstream实现无误后生成.bit文件。下载与调试连接好JTAG下载器给开发板上电。在Vivado中打开Hardware Manager自动识别设备然后Program Device选择生成的.bit文件进行下载。最直接的验证将源码中输出的key_code连接到开发板的LED灯上比如低8位接8个LED。按下键盘按键观察LED显示的二进制数是否与该键的扫描码对应例如按键‘A’LED可能显示00011100即0x1C。对于鼠标可以将x_pos和y_pos的某些位接到LED上移动鼠标看LED变化。4.3 使用ILA进行在线调试如果LED显示不正常或者你想更深入地观察内部信号Vivado的ILA集成逻辑分析仪是你的终极武器。标记调试网络Mark Debug在综合后的网表或RTL源码中右键点击你想观察的信号如state、bit_cnt、ps2_clk_sync、key_valid选择“Mark Debug”。重新综合与实现因为添加了调试逻辑需要重新运行综合和实现。设置ILA触发条件下载包含ILA核的比特流后在Hardware Manager中设置触发条件。例如可以设置为当key_valid信号从0变为1时触发捕获。抓取波形分析触发后ILA会捕获一段时间内这些信号的实际波形。你可以像看仿真波形一样检查状态机跳转、数据采样点是否与预期完全一致。这是定位时序问题的黄金手段。5. 项目进阶与优化方向这个基础项目跑通后你可以尝试以下方向进行深化和扩展这会让你的学习从“会用”上升到“理解”和“创造”。5.1 协议扩展从标准PS/2到更复杂的交互实现主机到设备的发送目前的设计是只读的。尝试增加FPGA向键盘/鼠标发送命令的功能例如初始化鼠标、设置采样率、点亮键盘指示灯Num Lock, Caps Lock, Scroll Lock。这需要你实现主机驱动时钟线和数据线的逻辑状态机会变得更加复杂。解析扩展键盘扫描码标准101/102键键盘使用一套扫描码集Scan Code Set 2。尝试让你的解析器支持更多的功能键。鼠标滚轮支持许多PS/2鼠标支持滚轮其数据包会扩展为4个字节。尝试修改鼠标解析模块支持解析第4个字节的滚轮信息。5.2 系统集成构建一个微型输入系统添加消抖与按键状态机在键盘解码器之后增加一个按键处理模块。它维护一个按键状态映射表比如128位的寄存器每一位代表一个扫描码是否被按下并产生“按下”、“保持”、“释放”等更高级的事件信号方便上层应用如游戏使用。VGA显示字符或光标将键盘输入的字符显示在VGA显示器上或者用鼠标控制一个屏幕上的光标。这需要你学习VGA时序控制并建立一个帧缓冲区Frame Buffer。这是一个经典的综合性项目能把输入、处理、输出完整串联起来。通过UART上传到PC将FPGA识别到的键值或鼠标坐标通过UART串口发送到电脑在串口助手上显示。这练习了另一个常用通信协议——异步串口。5.3 代码质量与可靠性提升添加参数化设计使用parameter或localparam来定义时钟频率、去抖延时时间等让代码更容易移植到不同时钟频率的FPGA上。完善的错误处理当前设计可能只是简单地丢弃错误帧。可以增加错误计数器当连续错误超过一定阈值时通过一个error信号通知系统进行复位或重新初始化。跨时钟域处理CDC规范化对于key_valid这类从慢速PS/2时钟域到快速系统时钟域的单脉冲信号仅用同步器可能不够。可以考虑使用“脉冲同步器”或“握手协议”来安全地传递控制信号避免数据丢失。6. 常见问题与调试技巧实录在实际调测中你几乎一定会遇到下面这些问题。我把我的踩坑记录和解决方法分享出来希望能帮你节省大量时间。问题现象可能原因排查思路与解决方法上电后按键无任何反应LED不亮1. 硬件连接错误线接反、虚焊。2. 约束文件错误引脚号、电平标准。3. 键盘/鼠标未供电或损坏。1.万用表检查测量PS/2接口的VCC和GND之间是否有~5V电压。测量CLK和DATA引脚在空闲时是否为高电平~3.3V或~5V。2.双重检查约束逐字核对.xdc文件中的引脚编号与原理图是否一致。3.替换法换一个已知好的键盘或鼠标试试。按键时LED有反应但显示的值乱七八糟或每次值都不同1.时序采样点错误最常见。在时钟的下降沿采样了数据。2. 亚稳态导致采样值错误。3. 消抖逻辑有问题一次按键被识别成多次。1.ILA抓波形这是最有效的办法。抓取ps2_clk和ps2_data放大看一个数据位。确认你的采样点代码中always(posedge ps2_clk_sync)内的操作是否发生在ps2_clk的上升沿并且此时ps2_data已经稳定。2.检查同步器确保对输入的ps2_clk使用了至少两级D触发器进行同步。3.简化测试在测试平台中发送一个固定的扫描码如0xAA二进制10101010看波形是否完美匹配先排除硬件问题。鼠标能检测到按键但移动时坐标变化不对或不动1. Y坐标符号处理错误。2. 位移量拼接错误符号位从状态字节取错。3. 鼠标未正确初始化可能需要发送初始化命令。1.打印/显示原始数据包将鼠标输出的3个字节原始数据通过UART发送到电脑与已知的鼠标数据包格式对比。移动鼠标看第二个字节X位移的变化是否符合预期右移增大左移减小。2.检查符号位提取确认从状态字节Byte1提取X、Y符号位第4、5位的代码是否正确。3.查阅鼠标数据手册确认你的鼠标默认的数据报告模式。有些鼠标可能需要上电后由主机发送0xF4使能数据报告命令。仿真完全正确但下板后行为异常1. 约束中的时钟频率与实际板载晶振频率不符。2. 输入引脚未设置上拉电阻PS/2总线空闲时为高阻态而非高电平。3. 物理信号质量问题导线过长干扰大。1.检查时钟约束create_clock语句中的-period值是否与板卡实际晶振频率匹配例如100MHz晶振周期是10ns。2.启用内部上拉在XDC约束中为ps2_clk和ps2_data添加上拉属性set_property PULLUP TRUE [get_ports ps2_clk]。3.缩短连接线并确保连接牢固。如果可能使用带屏蔽的PS/2连接线直接连接到板载接口如果板子有的话。资源利用率异常高1. 代码中存在不可综合的语句如initial块用于寄存器赋值#延时。2. 状态机编码方式效率低如独热码One-Hot在状态多时耗资源。3. 不必要的寄存器被综合出来。1.检查综合报告看哪些模块占用资源多。确保所有用于硬件实现的代码都是可综合的。2.状态机编码对于小状态机8个状态二进制编码Binary或格雷码Gray比独热码更省资源。可以通过(* fsm_encoding “gray” *)等综合属性指导工具。3.优化代码移除调试用的、不影响功能的中间寄存器。这个项目源码的价值远不止几段可运行的Verilog代码。它更像一个精心设计的教学样本完整展示了一个数字接口从协议理解、状态机设计、RTL编码、功能仿真到最终上板调试的全过程。我建议你不要满足于直接使用它而是以它为蓝图亲手重新敲一遍代码过程中故意制造一些上述的“常见问题”然后利用仿真和ILA工具去定位和解决。这个过程里积累的调试经验和对时序的深刻理解才是你从“FPGA学习者”迈向“FPGA开发者”最关键的一步。当你能够游刃有余地修改、扩展并稳定运行这个项目时你会发现再去学习SPI、I2C、UART等其他接口协议思路都会清晰很多因为底层的同步、采样、状态机思想都是相通的。本文还有配套的精品资源点击获取
返回列表