imut FPGA课设 基于FPGA的VGA弹球游戏设计 *秋昊

写在前面的话:

本文主要呈现了一篇IMUT的FPGA课设报告。

课设报告内容(word版),视频演示,程序源码,专业创新实践简介,专业创新实践指导书均已放入下面的百度云链接中,也不大,总共不到20MB,轻松下载。

链接:https://pan.baidu.com/s/1rIRQje7ogW2CHsp_FBYdFw 
提取码:1111

 

下面是课设报告的具体内容(格式有些乱。再次说明:如果要word版,上面的链接中有~~~)

内蒙古工业大学课程设计(论文)任务书

课程名称:   专业创新实践     学院: 信息工程学院  班级:                          

学生姓名:                             学号:                         指导教师: 董**     沈**

一、题目

       基于FPGA的VGA游戏设计

  • 目的与意义

       随着半导体工艺的进步,现场可编程门阵列FPGA (Field Programmable Gate Array)近年来发展迅速,在航天、军工、电信领域获得了广泛的应用。不同于传统的单片机和处理器的软件设计,FPGA的开发通常采用硬件描述语言,需要对底层硬件时序有深入的理解。在本课程中,学生需要查阅科技文献和资料,熟悉VGA接口协议和PS/2接口协议,利用VHDL编程实现FPGA与外设电路的控制通信。通过以小组形式完成综合设计任务,培养学生利用专业知识解决复杂工程问题的能力,同时培养学生的创新精神与团队精神。

三、要求

       1. 查阅FPGA、VGA标准和PS/2接口的相关资料文献。

       2. 编写VHDL代码实现VGA显示模块和PS/2键盘输入模块,并下载到开发板进行测试。

       3. 完成整个系统的设计与实现,进行调试与完善。

       最终设计目标是实现一个通过VGA显示器进行显示并通过PS/2键盘进行控制的游戏。游戏具体内容和细节自行设计,鼓励学生充分发挥创意。

       4. 撰写课程设计说明书,详细说明设计实现的细节。

四、工作内容、进度安排

专业创新实践为期2周,具体进度安排如下:

       1. 分组和调研(1-2天):进行分组和分工,利用图书馆和网上检索等各种方式获取相关信息,熟悉接口协议,理解设计目标;

       2. 模块设计与测试(2-3天):在查阅资料的基础上,设计实现主要功能模块并进行测试;

       3. 系统设计与调试(3-5天):进一步完成整个系统的设计,通过调试进行完善,得到最终设计结果;

       4. 撰写报告与答辩(1-2天):以小组为单位进行演示和答辩,成员分别撰写设计说明书。

  • 主要参考文献

       1.《EDA技术与CPLD/FPGA开发应用简明教程(第2版)》刘爱荣等编著。

       2.《基于Quartus Prime的FPGA/CPLD数字系统设计实例(第4版)》周润景等编著。

审核意见

                                              系(教研室)主任(签字)                              

指导教师下达时间              年    月    日

                                              指导教师签字:________________________


 

摘  要

        本文主要从设计背景,设计思路,设计结果等方面阐述了一款用FPGA实现的弹球游戏。具有VGA显示,PS/2键盘输入,采用Cyclone IV EP4CE15F17C8芯片。弹球游戏:界面中显示一个球和一个长方形,初始位置固定:球在屏幕正中央,长方形在屏幕底部正中央。游戏开始后,玩家通过“A”和“D”键控制长方形在界面的底部左移和右移,按任意键长方形停止移动,球会一直在界面中四处移动,当球触碰到边框或者长方形的时候就会反弹,当长方形没有接住球的时候蜂鸣器就会响起,直至下一次小球触碰除底边界外的边界或者被长方形接住时蜂鸣器停止发声。球的颜色是黄色、长方形和边框的颜色均是叠加颜色。项目工程采用模块化设计,顶层文件是dynamic,子模块有driver驱动模块,displayVGA显示模块,square图形定义模块, keyscan键盘扫描模块,keyled键值传递模块。

关键字:VGA显示,PS/2键盘,FPGA弹球游戏


目   录

摘  要

一、背景介绍

1.1 FPGA简介/Altera FPGA简介/Cyclone IV简介/Quartus Prime简介

1.1.1 FPGA简介

1.1.2 Altera FPGA简介

1.1.3 Cyclone IV简介

1.1.4 Quartus Prime简介

1.2 VGA简介、PS/2简介

1.2.1 VGA简介

1.2.2 PS/2简介

二、系统设计

2.1 设计方案/模块划分

2.2 VGA显示

2.1.1 设计详述

2.1.2 功能仿真

2.1.3 板级调试

2.2 键盘控制

2.2.1 设计详述

2.2.2 功能仿真

2.1.3 板级调试

2.3 square图形定义模块

三、系统测试

四、设计感悟

参考文献


一、背景介绍

1.1 FPGA简介/Altera FPGA简介/Cyclone IV简介/Quartus Prime简介

