绘图板
作为 VGA 显示器的一个简单而不失趣味的应用,我们将实现一个绘图板。用户可以通过按键来选择画笔的颜色,移动画笔,以绘制图形。这一过程对于加深对 VGA 显示器的理解也有很大帮助。
3.2.1 系统设计
整个项目可以分为如下的几个部分:
- PU:绘画单元
- VRAM:画布
- DU:显示单元
其中,VRAM
与 DU
我们已在前面一节完成了,所以这里的主要任务就是设计 PU
。
★ 3.2.2 基础部分
- 通过 12 个拨动开关设置画笔颜色 (prgb)
- 通过上/下/左/右 (dir) 按钮,移动画笔位置 (x, y)
- 绘画 (draw=1) 时,依据 rgb 和 (x, y),通过 VRAM 写端口 (waddr, wdata, we) 存储绘图信息
可供参考的代码框架如下:
PU.v | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
提示
与 FPGAOL 不同,实际开发板的按键在按下时,会产生信号的抖动,你可能需要参考 Lab3 中 3.1 小节的相关内容,为输入的按键信号添加去抖动电路。
此外,为防止因为按键信号持续多周期置高,你可能需要对去抖动后的信号取边沿,请参考 Lab3 中 3.2 小节的相关内容。
题目 3-A-1:笔尖即世界
请根据以上内容,按要求完成本项练习。
3.2.3 进阶部分
3.2.3.1 复位清除
你可能注意到了,在当前的设计里,如果我们按下 rstn,只有画笔的位置会被重置,画布上的绘画内容并不会清除。
为了在按下复位键时清空绘画内容,一种参考的实现方案是:额外使用一个 ROM,专门存放背景。为绘画板添加一个刷新状态,当按下 rstn 后,状态机进入刷新状态,将背景 ROM 中的 rgb 信息逐个写入画布中。
题目 3-A-2:一键清除
请根据以上内容,按要求完成本项练习。
3.2.3.2 连续移动
由于我们对移动按钮去抖动后取边沿,所以我们每次按下按钮,画笔只会移动一个像素。这样的移动方式太累啦!我们能否通过长按按钮,实现连续移动呢?
你可以考虑对去抖动后的移动信号进行计数,每持续若干时间就移动一次画笔,从而实现连续移动,并可通过调整时间长短改变画笔移动的速率。
题目 3-A-3:笔一直画
请根据以上内容,按要求完成本项练习。
3.2.3.3 45°移动
我们暂时的画笔移动是不支持 45° 移动的,无法直接画出斜线。
为了支持 45° 移动,你可能需要对移动信号的处理部分的代码进行一定的修改。
题目 3-A-4:你这斜线不连续呀
请根据以上内容,按要求完成本项练习。
提示:绘图板状态
以下的三道题需要使用sw[12:15]
控制绘图板的状态,可能需要实现的状态有:
- 光标状态:默认状态,可以移动光标,但不会修改画布。可以进行撤销。
- 画笔状态:可以移动光标,同时会修改画布。可以进行撤销。
- 长方形绘制状态:可以移动光标,可以选择长方形的两个顶点并进行绘制。
- 填色状态:可以移动光标,可以选择一个点,对其所在的区域进行填色。
具体的对应关系你可以自行定义。
3.2.3.4 十字光标
看不见我们的画笔位置是一件挺苦恼的事,所以,我们能否通过一个十字光标来指示画笔的位置呢?
如上图,通过一个显示在画笔四周的十字光标,指示画笔的位置。(注意:画笔所在的像素应当显示画布的颜色)
由于光标并没有实际修改画布的内容,所以你可能需要修改 DDP.v
的显示逻辑,根据坐标信息,在画笔的坐标四周显示黑色。
题目 3-A-5:嘿,我在这儿
请根据以上内容,按要求完成本项练习。
3.2.3.5 长方形绘制
许多绘图软件支持绘制图形,如长方形、圆形等。鉴于硬件上计算圆形等图形的像素位置的难度较大,我们这里只尝试实现绘制长方形。
要求在长方形绘制状态下,可以通过先后在两个位置按下 btnc
键后,以此两点为基准点绘制长方形边框,线条颜色由 sw[11:0]
决定,长方形内部的颜色保持不变。
为实现此功能,你可能需要精心设计状态机。此外,我们不限制绘制出长方形所需要的周期数,你可以根据需要自行安排。一种参考的思路是,你可以在 DDP 中,当遍历到在长方形边界上的像素点时,使之显示要求的颜色,并在此之后写入 VRAM 中。
题目 3-A-6:方方正正
请根据以上内容,按要求完成本项练习。
3.2.3.6 撤回
我们的绘画板目前并不支持撤回功能。这里,我们希望能够在光标状态和画笔状态下,按下 btnc 后能撤回上一个像素的修改。要求最多能够撤回过去绘制的 16 个像素。注意:撤回时,画笔的位置可以不做改变;只需要记录和撤回画笔模式下更改的像素。
为了实现这一功能,你可能需要维护一个队列,其中存储着画笔模式下最近 16 个像素的修改信息。当按下 btnc 后,从队列中取出最近的一个像素的修改信息,将其恢复到画布中。
当达到撤回个数上限时,你需要用 led 灯指示,具体的指示方式你可以自行定义。
题目 3-A-7:我撤回了一个像素
请根据以上内容,按要求完成本项练习。
3.2.3.7 填色
许多画图软件都可以对光标所在的封闭区域进行填色,我们也希望能够实现这一功能。当然,为了降低实现难度,我们只要求为由黑色边界包围成的封闭区域填色,区域中的黑色点不改变颜色。
要求在填色状态下,可以通过按下开发板上的 btnc
键,对光标所在的封闭区域进行填色,填充颜色由 sw[11:0]
决定。
为了实现此功能,你可以参考洪泛算法,即从光标所在的像素点开始,如同洪水的扩散一般,不断地向四周填充颜色,直到遇到黑色边界为止。
由于硬件上队列的长度有限,不适合用 BFS 的方式实现该算法,你需要尽量发掘其中的并行性。一种参考的思路是,在洪泛状态下,对整个画布进行若干轮扫描。在每轮扫描中,对画布中的每个像素点进行判断:如果该像素点有洪泛标记且无完成标记,则对其四周未被洪泛标记且非黑色的像素点,进行洪泛标记,并将其颜色修改为要求的填充颜色。在此之后,为该像素点添加完成标记。持续若干轮直到所有具有洪泛标记的像素点都添加了完成标记,即可结束洪泛状态,并将所有像素点的洪泛标记和完成标记清除。
你也可以使用其他方式,如 DFS 来实现。同样的,我们不限制完成填色所需要的周期数,你可以根据需要自行安排。(用上面的方法可能需要最多200轮扫描)
题目 3-A-8:洪水泛滥
请根据以上内容,按要求完成本项练习。
3.2.4 示例
下面是实现本节全部功能后的一个示例,供大家参考: