【FPGA】VGA显示文字、彩条、图片——基于DE2-115

文章目录

  • 前言
  • 一、VGA概述
    • 1.1 简述
    • 1.2 管脚定义
    • 1.3 VGA显示原理
    • 1.4 VGA时序标准
    • 1.5 VGA 显示模式及相关参数
  • 二、VGA显示自定义的汉字字符
    • 2.1 点阵汉字生成
    • 2.2 生成BMP文件
    • 2.3 生成txt文件
    • 2.4 实现效果
  • 三、VGA显示条纹
    • 3.1 实现流程
    • 3.2 实现效果
  • 四、VGA输出一幅彩色图像
    • 4.1 bmp图片转hex文件
    • 4.2 引入ROM ip核
    • 4.3 代码实现
    • 4.4 实现效果
  • 五、代码
    • 5.1 时钟分频
    • 5.2 vga驱动模块
    • 5.3 显示数据生成模块
    • 5.4 按键消抖模块
  • 六、整体实现效果(加入按键)
  • 总结
  • 参考

前言

本文内容:

  1. 深入了解VGA协议,理解不同显示模式下的VGA控制时序参数(行频、场频、水平/垂直同步时钟周期、显示后沿/前沿等概念和计算方式);

  2. 通过Verilog编程,在至少2种显示模式下(640480@60Hz,1024768@75Hz)分别实现以下VGA显示,并对照VGA协议信号做时序分析:1)屏幕上显示彩色条纹;2)显示自定义的汉字字符(姓名-学号);3)输出一幅彩色图像。

  3. 在Verilog代码中,将行、场同步信号中,故意分别加入一定 ms延时(用delay命令),观察会出现什么现象。


一、VGA概述

1.1 简述

图像显示设备在日常生活中随处可见,例如家庭电视机、计算机显示屏幕等,这些设备之所以能够显示我们需要的数据图像信息,归功于视频传输接口。常见的视频传输接口有三种:VGA 接口、 DVI 接口和 HDMI 接口,目前的显示设备都配有这三种视频传输接口。

三类视频接口的发展历程为 VGA → DVI → HDMI 。其中 VGA 接口出现最早,只能传输模拟图像信号; 随后出现的 DVI 接口又分为三类: DVI-A 、 DVI-D 、 DVI-I ,分别可传输纯模拟图像信号、纯数字图像信号和兼容模拟、数字图像信号;最后的 HDMI 在传输数字图像信号的基础上又可以传输音频信号。

VGA,英文全称“ Video Graphics Array ”,译为视频图形阵列,是一种使用模拟信号进行视频传输的标准协议,由 IBM 公司于 1987 年推出,因其分辨率高、显示速度快、颜色丰富等优点,广泛应用于彩色显示器领域。由于 VGA 接口体积较大,与追求小巧便携的笔记本电脑背道而驰,在笔记本电脑领域,VGA 接口已被逐渐淘汰,但对于体积较大的台式机,这种情况并未发生,虽然 VGA 标准在当前个人电脑市场中已经过时,但因其在显示标准中的重要性和良好的兼容性,VGA 仍然是最多制造商所共同支持的一个标准,个人电脑在加载自己独特驱动程序之前,都必须支持 VGA 的标准。
早期的 CRT 显示器只能接收模拟信号,不能接收数字信号,计算机内部显卡将数字信号转换成模拟信号,通过 VGA 接口传给 VGA 显示器,虽然现如今许多种类的显示器可以直接接收数字信号,但为了兼容显卡的 VGA 接口,大都支持 VGA 标准。

在这里插入图片描述

VGA 接口中以针式引出信号线的称为公头,以孔式引出信号线的称为母头。在计算机VGA 显示器上一般引出母头接口,使用两头均为公头的 VGA 连接线将计算机与 VGA显示器连接起来,两者图像传输时,使用的是 VGA 图像传输标准,该标准的具体内容在后面博文会详细说明。VGA 公头、母头接口和 VGA 连接线。在这里插入图片描述

图2 VGA接口(左边为母头,右边为公头)

在这里插入图片描述

图3 VGA连接线

1.2 管脚定义

下面,我们结合 VGA 接口引脚图和各引脚定义表格,对 VGA 接口各引脚做一下简单介绍。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a669aee5a12343f9807b1bbae8fe3199.png

图4 VGA接口引脚图
引脚1:红基色;引脚2:绿基色;引脚3:蓝基色;
引脚6:红色地;引脚7:绿色地;引脚8:蓝色地;引脚10:数字地
引脚11:地址码0;引脚12:地址码1;引脚4:地址码2;引脚15:地址码3
引脚13:行同步;引脚14:场同步
引脚5:自测试(各厂家定义不同);引脚9:保留(各厂家定义不同)

      由图4可知,VGA 接口共有 15 个引脚,分为 3 排,每排各 5 个, 按照自上而下、从左向右的顺序排列。其中第一排的引脚 1 、 2 、 3 和第三排的引脚 13 、 14 最为重要。
      VGA 使用工业界通用的 RGB 色彩模式作为色彩显示标准,这种色彩显示标准是根据三原色中红色、绿色、蓝色所占比例多少及三原色之间的相互叠加得到各式各样的颜色。引脚 1 红基色 (RED) 、引脚 2 绿基色 (GREEN) 、引脚 3 蓝基色 (BLUE) 就是 VGA 接口中负责传输三原色的传输通道。要注意的是,这 3 个引脚传输的是模拟信号。
      引脚 13 行同步信号 (HSYNC) 、引脚 14 场同步信号 (VSYNC) ,这两个信号,是在 VGA显示图像时,负责同步图像色彩信息的同步信号。在后面博文中,我们会对这两个信号进行详细讲解。
      引脚 5 、 9 :这两个引脚分别是 VGA 接口的自测试和预留接口,不过不同生产厂家对这两个接口定义不同,在接线时,两引脚可悬空不接。
      引脚 4 、 11 、 12 、 15 :这四个是 VGA 接口的地址码,可以悬空不接。
      引脚 6 、 7 、 8 、 10 :这四个引脚接地,无需解释。