1.1.1 FPGA简介

        FPGA设计不是简单的芯片研究,主要是利用 FPGA 的模式进行其他行业产品的设计。 与 ASIC 不同,FPGA在通信行业的应用比较广泛。通过对全球FPGA产品市场以及相关供应商的分析,结合当前我国的实际情况以及国内领先的FPGA产品可以发现相关技术在未来的发展方向,对我国科技水平的全面提高具有非常重要的推动作用。

        与传统模式的芯片设计进行对比,FPGA 芯片并非单纯局限于研究以及设计芯片,而是针对较多领域产品都能借助特定芯片模型予以优化设计。从芯片器件的角度讲,FPGA 本身构成 了半定制电路中的典型集成电路,其中含有数字管理模块、内嵌式单元、输出单元以及输入单元等。在此基础上,关于FPGA芯片有必要全面着眼于综合性的芯片优化设计,通过改进当前的芯片设计来增设全新的芯片功能,据此实现了芯片整体构造的简化与性能提升。

1.1.2 Altera FPGA简介

        Altera FPGA分为两大类,一种侧重低成本应用,容量中等,性能可以满足一般的逻辑设计要求,如Cyclone,CycloneII;还有一种侧重于高性能应用,容量大,性能能满足各类高端应用,如Stratix,StratixII等,用户可以根据自己实际应用要求进行选择。在性能可以满足的情况下,优先选择低成本器件。

        Altera FPGA分为两大类,一种侧重低成本应用,容量中等,性能可以满足一般的逻辑设计要求,如Cyclone,CycloneII;还有一种侧重于高性能应用,容量大,性能能满足各类高端应用,如Stratix,StratixII等,用户可以根据自己实际应用要求进行选择。在性能可以满足的情况下,优先选择低成本器件。

        Altera FPGA结合带有软件工具的可编程逻辑技术、知识产权(IP)和技术服务,在世界范围内为14,000多个客户提供高质量的可编程解决方案。我们新产品系列将可编程逻辑的内在优势——灵活性、产品及时面市——和更高级性能以及集成化结合在一起,专为满足当今大范围的系统需求而开发设计。

图1 Altera FPGA EP4CE15F17C8芯片

1.1.3 Cyclone IV简介

        Cyclone IV FPGA系列2009年推出,60nm工艺,面向对成本敏感的大批量应用,帮助您满足越来越大的带宽需求,同时降低了成本。

1.1.4 Quartus Prime简介

        Quartus II 是Altera公司的综合性CPLD/FPGA开发软件,原理图、VHDL、VerilogHDL以及AHDL(Altera Hardware 支持Description Language)等多种设计输入形式,内嵌自有的综合器以及仿真器,可以完成从设计输入到硬件配置的完整PLD设计流程。

        Quartus II可以在Windows、Linux以及Unix上使用,除了可以使用Tcl脚本完成设计流程外,提供了完善的用户图形界面设计方式。具有运行速度快,界面统一,功能集中,易学易用等特点。

        Quartus II支持Altera的IP核,包含了LPM/MegaFunction宏功能模块库,使用户可以充分利用成熟的模块,简化了设计的复杂性、加快了设计速度。对第三方EDA工具的良好支持也使用户可以在设计流程的各个阶段使用熟悉的第三方EDA工具。

        此外,Quartus II 通过和DSP Builder工具与Matlab/Simulink相结合,可以方便地实现各种DSP应用系统;支持Altera的片上可编程系统(SOPC)开发,集系统级设计、嵌入式软件开发、可编程逻辑设计于一体,是一种综合性的开发平台。

        Maxplus II 作为Altera的上一代PLD设计软件,由于其出色的易用性而得到了广泛的应用。目前Altera已经停止了对Maxplus II 的更新支持,Quartus II 与之相比不仅仅是支持器件类型的丰富和图形界面的改变。Altera在Quartus II 中包含了许多诸如SignalTap II、Chip Editor和RTL Viewer的设计辅助工具,集成了SOPC和HardCopy设计流程,并且继承了Maxplus II 友好的图形界面及简便的使用方法。

        Altera Quartus II 作为一种可编程逻辑的设计环境, 由于其强大的设计能力和直观易用的接口,越来越受到数字系统设计者的欢迎。

1.2 VGA简介、PS/2简介

1.2.1 VGA简介

        VGA(Video Graphics Array)视频图形阵列是IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)。

        VGA(Video Graphics Array)即视频图形阵列,具有分辨率高、显示速率快、颜色丰富等优点。VGA接口不但是CRT显示设备的标准接口,同样也是LcD液晶显示设备的标准接口,具有广泛的应用范围。 [1]

       随着电子产业及视频图像处理技术的发展,VGA(视频图形阵列)作为一种标准的显示接口在视频和计算机领域得到了广泛的应用,在图像处理中若是采用传统的数据传输方式来使高分辨率图像实时显示在显示器上,一般要求晶振频率达到40MHz以上,传统的电子电路难以达到这个速度,若采用专门的图像处理芯片,其设计难度大、开发成本高成为一个瓶颈选择。

图2 VGA插线

图3 VGA接口

图4 VGA通信协议

图5 VGA时序图

表1 VGA分辨率参数

