基于开源fpga实现2048小游戏的VGA转hdmi输出实践学习

概要

本次实践是基于 briankwon123 大佬的开源项目 briankwon123/FPGA_2048_Game 的基础上开发VGA输出转HDMI输出,主要在于前期的学习过程稍微困难些,要去学习VGA和HDMI的传输协议,以及rgb2dvi ip核的原理,只有懂了原理,才能更高效的去调试信号量巨大的verilog代码。

不足之处还望指正!

开源项目介绍

本开源项目是纯fpga(verilog)实现,原项目是采用VGA输出,分辨率为640*480,帧率接近60fps;

首先简单介绍下2048小游戏:有16个格子,初始时会有两个格子上安放了两个数字2,每次可以选择上下左右其中一个方向去滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢外,系统也会在空白的地方随即出现一个数字方块,相同数字的方块在靠拢、相撞时会合并相加。

image-20231010004446704

该项目的整体框图如下:

image-20231010004939732

对于我来说,只是需要将图中右上角的VGA Display加一个VGA转HDMI模块,使其可以以HDMI数据格式输出到显示器上;

VGA2HDMI 实现

基于前面所述,做该扩展只需要了解以下两个模块

4ee4b87ae03f0410debab6e6836af0e

VGA 传输原理

vga接口引脚如下:

image-20231010010314219

VGA的显示效果取决于RGB三个分量的位数,最高24位(即RGB各8位),16位,12位也都存在;开源项目采用的是3-3-2长度的rgb信号,后续转为hdmi时均扩展为8位。传输显示数据把每个像素点对应的三通道的数值传输到显示屏。

HDMI传输原理

HDMI全称是 High-Definition Multimedia Interface,它能够同时传输视频和音频,HDMI 向下兼容 DVI,但是 DVI(数字视频接口)只能用来传输视频,而不能同时传输音 频,这是两者最主要的差别;

HDMI引脚定义

image-20231010010637861

DVI 和 HDMI 接口协议在物理层使用 TMDS (Transition Minimized Differential Signaling,最小化传输差分信号)标准传输音视频数据,TMDS 差 分传输技术使用两个引脚(如图中的“数据 2+”和“数据 2-”)来传输一路信号,利 用这两个引脚间的电压差的正负极性和大小来决定传输数据的数值(0 或 1);

下面是 pynq-z1开发板中关于HDMI-OUT的引脚

##HDMI Tx

#set_property -dict { PACKAGE_PIN G15   IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_cec }]; #IO_L19N_T3_VREF_35 Sch=hdmi_tx_cec
set_property -dict { PACKAGE_PIN L17   IOSTANDARD TMDS_33  } [get_ports { TMDS_Clk_n }]; #IO_L11N_T1_SRCC_35 Sch=hdmi_tx_clk_n
set_property -dict { PACKAGE_PIN L16   IOSTANDARD TMDS_33  } [get_ports { TMDS_Clk_p }]; #IO_L11P_T1_SRCC_35 Sch=hdmi_tx_clk_p
set_property -dict { PACKAGE_PIN K18   IOSTANDARD TMDS_33  } [get_ports { TMDS_Data_n[0] }]; #IO_L12N_T1_MRCC_35 Sch=hdmi_tx_d_n[0]
set_property -dict { PACKAGE_PIN K17   IOSTANDARD TMDS_33  } [get_ports { TMDS_Data_p[0] }]; #IO_L12P_T1_MRCC_35 Sch=hdmi_tx_d_p[0]
set_property -dict { PACKAGE_PIN J19   IOSTANDARD TMDS_33  } [get_ports { TMDS_Data_n[1] }]; #IO_L10N_T1_AD11N_35 Sch=hdmi_tx_d_n[1]
set_property -dict { PACKAGE_PIN K19   IOSTANDARD TMDS_33  } [get_ports { TMDS_Data_p[1] }]; #IO_L10P_T1_AD11P_35 Sch=hdmi_tx_d_p[1]
set_property -dict { PACKAGE_PIN H18   IOSTANDARD TMDS_33  } [get_ports { TMDS_Data_n[2] }]; #IO_L14N_T2_AD4N_SRCC_35 Sch=hdmi_tx_d_n[2]
set_property -dict { PACKAGE_PIN J18   IOSTANDARD TMDS_33  } [get_ports { TMDS_Data_p[2] }]; #IO_L14P_T2_AD4P_SRCC_35 Sch=hdmi_tx_d_p[2]
#set_property -dict { PACKAGE_PIN R19   IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_hpdn }]; #IO_0_34 Sch=hdmi_tx_hpdn
#set_property -dict { PACKAGE_PIN M17   IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_scl }]; #IO_L8P_T1_AD10P_35 Sch=hdmi_tx_scl
#set_property -dict { PACKAGE_PIN M18   IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_sda }]; #IO_L8N_T1_AD10N_35 Sch=hdmi_tx_sda

由于我们只需要传输TMDS信号,因此只用到相关的引脚(图中取消注释的)。

