跳转至

绘图板

作为 VGA 显示器的一个简单而不失趣味的应用,我们将实现一个绘图板。用户可以通过按键来选择画笔的颜色,移动画笔,以绘制图形。这一过程对于加深对 VGA 显示器的理解也有很大帮助。


3.2.1 系统设计

Image title

电路结构

整个项目可以分为如下的几个部分:

  • PU:绘画单元
  • VRAM:画布
  • DU:显示单元

其中,VRAMDU 我们已在前面一节完成了,所以这里的主要任务就是设计 PU

★ 3.2.2 基础部分

Image title

PU
  • 通过 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
module PU #(
    parameter               DW                  = 15,
    parameter               TimePerAction       = 10000     // 0.5s
)(
    input                   [ 0 : 0]            clk,
    input                   [ 0 : 0]            rstn,

    // 方向,接 DB
    input                   [ 0 : 0]            up,
    input                   [ 0 : 0]            down,
    input                   [ 0 : 0]            left,
    input                   [ 0 : 0]            right,

    // 像素颜色
    input                   [ 3 : 0]            pred,
    input                   [ 3 : 0]            pgreen,
    input                   [ 3 : 0]            pblue,

    // 绘画使能
    input                   [ 0 : 0]            draw,

    output      reg         [DW-1:0]            waddr,
    output                  [11 : 0]            wdata,
    output                  [ 0 : 0]            we,
);

localparam H_LEN = 200;
localparam V_LEN = 150;

assign wdata  {pred,pgreen,pblue};
assign we = draw;

reg [7 : 0]    x_coordinate;
reg [7 : 0]    y_coordinate;

// 根据方向信号移动画笔
// TODO
endmodule
提示

与 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 十字光标

看不见我们的画笔位置是一件挺苦恼的事,所以,我们能否通过一个十字光标来指示画笔的位置呢?

Image title

光标示例

如上图,通过一个显示在画笔四周的十字光标,指示画笔的位置。(注意:画笔所在的像素应当显示画布的颜色)

由于光标并没有实际修改画布的内容,所以你可能需要修改 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 示例

下面是实现本节全部功能后的一个示例,供大家参考:

Image title

绘图板示例

最后更新: November 29, 2023

评论