1.2.2 PS/2简介

        PS/2是在较早之前,用于鼠标、键盘等设备。一般情况下,PS/2接口的鼠标为绿色,键盘为紫色。PS/2 原是“personal 2”的意思,“个人系统2”,是IBM公司在上个世纪80年代推出的一种个人电脑。以前完全开放的PC标准让IBM觉得利益受了损失。所以IBM设计了PS/2这种电脑,目的是重新定义PC标准,不再采用开放标准的方式。在这种电脑上IBM使用了新型MCA总线,新的OS/2操作系统。PS/2电脑上使用的键盘鼠标接口就是PS/2接口。因为标准不开放,PS/2电脑在市场中失败了。只有PS/2接口一直沿用到今天。

        PS/2接口是输入装置接口,而不是传输接口。所以PS2口根本没有传输速率的概念,只有扫描速率。在Windows环境下,ps/2鼠标的采样率默认为60次/秒,USB鼠标的采样率为120次/秒。较高的采样率理论上可以提高鼠标的移动精度。

        PS/2接口支持热插拔(相对于传统AT键盘而言),但插入后需先重新启动,有别于热插拔。

        PS/2可以与USB接口互转,即PS/2接口设备可以转成USB,USB接口设备也可以转成PS/2。

       早期,在Ps/2键盘中,包含了一个嵌入式的微控制器(如InDl,8048系列),以用来执行各项的工作并减少整个系统工作中的负担。微控制器所要作的工作就是监测所有的按键,以及当按键被按下或放开时,就回报给主机。

图6 PS/2接口键盘

图7 PS/2接口(紫色插键盘,绿色插鼠标)

图8 PS/2通信协议

二、系统设计

2.1 设计方案/模块划分

图9 设计框图

图10 流程框图

图11 VHDL结构框图

表2 VHDL端口定义关键字及意义

       本项目工程的顶层文件是dynamic,子模块有:driver驱动模块,displayVGA显示模块,square图形定义模块,keyscan键盘扫描模块,keyled键值传递模块,其中,square图形定义模块是本项目中最难也是最重要的一个模块。下面将进行详细讲解。

2.2 VGA显示

2.1.1 设计详述

        driver驱动模块:

        源代码+注释解读:

1.	library ieee;  
2.	use ieee.std_logic_1164.all;  
3.	entity driver is   --vga驱动模块 输入输出信号  
4.	port(  
5.	    rst_n : in std_logic;  --复位信号  
6.	    clk : in std_logic;    --vga驱动时钟  
7.	    animate : out std_logic; --动态显示刷新  
8.	    xpos    : out integer;   --当前像素点横坐标  
9.	    ypos    : out integer;   --当前像素点纵坐标  
10.	    data    : in std_logic_vector(2 downto 0);--VGA数据  
11.	    vga_h : out std_logic;   --行同步  
12.	    vga_v : out std_logic;   --场同步  
13.	    vga_r   : out std_logic; --红色  
14.	    vga_g   : out std_logic; --绿色  
15.	    vga_b   : out std_logic  --蓝色  
16.	);  
17.	end driver;  
18.	  
19.	architecture a of driver is  
20.	    constant h_sync : integer := 136;   --行同步  
21.	    constant h_back : integer := 160;     --显示后沿  
22.	    constant h_disp : integer := 1024;   --有效数据  
23.	    constant h_front    : integer := 24;    --显示前沿  
24.	    constant h_total    : integer := 1344;  --行显示周期  
25.	    constant v_sync : integer := 6;     --场同步  
26.	    constant v_back : integer := 29;    --显示后沿  
27.	    constant v_disp : integer := 768;   --有效数据  
28.	    constant v_front    : integer := 3;     --显示前沿  
29.	    constant v_total    : integer := 806;   --场显示周期  
30.	    signal cnt_h : integer;   --行计数器  
31.	    signal cnt_v : integer;   --场计数器  
32.	    signal vga_en : std_logic;  --判断行场计数器是否在有效数据段里  
33.	begin  
34.	    process(rst_n,clk)   --行计数器进程  
35.	    begin  
36.	        if rst_n = '0' then   --如果复位  
37.	            cnt_h <= 0;       --那么行计数器清零  
38.	        elsif clk'event and clk='1' then   --如果上升沿有效  
39.	            if cnt_h < (h_total-1) then    --如果cnt_h小于h_total-1  
40.	                cnt_h <= cnt_h + 1;        --那么行计数器加1  
41.	            else  
42.	                cnt_h <= 0;        --否则就是像素点已经到达行边界,行计数器清零  
43.	            end if;  
44.	        end if;  
45.	    end process;  
46.	      
47.	    process(rst_n,clk)   --场计数器进程  
48.	    begin  
49.	        if rst_n='0' then     --如果复位  
50.	            cnt_v <= 0;       --那么场计数器清零  
51.	        elsif clk'event and clk='1' then   --如果上升沿有效  
52.	            if cnt_h = (h_total-1) then    --如果cnt_h等于h_total-1,表示到达行边界  
53.	                if cnt_v < (v_total-1) then--如果cnt_v小于v_total-1  
54.	                    cnt_v <= cnt_v + 1;    --那么场计数器加1  
55.	                else  
56.	                    cnt_v <= 0;--否则就是像素点已经到达右下角边界处,场计数器清零  
57.	                end if;  
58.	            end if;  
59.	        end if;           
60.	    end process;  
61.	      
62.	--vga行场同步信号的产生    
63.	    vga_h <= '0' when cnt_h < h_sync else '1';   --行同步时序产生  
64.	    vga_v <= '0' when cnt_v < v_sync else '1';   --场同步时序产生  
65.	      
66.	--圆使能信号  
67.	    animate <= '1' when (cnt_h=h_total-1) and (cnt_v=v_total-1) else '0';   
68.	    --当像素点已经到达右下角边界处时,animat=1,否则=0  
69.	  
70.	--像素点在显示屏中的位置(有效数据段里)  
71.	    xpos <= cnt_h - (h_sync + h_back);  
72.	    ypos <= cnt_v - (v_sync + v_back);  
73.	      
74.	--vga_en标注行场有效数据段,1为有效,0为无效  
75.	    vga_en <= '1' when  (cnt_h >= h_sync+h_back) and (cnt_h < h_sync+h_back+h_disp) and  
76.	                        (cnt_v >= v_sync+v_back) and (cnt_v < v_sync+v_back+v_disp)  
77.	                  else '0';  
78.	--当vga_en为1时将颜色放置到数据线上,否则为零就不放  
79.	    vga_r <= data(2) when vga_en='1' else '0';  
80.	    vga_g <= data(1) when vga_en='1' else '0';  
81.	    vga_b <= data(0) when vga_en='1' else '0';  
82.	end a;  

        代码设计思路:architecture常量设置:由于本文想做一个1024*768分辨率的VGA显示,所以在architecture里配置的行、场同步,显示前、后沿,有效数据,行、场显示周期等参数都参照《专业创新实践简介》里提供的数值进行设置。

       行计数器进程:异步清零,同步计数,如果当前像素点没有到达该行的最后一个像素点那么就继续向右扫描显示,否则,把行计数器清零,将当前像素点拉回最左侧的点,开始下一次扫描显示。

       场计数器进程:异步清零,同步计数,如果当前像素点到达该行的最后一个像素点,并且当前像素点没有到达最后一行,那么就把场计数器加一,将当前像素点移到下一行进行扫描,否则,把场计数器清零,将当前像素点拉回第一行,开始下一次扫描显示。

        VGA动态显示刷新:当像素点已经到达右下角边界处时,animat=1,此时会执行square中的相关刷新代码(后面详述),否则,animat=0,不动态刷新。VGA的颜色是由data控制的,从高位到低位的颜色顺序是RGB。

        displayVGA显示模块:

       源代码+注释解读:

1.	library ieee;  
2.	use ieee.std_logic_1164.all;  
3.	  
4.	entity display is  
5.	port(  
6.	    rst_n : in std_logic;  --复位信号  
7.	    clk : in std_logic;    --时钟  
8.	    animate : in std_logic;--动态显示刷新  
9.	    xpos : in integer;   --当前像素点横坐标  
10.	    ypos : in integer;   --当前像素点纵坐标  
11.	    data : out std_logic_vector(2 downto 0);--颜色信号RGB  
12.	    ps2_data : in std_logic;--键盘数据  
13.	    ps2_clk  : in std_logic;--键盘时钟  
14.	    buzzer1: out std_logic--蜂鸣器  
15.	);  
16.	end display;  
17.	  
18.	architecture a of display is  
19.	  
20.	constant h_disp : integer := 1024;--显示屏的长  
21.	constant v_disp : integer := 768; --显示屏的宽  
22.	constant blue_w : integer := 20;  --边框宽  
23.	  
24.	signal x2,y2 : integer;  
25.	  
26.	component square is  
27.	generic(  
28.	    H_SIZE  : integer := 100;  
29.	    IX1     : integer;  
30.	    IY1     : integer;  
31.	    IX2     : integer;  
32.	    IY2     : integer;  
33.	    IX_DIR  : std_logic;  
34.	    IY_DIR  : std_logic  
35.	);  
36.	port(  
37.	    rst_n   : in std_logic;  
38.	    clk     : in std_logic;  
39.	    animate : in std_logic;  
40.	    x1      : buffer integer;  
41.	    y1      : buffer integer;  
42.	    x11 : out integer;  
43.	    x21 : out integer;  
44.	    y11 : out integer;  
45.	    y21 : out integer;  
46.	   ps2_data : in std_logic;  
47.	    ps2_clk  : in std_logic;  
48.	    buzzer  : out std_logic  
49.	      
50.	);  
51.	end component;  
52.	  
53.	signal  sq1_x1,sq1_x2,sq1_y1,sq1_y2 ,sq2_x1,sq2_x2,sq2_y1,sq2_y2 : integer;  
54.	signal sq1,sq2,sq3 : std_logic_vector(2 downto 0);   
55.	begin  
56.	  
57.	U1 : square  
58.	generic map(  
59.	    H_SIZE  => 100,--球的半宽  
60.	    IX1     => 400,--球的初始位置横坐标  
61.	    IY1     => 300,--球的初始位置纵坐标  
62.	    IX2     => 512,--长方形的初始位置横坐标  
63.	    IY2     => 718,--长方形的初始位置纵坐标  
64.	    IX_DIR  => '0',--球的移动方向  
65.	    IY_DIR  => '1' --球的移动方向  
66.	  
67.	)  
68.	port map(  
69.	    rst_n   => rst_n,  
70.	    clk     => clk,  
71.	    animate => animate,  
72.	    x11     => sq2_x1,  
73.	    x21     => sq2_x2,  
74.	    y11     => sq2_y1,  
75.	    y21     => sq2_y2,  
76.	    x1      => sq1_x1,  
77.	    y1      => sq1_x2,  
78.	    ps2_data => ps2_data,  
79.	    ps2_clk  => ps2_clk,  
80.	    buzzer  => buzzer1  
81.	      
82.	);  
83.	  
84.	sq1 <="110" when ((xpos-sq1_x1)*(xpos-sq1_x1)+ (ypos-sq1_x2)*(ypos-sq1_x2)<=100)  
85.	            else "100"; --球的颜色是黄色,球外是红色   
86.	                  
87.	sq2 <="111" when (xpos>sq2_x1 and xpos<sq2_x2 and ypos>sq2_y1 and ypos<sq2_y2)  
88.	            else "000"; --长方形的颜色是灰色加红色(注意不是白色),长方形外是黑色  
89.	  
90.	sq3 <="011" when ((xpos<blue_w) or (xpos >= h_disp-blue_w) or (ypos<blue_w) or (ypos >=v_disp-blue_w))   
91.	            else "000";--边界的颜色是青色加红色,边界外是黑色  
92.	   
93.	process(clk)  
94.	    begin  
95.	        data<=sq1 or sq2 or sq3;  
96.	end process;  
97.	end a;

       设计思路:例化声明square模块,有视频分辨率等计算得出球和长方形的初始位置,把长方形放在下方中间位置,把小球放在大约屏幕中央处。sq1、sq2、sq3分别表示球的颜色,长方形的颜色、边界的颜色。经过试验发现,111并不能显示白色,而是需要类似黄色+蓝色、青色+红色等叠加得到,但是,最后发现白色并不太好看,所以选用:球的颜色是黄色,球外是红色;长方形的颜色是灰色加红色(注意不是白色),长方形外是黑色;边界的颜色是青色加红色,边界外是黑色。

