2025寒假练 -基于STEP FPGA实现拍球游戏机
1. 项目介绍
在数字娱乐与硬件开发技术不断发展的当下,为了深入探索 FPGA 在模拟现实场景游戏中的应用,我们开展了本次拍球游戏机项目。该项目旨在通过硬件与软件的协同设计,打造一款能够高度模拟真实拍球体验的游戏机。
本项目的核心目标是在FPGA上,实现小球的多种运动状态模拟,包括自由落体、反弹以及模拟拍球动作。通过这些功能的实现,提供一个具有挑战性和趣味性的拍球游戏,同时展示 FPGA 在复杂系统控制中的强大能力。
项目目标包括:
1. 在 LCD 屏幕上动态显示小球实时位置与手掌位置
2. 通过按键和电位计实现反弹系数和放球位置的调节
3. 通过接近式传感器测量手掌位置模拟拍球
2. 硬件平台介绍
2.1 核心板
型号:Lattice MXO2 核心板,采用Lattice LCMXO2-4000HC-4MG132芯片。该芯片具备丰富的逻辑资源,能够支持多个功能模块的并行运行,为实现拍球游戏机的复杂功能提供了硬件基础。
功能:核心板提供稳定的 12MHz 系统时钟,这一时钟信号作为整个系统运行的节拍基准,驱动着各个模块内部的时序逻辑操作。同时,芯片内部集成的 PLL 硬核可实现时钟倍频,将时钟频率提升至 50MHz,满足如 LCD 刷屏等对高频时钟有需求的模块,确保系统各部分能够高效、稳定地运行。带有两个数码管与四个按键,便于显示并调节参数。
2.2 扩展底板
LCD 屏幕:配备 240×320 像素的 RGB 液晶屏幕,该屏幕具有良好的显示效果,能够清晰地呈现小球的运动轨迹、手掌位置以及其他游戏相关信息。其图形化显示能力为用户提供了直观的游戏界面,增强了游戏的沉浸感。
ADC 模块:采用 ADC081S101 模数转换器,它能够将连接在实验板上的电位计旋钮的模拟信号转换为数字信号。通过 SPI 通信协议,ADC081S101 与 FPGA 核心板进行数据传输与控制信号交互,实现对电位计输入信号的精确数字化处理,从而根据电位计旋钮的位置设定球的初始位置。
接近传感器:rpr-0521rs-e接近传感器实时检测放置于其上方的手的位置和运动速度。通过 I2C 通信协议,该传感器将采集到的数据传输给 FPGA 核心板,为模拟真实拍球效果提供关键数据支持。传感器具备较高的灵敏度和准确性,能够较为精准地捕捉手的动作,使得游戏中的拍球模拟更加逼真。
3. 系统方案设计
3.1 架构框图
在这个架构中,各个模块紧密协作。LCD模块负责将游戏中的各种图形信息,如小球、手掌线等呈现给用户;jumping 模块根据物理模型和输入信号控制小球的运动状态;hand_control 模块利用 rpr0521rs_driver 传感器测到的数据确定手的位置,实现拍球模拟;ADC081S101_driver 模块将电位计的模拟信号转换为数字信号,为球的初始位置设定提供数据支持;adjuster通过检测核心板上的按键来实现对反弹系数的调节
3.2 设计思路
状态机控制:采用三状态机来精确控制小球的运动逻辑,这三个状态分别为下落(DOWN)、上升(UP)。状态机根据时钟信号和各种输入条件进行状态转换,例如当球处于下落状态且到达地面时,状态机将其转换为上升状态,并根据反弹系数调整球的速度;当球碰到手时,根据手的位置和球的当前状态,状态机决定球的后续运动状态和速度。
物理模型:基于自由落体公式position = v0*t + 0.5*g*t²进行球的位置更新。在本设计中,为了适应硬件计算的特点,对公式进行了适当的变形和处理。时间t通过计数器(如cnt)和时钟频率(如FREQUENCY)来近似表示,即t = cnt/FREQUENCY。同时,为了简化计算电路,为小球运动模拟器jumping选择了合适的时钟频率(54.77Hz),使得系数计算更加简洁。在球的上升和下落过程中,根据不同的状态和物理参数,利用该公式准确地更新球的位置。
碰撞检测:实时监测小球与边界(地面和天花板)、手部的位置关系。当球到达边界时,根据反弹系数计算反弹后的速度,并将球的位置和速度传递至下一个状态;当球碰到手时,即球的位置与手的位置满足特定条件(handline >pic_y),则会使球的速度变为手的速度,并根据球的当前运动状态决定后续的运动方向。这种碰撞检测机制确保了游戏中球的运动能够真实地模拟现实情况。
模块化设计:将整个系统的功能拆分为多个独立的模块,如 ADC 驱动模块、传感器处理模块、显示控制模块、声音控制模块等。每个模块负责特定的功能,模块之间通过定义明确的接口进行数据交互和协同工作。这种模块化设计方式提高了代码的可读性、可维护性和可扩展性,便于团队成员分工合作,同时也降低了系统开发的复杂性。
4. 代码实现
4.1 jumping模块
jumping模块主要用于模拟小球的运动。模块内部利用fre_div子模块将 12MHz 时钟分频为54.77Hz的时钟频率,作为小球运动的计时时钟。模块通过状态机来控制小球的运动状态,状态包括下落DOWN和弹起UP。同时,定义了多个寄存器用于存储小球的位置、速度、时间计数等信息。
功能实现方面,当reset信号有效(即按下放球键且复位信号有效)时,模块对所有相关寄存器进行初始化,包括时间计数器、位置寄存器、速度寄存器等,并将小球状态设置为下落DOWN。在下落状态下,模块根据自由落体公式s = g * t * t / 2(此处将重力加速度及相关计算进行了简化)更新小球位置pic_y,同时判断小球是否到达地面(position + init_position <= MAX_Y),若到达则计算反弹速度并转换到弹起状态UP,同时设置蜂鸣器标志位beep_flag[1]表示球落地。在弹起状态下,模块根据反弹后的时间计数time_count_up与反弹时间time_count_r的比较,判断小球是否到达最高点或其他边界条件;若小球在上升过程中遇到手(pic_y < handline),则更新小球位置和速度,并转换到下落状态。通过这样的状态机和寄存器操作,模块实现了小球在重力和拍球作用下的运动模拟 。
4.2 LCD模块
负责操控 LCD 显示屏的驱动芯片,实现初始化、图像显示和线条绘制。通过多个内部子模块协同工作,控制 LCD 并传输数据,在屏幕上展示游戏图形。LCD 模块先设置好显示屏的工作模式和分辨率等参数,再将游戏图形数据转换成显示屏能识别的信号,经内部子模块处理,通过数据传输线路逐行发送到 LCD 显示屏,完成图像显示和线条绘制,最终呈现游戏相关图形。
为了提升屏幕刷新率、优化图像绘制效果,模块采用部分刷新而非全屏刷新的方式。具体架构框图如下:
4.2.1 lcd_draw_line模块
这个模块主要功能是在 LCD 显示屏上画线条。为了不让上一次画的线留痕迹,模块通过状态机来控制,在不同状态下,先清除上一次的线条,再画新的线条。模块里面设定了好几种颜色参数,还有操作状态参数,用 edge_detect 子模块来检测清屏和画完标志的上升沿,这样就能控制状态机切换状态。
当状态机处于 CLEAR 状态时,会调用 lcd_draw_line 子模块,用白色线条把指定位置的线擦掉,擦完后就切换到 DRAW 状态。在 DRAW 状态下,会在新的 y_coord 位置用棕色线条画一条线,画完就回到 IDLE 状态。要是检测到手掌位置有变化,也就是说接近传感器测到的距离 y_coord 跟上一次不一样,那就从 IDLE 状态进入 CLEAR 状态,开始新的画线条流程。最后,模块会根据状态机的状态,输出要传输的命令或数据 draw_line_data、画完标志 draw_line_done,还有写入使能信号 en_write_draw_line,实现 LCD 显示屏上的线条绘制功能。
4.2.2 control模块
control 模块是 LCD 显示系统的 “指挥官”,负责管理整个显示流程。它接收多个输入信号,比如 50MHz 的时钟信号 sys_clk_50MHz、复位信号 sys_rst_n,还有来自其他模块的状态和数据信号,像 init_data、init_done、show_pic_data、show_pic_done、draw_line_data、draw_line_done 等。模块里用状态机来安排不同的显示阶段,一共有 IDLE、INIT、SHOW_PIC、DRAW_LINE 这四种状态。系统复位后,control 模块会直接进入 INIT 状态。当收到 init_done 信号时,就切换到 SHOW_PIC 状态,开始显示图像。图像显示完,show_pic_done 信号有效,又进入 DRAW_LINE 状态,开始绘制线条。线条画好,draw_line_done 信号出现,模块就回到 IDLE 状态,等待下一次任务。
为了更好地控制显示过程,模块里还设置了两个计数器,draw_line_counter 和 show_pic_counter,分别在 SHOW_PIC 和 DRAW_LINE 状态下工作。它们的作用是辅助控制 show_pic_flag 和 draw_line_flag 信号的输出。在输出逻辑部分,control 模块会根据当前所处的状态,把相应的数据,像 init_data、show_pic_data、draw_line_data,赋值给 data 信号,同时控制 en_write 信号,以及 show_pic_flag 和 draw_line_flag 信号的输出。这样一来,LCD 显示系统就能按照顺序,依次完成初始化、图像显示和线条绘制等操作。另外,control 模块还通过 led 信号,在不同状态下给出状态指示,方便我们了解系统的运行情况。
4.2.3 lcd_pic_show模块
lcd_show_pic 模块的作用是在 LCD 屏幕上显示图像,采用状态机和计数器来完成。这个模块接收系统时钟 sys_clk、低电平有效的复位信号 sys_rst_n、写入完成标志 wr_done,以及显示图像标志 show_pic_flag 作为输入,输出 ROM 地址 rom_addr、要显示的图像数据 show_pic_data、显示完成标志 show_pic_done,还有写入使能信号 en_write_show_pic。
模块利用状态机和计数器实现部分刷新。状态机有 STATE0、STATE1、STATE2 和 DONE 这几个状态。当 show_pic_flag 信号有效时,模块从 STATE0 状态进入 STATE1 状态。在 STATE1 状态下,由 cnt_set_windows 计数器控制,当它计数到 10,并且 the1_wr_done 信号有效时,状态转移到 STATE2 状态,这一步可能是在设置显示窗口。进入 STATE2 状态后,cnt_rom_prepare 计数器等待 ROM 数据读取完成,当计数到 3 时,将 ROM 数据 rom_q 赋值给 temp。接着,cnt_wr_color_data 计数器对 temp 中的数据按位处理,根据 temp 的最低位决定输出颜色数据 data:最低位为 0 就输出白色,为 1 则输出棕色。同时,通过 cnt_wr_color_data 的最低位决定输出数据的高 8 位还是低 8 位,这样就能一个点一个点地绘制图像数据。绘制时,length_num_flag 和 cnt_length_num 计数器控制绘制长度,当 cnt_length_num 达到 SIZE_LENGTH_MAX,并且 length_num_flag 信号有效时,状态转移到 DONE 状态,表示当前图像绘制完成,完成一次部分刷新操作。通过状态机和计数器相互配合,模块每次只绘制需要更新的图像部分,避免了全屏刷新造成的资源浪费和显示延迟,提高了屏幕刷新率和显示效率。
4.3 hand_control模块
这段Verilog代码定义了名为hand_control的模块,用于处理拍球的手对应位置和速度信息。模块接收数据有效脉冲dat_valid和16位位置传感器数据prox_dat作为输入,输出9位的拍球手对应位置handline和8位手的速度hand_velocity。
模块内通过一系列寄存器和逻辑实现功能。利用dat_valid的上升沿触发操作,先更新用于滤波的寄存器prox_dat0和prox_dat1,根据当前和前一时刻位置传感器数据的差值diff进行滤波,若差值大于16'h800则保持prox_dat2不变,否则更新prox_dat2为当前数据prox_dat0。同时,每次dat_valid上升沿到来时,count自增1,当count有效时,根据滤波后数据prox_dat2计算handline,将prox_dat2[11:6]乘以5得到handline并进行限幅处理,若计算结果小于0则置为0;通过比较当前和前一时刻的handline计算速度velocity_temp,同样进行限幅处理后赋值给hand_velocity。
4.4 adjuster模块
本模块 “adjuster” 主要实现了反弹系数调整及其数码管显示控制功能。通过接收外部输入的两个按键信号(key_up 和 key_down),对反弹系数 k 进行调整。具体而言,当按下 key_up 键且 k 的值小于 3 时,k 的值会递增 1;当按下 key_down 键且 k 的值大于 0 时,k 的值会递减 1。这样可以根据用户的按键操作灵活地改变 k 的值,以满足不同场景下对该参数的需求。之后再调用了数码管译码模块进行显示,调节显示范围为 0.5~0.8。
5. 功能展示
数码管显示的为反弹系数,目前显示的0.7为反弹系数,尺寸为 10×10 像素的实心圆作为小球,线为手掌
6. 技术难点与解决方案
6.1 显示卡顿优化
问题:LCD 屏幕的刷屏频率不足,导致在小球快速运动时,画面出现撕裂现象,严重影响用户体验。
解决:将 LCD 刷屏时钟从原来的频率提升至 120MHz,提高了屏幕的刷新速度。同时,采用部分刷新来替代全屏刷新,即只刷新球和手的运动区域,从而提高屏幕刷新率。通过这种方式,有效地解决了画面撕裂以及痕迹遗留问题,使小球的运动显示更加平滑、流畅,提升了游戏的视觉效果。
6.2 物理模型精度
问题:在实现小球运动的物理模型时,使用自由落体公式进行浮点运算,这不仅占用了大量的硬件资源,还导致计算速度较慢,影响系统性能。
解决:采用移位运算代替除法运算。通过合理地选择数据位宽和移位数,在保证计算精度的前提下,大大减少了硬件资源的占用,提高了计算速度,使系统能够高效地运行小球运动的物理模型计算,确保小球运动的模拟更加准确和流畅。
7. 心得体会
深入理解了状态机的原理和应用,学会了如何通过定义不同的状态(如DOWN、UP)以及状态之间的转换条件来控制小球的运动逻辑。同时,我掌握了如何根据物理模型(如自由落体运动公式)来模拟小球在不同状态下的位置和速度变化,这让我对物理原理在实际工程中的应用有了更深刻的认识。通过修改屏幕刷新程序,对屏幕刷新的原理也更加深刻了。在实践方面,我学会了使用硬件描述语言(如 Verilog)来实现模块的功能,包括定义输入输出端口、内部寄存器和逻辑运算。我还了解了如何使用时钟分频器来控制模块的时钟频率,以满足不同的功能需求。
8. 资源占用报告
省流:该FPGA项目基于Lattice LCMXO2-4000HCCSBGA132芯片,整体资源利用率较高,SLICE和LUT4使用率均超过40%,寄存器占用14%。设计中使用了两个PLL分别生成96MHz和24MHz时钟,全局复位(GSR)功能启用,但仍有部分寄存器未启用。I/O接口丰富,支持多种功能。报告未显示错误或警告,表明设计已通过基本检查,但仍有优化空间,例如进一步利用未占用的块RAM资源、优化高负载时钟信号及时钟使能逻辑,以及考虑启用更多寄存器的全局复位功能以提高可靠性。