PL端:HDMI 输出实验
实验环境
vivado 2024.1
实验任务
做一个 HDMI 输出的彩条
硬件介绍
开发板没有HDMI编码芯片,是将FPGA的3.3差分IO直接连接到HDMI连接器,FPGA 完成 24 位 RGB 编码输出TMDS 差分信号。
HDMI传输要素:
TMDS 差分信号
TMDS(Transition Minimized Differential Signaling)即过渡调制差分信号,也被称为最小化传输差分信号,具有以下特点:
编码方式:
通过异或及异或非等逻辑算法将原始的 8 位信号数据转换成 10 位。前 8 位数据由原始信号经运算后获得,第 9 位指示运算的方式,第 10 位用来实现直流平衡(DC-balanced)。这种编码方式使得被传输信号过渡过程的上冲和下冲减小,能保证信道中直流偏移为零,让电平转化实现不同逻辑接口间的匹配。
传输模式:
差分信号传输:TMDS 采用差分传动技术,利用两个引脚间的电压差来传送信号。传输数据的数值(“0” 或者 “1”)由两脚间电压正负极性和大小决定。即采用 2 根线来传输信号,一根线上传输原来的信号,另一根线上传输与原来信号相反的信号。接收端通过让一根线上的信号减去另一根线上的信号的方式来屏蔽电磁干扰,从而得到正确的信号。这种方式可以减少信号对传输线的电磁干扰,提高信号传输的可靠性。
串行传输:和 LVDS 相似,TMDS 是一个串行的传输设计,这意味着数据是一位一位按顺序传输的,能够在较长的距离上实现高速数据传输。
应用领域:TMDS 技术主要应用于 DVI(Digital Visual Interface)和 HDMI(High Definition Multimedia Interface)等视频接口。在这些接口中,TMDS 链路包括 3 个传输 RGB 信号的数据通道和 1 个传输时钟信号的通道。例如,HDMI 把视频信号分为 R、G、B、H、V 五种信号用 TMDS 技术编码,其中 HV 编码在 B 信号通道里面传输,R、G 的多余位置用来传输音频信号。
总之,TMDS 差分信号技术通过独特的编码和传输方式,实现了高速、可靠的数据传输,在高清视频传输领域得到了广泛应用。
Vivado 工程建立
添加 HDMI 编码器 IP 核
很多人都熟知 VGA 的数据是 RGB 数据,而 HDMI 使用的是 TMDS 差分信号。在 FPGA 中,RGB 数据操作起来相对容易。所以,我们的任务就是将 RGB 数据转换为 HDMI 的 TMDS 差分信号。基于此,我们采用了 RGB to DVI 的 IP,这是因为 DVI 和 HDMI 所使用的都是 TMDS 信号。
复制 repo 文件夹,别的厂家提供的HDMI 编码器的 IP
点击“IP Catalog”,添加IP
路径选择刚才复制的 repo 文件夹
添加 IP 成功提示添加了多少个 IP
搜索“RGB to DVI Video Encoder(Source)”,双击
保持默认,点击OK
看到一个名为 rgb2dvi_0
添加像素时钟 PLL 模块
驱动 HDMI 编码器,需要提供像素时钟和 5 倍像素时钟,5 倍像素时钟用于 10:1 串行化
在“IP Catlog”窗口搜索关键字“clock”,双击“Clocking Wizard”
在“Component Name”中填写“video_clock”,“clk_in1”填写
50,这里 50Mhz 和开发板 PL 端晶振频率一致。
输出时钟“clk_out1”用于视频像素时钟,这里填写 74.25,这是 1280x720@60 分辨率的像素时钟,每一种分辨率的像素时钟都不同,需要非常了解视频标准才能知道每一种视频分辨率的像素时钟,“clk_out2”用于编码器串行化,像素时钟的 5 倍,这里填写 371.25,然后点击“OK”生成 IP。
添加彩条发生模块
彩条发生模块是一段 Verilog 代码,用于产生视频时序和水平方向的 8 个彩条
主要是理解代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/11/06 13:09:03
// Design Name:
// Module Name: color_bar
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module color_bar(
input clk, //����ʱ�����룬1280x720@60P������ʱ��Ϊ74.25
input rst, //��λ,����Ч
output hs, //��ͬ��������Ч
output vs, //��ͬ��������Ч
output de, //������Ч
output[7:0] rgb_r, //�������ݡ���ɫ����
output[7:0] rgb_g, //�������ݡ���ɫ����
output[7:0] rgb_b //�������ݡ���ɫ����
);
/*********��Ƶʱ���������******************************************/
parameter H_ACTIVE = 16'd1280; //����Ч���ȣ�����ʱ�����ڸ�����
parameter H_FP = 16'd110; //��ͬ��ǰ�糤��
parameter H_SYNC = 16'd40; //��ͬ������
parameter H_BP = 16'd220; //��ͬ����糤��
parameter V_ACTIVE = 16'd720; //����Ч���ȣ��еĸ�����
parameter V_FP = 16'd5; //��ͬ��ǰ�糤��
parameter V_SYNC = 16'd5; //��ͬ������
parameter V_BP = 16'd20; //��ͬ����糤��
//parameter H_ACTIVE = 16'd1920;
//parameter H_FP = 16'd88;
//parameter H_SYNC = 16'd44;
//parameter H_BP = 16'd148;
//parameter V_ACTIVE = 16'd1080;
//parameter V_FP = 16'd4;
//parameter V_SYNC = 16'd5;
//parameter V_BP = 16'd36;
parameter H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP;//���ܳ���
parameter V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP;//���ܳ���
/*********����RGB color bar��ɫ��������*****************************/
parameter WHITE_R = 8'hff;
parameter WHITE_G = 8'hff;
parameter WHITE_B = 8'hff;
parameter YELLOW_R = 8'hff;
parameter YELLOW_G = 8'hff;
parameter YELLOW_B = 8'h00;
parameter CYAN_R = 8'h00;
parameter CYAN_G = 8'hff;
parameter CYAN_B = 8'hff;
parameter GREEN_R = 8'h00;
parameter GREEN_G = 8'hff;
parameter GREEN_B = 8'h00;
parameter MAGENTA_R = 8'hff;
parameter MAGENTA_G = 8'h00;
parameter MAGENTA_B = 8'hff;
parameter RED_R = 8'hff;
parameter RED_G = 8'h00;
parameter RED_B = 8'h00;
parameter BLUE_R = 8'h00;
parameter BLUE_G = 8'h00;
parameter BLUE_B = 8'hff;
parameter BLACK_R = 8'h00;
parameter BLACK_G = 8'h00;
parameter BLACK_B = 8'h00;
reg hs_reg;//����һ���Ĵ���,������ͬ��
reg vs_reg;//����һ���Ĵ���,�û���ͬ��
reg hs_reg_d0;//hs_regһ��ʱ�ӵ��ӳ�
//������_d0��d1��d2��Ϊ���ľ�Ϊij���Ĵ������ӳ�
reg vs_reg_d0;//vs_regһ��ʱ�ӵ��ӳ�
reg[11:0] h_cnt;//�����еļ�����
reg[11:0] v_cnt;//���ڳ���֡���ļ�����
reg[11:0] active_x;//��Чͼ��ĵ�����x
reg[11:0] active_y;//��Чͼ�������y
reg[7:0] rgb_r_reg;//��������r����
reg[7:0] rgb_g_reg;//��������g����
reg[7:0] rgb_b_reg;//��������b����
reg h_active;//��ͼ����Ч
reg v_active;//��ͼ����Ч
wire video_active;//һ֡��ͼ�����Ч����h_active & v_active
reg video_active_d0;
assign hs = hs_reg_d0;
assign vs = vs_reg_d0;
assign video_active = h_active & v_active;
assign de = video_active_d0;
assign rgb_r = rgb_r_reg;
assign rgb_g = rgb_g_reg;
assign rgb_b = rgb_b_reg;
always@(posedge clk or posedge rst)
begin
if(rst)
begin
hs_reg_d0 <= 1'b0;
vs_reg_d0 <= 1'b0;
video_active_d0 <= 1'b0;
end
else
begin
hs_reg_d0 <= hs_reg;
vs_reg_d0 <= vs_reg;
video_active_d0 <= video_active;
end
end
always@(posedge clk or posedge rst)
begin
if(rst)
h_cnt <= 12'd0;
else if(h_cnt == H_TOTAL - 1)//�м����������ֵ����
h_cnt <= 12'd0;
else
h_cnt <= h_cnt + 12'd1;
end
always@(posedge clk or posedge rst)
begin
if(rst)
active_x <= 12'd0;
else if(h_cnt >= H_FP + H_SYNC + H_BP - 1)//����ͼ���x����
active_x <= h_cnt - (H_FP[11:0] + H_SYNC[11:0] + H_BP[11:0] - 12'd1);
else
active_x <= active_x;
end
always@(posedge clk or posedge rst)
begin
if(rst)
v_cnt <= 12'd0;
else if(h_cnt == H_FP - 1)//������������ΪH_FP - 1��ʱ������+1������
if(v_cnt == V_TOTAL - 1)//�������������ֵ�ˣ�����
v_cnt <= 12'd0;
else
v_cnt <= v_cnt + 12'd1;//û�����ֵ��+1
else
v_cnt <= v_cnt;
end
always@(posedge clk or posedge rst)
begin
if(rst)
hs_reg <= 1'b0;
else if(h_cnt == H_FP - 1)//��ͬ����ʼ��...
hs_reg <= 1'b1;
else if(h_cnt == H_FP + H_SYNC - 1)//��ͬ����ʱ��Ҫ������
hs_reg <= 1'b0;
else
hs_reg <= hs_reg;
end
always@(posedge clk or posedge rst)
begin
if(rst)
h_active <= 1'b0;
else if(h_cnt == H_FP + H_SYNC + H_BP - 1)
h_active <= 1'b1;
else if(h_cnt == H_TOTAL - 1)
h_active <= 1'b0;
else
h_active <= h_active;
end
always@(posedge clk or posedge rst)
begin
if(rst)
vs_reg <= 1'd0;
else if((v_cnt == V_FP - 1) && (h_cnt == H_FP - 1))
vs_reg <= 1'b1;
else if((v_cnt == V_FP + V_SYNC - 1) && (h_cnt == H_FP - 1))
vs_reg <= 1'b0;
else
vs_reg <= vs_reg;
end
always@(posedge clk or posedge rst)
begin
if(rst)
v_active <= 1'd0;
else if((v_cnt == V_FP + V_SYNC + V_BP - 1) && (h_cnt == H_FP - 1))
v_active <= 1'b1;
else if((v_cnt == V_TOTAL - 1) && (h_cnt == H_FP - 1))
v_active <= 1'b0;
else
v_active <= v_active;
end
always@(posedge clk or posedge rst)
begin
if(rst)
begin
rgb_r_reg <= 8'h00;
rgb_g_reg <= 8'h00;
rgb_b_reg <= 8'h00;
end
else if(video_active)
if(active_x == 12'd0)
begin
rgb_r_reg <= WHITE_R;
rgb_g_reg <= WHITE_G;
rgb_b_reg <= WHITE_B;
end
else if(active_x == (H_ACTIVE/8) * 1)
begin
rgb_r_reg <= YELLOW_R;
rgb_g_reg <= YELLOW_G;
rgb_b_reg <= YELLOW_B;
end
else if(active_x == (H_ACTIVE/8) * 2)
begin
rgb_r_reg <= CYAN_R;
rgb_g_reg <= CYAN_G;
rgb_b_reg <= CYAN_B;
end
else if(active_x == (H_ACTIVE/8) * 3)
begin
rgb_r_reg <= GREEN_R;
rgb_g_reg <= GREEN_G;
rgb_b_reg <= GREEN_B;
end
else if(active_x == (H_ACTIVE/8) * 4)
begin
rgb_r_reg <= MAGENTA_R;
rgb_g_reg <= MAGENTA_G;
rgb_b_reg <= MAGENTA_B;
end
else if(active_x == (H_ACTIVE/8) * 5)
begin
rgb_r_reg <= RED_R;
rgb_g_reg <= RED_G;
rgb_b_reg <= RED_B;
end
else if(active_x == (H_ACTIVE/8) * 6)
begin
rgb_r_reg <= BLUE_R;
rgb_g_reg <= BLUE_G;
rgb_b_reg <= BLUE_B;
end
else if(active_x == (H_ACTIVE/8) * 7)
begin
rgb_r_reg <= BLACK_R;
rgb_g_reg <= BLACK_G;
rgb_b_reg <= BLACK_B;
end
else
begin
rgb_r_reg <= rgb_r_reg;
rgb_g_reg <= rgb_g_reg;
rgb_b_reg <= rgb_b_reg;
end
else
begin
rgb_r_reg <= 8'h00;
rgb_g_reg <= 8'h00;
rgb_b_reg <= 8'h00;
end
end
endmodule
添加顶层模块
top 模块例化了彩条发生模块,HDMI 编码模块,和像素时钟生成模块
将hdmi_out_test.srcs\sources_1\new的top文件copy过来,然后点击添加现有资源文件
添加 XDC 约束文件
点击“Run Synthesis”开始综合
综合完成以后点击“Cancel”
点击“Constraints Wizard”
在弹出的窗口中点击“Next”
set_property PACKAGE_PIN U18 [get_ports {sys_clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {sys_clk}]
create_clock -period 20.000 -waveform {0.000 10.000} [get_ports sys_clk]
set_property IOSTANDARD TMDS_33 [get_ports TMDS_clk_n]
set_property PACKAGE_PIN U13 [get_ports TMDS_clk_p]
set_property IOSTANDARD TMDS_33 [get_ports TMDS_clk_p]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_data_n[0]}]
set_property PACKAGE_PIN W14 [get_ports {TMDS_data_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_data_p[0]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_data_n[1]}]
set_property PACKAGE_PIN Y18 [get_ports {TMDS_data_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_data_p[1]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_data_n[2]}]
set_property PACKAGE_PIN Y16 [get_ports {TMDS_data_p[2]}]
set_property IOSTANDARD TMDS_33 [get_ports {TMDS_data_p[2]}]
set_property PACKAGE_PIN V16 [get_ports hdmi_oen]
set_property IOSTANDARD LVCMOS33 [get_ports hdmi_oen]
下载调试
编译生成 bit 文件
需要注意,这里使用1280x720@60Hz
显示屏显示结果
总结
1、学会使用第三点IP核,首先是添加,后面的使用和自带IP核操作一致。
2、主要还是在梳理vivado使用流程。
参考
《cource_s1_ALINX_ZYNQ(AX7Z010_AX7Z020)开发平台基础教程V1.04.pdf》