1.3 VGA显示原理

VGA 显示器显示图像,并不是直接让图像在显示器上显示出来,而是采用扫描的方式,将构成图像的像素点,在行同步信号和场同步信号的同步下,按照从上到下、由左到右的顺序扫描到显示屏上。VGA 显示器扫描方式,具体见图 1 。
在这里插入图片描述

结合

  结合 VGA 显示器扫描方式示意图,我们简要说明一下 VGA 显示器的扫描规律。
   (1) 在行、场同步信号的同步作用下,扫描坐标定位到左上角第一个像素点坐标;
   (2) 自左上角 ( 第一行 ) 第一个像素点坐标,逐个像素点向右扫描 ( 图中第一个水平方向箭头) ;
   (3) 扫描到第一行最后一个数据,一行图像扫描完成,进行图像消隐,扫描坐标自第一行行尾转移到第二行行首( 图中第一条虚线 ) ;
   (4) 重复若干次扫描至最后一行行尾,一帧图像扫描完成,进行图像消隐,扫描坐标跳转回到左上角第一行行首( 图中对角线箭头 ) ,开始下一帧图像的扫描。

   在扫描的过程中会对每一个像素点进行单独赋值,使每个像素点显示对应色彩信息,
  当一帧图像扫描结束后,开始下一帧图像的扫描,循环往复,当扫描速度足够快,加之人眼的视觉暂留特性,我们会看到一幅完整的图片,而不是一个个闪烁的像素点。这就是VGA 显示的原理。

1.4 VGA时序标准

      为了适应匹配不同厂家的 VGA 显示器, VGA 视频传输接口有自己的一套 VGA 时序标准,只有遵循 VGA 的时序标准,才能正确的进行图像信息的显示。在这里我们以 VESA VGA 时序标准为例,为大家讲解一下 VGA 时序标准,具体见图 2 。
在这里插入图片描述

      由 VESA VGA 时序标准图可知, VGA 时序由两部分构成,行同步时序与场同步时序,为了方便大家理解,我们将行同步时序与场同步时序分开讲解。
     (1) 行同步时序,具体见图 3 。

在这里插入图片描述

图 3 行同步时序图

      图中 Video 代表传输的图像信息, HSync 表示行同步信号。 HSync 自上升沿起到下一个上升沿止为一个完整周期,我们称之为行扫描周期。
      一个完整的行扫描周期,包含 6 部分: Sync (同步)、 Back Porch (后沿)、 Left Border(左边框)、 Addressable Video (有效图像)、 Right Border (右边框)、 Front Porch(前沿),这 6 部分的基本单位是 pixel (像素),即一个像素时钟周期。在一个完整的行扫描周期中,Video 图像信息在 HSync 行同步信号的同步下完成一行图像的扫描显示,Video 图像信息只有在 “Addressable” Video (有效图像)阶段,图像信息有效,其他阶段图像信息无效。
      HSync 行同步信号在 Sync (同步)阶段,维持高电平,其他阶段均保持低电平,在下一个行扫描周期的 Sync (同步)阶段, HSync 行扫描信号会再次拉高,其他阶段拉低,周而复始。
      (2) 场同步时序,具体见图 4 。

在这里插入图片描述

图 4 场同步时序图

      理解了行同步时序,场同步时序就更容易理解了,两者相类似,如图所示,图中Video 代表传输的图像信息, VSync 表示场同步信号VSync 自上升沿起到下一个上升沿止为一个完整周期,我们称之为场扫描周期
      一个完整的场扫描周期,也包含 6 部分: Sync (同步)、 Back Porch (后沿)、 Top Border(上边框)、 “Addressable” Video (有效图像)、 Bottom Border (底边框)、 Front Porch(前沿),与行同步信号不同的是,这 6 部分的基本单位是 line (行),即一个完整的行扫描周期。
       在一个完整的场扫描周期中,Video 图像信息在 HSync (行同步信号)VSync (场同步信号)的共同作用下完成一帧图像的显示,Video 图像信息只有在 “Addressable” Video(有效图像)阶段,图像信息有效,其他阶段图像信息无效。VSync 行同步信号在 Sync (同步)阶段维持高电平,其他阶段均保持低电平,完成一个场扫描周期后,进入下一帧图像的扫描。
       综上所述,将行同步时序图与场同步时序图结合起来就构成了 VGA 时序图,具体见图 5。

在这里插入图片描述

图 5 VGA时序图

图中的红色区域表示在一个完整的行扫描周期中,Video 图像信息只在此区域有效,黄色区域表示在一个完整的场扫描周期中,Video 图像信息只在此区域有效,两者相交的橙色区域,就是 VGA 图像的最终显示区域

1.5 VGA 显示模式及相关参数