2.1.2 功能仿真

图12 VGA功能仿真

2.1.3 板级调试

图13 VGA板级调试

2.2 键盘控制

2.2.1 设计详述

        keyscan键盘扫描模块:

        源代码+注释解读:

1.	library ieee;  
2.	use ieee.std_logic_1164.all;  
3.	  
4.	entity keyscan is  
5.	port(  
6.	    rst_n     : in std_logic;--复位  
7.	    clk       : in std_logic;--时钟  
8.	    ps2_data  : in std_logic;--键盘数据  
9.	    ps2_clk   : in std_logic;--键盘时钟  
10.	    ps2_byte  : out std_logic_vector(7 downto 0);--读取的键盘数据值  
11.	    ps2_flag  : out std_logic--标志位,1键盘被按下,0键盘没有被按下  
12.	);  
13.	end keyscan;  
14.	  
15.	architecture a of keyscan is  
16.	  
17.	    signal byte_cnt  : integer;--时钟计数器  
18.	    signal temp_data : std_logic_vector(7 downto 0);  
19.	    signal f0_flag   : std_logic;  
20.	    signal ps2_clk_1, ps2_clk_2  : std_logic;  
21.	  
22.	begin  
23.	  
24.	    process(rst_n, clk)  
25.	    begin  
26.	        if rst_n='0' then  
27.	            ps2_clk_1<='0';  
28.	            ps2_clk_2<='0';  
29.	        elsif clk'event and clk='1' then  
30.	--虽然FPGA的时钟频率远大于键盘的时钟频率,但依旧会存在FPGA漏读键盘的时钟的情况,  
31.	--因此,需要用ps2_clk_1表示当前键盘时钟,ps2_clk_2表示上一时刻键盘的时钟,通过  
32.	--比较二者,即可分辨出此刻键盘的时钟是上升沿还是下降沿了。  
33.	            ps2_clk_1 <=ps2_clk;  
34.	            ps2_clk_2 <=ps2_clk_1;  
35.	        end if;  
36.	    end process;  
37.	              
38.	    process(rst_n, clk)   
39.	    begin  
40.	        if rst_n='0' then--复位  
41.	            byte_cnt <=0;  
42.	            temp_data <= (others=>'0');  
43.	        elsif clk'event and clk='1' then  
44.	            if ps2_clk_1='0' and ps2_clk_2='1' then  
45.	            --键盘时钟下降沿,上一时刻是1,此刻是0,表示下降沿  
46.	                if byte_cnt=0 then--如果是起始位  
47.	                    byte_cnt <= byte_cnt + 1;--则加一跳过  
48.	                elsif byte_cnt<9 then--如果是数据位  
49.	                    byte_cnt <= byte_cnt + 1;--加一  
50.	                    temp_data <= ps2_data & temp_data(7 downto 1);--并且左移读入数据  
51.	                elsif byte_cnt< 10 then--如果是校验位和结尾位  
52.	                    byte_cnt <= byte_cnt + 1;--则加一跳过,我们就不校验了  
53.	                else  
54.	                    byte_cnt <= 0;--一个数据包发送完后,清零  
55.	              
56.	                end if;  
57.	            end if;  
58.	        end if;  
59.	    end process;  
60.	              
61.	    process(rst_n, clk)  
62.	    begin  
63.	        if rst_n='0' then  
64.	            ps2_byte <= x"00";  
65.	            ps2_flag <= '0';  
66.	            f0_flag <= '0';  
67.	        elsif clk'event and clk='1' then  
68.	            if byte_cnt=10 then --如果byte_cnt=10  
69.	                if temp_data=x"F0" then--如果temp_data=x"F0"  
70.	                    f0_flag <= '1';--那么标志f0_flag=1  
71.	                elsif f0_flag='1' then--否则如果f0_flag=1  
72.	                    ps2_byte <= temp_data;--则把temp_data的值传给ps2_byte  
73.	                    ps2_flag <= '1';--把标志ps2_flag置1  
74.	                    f0_flag <= '0';--把标志f0_flag置0  
75.	                else   
76.	                    ps2_flag <= '0';--否则把标志ps2_flag置0  
77.	                end if;  
78.	            end if;  
79.	        end if;  
80.	    end process;  
81.	end a;  

       设计思路:键盘发送数据时,每个数据包总共有11位,即1位起始位,8位数据位,1位校验位,1位停止位,keyscan键盘扫描模块就是用来实现这个通信协议的。模块中共有2个时钟,一个是系统时钟,另一个是键盘时钟,需要用系统时钟扫描键盘时钟,虽然FPGA的时钟频率远大于键盘的时钟频率,但依旧会存在FPGA漏读键盘时钟的情况,因此,需要用ps2_clk_1表示当前键盘时钟,ps2_clk_2表示上一时刻键盘的时钟,通过比较二者,即可分辨出此刻键盘的时钟是上升沿还是下降沿了。后两个进程主要用于封装按键读取功能,不对外显示代码细节,只要有按键输入ps2_flag=1,否则ps2_flag=0。

        keyled键值传递模块:

        源代码+注释解读:

