HDMI 输出实验

FPGA教程学习

第十四章 HDMI 输出实验


文章目录


前言

  1. FPGA通过HDMI编码芯片输出彩条。

实验原理

开发板有一个HDMI编码芯片,ADV7511,将 24 位 RGB 编码输出 TMDS 差分信号。

本实验使用其将RGB24视频数据显示出来。

硬件原理图如下:

结合前面,从原理图(或者看手册,这里只是大致分析一下)上可以分析出:

  1. 编码输入是24位的数据,本实验是RGB
  2. 编码输出时TMDS 差分信号
  3. 编码芯片的配置接口是I2C
  4. HDMI接口上有I2C

实验过程

程序设计

时钟模块(video_pll)

负责产生一个100Mhz的时钟和一个1080P的148.5Mhz的像素时钟。

生成时钟 IP 的方法是点击 Project Manager目录下的 IP Catalog,再选择 FPGA Features and Design->Clocking->Clocking Wizard 图标。然后进行设置即可。

彩条产生模块(color_bar)

是产生 8 种颜色的 VGA 格式的彩条,彩条分别为白、黄、青、绿、紫、红、蓝和黑。产生分辨率为 1920x1080 刷新率为 60Hz 的彩条,也就是所谓的 1080P 的高清视频图像。

视频时序图:

模块要根据这个视频时序去看,用了几个寄存器来计数行场信号。

模块接口参数比较少,输入只有时钟和复位,其他都是输出。这里输出的时序是给编码芯片的。

c 复制代码
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     //像素数据、蓝色分量
);
c 复制代码
//*************************************************************************\
//==========================================================================
//   Description:
//  彩条发生模块
//==========================================================================
//   Revision History:
//	Date		  By			Revision	Change Description
//--------------------------------------------------------------------------
//2013/5/7                     1.2         remove some warning
//2013/4/18                    1.1         vs timing
//2013/4/16	        		   1.0			Original
//*************************************************************************/
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等为后缀的均为某个寄存器的延迟
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,vs,de的输出,hs_reg和vs_reg有关系
			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

// x坐标,像素坐标,当前肩+同步+后肩结束时,输出有效像素,x用来计数有效的图像数据,或者说是x的坐标
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

// 场数据有效信号,在前肩+同步+后肩内为低,图像数据有效时为高,h_active和v_active共同用来指示数据是否有效,作为数据使能信号DE
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 

配置数据查找表模块(lut_adv7511)

这个模块就是根据输入的下标输出设定好的值。

c 复制代码
module lut_adv7511(
	input[9:0]             lut_index, // Look-up table index address
	output reg[31:0]       lut_data   // I2C device address register address register data
);

always@(*)
begin
	case(lut_index)
		//To be compatible with the 16bit register address, add 8'h00
		8'd0 	: 	lut_data	<= 	{8'h72,24'h004100};   //16'h4110; 	
		8'd1    : 	lut_data	<= 	{8'h72,24'h00d6c0};			
		8'd2 	: 	lut_data	<= 	{8'h72,24'h005512};			
		8'd3 	: 	lut_data	<= 	{8'h72,24'h001500};  //input id = 0x0 = 0000 = 24 bit RGB 4:4:4 or YCbCr 4:4:4 (separate syncs)
		8'd4    : 	lut_data	<= 	{8'h72,24'h00d03c};
		8'd5    : 	lut_data	<= 	{8'h72,24'h00af04};	
		8'd6    : 	lut_data	<= 	{8'h72,24'h004c04};	
		8'd7    : 	lut_data	<= 	{8'h72,24'h004000};		
		8'd8 	: 	lut_data	<= 	{8'h72,24'h009803};
		8'd9 	: 	lut_data	<= 	{8'h72,24'h009ae0};	
		8'd10	: 	lut_data	<=	{8'h72,24'h009c30};	
		8'd11	: 	lut_data	<= 	{8'h72,24'h009d61};	
		8'd12	: 	lut_data	<= 	{8'h72,24'h00a2a4};	
		8'd13	: 	lut_data	<= 	{8'h72,24'h00a3a4};	
		8'd14	: 	lut_data	<= 	{8'h72,24'h00e0d0};	
		8'd15	: 	lut_data	<= 	{8'h72,24'h00f900};
		
		default:lut_data <= {8'hff,16'hffff,8'hff};
	endcase
end

endmodule 

I2C Master 寄存器配置模块(i2c_config)

这个模块使用了查找表模块,输出对应的下标,将查找的数据作为输入。然后这里还有一大堆使用I2C读写的代码,以后再看。

c 复制代码
module i2c_config(
	input              rst,
	input              clk,
	input[15:0]        clk_div_cnt,
	input              i2c_addr_2byte,
	output reg[9:0]    lut_index,
	input[7:0]         lut_dev_addr,
	input[15:0]        lut_reg_addr,
	input[7:0]         lut_reg_data,
	output reg         error,
	output             done,
	inout              i2c_scl,
	inout              i2c_sda
);

TODO

  1. 分析I2C读写代码
  2. 了解视频时序
  3. 查看编码芯片数据手册

总结

本实验通过使用I2C配置编码芯片,构建模拟图像输出视频时序模块,完成了使用HDMI输出模拟图像(彩条)的功能。通过本次实验,了解了HDMI输出、视频时序、I2C读写的一些知识点。HDMI输出的是差分信号,芯片输出是可能需要编码芯片进行编码,不能够直接输出。视频时序中有前肩、后肩、同步等术语,行场信号是一个重要的知识点,要熟悉行场信号的构造。I2C是一个常用的配置接口,实验中的编码芯片由I2C进行配置,HDMI接口上也有一个。

参考

  1. 8k,4k,2k视频时序参数分享
  2. HDMI中的视频时序分析
  3. 基于FPGA的视频时序生成
相关推荐
内有小猪卖3 小时前
时序约束 记录
fpga开发
Cao1234567893216 小时前
FPGA时钟设计
fpga开发
JNTeresa9 小时前
锁存器知识点详解
fpga开发
Cao12345678932112 小时前
FPGA基础之基础语法
fpga开发
一大Cpp13 小时前
通过Quartus II实现Nios II编程
fpga开发
7yewh13 小时前
Verilog 语法 (二)
fpga开发
边缘计算社区1 天前
FPGA与边缘AI:计算革命的前沿力量
人工智能·fpga开发
S&Z34631 天前
[官方IP] Shift RAM
网络协议·tcp/ip·fpga开发
S&Z34631 天前
[FPGA Video IP] Video Processing Subsystem
网络协议·tcp/ip·fpga开发·video
FPGA_Linuxer1 天前
FPGA 100G UDP纯逻辑协议栈
网络协议·fpga开发·udp