HDMI显示器驱动设计与验证

本文详细介绍了HDMI(高清多媒体接口)的发展历程、标准特性,包括HDMI2.0和HDMI2.1的分辨率与刷新率支持。HDMI接口相较于VGA和DVI接口,具有体积小、抗干扰强、音频视频同步传输等优势。文章还深入解析了HDMI的TMDS传输原理,以及编码、并串转换的逻辑实现,展示了如何设计HDMI显示器驱动模块的编码和并行转串行模块代码。最后,给出了顶层模块的实例化和测试激励。
摘要由CSDN通过智能技术生成

1、介绍

        HDMI,英文全称“High Definition Multimedia Interface”,高清多媒体接口。

        HDMI接口是目前最主流的接口了。它在2002年提出,现在已经发展到HDMI 2.1标准,而且随着行业发展,HDMI 2.1标准已经能够支持4K 120Hz及8K 60Hz,支持高动态范围成像(HDR),可以针对场景或帧数进行优化,向后兼容HDMI 2.0、HDMI 1.4。最主要的是,它是视音频同时传输的。

HDMI规格诞生日期支持的分辨率和对应的刷新率
HDMI2.02013年1080p 144Hz
2K 144Hz
4K 60Hz
HDMI2.12017年1080p 240Hz
2K 240Hz
4K 120Hz
8K 30Hz

        VGA接口成本低,结构简单,应用灵活,但是传输的是模拟信号,就容易受到外界干扰源的影响,受到影响会导致信号畸变,传输的图像就会出现问题,VGA接口体积大,不利于便携设备的使用。后来出现了DVI接口,DVIA(只传输模拟信号)、DVID(只传输数字信号)、DVII(兼容数字信号和模拟信号),数字信号比较不容易受到干扰,体积比VGA还大,只能传输图像,不能传输音频信号。后来出现了HDMI接口,体积小,抗干扰能力更强,可以实现音频和数字信号的同步传输,兼容性好。不同接口的区别如下:

接口类型设计年代 最大支持分辨率刷新率 特点
VGA1987年   1080p 60Hz1.传输的是模拟信号
2.画面传输质量不高
3.不能传输声音信号
DVI 1999年    2K  120Hz(1080p下)或60Hz(2K下)1.传输的是数字信号
2.画面传输质量不高
3.不能传输声音信号
HDMI2002年   8K 240Hz(1080P下)  1.传输的是数字信号
2.画面传输质量高
3.可以传输声音信号
DP2006年   16K 240Hz(4K下)  1.传输的是数字信号
2.画面传输质量高
3.可以传输声音信号

HDMI引脚:

引脚定义引脚定义
1数据2+11时钟屏蔽
2数据2屏蔽12时钟-
3数据2-13CEC
4数据1+14保留
5数据1屏蔽15DDC时钟线(SCL)
6数据1-16DDC数据线(SDA)
7数据0+17DDC / CEC地
8数据0屏蔽18+5V电源
9数据0-19热插拔检测
10时钟+

 引脚分类:

(1)TMDS,是一种传输技术,引脚1~引脚12,用于发送音频、视频和各种辅助数据,遵循DVI编码方式,引脚1和引脚3、引脚4和引脚6、引脚7和引脚9、引脚10和引脚12是四对差分信号
(2)DDC通道,显示数据通道,引脚15~引脚17,只需要单向获取接收端的信息,也就是显示器,其实就是I2C接口
(3)CEC通道,引脚13和引脚17
(4)其他,保留、电源、热插拔检测

HDMI显示原理:

发送端将图像数据、音频数据和控制信号转换成4对差分信号,传给接收方,供接收方解码使用。

TMDS传输原理 : 

         发送端收到表示RGB信号的24bit并行数据,这些数据在行场同步信号和使能信号下,与时钟信号一起进行编码和并串转换,之后将3个表示RGB的数据和1个时钟信号分别分配到4个独立的通道发送出去。

        接收端接收到发送端发过来的串行信号,进行解码和串并转换,之后发送到显示器的控制端,在时钟信号的同步下进行显示。

信号传输步骤:

(1)8bit视频、音频数据转换为10bit的最小传输、直流均衡数据,编码
(2)并行10bit编码后的数据转换为串行差分信号,并串转换