1.	library ieee;  
2.	use ieee.std_logic_1164.all;  
3.	  
4.	entity keyled is  
5.	port(  
6.	    rst_n      : in std_logic;  
7.	    clk        : in std_logic;  
8.	    ps2_data   : in std_logic;  
9.	    ps2_clk    : in std_logic;  
10.	    ps2_dir    : out integer  
11.	);  
12.	end  keyled;  
13.	  
14.	architecture a of  keyled is  
15.	  
16.	component keyscan is  
17.	    port(  
18.	        rst_n    : in std_logic;  
19.	        clk      : in std_logic;  
20.	        ps2_data : in std_logic;  
21.	        ps2_clk  : in std_logic;  
22.	        ps2_byte : out std_logic_vector(7 downto 0);  
23.	        ps2_flag : out std_logic  
24.	        );  
25.	end component;  
26.	      
27.	signal ps2_flag,ps2_flag_1, ps2_flag_2 : std_logic;  
28.	signal ps2_byte : std_logic_vector(7 downto 0);  
29.	  
30.	begin  
31.	U1 : keyscan   
32.	    port map(  
33.	        rst_n    => rst_n,  
34.	        clk      => clk,  
35.	        ps2_data => ps2_data,  
36.	        ps2_clk  => ps2_clk,  
37.	        ps2_byte => ps2_byte,  
38.	        ps2_flag => ps2_flag  
39.	    );  
40.	  
41.	process(rst_n,clk)  
42.	begin  
43.	    if rst_n='0' then  
44.	        led <= "0000";  
45.	    elsif clk'event and clk='1' then  
46.	        if ps2_flag_2='0' and ps2_flag_1='1' then--如果是上升沿  
47.	            case ps2_byte is  
48.	                when x"1C"=>--当按下A键时,长方形向左走  
49.	                    ps2_dir <= 1 ;  
50.	                when x"23"=>--当按下D键时,长方形向右走  
51.	                    ps2_dir <= 2 ;  
52.	                when others=>--当按下任意其他键时,长方形暂停移动  
53.	                    ps2_dir <= 0 ;  
54.	            end case;  
55.	        end if;  
56.	    end if;  
57.	end process;  
58.	  
59.	process(rst_n,clk)  
60.	begin  
61.	    if rst_n='0' then  
62.	        ps2_flag_1 <= '1';  
63.	        ps2_flag_2 <= '1';  
64.	    elsif clk'event and clk='1' then--通过ps2_flag_1和ps2_flag_2实现精准读取键盘时钟的功能  
65.	        ps2_flag_1 <= ps2_flag;  
66.	        ps2_flag_2 <= ps2_flag_1;  
67.	    end if;  
68.	end process;  
69.	  
70.	end a; 

       设计思路:例化声明keyled键值传递模块,当按下A键时,长方形向左走,ps2_dir=1;当按下D键时,长方形向右走,ps2_dir=2;当按下任意其他键时,长方形暂停移动,ps2_dir=0。ps2_dir最终会传递到square模块中,此时会执行square中的相关代码刷新长方形的位置(后面详述)。

2.2.2 功能仿真

图14 PS/2功能仿真

2.1.3 板级调试

图15 PS/2板级调试

2.3 square图形定义模块

