2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html
第十九章 VGA方块移动实验
在VGA彩条显示实验中,我们成功地在显示器上显示出了静态的彩条图案。本章我们通过
在屏幕上显示一个移动的小方块,来给大家演示如何使用VGA接口显示动态图案。
本章包括以下几个部分:
19.1 VGA简介
19.2 实验任务
19.3 硬件设计
19.4 程序设计
19.5 下载验证
VGA简介
我们在“VGA彩条显示实验”中对VGA视频传输标准作了详细的介绍,包括VGA接口定义、
行场同步时序、以及显示分辨率等。如果大家对这部分内容不是很熟悉的话,请参考“VGA彩
条显示实验”中的VGA简介部分。
实验任务
本章的实验任务是使用开拓者开发板上的VGA接口在显示器上显示一个不停移动的方块,
要求方块移动到边界处时能够改变移动方向。显示分辨率为640*480,刷新速率为60hz。
硬件设计
VGA接口部分的硬件设计原理及本实验中各端口信号的管脚分配与“VGA彩条显示实验”完
全相同,请参考“VGA彩条显示实验”中的硬件设计部分。
程序设计
图 19.4.1是根据本章实验任务画出的系统框图。其中,时钟分频模块负责产生像素时钟,
VGA驱动模块产生行场同步信号及像素点的纵横坐标,VGA显示模块输出图像数据。
图 19.4.1 VGA方块移动实验系统框图
在“VGA彩条显示实验”中,我们利用VGA驱动模块输出的像素点的横坐标,在VGA显示模
块中完成了彩条图案的绘制。而在本次实验中,为了完成方块的显示,需要同时使用像素点的
横坐标和纵坐标来绘制方块所在的矩形区域,另外还需要知道矩形区域左上角的顶点坐标。由
于VGA显示的图像在行场同步信号的同步下不停的刷新,因此只要连续改变方块左上角顶点的
坐标,并在新的坐标点处重新绘制方块,即可实现方块移动的效果。
各模块端口及信号连接如图 19.4.2所示:
图 19.4.2 顶层模块原理图
图 19.4.2中的顶层模块(vga_blockmove)、时钟分频模块(vga_pll)以及VGA驱动模块
(vga_driver)均与“VGA彩条显示实验”完全相同,只对VGA显示模块(vga_display)作了
修改。因此,这里我们重点讲解VGA显示模块,其他部分大家可以参考“VGA彩条显示实验”。
VGA显示模块的代码如下:
1
module vga_display
(
2
input vga_clk
, //VGA驱动时钟
3
input sys_rst_n
, //复位信号
4
5
input [ 9
:0
] pixel_xpos
, //像素点横坐标
6
input [ 9
:0
] pixel_ypos
, //像素点纵坐标
7
output reg [15
:0
] pixel_data //像素点数据
8
);
9
10 //parameter define
11
parameter H_DISP
= 10'd640
; //分辨率——行
12
parameter V_DISP
= 10'd480
; //分辨率——列
13
14
localparam SIDE_W
= 10'd40
; //边框宽度
15
localparam BLOCK_W
= 10'd40
; //方块宽度
16
localparam BLUE
= 16'b00000_000000_11111
; //边框颜色 蓝色
17
localparam WHITE
= 16'b11111_111111_11111
; //背景颜色 白色
18
localparam BLACK
= 16'b00000_000000_00000
; //方块颜色 黑色
19
20 //reg define
21
reg [ 9
:0
] block_x
; //方块左上角横坐标
22
reg [ 9
:0
] block_y
; //方块左上角纵坐标
23
reg [21
:0
] div_cnt
; //时钟分频计数器
24
reg h_direct
; //方块水平移动方向,1:右移,0:左移
25
reg v_direct
; //方块竖直移动方向,1:向下,0:向上
26
27 //wire define
28
wire move_en
; //方块移动使能信号,频率为100hz
29
30 //*****************************************************
31 //** main code
32 //*****************************************************
33
assign move_en
= (div_cnt
== 22'd250000
- 1'b1
) ? 1'b1
: 1'b0
;
34
35 //通过对vga驱动时钟计数,实现时钟分频
36
always @(posedge vga_clk
or negedge sys_rst_n
) begin
37
if (!sys_rst_n
)
38 div_cnt
<= 22'd0
;
39
else begin
40
if(div_cnt
< 22'd250000
- 1'b1
)
41 div_cnt
<= div_cnt
+ 1'b1
;
42
else
43 div_cnt
<= 22'd0
; //计数达10ms后清零
44
end
45
end
46
47 //当方块移动到边界时,改变移动方向
48
always @(posedge vga_clk
or negedge sys_rst_n
) begin
49
if (!sys_rst_n
) begin
50 h_direct
<= 1'b1
; //方块初始水平向右移动
51 v_direct
<= 1'b1
; //方块初始竖直向下移动
52
end
53
else begin
54
if(block_x
== SIDE_W
- 1'b1
) //到达左边界时,水平向右
55 h_direct
<= 1'b1
;
56
else //到达右边界时,水平向左
57
if(block_x
== H_DISP
- SIDE_W
- BLOCK_W
)
58 h_direct
<= 1'b0
;
59
else
60 h_direct
<= h_direct
;
61
62
if(block_y
== SIDE_W
- 1'b1
) //到达上边界时,竖直向下
63 v_direct
<= 1'b1
;
64
else //到达下边界时,竖直向上
65
if(block_y
== V_DISP
- SIDE_W
- BLOCK_W
)
66 v_direct
<= 1'b0
;
67
else
68 v_direct
<= v_direct
;
69
end
70
end
71
72 //根据方块移动方向,改变其纵横坐标
73
always @(posedge vga_clk
or negedge sys_rst_n
) begin
74
if (!sys_rst_n
) begin
75 block_x
<= 22'd100
; //方块初始位置横坐标
76 block_y
<= 22'd100
; //方块初始位置纵坐标
77
end
78
else if(move_en
) begin
79
if(h_direct
)
80 block_x
<= block_x
+ 1'b1
; //方块向右移动
81
else
82 block_x
<= block_x
- 1'b1
; //方块向左移动
83
84
if(v_direct
)
85 block_y
<= block_y
+ 1'b1
; //方块向下移动
86
else
87 block_y
<= block_y
- 1'b1
; //方块向上移动
88
end
89
else begin
90 block_x
<= block_x
;
91 block_y
<= block_y
;
92
end
93
end
94
95 //给不同的区域绘制不同的颜色
96
always @(posedge vga_clk
or negedge sys_rst_n
) begin
97
if (!sys_rst_n
)
98 pixel_data
<= BLACK
;
99
else begin
100
if((pixel_xpos
< SIDE_W
) || (pixel_xpos
>= H_DISP
- SIDE_W
)
101
|| (pixel_ypos
< SIDE_W
) || (pixel_ypos
>= V_DISP
- SIDE_W
))
102 pixel_data
<= BLUE
; //绘制边框为蓝色
103
else
104
if((pixel_xpos
>= block_x
) && (pixel_xpos
< block_x
+ BLOCK_W
)
105
&& (pixel_ypos
>= block_y
) && (pixel_ypos
< block_y
+ BLOCK_W
))
106 pixel_data
<= BLACK
; //绘制方块为黑色
107
else
108 pixel_data
<= WHITE
; //绘制背景为白色
109
end
110
end
111
112
endmodule
代码中14至18行声明了一系列的变量,方便大家修改边框尺寸、方块大小、以及各部分的
颜色等。其中边框尺寸和方块宽度均以像素点为单位。当方块的宽度确定时,如果我们知道方
块左上方顶点的坐标,就能轻而易举的画出整个方块区域。因此,我们将方块的移动简化为其
左上角顶点的移动。
由于VGA驱动时钟相对于方块移动速度而言过快,我们通过计数器对时钟计数,得到一个
频率为100hz的脉冲信号move_en,用它作为使能信号来控制方块的移动(33~45行)。方块的
移动方向分为水平方向h_direct和竖直方向v_direct,由程序的47至70行可知,当方块移动到
上下边框时,竖直移动方向改变;当方块移动到左右边框时,水平移动方向改变。程序72至93行根据方块的移动方向,在使能信号move_en到来时改变其左上角顶点的纵横坐标值。当
move_en的频率为100hz时,方块每秒钟在水平和竖直方向上分别移动100个像素点的距离,也
可以通过调整move_en的频率,来加快或减慢方块移动的速度。
程序95至110行根据VGA驱动模块输出的纵横坐标判断当前像素点所在的区域,对不同区域
中的像素点赋以不同的颜色值,从而实现边框、方块以及背景颜色的绘制。
图 19.4.3为VGA显示模块显示一行图像时SignalTap抓取的波形图,图中包含了一个完整
的行扫描周期,其中的有效图像部分被划分为三个不同的颜色区域:左右两侧蓝色边框
(pixel_data = 001F)、中间虚线处的黑色方块(pixel_data = 0000)、以及两者之间的白
色背景区域(pixel_data = FFFF)。
图 19.4.3 SignalTap波形图
下载验证
首先我们打开VGA方块移动工程,在工程所在的路径下打开vga_blockmove/par文件夹,在
里面找到“vga_blockmove.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以及
下划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 19.5.1所示。
图 19.5.1 VGA方块移动工程
然后将VGA连接线一端连接显示器,另一端与开发板上的VGA接口连接。再将下载器一端连
电脑,另一端与开发板上对应端口连接,最后连接
电源线并打开电源开关。
图 19.5.2 开拓者VGA接口
接下来我们下载程序,验证VGA显示方块移动的功能。
工程打开后通过点击工具栏中的“Programmer”图标打开下载界面,通过“Add File”按
钮选择vga_blockmove/par/output_files目录下的“vga_ blockmove.sof”文件。开发板电源
打开后,在程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为
“USB-Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板
中,如图 19.5.3所示。
图 19.5.3 程序下载界面
下载完成后观察显示器显示的图案如图 19.5.4所示,图中的黑色方块能够不停的移动,
且碰撞到蓝色边框时能改变移动方向,说明VGA方块移动程序下载验证成功。
图 19.5.4 VGA方块移动效果图