行同步时序可分为 6 个阶段,对于这 6 个阶段的参数是有严格定义的,参数配置不正确,VGA 不能正常显示。 VGA 显示器可支持多种分辨率,不同分辨率对应个阶段的参数是不同的,常用 VGA 分辨率时序参数,具体见图 6 。
在这里插入图片描述
       下面我们以经典 VGA 显示模式 640x480@60 为例,为读者讲解一下 VGA 显示的相关参数。

       (1) 显示模式: 640x480@60
              640x480 是指 VGA 的分辨率, 640 是指有效显示图像每一行有 640 个像素点, 480 是指每一帧图像有 480 行, 640 * 480 = 307200 ≈ 300000 ,每一帧图片包含约 30 万个像素点,之前某品牌手机广告上所说的 30 万像素指的就是这个; @60 是指 VGA 显示图像的刷新频率,60 就是指 VGA 显示器每秒刷新图像 60 次,即每秒钟需要显示 60 帧图像。

       (2) 时钟 (MHz) : 25.175MHz
              这是 VGA 显示的工作时钟,像素点扫描频率

       (3) 行同步信号时序 ( 像素 ) 、场同步信号时序 ( 行数 )
              行同步信号时序分为 6 段, Sync (同步)、 Back Porch (后沿)、 Left Border (左边框)、 “Addressable” Video (有效图像)、 Right Border (右边框)、 Front Porch (前沿),这 6 段构成一个行扫描周期,单位为像素时钟周期。
              同步阶段,参数为 96,指在行时序的同步阶段,行同步信号需要保持 96 个像素时钟周期的高电平, 其他几个阶段与此相似。
              场同步信号时序与其类似,只是单位不再是像素时钟周期,而是一个完整的行扫描周期,在此不再赘述。
              在这里,我们看回图 6,由图可知,即使 VGA 显示分辨率相同,但刷新频率不同的话,相关参数也存在差异,如 640x480@60 、 640x480@75 ,这两个显示模式虽然具有相同的分辨率,但是 640x480@75 的刷新频率更快,所以像素时钟更快,时序参数也有区别。

       下面我们以显示模式 640x480@60 、 640x480@75 为例,学习一下时钟频率的计算方法。

       行扫描周期 * 场扫描周期 * 刷新频率 = 时钟频率
       640x480@60:
       行扫描周期:800( 像素 ) ,场扫描周期: 525( 行扫描周期 ) 刷新频率: 60Hz
       800 * 525 * 60 = 25,200,000 ≈ 25.175MHz(误差忽略不计)
       640x480@75:
       行扫描周期:840( 像素 ) 场扫描周期: 500( 行扫描周期 ) 刷新频率: 75Hz
       840 * 500 * 75 = 31,500,000 = 31.5MHz
       在计算时钟频率时,大家要谨记一点,要使用行扫描周期场扫描周期的参数进行计算,不能使用有效图像的参数进行计算,虽然在有效图像外的其他阶段图像信息均无效,但图像无效阶段的扫描也花费了扫描时间。
       以上就是对 VGA 显示标准中分辨率相关参数的讲解,在编写 VGA 驱动时,我们要根据 VGA 显示模式的不同调整相关参数,只有这样 VGA 图像才能正常显示。

二、VGA显示自定义的汉字字符

笔主整篇文章中使用的板子都是 EP4CE115F29C7,使用其他板子问题不大,但引脚绑定不太一样,可自行对应板子查找更改。

2.1 点阵汉字生成

字模的提取可通过字符取模软件来实现,在这里我们使用取模软件“PCtoLCD2002”来获取汉字 李菊芳-632109160602 的字模。如下图所示:
在这里插入图片描述

打开之后会发现软件中的字体、字宽和字高都是无法设置的,这个时候点击菜单栏的“模式”,选择“字符模式”。
切换到字符模式后,就可以设置字体、字宽和字高了。字宽和字高的值越高,显示在LCD屏上的字符就越大,但是代码也需要做相应的修改。这里将字体选择默认的“宋体”,字宽和字高设置成“32”,然后在下方文本框中输入汉字,如下图所示:

在这里插入图片描述

2.2 生成BMP文件

然后点击文件-另存为,把图片保存为BMP图片,再点击文件-打开,切换到图形模式,把保存的BMP图片打开得到整体的字符

在这里插入图片描述

2.3 生成txt文件

再点击选项按如下参数设置

在配置界面中,当鼠标悬浮在各配置选项上时,软件会自动提示当前配置的含义。需要注意的是左下角“每行显示数据”是以字节(Byte)为单位的,而一个字节的数据为8个bit,即可以表示一行点阵中的8个像素点。由于图中的点阵每行为304个像素点,所以需要304/8=38个Byte的数据来表示一行,因此将“每行显示数据—点阵”处设置为38
配置字模选项完成后,点击“生成字模”,即可得到汉字所对应的点阵数据。
在这里插入图片描述
最后点击生成字符并保存字符为文本文件
在这里插入图片描述

最后得到字符如下
在这里插入图片描述

把得到的字符在verilog里面使用即可
在这里插入图片描述

2.4 实现效果

姓名+学号 居中显示:

在这里插入图片描述

三、VGA显示条纹

3.1 实现流程

时钟分频模块
640x480像素的VGA协议所需时钟频率25MHZ,使用clk IP核进行时钟分频

根据当前行地址判断需要显示的颜色即可。

输出颜色竖条

// 状态输出逻辑,根据不同的状态输出不同的RGB数据
always @( * ) begincase ( states_current )//彩条states_1 : beginif ( addr_h == 0 ) beginrgb_data = black;endelse if ( addr_h >0 && addr_h <81 ) beginrgb_data = red;endelse if ( addr_h >80 && addr_h <161 ) beginrgb_data = orange;// rgb_data = red;endelse if ( addr_h >160 && addr_h <241 ) beginrgb_data = yellow;endelse if ( addr_h >240 && addr_h <321 ) beginrgb_data = green;endelse if ( addr_h >320 && addr_h <401 ) beginrgb_data = blue;endelse if ( addr_h >400 && addr_h <481 ) beginrgb_data = indigo;endelse if ( addr_h >480 && addr_h <561 ) beginrgb_data = purple;endelse if ( addr_h >560 && addr_h <641 ) beginrgb_data = white;endelse beginrgb_data = black;endend

3.2 实现效果

在这里插入图片描述

四、VGA输出一幅彩色图像