1.	library ieee;  
2.	use ieee.std_logic_1164.all;  
3.	use ieee.std_logic_unsigned.all;  
4.	  
5.	entity square is  
6.	generic(  --类属表  
7.	    H_SIZE  : integer;--半宽  
8.	    IX1     : integer;--球  
9.	    IY1     : integer;  
10.	    IX2     : integer;--长方形  
11.	    IY2     : integer;  
12.	    IX_DIR  : std_logic;--‘1’右移,‘0’左移  
13.	    IY_DIR  : std_logic --‘1’下移,‘0’上移  
14.	);  
15.	port(  
16.	    rst_n   : in std_logic;--复位信号  
17.	    clk     : in std_logic;--时钟  
18.	    animate : in std_logic;--动态显示刷新  
19.	    led     : out std_logic_vector(3 downto 0);  
20.	    x1      : buffer integer;  
21.	    y1      : buffer integer;  
22.	    x11     : out integer;--左边界  
23.	    x21     : out integer;--右边界  
24.	    y11     : out integer;--上边界  
25.	    y21     : out integer;--下边界  
26.	    ps2_clk : in std_logic;--键盘时钟  
27.	    ps2_data: in std_logic;--键盘数据  
28.	    buzzer  : out std_logic--蜂鸣器  
29.	);  
30.	end square;  
31.	  
32.	architecture a of square is  
33.	  
34.	component keyled is  
35.	port(  
36.	    rst_n   : in std_logic;  
37.	    clk     : in std_logic;  
38.	    ps2_clk : in std_logic;  
39.	    ps2_data: in std_logic;  
40.	    led     : out std_logic_vector(3 downto 0) ;  
41.	    ps2_dir : out integer   
42.	);  
43.	end component;  
44.	  
45.	    constant D_WIDTH    : integer :=1024;--方块移动范围  
46.	    constant D_HEIGHT   : integer :=768; --方块移动范围  
47.	    constant blue_w : integer :=20;      --边界  
48.	    signal x2,y2 : integer;              --方块中心位置坐标  
49.	    signal x1_dir,y1_dir,x2_dir,y2_dir  : std_logic;  
50.	    signal sq1 : std_logic;  
51.	    signal sq2 : std_logic;  
52.	    signal ps2_d : integer;  
53.	      
54.	begin  
55.	    x11 <= x2-H_SIZE/2;--长方形的左边  
56.	    x21 <= x2+H_SIZE/2;--长方形的右边  
57.	    y11 <= y2-H_SIZE/8;--长方形的上边  
58.	    y21 <= y2+H_SIZE/8;--长方形的下边  
59.	      
60.	U1 : keyled  
61.	port map(  
62.	    rst_n   =>rst_n,  
63.	    clk     =>clk,  
64.	    ps2_data  =>ps2_data,  
65.	    ps2_clk   =>ps2_clk,  
66.	    ps2_dir  =>ps2_d  
67.	);  
68.	  
69.	  
70.	process(rst_n,clk)   --圆球屏幕方框中移动  
71.	    begin  
72.	        if rst_n='0' then  
73.	            x1 <= IX1;  
74.	            y1 <= IY1;  
75.	            x1_dir <= IX_DIR;  
76.	            y1_dir <= IY_DIR;  
77.	              
78.	            x2 <= IX2;  
79.	            y2 <= IY2;  
80.	            x2_dir <= IX_DIR;  
81.	            y2_dir <= IY_DIR;  
82.	              
83.	        elsif clk'event and clk='1' then  
84.	            if animate='1' then  
85.	                if x1_dir='1' then--如果x1_dir='1'  
86.	                    x1 <= x1 + 1;--那么小球向右移  
87.	                else  
88.	                    x1 <= x1 - 1;--否则小球向左移  
89.	                end if;  
90.	                  
91.	                if y1_dir='1' then--如果x1_dir='0'  
92.	                    y1 <= y1 + 1;--那么小球向下移  
93.	                else  
94.	                    y1 <= y1 - 1;--那么小球向上移  
95.	                end if;  
96.	                              
97.	                if x1<=30 then--如果小球触碰左边界  
98.	                    x1_dir <= '1';--那么变为小球向右移  
99.	                    buzzer <= '1';--蜂鸣器不响  
100.	                elsif x1>=(D_WIDTH-blue_w-10-1) then--如果小球触碰右边界  
101.	                    x1_dir <= '0';--那么变为小球向左移  
102.	                    buzzer <= '1';--蜂鸣器不响  
103.	                elsif (((x2-x1)**2<=((H_SIZE/2)**2+(10+H_SIZE/8)**2)) and(abs(y2-y1)=(10+H_SIZE/8))) then  
104.	                    --如果小球和长方形触碰  
105.	                    x1_dir <= '0';--那么变为小球向左移  
106.	                    buzzer <= '1';--蜂鸣器不响  
107.	                end if;  
108.	  
109.	                if y1<=30 then--如果小球触碰上边界  
110.	                    y1_dir <= '1';--那么变为小球向下移  
111.	                    buzzer <= '1';--蜂鸣器不响  
112.	                elsif y1>=(D_HEIGHT-blue_w-10-1) then--如果小球触碰下边界,也就是长方形没接住小球  
113.	                    y1_dir <= '0';--那么变为小球向上移  
114.	                    buzzer <= '0';--蜂鸣器响           
115.	                elsif (((x2-x1)**2<=((H_SIZE/2)**2+(10+H_SIZE/8)**2)) and (abs(y2-y1)=(10+H_SIZE/8))) then  
116.	                    --如果小球和长方形触碰  
117.	                    y1_dir <= '0';--那么变为小球向上移  
118.	                    buzzer <= '1';--蜂鸣器响  
119.	                end if;  
120.	  
121.	                case ps2_d is   
122.	                    when  2 =>--当ps2_d=2时  
123.	                      if x2>=D_WIDTH-H_size/2 then--如果长方形已经到达屏幕最右边  
124.	                      x2 <= x2;--那么长方形保持不动  
125.	                      else x2 <= x2+5;--否则长方形向右移动  
126.	                      end if;  
127.	                    when  1 =>--当ps2_d=1时  
128.	                      if x2<=0+H_size/2 then--如果长方形已经到达屏幕最左边  
129.	                      x2 <= x2;--那么长方形保持不动  
130.	                      else x2 <= x2-5;--否则长方形向左移动  
131.	                      end if;  
132.	                    when others=>--当ps2_d=其他时  
133.	                      x2 <= x2;--长方形保持不动  
134.	                end case;             
135.	            end if;  
136.	        end if;  
137.	    end process;          
138.	end a;  

       设计思路:该模块主要用于定义小球,长方形与边界的移动规则、运动形态等,是整个工程中最难、最重要的模块。85-95行主要用于实现小球的移动,每一帧图片,小球移动一个像素。97-119行用于实现小球与边界和长方形的相对位置的判定。121-134行用于实现按键控制长方形移动。

