ZYNQ基础系列(二) IO口模拟HDMI

IO口直接驱动HDMI接口
HDMI主要用于给高清显示设备传输视频和音频数据,除了使用专门的HMDI芯片外,当然还可以用ZYNQ的PL部分产生相应的时序,本文就是用FPGA的IO口与HDMI显示设备直接进行通信。

本文的工程rgb2dvi的IP核都上传到了这里

一、原理图
本文采用的是米联客的Mi701开发板,可以看到IO口是直接连到接口上的,只加了一些简单的上拉增加一下驱动能力,IIC也是经过了5V的电平转换,HDMI的5V供电由使能引脚控制。


二、RGB数据转DVI的IP核(不重要)
现在暂时不自己去写这个IP核的底层,直接找到例程里的文件,并自己重新封装一下IP核,文件包括:
DVITransmitter.vhd
hdmi_tx.vhd
SerializerN_1.vhd
TMDSEncoder.vhd

1.新建IP工程,命名为rgb2dvi,设置器件型号
2.将上述四个文件添加到工程中,工程自动识别顶层
3.设置相关信息

4.Tools菜单中,新建IP核,一路默认即可

5.在弹出的窗口中,修改IP信息,添加总线(用于连接其他IP模块)等

弹出的界面的第一页,设置总线类型、名称等

在第二页中,把自己的线和系统预定的线进行一个匹配,匹配的结果如下:

如图创建VGA总线,创建完成后预览IP核

6.最后把IP打包即可

三、工程文件
创建工程,并在setting中加载IP核的路径后,即可在IP核搜索器中搜索到rgb2dvi模块,现在创建这个工程还需要四步

创建一个rgb2dvi的IP,然后准备用这个IP在工程中创建实例
创建一个Clock Wizard的IP,准备用于产生时钟
创建一个.v文件,用于产生显示所需的时序
创建顶层.v文件
引脚约束

1.VGA相关基础
时序图,以及消隐的概念,还有不同分辨率时,各个区域的参数


2.时钟管理模块
由于我的屏幕分辨率为800*600,所以,时钟管理模块,CLK1输出为40M,CLK2是CLK1的5倍200M,选择MMCM模式

3.rgb2dvi模块
直接在IP拾取器中,创建IP

4.时序产生模块
此模块针对800*600的屏幕,产生一个彩色的网格,如需更改为其他分辨率,相关的参数见上表
`timescale 1ns / 1ps
module hdmi_data_gen (
input pix_clk,
output [7:0] VGA_R,
output [7:0] VGA_G,
output [7:0] VGA_B,
output VGA_HS,
output VGA_VS,
output VGA_DE
);

//---------------------------------//
// 水平扫描参数的设定800*600 60HZ
//--------------------------------//
parameter H_Sync = 128;
parameter H_Back = 88;
parameter H_Active = 800;
parameter H_Front = 40;
parameter H_Start = H_Sync + H_Back;
parameter H_End = H_Start + H_Active;
parameter H_Total = H_End + H_Front;
//-------------------------------//
// 垂直扫描参数的设定800*600 60HZ
//-------------------------------//
parameter V_Sync = 4;
parameter V_Back = 23;
parameter V_Active = 600;
parameter V_Front = 1;
parameter V_Start = V_Sync + V_Back;
parameter V_End = V_Start + V_Active;
parameter V_Total = V_End + V_Front;

reg[11:0] x_cnt;
always @(posedge pix_clk) //水平计数
begin
if(x_cnt==H_Total)
x_cnt <= 1;
else
x_cnt <= x_cnt + 1;
end

reg hsync_r;
reg hs_de;
always @(posedge pix_clk)
begin
if(x_cnt==1)
hsync_r <= 1'b0;
else if(x_cnt==H_Sync)
hsync_r <= 1'b1;

if(x_cnt==H_Start)
hs_de <= 1'b1;
else if(x_cnt==H_End)
hs_de <= 1'b0;
end

reg[11:0] y_cnt;
always @(posedge pix_clk)
begin
if(y_cnt==V_Total)
y_cnt <= 1;
else if(x_cnt==H_Total)
y_cnt <= y_cnt + 1;
end

reg vsync_r;
reg vs_de;
always @(posedge pix_clk)
begin
if(y_cnt==1)
vsync_r <= 1'b0;
else if(y_cnt==V_Sync)
vsync_r <= 1'b1;

if(y_cnt==V_Start)
vs_de <= 1'b1;
else if(y_cnt==V_End)
vs_de <= 1'b0;
end

reg[7:0] VGA_R_reg;
reg[7:0] VGA_G_reg;
reg[7:0] VGA_B_reg;
always @(posedge pix_clk) //格子图像
begin
if((x_cnt[4]==1'b1)^(y_cnt[4]==1'b1))
VGA_R_reg <= 8'h00;
else
VGA_R_reg <= 8'hff;

if((x_cnt[5]==1'b1)^(y_cnt[5]==1'b1))
VGA_G_reg <= 8'h00;
else
VGA_G_reg <= 8'hff;

if((x_cnt[6]==1'b1)^(y_cnt[6]==1'b1))
VGA_B_reg <= 8'h00;
else
VGA_B_reg <= 8'hff;
end

assign VGA_HS = hsync_r;
assign VGA_VS = vsync_r;
assign VGA_DE = hs_de & vs_de;
assign VGA_R = VGA_DE ? VGA_R_reg : 8'h0;
assign VGA_G = VGA_DE ? VGA_G_reg : 8'h0;
assign VGA_B = VGA_DE ? VGA_B_reg : 8'h0;
endmodule

5.顶层模块
顶层也就是将三个模块连接在一起

6.引脚约束
引脚约束,需要注意的是差分线的约束,模式为TMDS_33,然后只需约束各个差分对的P脚即可
set_property IOSTANDARD TMDS_33 [get_ports HDMI_CLK_P]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_D0_P]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_D1_P]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_D2_P]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_en]

set_property PACKAGE_PIN K17 [get_ports HDMI_CLK_P]
set_property PACKAGE_PIN L19 [get_ports HDMI_D0_P]
set_property PACKAGE_PIN M17 [get_ports HDMI_D1_P]
set_property PACKAGE_PIN L16 [get_ports HDMI_D2_P]
set_property PACKAGE_PIN D18 [get_ports hdmi_en]

四、实验现象

到此为止,对rgb2dvi的IP核的测试也就完成了,此例中的显示时序都是由PL中产生的,而且如果要换分辨率还是要改很多参数,很不方便,如果想用此种方式显示个图片,那估计就更麻烦了。所以之后会用xilinx自带的VTC的IP核,专门产生时序控制 之后将介绍视频通路的搭建。

文章来源:long_fly