在前面的学习中了解到图像的格式有多种,例如JPEG,BMP,PNG,JPG等,图像的位数也有单色、16色、256色、4096色、16位真彩色、24位真彩色、32位真彩色在这里插入图片描述
这几种。
VGA的驱动程序显示的格式为RGB565,我们先找到一张需要显示的彩色图片,经过处理,将该图片转化为ROM可以存储的格式,然后VGA驱动程序从ROM中读取数据,输出到VGA显示屏显示。尽量选一张小的图片,因为ROM存储空间有限。

4.1 bmp图片转hex文件

使用BMP2Mif软件将bmp格式图片转换为mif文件

在这里插入图片描述

转换后的.mif文件:
在这里插入图片描述

4.2 引入ROM ip核

新建Quartus工程,产生ROM IP核,将生成的mif文件保存在ROM中
双击选择ROM:1-PORT
在这里插入图片描述

更改设置,words大小设置要大于图片大小(50x49x24=58800< 65536),然后next
在这里插入图片描述

取消勾选q
在这里插入图片描述

如果是16位位图,就加载HEX文件,然后next

这里因为我们转换的是24位位图,所以在ROM里要引入 .mif 文件。(16位位图不用管)
在这里插入图片描述

勾选上输出 inst,方便例化,然后finish

在这里插入图片描述

这里同时要调用前面的pll ip核生成一个25mHz的时钟。

4.3 代码实现

data_drive.v文件里,从ikun_rom取出图片数据。
在这里插入图片描述
其他 .v 文件与前文一致。

4.4 实现效果

640*480分辨率显示下:彩色图像显示在正中间

在这里插入图片描述

五、代码

5.1 时钟分频

分别使用640×480 60HZ和800×600 72HZ,对应时钟分别为25M和50M,需要使用PLL进行分频 时钟频率 = 行帧长 × 列帧长 * 刷新率

640 ×480 60HZ对应时钟频率= 800 ×525 × 60 = 25.2M

ip核里面找到ALTPLL
在这里插入图片描述

基础时钟选择50M
在这里插入图片描述

取消勾选输出使能
在这里插入图片描述

c0默认输出50M即可, c1分频到25M,如需其他时钟频率可以自己进行设置
在这里插入图片描述
勾选如下选项后finish
在这里插入图片描述

5.2 vga驱动模块

vga.drive.v

// /*
module vga_dirve (input			wire						clk,            //系统时钟    input			wire						rst_n,          //复位input			wire		[ 15:0 ]		rgb_data,       //RGB--565,即pixel_data[15:11]控制R、pixel_data[10:5]控制G、pixel_data[4:0]控制Boutput			wire							vga_clk,    //vga时钟 25Moutput			reg							h_sync,     //行同步信号output			reg							v_sync,     //场同步信号output			reg		[ 11:0 ]				addr_h, //行地址output			reg		[ 11:0 ]				addr_v,  //列地址output			wire		[ 4:0 ]				rgb_r,  //红基色output			wire		[ 5:0 ]				rgb_g,  //绿基色output			wire		[ 4:0 ]				rgb_b  //蓝基色
);// 定义VGA信号的参数,基于640x480 60Hz的VGA模式
// 640 * 480 60HZ
localparam	 H_FRONT = 16; // 行同步前沿信号周期长
localparam	 H_SYNC  = 96; // 行同步信号周期长
localparam	 H_BLACK = 48; // 行同步后沿信号周期长
localparam	 H_ACT   = 640; // 行显示周期长
localparam	 V_FRONT = 11; // 场同步前沿信号周期长
localparam	 V_SYNC  = 2; // 场同步信号周期长
localparam	 V_BLACK = 31; // 场同步后沿信号周期长
localparam	 V_ACT   = 480; // 场显示周期长// 800 * 600 72HZ
// localparam	 H_FRONT = 40; // 行同步前沿信号周期长
// localparam	 H_SYNC  = 120; // 行同步信号周期长
// localparam	 H_BLACK = 88; // 行同步后沿信号周期长
// localparam	 H_ACT   = 800; // 行显示周期长
// localparam	 V_FRONT = 37; // 场同步前沿信号周期长
// localparam	 V_SYNC  = 6; // 场同步信号周期长
// localparam	 V_BLACK = 23; // 场同步后沿信号周期长
// localparam	 V_ACT   = 600; // 场显示周期长// 计算总的行和场周期
localparam	H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期 16+96+48+640 = 800
localparam	V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期 11+2+6+31+480 = 512
reg			[ 11:0 ]			cnt_h			; // 行计数器 0-799
reg			[ 11:0 ]			cnt_v			; // 场计数器 0-524
reg			[ 15:0 ]			rgb			; // 对应显示颜色值// 对应计数器开始、结束、计数信号
wire							flag_enable_cnt_h			;
wire							flag_clear_cnt_h			;
wire							flag_enable_cnt_v			;
wire							flag_clear_cnt_v			;
wire							flag_add_cnt_v  			;
wire							valid_area      			;// 25M时钟 行周期*场周期*刷新率 = 800 * 525 * 60
wire							clk_25			;
// 50M时钟 1040 * 666 * 72
wire							clk_50			;
//PLL
pll	pll_inst (.areset ( ~rst_n ),.inclk0 ( clk ),.c0 ( clk_50 ), //50M.c1 ( clk_25 ) //25M
);//根据不同分配率选择不同频率时钟
assign vga_clk = clk_25;// 行计数
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) begincnt_h <= 0;endelse if ( flag_enable_cnt_h ) beginif ( flag_clear_cnt_h ) begincnt_h <= 0;endelse begincnt_h <= cnt_h + 1;endendelse begincnt_h <= 0;end
end
assign flag_enable_cnt_h = 1;
assign flag_clear_cnt_h  = cnt_h == H_TOTAL - 1;// 行同步信号
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginh_sync <= 1;endelse if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1h_sync <= 0;endelse if ( flag_clear_cnt_h ) begin // 其余为0h_sync <= 1;endelse beginh_sync <= h_sync;end
end// 场计数
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) begincnt_v <= 0;endelse if ( flag_enable_cnt_v ) beginif ( flag_clear_cnt_v ) begincnt_v <= 0;endelse if ( flag_add_cnt_v ) begincnt_v <= cnt_v + 1;endelse begincnt_v <= cnt_v;endendelse begincnt_v <= 0;end
endassign flag_enable_cnt_v = flag_enable_cnt_h;
assign flag_clear_cnt_v  = cnt_v == V_TOTAL - 1;
assign flag_add_cnt_v    = flag_clear_cnt_h;// 场同步信号
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginv_sync <= 1;endelse if ( cnt_v == V_SYNC - 1 ) beginv_sync <= 0;endelse if ( flag_clear_cnt_v ) beginv_sync <= 1;endelse beginv_sync <= v_sync;end
end// 对应有效区域行地址 1-640
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginaddr_h <= 0;endelse if ( valid_area ) beginaddr_h <= cnt_h - H_SYNC - H_BLACK + 1;endelse beginaddr_h <= 0;end
end
// 对应有效区域列地址 1-480
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginaddr_v <= 0;endelse if ( valid_area ) beginaddr_v <= cnt_v -V_SYNC - V_BLACK + 1;endelse beginaddr_v <= 0;end
end
// 有效显示区域
assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h <= H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v <= V_SYNC + V_BLACK + V_ACT;// 显示颜色
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginrgb <= 16'h0;endelse if ( valid_area ) beginrgb <= rgb_data;endelse beginrgb <= 16'b0;end
end
assign rgb_r = rgb[ 15:11 ];
assign rgb_g = rgb[ 10:5 ];
assign rgb_b = rgb[ 4:0 ];
endmodule // vga_dirve
// */

