从键盘敲击到屏幕显示:手把手用Verilog在HDLbits上实现一个简易PS/2键盘数据包解析器
从键盘敲击到屏幕显示手把手用Verilog在HDLbits上实现一个简易PS/2键盘数据包解析器当你按下键盘上的某个按键时这个简单的物理动作背后隐藏着一系列复杂的数字通信过程。作为FPGA开发者理解并实现这一过程不仅能加深对硬件描述语言的理解更能为自定义输入设备开发打下坚实基础。本文将带你从零开始在HDLbits平台上构建一个完整的PS/2键盘数据包解析系统涵盖从状态机设计到数据路径处理的完整流程。1. PS/2协议基础与项目架构PS/2接口虽然已被USB取代但其简洁的同步串行协议仍是学习数字通信的理想案例。每个按键动作会触发键盘发送包含3个字节的数据包起始字节最高位为1通常为0xE0或0xF0扫描码字节表示实际按键位置结束字节校验和或附加功能标识// 典型PS/2数据包示例 8hE0 // 扩展键标识 8h1F // 左Ctrl键扫描码 8h00 // 保留字节在FPGA中处理这类数据流需要解决三个核心问题时序同步PS/2时钟频率为10-16.7kHz需正确采样数据线数据完整性检测起始位、校验停止位状态管理跟踪数据包接收进度提示HDLbits的Fsm_ps2题目已简化流程假设数据已正确采样到in[7:0]总线我们只需关注协议解析逻辑。2. 有限状态机设计精要2.1 状态机拓扑选择对于PS/2解析器最适合采用Mealy型状态机因为输出(done信号)不仅取决于当前状态还与输入数据(in[3])直接相关。基本状态应包括状态功能描述转移条件IDLE等待数据包开始in[3]1 → BYTE1BYTE1接收第一个数据字节自动→ BYTE2BYTE2接收第二个数据字节自动→ BYTE3BYTE3接收第三个数据字节自动→ DONEDONE标记数据包接收完成in[3]1 → BYTE1parameter IDLE 0, BYTE1 1, BYTE2 2, BYTE3 3, DONE 4; reg [2:0] state; always (posedge clk) begin if (reset) state IDLE; else case(state) IDLE: state in[3] ? BYTE1 : IDLE; BYTE1: state BYTE2; BYTE2: state BYTE3; BYTE3: state DONE; DONE: state in[3] ? BYTE1 : IDLE; endcase end2.2 关键设计技巧同步复位优先确保状态机从已知初始状态启动组合逻辑分离状态转移条件单独处理提升可读性输出寄存器化避免组合逻辑输出导致的毛刺注意HDLbits的Fsm_ps2题目特别要求done信号在DONE状态有效这需要严格同步输出逻辑。3. 数据路径扩展实现在Fsm_ps2data题目中我们需要扩展基本状态机以捕获完整数据包。此时需注意数据锁存时机每个字节应在状态转移时存储字节序管理24位输出需按[23:16],[15:8],[7:0]组织输出使能仅当done有效时更新输出总线reg [23:0] packet_data; always (posedge clk) begin if (next_state BYTE2) packet_data[23:16] in; else if (next_state BYTE3) packet_data[15:8] in; else if (next_state DONE) packet_data[7:0] in; end assign out_bytes done ? packet_data : 24b0;常见问题排查数据错位检查状态机与锁存逻辑的同步性重复捕获确保每个字节只锁存一次输出抖动添加done信号作为输出使能4. 系统集成与调试技巧4.1 七段数码管显示集成将解析出的键值映射到数码管显示是验证工作的有效方式。典型实现包括扫描码转换建立查找表将原始数据转为ASCII或自定义编码显示驱动分时复用多个数码管消抖处理添加20ms延迟防止重复触发// 简化的扫描码到七段码转换 always (*) begin case(out_bytes[15:8]) // 取中间字节作为主键值 8h1C: seg_data 8b01100011; // A 8h32: seg_data 8b01100011; // B // ...其他键值映射 default: seg_data 8b00000000; // 空白 endcase end4.2 在线调试策略仿真验证使用$display实时监控状态转移always (state) begin $display([%t] State change: %d, $time, state); end分段测试先验证状态机再添加数据路径边界测试特别测试数据包边界条件如连续快速按键5. 性能优化进阶对于需要处理高速输入的系统可考虑以下优化流水线架构分离协议解析与数据处理阶段双缓冲技术避免显示更新期间的数据冲突时钟域交叉使用FIFO处理异步PS/2时钟// 双缓冲实现示例 reg [23:0] active_buffer, shadow_buffer; always (posedge clk) begin if (done) begin shadow_buffer packet_data; if (display_idle) active_buffer shadow_buffer; end end实际项目中我曾遇到快速连续按键导致数据覆盖的问题。通过添加数据有效标志位和简单的流控机制最终实现了稳定的1000Hz按键采样率——这远高于PS/2协议的标准速率为特殊应用场景提供了充足余量。