2、HDMI显示器驱动设计与验证

 ① 模块设计

        可以使用前面的vga_coloarbar工程,得到rgb、hsync、vsync信号,clk由PPL锁相环得到,DE使能信号可以用从vga_ctrl模块引出的rgb_valid信号代替;经过了vga_pic和vga_ctrl,就可以得到基于TMDS传输原理的hdmi_ctrl模块输入端口信号,R、G、B信号,hsync、vsync行场同步和DE使能信号。

在这里插入图片描述

        hdmi_ctrl模块要完成TMDS传输方式,需要有两组逻辑实现,encode对接收过来的数据进行编码、par_to_ser对编码后的10bit数据进行串行发送,可以给里面安排独立的两个模块逻辑。

在这里插入图片描述

        HDMI的15、16引脚分别是DDC时钟线(SCL)、DDC数据线(SDA),这两路信号连接的是显示器的ROM,采用的是I2C协议,用于读取显示器ROM中的数据,比如生产厂商、型号、支持的分辨率等。这两个接口暂时不使用,强制置位高电平。

encode模块代码:

module encode (
	input	wire			vga_clk		,
	input	wire			sys_rst_n	,
	input	wire			hsync		,
	input	wire			vsync		,
	input	wire			rgb_valid	,
	input	wire	[ 7: 0]	data_in		,
	
	output	reg		[ 9: 0]	data_out
);

	wire				ctrl_1		;		// 组合逻辑计算D数据1的个数
	wire				ctrl_2		;
	wire				ctrl_3		;
	wire	[ 8: 0]		q_m			;		// 8bit到9bit扩展
	
	reg		[ 3: 0]		data_in_n1	;
	reg		[ 7: 0]		data_in_reg	;		// 输入数据打拍	
	reg		[ 4: 0]		cnt			;		// 后面要计算(cnt(t-1) + (N0{q_m[0:7]} - N1{q_m[0:7]})),8+8就需要4bit,再加上一个符号位
	reg		[ 3: 0]		q_m_n1		;
	reg		[ 3: 0]		q_m_n0		;
	reg		[ 8: 0]		q_m_reg		;
	reg					de_reg1		;
	reg					de_reg2		;
	reg					c0_reg1		;
	reg					c0_reg2		;
	reg					c1_reg1		;
	reg					c1_reg2		;

	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			data_in_n1	<=	4'd0;
		else
			data_in_n1	<=	data_in[0] + data_in[1] + data_in[2] + data_in[3] + 
							data_in[4] + data_in[5] + data_in[6] + data_in[7];
							
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			data_in_reg	<=	8'b0;
		else
			data_in_reg	<=	data_in;
			
	assign	ctrl_1	=	((data_in_n1 > 4'd4) || ((data_in_n1 == 4'd4) && (data_in_reg[0] == 1'b0)))
						? 1'b1 : 1'b0 ;

	
	assign	q_m[0]	=	data_in_reg[0];
	assign	q_m[1]	=	(ctrl_1 == 1'b1) ? (q_m[0] ^~ data_in_reg[1]) : (q_m[0] ^ data_in_reg[1]) ;
	assign	q_m[2]	=	(ctrl_1 == 1'b1) ? (q_m[1] ^~ data_in_reg[2]) : (q_m[1] ^ data_in_reg[2]) ;
	assign	q_m[3]	=	(ctrl_1 == 1'b1) ? (q_m[2] ^~ data_in_reg[3]) : (q_m[2] ^ data_in_reg[3]) ;
	assign	q_m[4]	=	(ctrl_1 == 1'b1) ? (q_m[3] ^~ data_in_reg[4]) : (q_m[3] ^ data_in_reg[4]) ;
	assign	q_m[5]	=	(ctrl_1 == 1'b1) ? (q_m[4] ^~ data_in_reg[5]) : (q_m[4] ^ data_in_reg[5]) ;
	assign	q_m[6]	=	(ctrl_1 == 1'b1) ? (q_m[5] ^~ data_in_reg[6]) : (q_m[5] ^ data_in_reg[6]) ;
	assign	q_m[7]	=	(ctrl_1 == 1'b1) ? (q_m[6] ^~ data_in_reg[7]) : (q_m[6] ^ data_in_reg[7]) ;
	assign	q_m[8]	=	(ctrl_1 == 1'b1) ? 1'b0 : 1'b1;

	
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			begin
				q_m_n1	<=	4'd0;
				q_m_n0	<=	4'd0;
			end
		else
			begin
				q_m_n1	<=	q_m[0] + q_m[1] + q_m[2] + q_m[3] + 
							q_m[4] + q_m[5] + q_m[6] + q_m[7];
				q_m_n0	<=	4'd8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + 
									q_m[4] + q_m[5] + q_m[6] + q_m[7]);
			end

//	assign	ctrl_2	=	((cnt == 5'd0) || (q_m_n1 == q_m_n0)) ? 1'b1 : 1'b0;
//	assign	ctrl_3	=	(((cnt > 5'd0) && (q_m_n1 > q_m_n0)) 
//						|| ((cnt < 5'd0) && (q_m_n0 > q_m_n1)))
//						? 1'b1 : 1'b0;
	/* cnt是由符号位的,所以不能简单的进行大于小于判断,而是应该判断符号位*/
	assign	ctrl_2	=	((cnt == 5'd0) || (q_m_n1 == q_m_n0)) ? 1'b1 : 1'b0;
	assign	ctrl_3	=	(((cnt[4] == 1'b0) && (q_m_n1 > q_m_n0)) 
						|| ((cnt[4] == 1'b1) && (q_m_n0 > q_m_n1)))
						? 1'b1 : 1'b0;

	// 打拍信号统一赋值
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			begin
				q_m_reg	<=	9'b0;
				de_reg1	<=	1'b0;
				de_reg2	<=	1'b0;
				c0_reg1	<=	1'b0;
				c0_reg2	<=	1'b0;
				c1_reg1	<=	1'b0;
				c1_reg2	<=	1'b0;
			end
		else
			begin
				q_m_reg	<=	q_m;
				de_reg1	<=	rgb_valid;
				de_reg2	<=	de_reg1;
				c0_reg1	<=	hsync;
				c0_reg2	<=	c0_reg1;
				c1_reg1	<=	vsync;
				c1_reg2	<=	c1_reg1;
			end
	
	always @ (posedge vga_clk or negedge sys_rst_n)
		if (sys_rst_n == 1'b0)
			begin
				data_out	<=	10'b0;
				cnt			<=	5'd0;
			end
		else
			begin
				if (de_reg2 == 1'b1)
					begin
						if (ctrl_2 == 1'b1)
							begin
								data_out[9]		<=	~q_m_reg[8];
								data_out[8]		<=	q_m_reg[8];
								data_out[ 7: 0]	<=	(q_m_reg[8] ? q_m_reg[ 7: 0] : ~q_m_reg[ 7: 0]);
								cnt	<=	(q_m_reg[8] == 1'b0) ? (cnt + (q_m_n0 - q_m_n1)) : (cnt + (q_m_n1 - q_m_n0));
							end
						else
							begin
								if (ctrl_3 == 1'b1)
									begin
										data_out[9]		<=	1'b1;
										data_out[8]		<=	q_m_reg[8];
										data_out[ 7: 0]	<=	~q_m_reg[ 7: 0];
										cnt	<=	cnt + {q_m_reg[8], 1'b0} + (q_m_n0 - q_m_n1);
									end
								else
									begin
										data_out[9]		<=	1'b0;
										data_out[8]		<=	q_m_reg[8];
										data_out[ 7: 0]	<=	q_m_reg[ 7: 0];
										cnt	<=	cnt - {~q_m_reg[8], 1'b0} + (q_m_n1 - q_m_n0);
									end
							end
					end
				else
					begin
						case ({c1_reg2, c0_reg2})
							2'b00:	data_out	<=	10'b00_1010_1011;
							2'b01:	data_out	<=	10'b11_0101_0100;
							2'b10:	data_out	<=	10'b00_1010_1010;
							2'b11:	data_out	<=	10'b11_0101_0101;
							default:	;
						endcase
						cnt	<=	5'd0;
					end
			end
			
endmodule

par_to_ser模块代码:

  1. 并行转串行
  2. 单端信号到差分信号的转换
  3. 单沿到双沿的转换

该模块分为两部分:

        第一部分,将并行的10bit数据在25MHz下经过重组、移位,变成串行的10bit数据,再将这10bit数据分成两组5bit信号data_rise、data_fall,方便区分双沿发送,在125MHz下,以移位寄存的方式从低位逐比特发送

        第二部分,调用ddio模块,在125MHz下,上升沿发送送入的data_rise[0]、下降沿发送送入的data_fall[0]

module par_to_ser (
	input	wire			clk_5x	,
	input	wire	[ 9: 0]	data_in	,
	
	output	wire			ser_p	,
	output	wire			ser_n
);

	wire	[ 4: 0]		data_rise	;	// 偶数位上升沿发送
	wire	[ 4: 0]		data_fall	;	// 奇数位下降沿发送
	reg		[ 4: 0]		data_rise_s	= 0;
	reg		[ 4: 0]		data_fall_s	= 0;
	reg		[ 2: 0]		cnt = 0;

	assign	data_rise = {data_in[8], data_in[6], data_in[4], data_in[2], data_in[0]};
	assign	data_fall = {data_in[9], data_in[7], data_in[5], data_in[3], data_in[1]};

	always @ (posedge clk_5x)
		begin
			cnt	<=	(cnt[2] == 1'b1) ? 3'd0 : (cnt + 1'b1);
			data_rise_s	<=	(cnt[2] == 1'b1) ? data_rise : {1'b0, data_rise_s[4:1]};
			data_fall_s	<=	(cnt[2] == 1'b1) ? data_fall : {1'b0, data_fall_s[4:1]};
		end

// 时钟取反的好处:源clk_5x上升沿数据传送过来,取反后ddio相当于在源clk_5x的下降沿位置进行数据采样,更稳定	
ddio_out	ddio_out_inst (
	.datain_h ( data_rise_s[0] ),
	.datain_l ( data_fall_s[0] ),
	.outclock ( ~clk_5x ),
	.dataout ( ser_p )
	);
// 对data_in进行取反,两根线传输
ddio_out	ddio_out_inst1 (
	.datain_h ( ~data_rise_s[0] ),
	.datain_l ( ~data_fall_s[0] ),
	.outclock ( ~clk_5x ),
	.dataout ( ser_n )
	);


endmodule

hdmi_ctrl模块:
        hdmi_ctrl是采用TDMS原理实现的,需要将输入数据进行扩展编码、双沿发送差分信号,差分信号也就是一组数据和该组数据的反,比如有一组数据3’b010,那么它本身和3’b101就是一组差分信号,分成两个字模块实现,hdmi_ctrl内部就没有逻辑代码,全都是连线。

module hdmi_ctrl(
	input	wire	vga_clk,
	input	wire	clk_5x,
	input	wire	sys_rst_n,
	input	wire	hsync,
	input	wire	vsync,
	input	wire	rgb_valid,
	input	wire	[7:0]rgb_blue,
	input	wire	[7:0]rgb_green,
	input	wire	[7:0]rgb_red,

	output	wire	hdmi_r_p,
	output	wire	hdmi_r_n,
	output	wire	hdmi_g_p,
	output	wire	hdmi_g_n,
	output	wire	hdmi_b_p,
	output	wire	hdmi_b_n,
	output	wire	hdmi_clk_p,
	output	wire	hdmi_clk_n

);

wire	[9:0]	red;
wire	[9:0]	green;
wire	[9:0]	blue;

encode encode_inst_r(
	.vga_clk(vga_clk)		,
	.sys_rst_n(sys_rst_n)	,
	.hsync(hsync)			,
	.vsync(vsync)			,
	.rgb_valid(rgb_valid)	,
	.data_in(rgb_red)		,
	
	.data_out(red)
);
encode encode_inst_g(
	.vga_clk(vga_clk)		,
	.sys_rst_n(sys_rst_n)	,
	.hsync(hsync)			,
	.vsync(vsync)			,
	.rgb_valid(rgb_valid)	,
	.data_in(rgb_green)		,
	
	.data_out(green)
);
encode encode_inst_b(
	.vga_clk(vga_clk)		,
	.sys_rst_n(sys_rst_n)	,
	.hsync(hsync)			,
	.vsync(vsync)			,
	.rgb_valid(rgb_valid)	,
	.data_in(rgb_blue)		,
	
	.data_out(blue)
);

par_to_ser par_to_ser_inst_r(
	.clk_5x(clk_5x)	,
	.data_in(red)	,
	
	.ser_p(hdmi_r_p)	,
	.ser_n(hdmi_r_n)
);
par_to_ser par_to_ser_inst_g(
	.clk_5x(clk_5x)	,
	.data_in(green)	,
	
	.ser_p(hdmi_g_p)	,
	.ser_n(hdmi_g_n)
);
par_to_ser par_to_ser_inst_b(
	.clk_5x(clk_5x)	,
	.data_in(blue)	,
	
	.ser_p(hdmi_b_p)	,
	.ser_n(hdmi_b_n)
);
par_to_ser par_to_ser_inst_clk(
	.clk_5x(clk_5x)	,
	.data_in(10'b11111_00000)	,
	
	.ser_p(hdmi_clk_p)	,
	.ser_n(hdmi_clk_n)
);

endmodule

顶层文件:

module hdmi_colorbar(
	input	wire	sys_clk,
	input	wire	sys_rst_n,

	output	wire	ddc_scl,
	output	wire	ddc_sda,
	output	wire	hdmi_r_p,
	output	wire	hdmi_r_n,
	output	wire	hdmi_g_p,
	output	wire	hdmi_g_n,
	output	wire	hdmi_b_p,
	output	wire	hdmi_b_n,
	output	wire	hdmi_clk_p,
	output	wire	hdmi_clk_n
);

wire	vga_clk;
wire	clk_5x;
wire	locked;
wire	rst_n;
wire	[15:0]pix_data;

wire	[9:0]pix_x;
wire	[9:0]pix_y;
wire	hsync;
wire	vsync;
wire	[15:0]vga_rgb;
wire	rgb_valid;

assign	rst_n = (sys_rst_n & locked);
assign	ddc_scl = 1'b1;
assign	ddc_sda = 1'b1;

clk_gen	clk_gen_inst (
	.areset ( ~sys_rst_n ),
	.inclk0 ( sys_clk ),
	.c0 ( vga_clk ),
	.c1 ( clk_5x ),
	.locked ( locked )
	);
	
vga_pic vga_pic_inst
(
	.vga_clk(vga_clk),
	.sys_rst_n(rst_n),
	.pix_x(pix_x),
	.pix_y(pix_y),
	.pix_data(pix_data)
	
);

vga_ctrl vga_ctrl_inst
(
	.vga_clk(vga_clk),
	.sys_rst_n(rst_n),
	.pix_data(pix_data),
	
	.pix_x(pix_x),
	.pix_y(pix_y),
	.hsync(hsync),
	.vsync(vsync),
	.vga_rgb(vga_rgb),
	.rgb_valid(rgb_valid)
);

hdmi_ctrl hdmi_ctrl_inst(
	.vga_clk(vga_clk),
	.clk_5x(clk_5x),
	.sys_rst_n(rst_n),
	.hsync(hsync),
	.vsync(vsync),
	.rgb_valid(rgb_valid),
	.rgb_blue({vga_rgb[4:0],3'b0}),
	.rgb_green({vga_rgb[10:5],2'b0}),
	.rgb_red({vga_rgb[15:11],3'b0}),
	
	.hdmi_r_p(hdmi_r_p),
	.hdmi_r_n(hdmi_r_n),
	.hdmi_g_p(hdmi_g_p),
	.hdmi_g_n(hdmi_g_n),
	.hdmi_b_p(hdmi_b_p),
	.hdmi_b_n(hdmi_b_n),
	.hdmi_clk_p(hdmi_clk_p),
	.hdmi_clk_n(hdmi_clk_n)

);

endmodule

testbench文件:

`timescale  1ns/1ns
module  tb_hdmi_colorbar();
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire  define
wire            ddc_scl     ;
wire            ddc_sda     ;
wire			hdmi_r_p    ;
wire			hdmi_r_n    ;
wire			hdmi_g_p    ;
wire			hdmi_g_n    ;
wire			hdmi_b_p    ;
wire			hdmi_b_n    ;
wire			hdmi_clk_p  ;
wire			hdmi_clk_n  ;
//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;

//********************************************************************//
//**************************** Clk And Rst ***************************//
//********************************************************************//

//sys_clk,sys_rst_n初始赋值
initial
    begin
        sys_clk     =   1'b1;
        sys_rst_n   <=  1'b0;
        #30
        sys_rst_n   <=  1'b1;
    end

//sys_clk:产生时钟
always  #10 sys_clk = ~sys_clk  ;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//------------- hdmi_colorbar_inst -------------

hdmi_colorbar hdmi_colorbar_inst(
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	
	.ddc_scl(ddc_scl),
	.ddc_sda(ddc_sda),
	.hdmi_r_p(hdmi_r_p),
	.hdmi_r_n(hdmi_r_n),
	.hdmi_g_p(hdmi_g_p),
	.hdmi_g_n(hdmi_g_n),
	.hdmi_b_p(hdmi_b_p),
	.hdmi_b_n(hdmi_b_n),
	.hdmi_clk_p(hdmi_clk_p),
	.hdmi_clk_n(hdmi_clk_n)
);

endmodule

 

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

发光中请勿扰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值