5.3 显示数据生成模块

data_drive.v


module data_drive (input			wire						vga_clk,      // VGA时钟输入input			wire						rst_n,        // 复位信号,低电平有效input			wire		[ 11:0 ]		addr_h,       // 水平地址输入input			wire		[ 11:0 ]		addr_v,       // 垂直地址输入input			wire		[ 2:0 ]		 key,          // 三个按键输入output			reg		[ 15:0 ]				rgb_data      // 输出的RGB数据);// 定义一些颜色的16位表示
localparam	red    = 16'd63488;
localparam	orange = 16'd64384;
localparam	yellow = 16'd65472;
localparam	green  = 16'd1024;
localparam	blue   = 16'd31;
localparam	indigo = 16'd18448;
localparam	purple = 16'd32784;
localparam	white  = 16'd65503;
localparam	black  = 16'd0;//显示的名字
// 存储显示字符的每一行数据
// reg [ 383:0 ] char_line[ 64:0 ];//李菊芳-632109160602  -16
//16行,每行152个bit
// reg [ 152:0 ] char_line[ 15:0 ];//李菊芳-632109160602 ——32
//32*3+16*13 = 304 304/8 = 38
reg [ 303:0 ] char_line[ 31:0 ];// 定义显示状态的参数
localparam	states_1 = 1; // 彩条
localparam	states_2 = 2; // 字符
localparam	states_3 = 3; // 图片// 图片的尺寸参数
// parameter	height = 78; // 图片高度
// parameter	width  = 128; // 图片宽度//ikun2
parameter	height = 52; // 图片高度
parameter	width  = 52; // 图片宽度// 当前状态和下一个状态的寄存器
reg			[ 2:0 ]			states_current			; // 当前状态
reg			[ 2:0 ]			states_next			    ; // 下个状态// ROM的地址寄存器和数据输出
reg			[ 13:0 ]		rom_address				; // ROM地址
// wire		[ 15:0 ]		rom_data				; // 图片数据
wire		[ 23:0 ]		rom_data				; // 显示彩色图片数据// 状态机的标志位
wire							flag_enable_out1			; // 文字有效区域标志
wire							flag_enable_out2			; // 图片有效区域标志
wire							flag_clear_rom_address		; // ROM地址清零标志
wire							flag_begin_h			    ; // 图片显示行开始标志
wire							flag_begin_v			    ; // 图片显示列开始标志// 状态转移逻辑
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginstates_current <= states_1;// 复位时设置初始状态为彩条endelse beginstates_current <= states_next;// 否则转移到下一个状态end
end// 状态判断逻辑,根据按键输入更新下一个状态
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginstates_next <= states_1;endelse if ( key[ 0 ] ) beginstates_next <= states_1;endelse if ( key[ 1 ] ) beginstates_next <= states_2;endelse if ( key[ 2 ] ) beginstates_next <= states_3;endelse beginstates_next <= states_next;end
end// 状态输出逻辑,根据不同的状态输出不同的RGB数据
always @( * ) begincase ( states_current )//彩条states_1 : beginif ( addr_h == 0 ) beginrgb_data = black;endelse if ( addr_h >0 && addr_h <81 ) beginrgb_data = red;endelse if ( addr_h >80 && addr_h <161 ) begin// rgb_data = orange;rgb_data = red;endelse if ( addr_h >160 && addr_h <241 ) beginrgb_data = yellow;endelse if ( addr_h >240 && addr_h <321 ) beginrgb_data = green;endelse if ( addr_h >320 && addr_h <401 ) beginrgb_data = blue;endelse if ( addr_h >400 && addr_h <481 ) beginrgb_data = indigo;endelse if ( addr_h >480 && addr_h <561 ) beginrgb_data = purple;endelse if ( addr_h >560 && addr_h <641 ) beginrgb_data = white;endelse beginrgb_data = black;endend//字符states_2 : beginif ( flag_enable_out1 ) begin//480*640// rgb_data = char_line[ addr_v-208 ][ 532 - addr_h ]? white:black;rgb_data = char_line[ addr_v-224 ][ 472 - addr_h ]? white:black;endelse beginrgb_data = black;endend//图片states_3 : beginif ( flag_enable_out2 ) beginrgb_data = rom_data;endelse beginrgb_data = black;endenddefault: begincase ( addr_h )0 : rgb_data      = black;1 : rgb_data      = red;81 : rgb_data     = orange;161: rgb_data     = yellow;241: rgb_data     = green;321: rgb_data     = blue;401: rgb_data     = indigo;481: rgb_data     = purple;561: rgb_data     = white;default: rgb_data = rgb_data;endcaseendendcase
end//李骏飞的居中显示参数
//32*3+16*13 = 304 304/8 = 38
// 根据当前状态和地址范围设置标志位
parameter ljf_width = 304;  // 字符数据的宽度
parameter ljf_height = 32;  // 字符数据的高度
assign flag_enable_out1 = states_current == states_2 && addr_h >= (640 - ljf_width) / 2 && addr_h <  ((640 - ljf_width) / 2) + ljf_width && addr_v >= (480 - ljf_height) / 2 && addr_v <  ((480 - ljf_height) / 2) + ljf_height;// assign flag_begin_h     = addr_h > ( ( 640 - width ) / 2 ) && addr_h < ( ( 640 - width ) / 2 ) + width + 1;
// assign flag_begin_v     = addr_v > ( ( 480 - height )/2 ) && addr_v <( ( 480 - height )/2 ) + height + 1;
assign flag_begin_h     = addr_h >= ( ( 640 - width ) / 2 ) && addr_h < ( ( 640 - width ) / 2 ) + width ;
assign flag_begin_v     = addr_v >= ( ( 480 - height )/2 ) && addr_v <( ( 480 - height )/2 ) + height ;
assign flag_enable_out2 = states_current == states_3 && flag_begin_h && flag_begin_v;//ROM地址计数器
always @( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) beginrom_address <= 0;// 复位时清零ROM地址endelse if ( flag_clear_rom_address ) begin //计数满清零rom_address <= 0;endelse if ( flag_enable_out2 ) begin  //在有效区域内+1rom_address <= rom_address + 1;endelse begin  //无效区域保持rom_address <= rom_address;end
end
assign flag_clear_rom_address = rom_address == height * width - 1 || states_current != states_3;// 初始化显示文字的逻辑
always@( posedge vga_clk or negedge rst_n ) beginif ( !rst_n ) begin//李菊芳-632109160602 ——32//32*3+16*13 = 304 304/8 = 38char_line[0] =  304'h0000000000000000000000000000000000000000000000000000000000000000000000000000;char_line[1] =  304'h0000000000000000000000000000000000000000000000000000000000000000000000000000;char_line[2] =  304'h0003800000101000002008000000000000000000000000000000000000000000000000000000;char_line[3] =  304'h0003C000001C1C0000380E000000000000000000000000000000000000000000000000000000;char_line[4] =  304'h000380100018180000300C000000000000000000000000000000000000000000000000000000;char_line[5] =  304'h000380380018181800300C300000000000000000000000000000000000000000000000000000;char_line[6] =  304'h3FFFFFFC3FFFFFFC1FFFFFF8000001E007C007E0008003C007C0008001E003C001E003C007E0;char_line[7] =  304'h180FE0000098180000300C000000061818600838018006201820018006180620061806200838;char_line[8] =  304'h001FF00000D8180000330C0000000C18303010181F800C3030101F800C180C300C180C301018;char_line[9] =  304'h003FB80000F8180000318C00000008183018200C01801818301801800818181808181818200C;char_line[10] = 304'h 007B9C00019000200030C800000018003018200C01801818600801801800181818001818200C;char_line[11] = 304'h 00F39E0001FFFFF000006000000010003018300C01801808600C01801000180810001808300C;char_line[12] = 304'h 01E38F800300003000004030000010000018300C0180300C600C01801000300C1000300C300C;char_line[13] = 304'h 03C387F0020300301FFFFFF8000030000018000C0180300C600C01803000300C3000300C000C;char_line[14] = 304'h 07838DFE0483083000060000000033E0003000180180300C600C018033E0300C33E0300C0018;char_line[15] = 304'h 1FFFFEF808431C300006000000003630006000180180300C600C01803630300C3630300C0018;char_line[16] = 304'h 38401F3010631830000C00007FFE381803C000300180300C701C01803818300C3818300C0030;char_line[17] = 304'h 60003C0020633030000C008000003808007000600180300C302C01803808300C3808300C0060;char_line[18] = 304'h 0001F00000232330000FFFC00000300C001800C00180300C186C0180300C300C300C300C00C0;char_line[19] = 304'h 0001E0000FFFFFB0000C00C00000300C000801800180300C0F8C0180300C300C300C300C0180;char_line[20] = 304'h 0001E010000F0030000C01800000300C000C03000180300C000C0180300C300C300C300C0300;char_line[21] = 304'h 0001C038000B8030001801800000300C000C02000180300C00180180300C300C300C300C0200;char_line[22] = 304'h 7FFFFFFC001B6030001801800000300C300C04040180180800180180300C1808300C18080404;char_line[23] = 304'h 3801C00000333830003001800000180C300C08040180181800100180180C1818180C18180804;char_line[24] = 304'h 0001C00000631C20003001800000180830081004018018183030018018081818180818181004;char_line[25] = 304'h 0001C00000C30C200060030000000C183018200C01800C30306001800C180C300C180C30200C;char_line[26] = 304'h 0001C0000183046000C0030000000E3018303FF803C0062030C003C00E3006200E3006203FF8;char_line[27] = 304'h 0001C000020300600180C300000003E007C03FF81FF803C00F801FF803E003C003E003C03FF8;char_line[28] = 304'h 003FC0000C030FE002003E000000000000000000000000000000000000000000000000000000;char_line[29] = 304'h 0007C000300303C00C001E000000000000000000000000000000000000000000000000000000;char_line[30] = 304'h 0003800000020080300008000000000000000000000000000000000000000000000000000000;char_line[31] = 304'h 0000000000000000000000000000000000000000000000000000000000000000000000000000;end
end// /*
//ikun
// ROM实例化,根据地址输出数据
ikun_rom	ikun_rom_inst (
.address ( rom_address ),
.clock ( vga_clk ),
.q ( rom_data )
);
// */endmodule // data_drive

5.4 按键消抖模块

key_debounce.v

module key_debounce(input 	wire	clk,        // 时钟信号输入input 	wire 	rst_n,      // 复位信号,低电平有效input 	wire 	key,        // 按键输入output 	reg 	flag,       // 抖动标志,0表示抖动中,1表示抖动结束output 	reg	key_value  // 按键稳定后的值
);// 定义参数MAX_NUM,用于设置消抖计数的最大值
parameter MAX_NUM = 20'd1_000_000;// 内部信号声明
reg [19:0] delay_cnt;  // 用于消抖的计数器,足够大以覆盖抖动时间
reg key_reg;            // 上一次按键的状态// 第一个always块:处理按键抖动
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginkey_reg <= 1;        // 复位时,将上一次按键状态设为高电平delay_cnt <= 0;      // 复位时,清零计数器endelse beginkey_reg <= key;      // 非复位时,更新上一次按键状态// 当上一次按键状态与当前按键状态不一致时,认为是抖动开始,重置计数器if(key_reg != key) begindelay_cnt <= MAX_NUM;endelse begin// 如果按键状态一致,开始计数,直到计数器减到0if(delay_cnt > 0)delay_cnt <= delay_cnt - 1;elsedelay_cnt <= 0;endend
end// 第二个always块:在按键稳定后输出按键值和标志
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginflag <= 0;            // 复位时,抖动标志设为0key_value <= 1;       // 复位时,按键值设为高电平endelse begin// 当计数器的值减到1时,认为按键已经稳定,更新抖动标志和按键值if(delay_cnt == 1) beginflag <= 1;key_value <= key;endelse begin// 如果计数器还没减到1,保持抖动标志为0,按键值不变flag <= 0;key_value <= key_value;endend
endendmodule

六、整体实现效果(加入按键)

加入按键之后的实现效果
在这里插入图片描述


总结

  • VGA显示,难点在于显示驱动模块,行场同步时序的代码编写,可以先写计数器和大模块,一些内部信号与标志信号可以逐步完善;其次就是数据显示中显示区域代码的编写。

  • VGA显示,显示彩条部分还可以改进,可以定义不同彩条的显示区域。显示文字主要就是显示位置的确定与汉字点阵的生成,汉字点阵还是费了一些功夫,最开始实在没找到一个比较合适的转换工具和方法,后来在朋友的帮助指点下终于可以了。显示图片一定要注意24位位图的话在ROM里面引入的是 .mif 文件,16位位图的话就引入hex文件。

  • 代码或哪一部分有问题欢迎留言指正。

参考

VGA显示接口简介

基于FPGA的VGA显示彩条、字符、图片

【FPGA实验】基于DE2-115平台的VGA显示

FPGA VGA显示协议

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/15265.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ArcGIS不同图斑设置不同的透明度

对于设置一个图层的整体的透明度&#xff0c;我们在 ArcGIS制作带蒙版的遥感影像地图http://mp.weixin.qq.com/s?__bizMzIzNjM2NTYxMg&mid2247509080&idx1&sn38dccf0a52bb3bb3758f57114ee38b72&chksme8da161bdfad9f0d363da90959a8524dcf2b60d0e8d999f8ebeef0…

Unity面试八股文之基础篇

文章目录 前言1. Unity的生命周期加载第一个场景Editor在第一次帧更新之前帧之间更新顺序协程销毁对象时退出时 2. Unity 协程和线程,进程的区别3. 本地坐标系 世界坐标系4. 碰撞器和触发器的区别后话 前言 开设这个栏目的博文会写一些有关unity的面试题目&#xff0c;在面试的…

【spring】@ResponseBody注解学习

ResponseBody介绍 ResponseBody 是一个Spring框架中的注解&#xff0c;主要用于Web开发&#xff0c;特别是在Spring MVC框架中。它的核心作用是改变Spring MVC处理HTTP请求响应的行为&#xff0c;使得从控制器方法返回的数据直接写入HTTP响应体&#xff08;Response Body&…

Python高级进阶--slice切片

slice切片⭐⭐ 在 Python 中&#xff0c;切片操作是一种常见且方便的方式&#xff0c;用于从字符串、列表或元组中获取部分元素。这种操作通过指定起始索引、结束索引和步长来实现。下面我们来看一些关于切片的简单介绍以及一些常见用法。 1. 切片简介 取一个str、list、tup…

【Unity2D:Animator】为角色添加动画效果

一、添加Animator组件并创建Animator Controller文件 1. 添加Animator组件&#xff1a; 2. 在Assets-Art文件夹中新建一个名为Animations的文件夹&#xff0c;用来存储所有动画资源 3. 在Animations文件夹中新建一个名为Player的文件夹&#xff0c;再创建一个名为Animators的文…

LeetCode //C - 119. Pascal‘s Triangle II

119. Pascal’s Triangle II Given an integer rowIndex, return the rowIndexth (0-indexed) row of the Pascal’s triangle. In Pascal’s triangle, each number is the sum of the two numbers directly above it as shown: Example 1: Input: rowIndex 3 Output: …

【排序算法】——归并排序(递归与非递归)含动图

制作不易&#xff0c;三连支持一下吧&#xff01;&#xff01;&#xff01; 文章目录 前言一.归并排序递归方法实现二.归并排序非递归方法实现 前言 这篇博客我们将介绍归并排序的原理和实现过程。 一、归并排序递归方法实现 基本思想&#xff1a; 归并排序&#xff08;MERGE-…

JS(ES_6)_2

1.创建对象的6种方式&#xff1a; 1. obnew Object() ob.nameah ob.age18 2. ob{name:ah,gae:18} 3.工厂模式&#xff1a; 设计一个函数&#xff0c;专门生产Person类型的对象 <script>function createPerson(name,age,family) {var o new Object();o.name name;o.…

软件设计师备考 | 案例专题之数据流图 概念与例题

案例分析专题大纲&#xff1a; 数据流图基本概念 基本图形元素&#xff1a;外部实体、加工、数据存储、数据流 数据流&#xff1a;由一组固定成分的数据组成&#xff0c;表示数据的流向。在DFD中&#xff0c;数据流的流向必须经过加工。加工&#xff1a;描述了输入数据流到输出…

啊哈!算法-第2章-栈、队列、链表

啊哈!算法-第2章-栈、队列、链表 第1节 解密qq号——队列第2节 解密回文——栈第3节 纸牌游戏——小猫钓鱼第4节 链表第5节 模拟链表 第1节 解密qq号——队列 新学期开始了&#xff0c;小哈是小哼的新同桌(小哈是个大帅哥哦~)&#xff0c;小哼向小哈询问 QQ 号&#xff0c; 小…

uniapp微信小程序解决open-type获取用户头像,返回临时路径问题!

解决 open-type 为 chooseAvatar&#xff0c;返回临时路径问题 文章目录 解决 open-type 为 chooseAvatar&#xff0c;返回临时路径问题效果图Demo获取头像回调数据结构效果图解决方式上传到服务器转base64 基于微信小程序获取头像昵称规则调整后&#xff0c;当小程序需要让用户…

高通Android 12/13 设置和获取ADB状态

/*** 设置ADB状态** param isEnable*/public void setADB(boolean isEnable) {Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.ADB_ENABLED, isEnable ? 1 : 0);}/*** 获取ADB状态** return*/public boolean getADB() {return Settings.Global.getIn…

虚拟化技术[3]之网络虚拟化

网络虚拟化 网络虚拟化简介核心层网络虚拟化接入层网络虚拟化虚拟机网络虚拟化案例: VMware网络虚拟化技术虚拟网络接口卡虚拟交换机vSwitch分布式交换机端口组VLAN 网络虚拟化简介 传统的数据中心&#xff1a;服务器之间操作系统和上层软件异构、接口与数据格式不统一&#x…

基于hive的酒店价格数据可视化分析系统设计和实现

摘要 本文基于Django框架和Hive技术&#xff0c;设计和实现了一种酒店价格数据可视化分析系 统&#xff0c;旨在为酒店管理者提供直观、清晰的数据洞察和决策支持。在研究中&#xff0c;首先深入分 析了酒店价格数据可视化分析系统的背景和意义&#xff0c;认识到对于酒店行…

3.Redis之Redis的环境搭建redis客户端介绍

1.版本的选取 安装 Redis&#xff1a;Redis 5 系列~~ 在 Linux 中进行安装~~ Redis 官方是不支持 Windows 版本的~~ 微软维护了一个 Windows 版本的 Redis 分支 Centos和Ubuntu.Docker 2.如何进行安装&#xff1f;&#xff1f;&#xff1f; 1.ubuntu 2.centos yum instal…

arcgisPro将一个图层的要素复制到另一个图层

1、打开两个图层&#xff0c;如下&#xff0c;其中一个图层中有两个要素&#xff0c;需要将其中一个要素复制到另一个图层中&#xff0c;展示如下&#xff1a; 2、选中待复制要素&#xff0c;点击复制按钮&#xff0c;如下&#xff1a; 3、下拉粘贴按钮列表&#xff0c;选择【选…

Ubuntu22.04虚拟机设置静态IP

虚拟机设置静态IP 按下电脑的 “win”键&#xff0c;在弹出的输入框中输入“控制面板”&#xff0c;选中控制面板 1.选择 “网络和Internet” 2.选择 “网络和共享中心” 3.选择 “更改适配器设置” 4.选择 “VMnet8”&#xff0c;双击打开 5.选择 “属性” 找到 “Internet …

【idea】idea2024最新版本下载_安装_破解

1、下载 下载地址&#xff1a;下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 下载完成&#xff1a; idea破解脚本下载链接&#xff1a;https://pan.baidu.com/s/1L5qq26cRABw8XuEn_CngKQ 提取码&#xff1a;6666 下载完成&#xff1a; 2、安装 1、双击idea的安装包&…

《计算机网络微课堂》1-6 计算机体系结构

常见的计算机网络体系结构 从本节课开始&#xff0c;我们要用 4 次课的时间来介绍有关计算机网络体系结构的知识&#xff0c;具体包含以下内容&#xff1a; 一&#xff0c;常见的计算机网络体系结构二&#xff0c;计算机网络体系结构分层的必要性三&#xff0c;计算机网络体系…

给我瞅瞅呀

专业 流程&#xff08;一条龙服务&#xff09; 需求沟通-需求分析-产品架构-ue原型-ui设计-产品研发-产品测试-产品交付-产品运维 保障 1、按需定制&#xff0c;签订功能清单&#xff0c;根据功能报价 2、价格透明&#xff0c;签订合同保障&#xff0c;保障客户合法权益 3、源…