三、系统测试

图16 顶层文件结构框图

图17 mypll库文件结构框图

图18 U1 driver文件结构框图

图19 U2 display文件结构框图

四、设计感悟

        本次课设还是比较有趣的,第一次做游戏,而且还用的是FPGA。起初打算做一个带有接球后球速增加的功能,但是由于时间关系,这个功能一直没实现,有些遗憾,以后有机会一定实现。好在最后加入了蜂鸣器报警功能,使得这个项目工程有别于其他组的工程。FPGA这学期刚刚接触,它不同于单片机的地方很多,比如在模块化设计上:单片机常用的是C语言或python语言进行编程,更容易实现功能,尤其是用python进行编程,因为有大量的函数库,所以编程更为简单,而FPGA采用的是VHDL语言编写,该语言更底层一些,代码员要考虑信号和信号之间的联系,端口和端口之间的联系,尤其是在本文中提到的弹球游戏,需要用到VGA和PS/2,在实现二者的通信协议上就比单片机复杂了许多。本工程用到了模块化设计的思想,VHDL的模块化需要精确到连接每一个端口,使得代码量大为增加,而且还对代码员提出了更高的专业素质要求。

       我觉得本次课设最大的收货是第一次自己实际动手编写了通信协议,让外部设备能和上位机通信,以前总是学习IIC,CAN等的通信,但是从未自己动手去编写过它们的通信协议,我相信这次课设将对我未来开发项目起到很大的作用。同时,这一次课设,自己也用底层语言真真正正的编写了一个硬件程序,以前都是用高级语言编写,从未体验过底层语言编写硬件功能的快乐。

参考文献

[1]胡迎刚.基于FPGA的VGA图像控制器设计与实现[J].计算机光盘软件与应用,2010,(7):146-146.

[2]韦吉爵.基于FPGA的游戏平台开发[J].轻工科技,2010,26(5):59-60.

[3]桂淮濛.基于FPGA的VGA图像显示设计[J].电脑编程技巧与维护,2019,(8):136-138.

[4]陈权,朱卫华,曹亮,陈志勇.基于FPGA的图像叠加及VGA显示设计[J].南华大学学报,2017,31(3):55-59.

[5]蔡志民,陈晓芳,刘志坚.基于FPGA的乒乓游戏机设置[J].微计算机信息,2010,(5):159-160.

[6]薛枫,乔磊;基于FPGA的VGA控制原理[A];2020电力行业信息化年会 [C]; 2010年.

[7]申中杰,王素珍,胡安峰,宗卫华.基于FPGA的VGA多幅图像动态显示系统[J].单片机与嵌入式系统应用,2018,18(1):52-56.

[8]陈启武,白天蕊.基于FPGA的VGA显示系统的设计与实现[J].电子产品世界,2016,23(S01):48-50.

[9]彭若晨,闫妍.基于可编程逻辑器件(FPGA)为核心的PS/2接口键盘的输入输出电路的设计[J].电子设计工程,2015,(11):128-130.

[10]杨秀增,黎运宇.基于FPGA的标准PS/2键盘接口电路设计[J].广西民族师范学院学报,2009,26(3):129-131.

[11]韩娇,李金娜.PS/2接口键盘控制器的工作原理及硬件实现[J].电脑知识与技术:经验技巧,2012,08(4):836-838.

[12]王勇,宋潇,孙孟方.基于EDA技术的PS/2接口电路设计[J].电脑与电信,2014,(5):33-34.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值