DVI或 HDMI视频传输所使用的TMDS 连接通过四个串行通道实现。对于 DVI 来说,其中三个通道分别用于传输视频中每个像素点 的红、绿、蓝三个颜色分量(RGB 4:4:4 格式)。HDMI 默认也是使用三个 RGB 通道,但是它 同样可以选择传输像素点的亮度和色度信息(YCrCb 4:4:4 或 YCrCb 4:2:2 格式)。第四个通道 是时钟通道,用于传输像素时钟。独立的 TMDS 时钟通道为接收端提供接收的参考频率,保证数据在接收端能够正确恢复。

下图是TMDS结构示意图

image-20231010012857184

在传输视频图像的过程中,数据通道上传输的是编码后的有效像素字符。而在每一帧图像 的行与行之间,以及视频中不同帧之间的时间间隔(消隐期)内,数据通道上传输的则是控制 字符。每个通道上有两位控制信号的输入接口,共对应四种不同的控制字符。这些控制字符提 供了视频的行同步(HZYNC)以及帧同步(VSYNC)信息,也可以用来指定所传输数据的边 界(用于同步)。

对于 DVI 传输,整个视频的消隐期都用来传输控制字符。而 HDMI 传输的消隐期除了控 制字符之外,还可以用于传输音频或者其他附加数据,比如字幕信息等

RGB2DVI IP核

该IP 核 将 RGB888 格式的视 频图像转换成 TMDS 数据输出;

原理框图如下:

image-20231010011312357

图中 Encoder 模块负责对数据进行编码,Serializer 模块对编码后的数据进行并串 转换,最后通过 OBUFDS 转化成 TMDS 差分信号传输。

整个系统需要两个输入时钟,一个是视频的像素时钟 Pixel Clk,另外一个时钟 Pixel Clk x5 的频率是像素时钟的五倍。

TMDS 连接的时钟通道我们采用与数据通道相同的并转串逻辑来实现。通过对 10 位二进 制序列 10’b11111_00000 在 10 倍像素时钟频率下进行并串转换,就可以得到像素时钟频率下 的 TMDS 参考时钟。该时钟其实是逻辑产生的一个数据信号,不需要额外添加约束。

图像扫描时序分析

如果将显示一帧图像的过程想象成绘画,那么在显示的过程中就是用一根“笔”在 不同的像素点画上不同的颜色。这根笔按照从左至右、从上到下的顺序扫描每个像素点,并且 在像素画上对应的颜色,当画到最后一个像素点的时候一幅图像就绘制好了。假如一个图像的分辨率为 1024*600,那么其扫描如图所示:

image-20231010011449196

行显示时序如下图:

image-20231010011549109

HSYNC:行同步信号,当此信号有效的时候就表示开始显示新的一行数据,查阅所使用的数据手册可以知道此信号是低电平有效还是高电平有效,图为低电平有效。

HSPW:行同步信号宽度,也就是 HSYNC 信号持续时间。HSYNC 信号不是一个脉冲, 而是需要持续一段时间才是有效的,单位为 CLK。

HBP:行显示后沿(或后肩),单位是 CLK。

HOZVAL:行有效显示区域,即显示一行数据所需的时间,假如屏幕分辨率为 1024*600, 那么 HOZVAL 就是 1024,单位为 CLK。

HFP:行显示前沿(或前肩),单位是 CLK。

帧显示时序如下图:

image-20231010011704910

VSYNC:帧(场)同步信号,当此信号有效的时候就表示开始显示新的一帧数据,查阅所使用的数据手册可以知道此信号是低电平有效还是高电平有效,图为低电平有效。

VSPW:帧同步信号宽度,也就是 VSYNC 信号持续时间,单位为 1 行的时间。 VBP:帧显示后沿(或后肩),单位为 1 行的时间。

LINE:帧有效显示区域,即显示一帧数据所需的时间,假如屏幕分辨率为 1024*600,那 么 LINE 就是 600 行的时间。

VFP:帧显示前沿(或前肩),单位为 1 行的时间。

像素时钟

使用RGB2DVI IP 核时需要输入像素时钟,这里采用VGA的像素时钟作为输入,显示一帧图像需要的时钟数为:

N(CLK)= (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)

那么如果要显示1920*1080@60fps需要约125M clk,因此可以大致估算出后面需要提供多少时钟才能让人眼看清楚。

VESA时序说明

VESA(Video Electronics Standards Association)是指视频电子标准协会,此标准规定了电脑(VESA)制式的各种分辨率和刷新频率的显示监视器定时标准(简称为 VESA 标准)

其中关于1080P的参数说明如下:

img

以此来修改代码中的displaygrid模块

image-20231010012422781

小结

在本次实践过程中,发现如果不清楚原理一昧的使用所谓的经验去调试,只会迷茫的不知从何下手,还是要深究原理,一步一步分析,才能找到问题所在,尤其是对于fpga这种相对来说博客和讲解资源尚少(相对于计算机软件/AI方面的)的方向,尤其需要深入理解原理,多查找相关手册和说明文档,来找到一个个问题的根源。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的IC超大啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值