别只让灯流水!用BASYS3 FPGA的8个LED,我做了个简易‘贪吃蛇’游戏(附完整Verilog代码与管脚配置)

张开发
2026/4/7 21:38:38 15 分钟阅读

分享文章

别只让灯流水!用BASYS3 FPGA的8个LED,我做了个简易‘贪吃蛇’游戏(附完整Verilog代码与管脚配置)
用BASYS3 FPGA实现贪吃蛇游戏从流水灯到交互式设计的跃迁当BASYS3开发板上的8个LED灯第一次按照我的指令移动、转向、甚至吃掉随机出现的食物时那种成就感远非流水灯实验可比。这个看似简单的贪吃蛇游戏实际上包含了状态机设计、时钟分频复用、输入消抖等FPGA核心技能的综合运用。本文将带你从零实现这个项目完整展示Verilog代码和管脚配置方案。1. 硬件架构设计思路BASYS3开发板虽然资源有限但恰好适合实现简化版贪吃蛇8个LED可显示蛇身和食物四个方向按钮控制移动。关键在于如何用硬件思维实现游戏逻辑。1.1 系统框图设计整个系统包含三个核心模块时钟管理模块将100MHz系统时钟分频为游戏帧率(如10Hz)输入处理模块对机械按钮进行消抖处理游戏逻辑模块实现蛇的移动、生长和碰撞检测module top( input clk, // 100MHz系统时钟 input rst, // 复位按钮 input [3:0] btn, // 方向按钮(上、下、左、右) output [7:0] led // 8位LED显示 ); // 各模块将在此互联 endmodule1.2 显示编码方案用8位LED表示游戏状态最低有效位(led[0])代表食物位置其余亮起的LED代表蛇身蛇头总是当前移动方向最远的亮灯例如8b00010001表示食物在位置0蛇身在位置48b00110000表示食物在位置4蛇身在位置5和42. 核心模块实现细节2.1 可配置时钟分频器游戏需要稳定的帧率时钟以下参数化模块可实现任意频率分频module clk_div #( parameter DIV 10_000_000 // 默认产生10Hz时钟 )( input clk_in, output reg clk_out ); reg [31:0] counter; always (posedge clk_in) begin if(counter DIV-1) begin counter 0; clk_out ~clk_out; end else begin counter counter 1; end end endmodule提示调试时可先使用较高频率(如1Hz)验证逻辑正确后再降低到适合游戏的速度2.2 按钮消抖处理机械按钮会产生抖动信号需要滤波处理module debounce ( input clk, input btn_in, output reg btn_out ); reg [15:0] counter; reg btn_sync; always (posedge clk) begin btn_sync btn_in; if(btn_sync ^ btn_out) begin if(counter) btn_out btn_sync; else counter counter 1; end else begin counter 0; end end endmodule2.3 贪吃蛇游戏逻辑游戏核心是一个状态机包含以下状态转换初始化状态设置初始蛇身和食物位置移动状态根据当前方向更新蛇身位置生长检测判断是否吃到食物碰撞检测检查游戏是否结束module snake_game( input clk, input rst, input [3:0] dir_btn, // 上、下、左、右 output reg [7:0] leds, output reg game_over ); reg [2:0] snake_dir; // 当前移动方向 reg [7:0] snake_body; reg [2:0] food_pos; reg [15:0] random; // 方向编码 localparam UP 3b000, DOWN 3b001, LEFT 3b010, RIGHT 3b011; always (posedge clk or posedge rst) begin if(rst) begin // 初始化逻辑 snake_body 8b00010000; food_pos 3b000; snake_dir RIGHT; game_over 0; random 16hABCD; end else if(!game_over) begin // LFSR伪随机数生成 random {random[14:0], random[15] ^ random[13] ^ random[12] ^ random[10]}; // 方向控制 if(dir_btn[0]) snake_dir UP; else if(dir_btn[1]) snake_dir DOWN; else if(dir_btn[2]) snake_dir LEFT; else if(dir_btn[3]) snake_dir RIGHT; // 移动逻辑 case(snake_dir) UP: snake_body {snake_body[6:0], 1b0}; DOWN: snake_body {1b0, snake_body[7:1]}; LEFT: snake_body {snake_body[3:0], snake_body[7:4]}; RIGHT: snake_body {snake_body[0], snake_body[7:1]}; endcase // 食物检测与生长 if(snake_body[food_pos]) begin snake_body snake_body | (1 food_pos); food_pos random[2:0]; end // 碰撞检测 if(snake_body 8hFF) game_over 1; // 更新LED显示 leds snake_body | (1 food_pos); end end endmodule3. 系统集成与优化3.1 模块互联方案将各模块按以下方式连接wire game_clk; wire [3:0] debounced_btn; clk_div #(.DIV(5_000_000)) clk_10hz ( .clk_in(clk), .clk_out(game_clk) ); debounce db0(.clk(clk), .btn_in(btn[0]), .btn_out(debounced_btn[0])); debounce db1(.clk(clk), .btn_in(btn[1]), .btn_out(debounced_btn[1])); debounce db2(.clk(clk), .btn_in(btn[2]), .btn_out(debounced_btn[2])); debounce db3(.clk(clk), .btn_in(btn[3]), .btn_out(debounced_btn[3])); snake_game game( .clk(game_clk), .rst(rst), .dir_btn(debounced_btn), .leds(led), .game_over() );3.2 性能优化技巧资源复用利用LFSR(线性反馈移位寄存器)同时处理随机数生成和游戏时钟状态编码优化使用独热码(one-hot)表示蛇身位置简化碰撞检测逻辑显示压缩用8位LED同时显示蛇身和食物位置无需额外存储4. 完整实现与调试4.1 BASYS3管脚配置根据开发板原理图配置约束文件# 时钟信号 set_property PACKAGE_PIN W5 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] # 复位按钮 set_property PACKAGE_PIN T17 [get_ports rst] set_property IOSTANDARD LVCMOS33 [get_ports rst] # 方向按钮(上、下、左、右) set_property PACKAGE_PIN T18 [get_ports {btn[0]}] set_property PACKAGE_PIN U18 [get_ports {btn[1]}] set_property PACKAGE_PIN R18 [get_ports {btn[2]}] set_property PACKAGE_PIN V17 [get_ports {btn[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {btn[*]}] # LED输出 set_property PACKAGE_PIN U16 [get_ports {led[0]}] set_property PACKAGE_PIN E19 [get_ports {led[1]}] set_property PACKAGE_PIN U19 [get_ports {led[2]}] set_property PACKAGE_PIN V19 [get_ports {led[3]}] set_property PACKAGE_PIN W18 [get_ports {led[4]}] set_property PACKAGE_PIN U15 [get_ports {led[5]}] set_property PACKAGE_PIN U14 [get_ports {led[6]}] set_property PACKAGE_PIN V14 [get_ports {led[7]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}]4.2 常见问题排查蛇移动方向相反检查LED物理排列顺序与代码中的位序是否匹配修改snake_game模块中的位移方向按钮响应不灵敏调整消抖模块的计数器位宽确保游戏时钟频率适合人类反应时间(5-10Hz为宜)食物出现在蛇身上在随机数生成后添加冲突检测逻辑当新食物位置与蛇身重叠时重新生成// 改进的食物生成逻辑 always (posedge clk) begin if(need_new_food) begin do begin food_pos random[2:0]; end while(snake_body[food_pos]); end end实现这个贪吃蛇游戏后你会发现FPGA开发远比流水灯有趣。这种将抽象游戏逻辑映射到硬件描述语言的过程正是数字系统设计的精髓所在。当看到LED阵列按照你的代码规则生动地演绎着经典游戏时那种创造力的满足感会激励你探索更复杂的FPGA项目。

更多文章