基于FPGA的 TMDS 编码 及 HDMI 显示
创始人
2024-04-29 10:12:51
0


目录

引言

TMDS 编码

原理简介

TMDS编码实现

 HDMI差分数据串行

实现方法

源码

HDMI显示方法

思路

实现

工程结构

源代码分享

板级调试视频


引言

最近在开发板上倒腾了一下 TMDS 视频编码的原理以及实现。特在此做一个记录。文附 全部设计源码、MATLAB源码,需要的可以关注一下。



TMDS 编码

原理简介

TMDS,Transition Minimized Differential Signaling,是一种视频编码方式。其将8位数据编码为10位数据。分为两大阶段:

1、8bit —> 9bit

第一比特不变,接下来的7比特或者是与上一比特异或,或者是同或,取决于哪种结果导致翻转数较少;第9比特指示是哪种操作(异或或者同或);

2、9bit —> 10bit

第10比特决定是否要把阶段1中的前8比特反相,决策依据取决于操作是否有利于整体数据的0-1平衡(即,DC平衡)。

编码流程

D 是 8 位 的像素数据

C0C1 为 控制信号

DE 是数据有效信号
cnt是一个寄存器,用来记录数据流的极性,一个大 于0的正值表明了数据流中总共多传了多少个1,一个小于0 的负值表明了数据流中总共多传了多少个0。cnt{t-1}为上一次传输的中的极性,cnt{t}为当前输入的数据的极性。

q_m 最小化传输的编码结果,9 位,由输入的8 位数据经过最小化传输编码原理编码得到。

q_out 对最小化传输编码结果的9 位数据继续进行直流平衡编码后得到的10位输出结果。

N1{x} 统计输入的参数X 中1 的个数
N0{X} 统计输入的参数X 中0 的个数

TMDS编码实现

// | ===================================================---------------------------===================================================
// | ---------------------------------------------------      TMDS 编码模块 	   ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-12-21
// | 完成时间 : 2022-12-21
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// |			-1- 
// |			-2- 
// | 			-3- 
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:module TMDS_ENCODE_MDL(
// | ==================================== 模块输入输出端口声明 ==================================== 
input 												   	I_PIXEL_CLK  ,//像素数据时钟
input												   	I_SYS_RSTN   ,input 													I_DATA_EN    ,
input 			[7:0]									I_PIXEL_DATA ,input 													I_CTRL0      ,
input 													I_CTRL1      ,output		reg	[9:0]									O_ENCODE_DATA);
// | ====================================   模块内部参数声明   ====================================localparam 		LP_CTRL0 				= 				10'b1101010100;
localparam 		LP_CTRL1 				= 				10'b0010101011;
localparam 		LP_CTRL2 				= 				10'b0101010100;
localparam 		LP_CTRL3 				= 				10'b1010101011;// | ====================================   模块内部信号声明   ====================================
// 寄存
reg 			[1:0]									R_I_DATA_EN;
reg 			[7:0]									R_I_PIXEL_DATA;
reg 			[1:0]									R_I_CTRL0;
reg 			[1:0]									R_I_CTRL1;// 4 个判断条件 (依据流程图)
wire 													W_JUDGE_CONDT1;
wire 													W_JUDGE_CONDT2;
wire 													W_JUDGE_CONDT3;
wire 													W_JUDGE_CONDT4;// 计数
reg 			[3:0]									R_I_PIXEL_DATA_1_NUM;
reg 			[3:0]									R_Q_M_1_NUM;
reg 			[3:0]									R_Q_M_0_NUM;
reg 			[4:0]									R_CNT;// 9bit编码
wire 			[8:0]									W_Q_M;
reg 			[8:0]									R_Q_M;
// | ====================================   模块内部逻辑设计   ====================================
always @ (posedge I_PIXEL_CLK)
beginif(~I_SYS_RSTN)beginR_I_DATA_EN 	  <= 2'd0;R_I_PIXEL_DATA    <= I_PIXEL_DATA;R_I_CTRL0		  <= 2'd0;R_I_CTRL1         <= 2'd0;R_Q_M 			  <= 9'd0;endelsebeginR_I_DATA_EN 	  <= {R_I_DATA_EN[0],I_DATA_EN};R_I_PIXEL_DATA    <= I_PIXEL_DATA;R_I_CTRL0		  <= {R_I_CTRL0[0],I_CTRL0};R_I_CTRL1         <= {R_I_CTRL1[0],I_CTRL1};R_Q_M 			  <= W_Q_M;end
endalways @ (posedge I_PIXEL_CLK)
beginif(~I_SYS_RSTN)beginR_I_PIXEL_DATA_1_NUM <= 4'd0;endelsebeginR_I_PIXEL_DATA_1_NUM <= I_PIXEL_DATA[0] + I_PIXEL_DATA[1] + I_PIXEL_DATA[2] + I_PIXEL_DATA[3] + I_PIXEL_DATA[4] + I_PIXEL_DATA[5] + I_PIXEL_DATA[6] + I_PIXEL_DATA[7] ;end
endassign W_JUDGE_CONDT1 = (R_I_PIXEL_DATA_1_NUM >4) | ((R_I_PIXEL_DATA_1_NUM == 4) & !R_I_PIXEL_DATA[0]); assign W_Q_M[0] = R_I_PIXEL_DATA[0];
assign W_Q_M[1] = W_JUDGE_CONDT1 ? ~(W_Q_M[0] ^ R_I_PIXEL_DATA[1]) : W_Q_M[0] ^ R_I_PIXEL_DATA[1];
assign W_Q_M[2] = W_JUDGE_CONDT1 ? ~(W_Q_M[1] ^ R_I_PIXEL_DATA[2]) : W_Q_M[1] ^ R_I_PIXEL_DATA[2];
assign W_Q_M[3] = W_JUDGE_CONDT1 ? ~(W_Q_M[2] ^ R_I_PIXEL_DATA[3]) : W_Q_M[2] ^ R_I_PIXEL_DATA[3];
assign W_Q_M[4] = W_JUDGE_CONDT1 ? ~(W_Q_M[3] ^ R_I_PIXEL_DATA[4]) : W_Q_M[3] ^ R_I_PIXEL_DATA[4];
assign W_Q_M[5] = W_JUDGE_CONDT1 ? ~(W_Q_M[4] ^ R_I_PIXEL_DATA[5]) : W_Q_M[4] ^ R_I_PIXEL_DATA[5];
assign W_Q_M[6] = W_JUDGE_CONDT1 ? ~(W_Q_M[5] ^ R_I_PIXEL_DATA[6]) : W_Q_M[5] ^ R_I_PIXEL_DATA[6];
assign W_Q_M[7] = W_JUDGE_CONDT1 ? ~(W_Q_M[6] ^ R_I_PIXEL_DATA[7]) : W_Q_M[6] ^ R_I_PIXEL_DATA[7];
assign W_Q_M[8] = W_JUDGE_CONDT1 ? 1'b0:1'b1;assign W_JUDGE_CONDT2 = R_I_DATA_EN[1];always @ (posedge I_PIXEL_CLK)
beginif(~I_SYS_RSTN)beginR_Q_M_1_NUM <= 4'd0;R_Q_M_0_NUM <= 4'd0;endelsebeginR_Q_M_1_NUM <= W_Q_M[0] + W_Q_M[1] + W_Q_M[2] + W_Q_M[3] + W_Q_M[4] + W_Q_M[5] + W_Q_M[6] + W_Q_M[7] ;R_Q_M_0_NUM <= !W_Q_M[0] + !W_Q_M[1] + !W_Q_M[2] + !W_Q_M[3] + !W_Q_M[4] + !W_Q_M[5] + !W_Q_M[6] + !W_Q_M[7] ;end
endassign W_JUDGE_CONDT3 = (R_CNT == 5'd0) | (R_Q_M_0_NUM == R_Q_M_1_NUM);
assign W_JUDGE_CONDT4 = (!R_CNT[4] & (R_Q_M_1_NUM > R_Q_M_0_NUM)) | (R_CNT[4] & (R_Q_M_0_NUM > R_Q_M_1_NUM));always @ (posedge I_PIXEL_CLK)
beginif(~I_SYS_RSTN)beginR_CNT <= 5'd0;O_ENCODE_DATA <= 10'd0;endelse if(W_JUDGE_CONDT2)beginif(W_JUDGE_CONDT3)beginO_ENCODE_DATA[7:0] <=  R_Q_M[8] ? R_Q_M[7:0] : ~R_Q_M[7:0];O_ENCODE_DATA[8]   <=  R_Q_M[8];O_ENCODE_DATA[9]   <= ~R_Q_M[8];R_CNT			   <=  R_Q_M[8] ? (R_CNT + R_Q_M_1_NUM - R_Q_M_0_NUM) : (R_CNT + R_Q_M_0_NUM - R_Q_M_1_NUM);endelsebeginif(W_JUDGE_CONDT4)beginO_ENCODE_DATA[7:0] <= ~R_Q_M[7:0];O_ENCODE_DATA[8]   <= R_Q_M[8];O_ENCODE_DATA[9]   <= 1'b1;R_CNT              <= R_CNT + {R_Q_M[8],1'b0} + R_Q_M_0_NUM - R_Q_M_1_NUM;endelsebeginO_ENCODE_DATA[7:0] <= R_Q_M[7:0];O_ENCODE_DATA[8]   <= R_Q_M[8];O_ENCODE_DATA[9]   <= 1'b0;R_CNT              <= R_CNT - {~R_Q_M[8],1'b0} + R_Q_M_1_NUM - R_Q_M_0_NUM;endendendelsebeginR_CNT <= 5'd0;case({R_I_CTRL1[1],R_I_CTRL0[1]})2'b00:beginO_ENCODE_DATA <= LP_CTRL0;end2'b01:beginO_ENCODE_DATA <= LP_CTRL1;end2'b10:beginO_ENCODE_DATA <= LP_CTRL2;end2'b11:beginO_ENCODE_DATA <= LP_CTRL3;enddefault:beginO_ENCODE_DATA <= LP_CTRL0;endendcaseend
end
endmodule

 HDMI差分数据串行

实现方法

每个像素时钟到来时,给出10bit的编码数据,串行发送时,需要用 5倍 于像素数据时钟的时钟频率分别在时钟的双沿将数据逐比特发出。

双沿数据发送使用 ODDR 原语(相同边沿 模式),差分转换使用 OBUFDS 原语。

源码

// | ===================================================---------------------------===================================================
// | ---------------------------------------------------    HDMI 数据发送模块	   ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-12-21
// | 完成时间 : 2022-12-21
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// |			-1- 仅包含视频数据发送(无音频)
// |			-2- 1280 * 720 分辨率
// | 			-3- 
// |
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:module HDMI_DATA_TX_MDL(
// | ==================================== 模块输入输出端口声明 ==================================== 
input 												   I_CLK_74M25  ,
input												   	I_CLK_371M25 ,input 													I_SYS_RSTN   ,input 													I_PIXEL_DATA_EN,
input 				[7:0]								I_PIXEL_DATA_R, 
input 				[7:0]								I_PIXEL_DATA_G,
input 				[7:0]								I_PIXEL_DATA_B,input 													I_HSYNC,
input													   I_VSYNC,output 				[2:0]								O_TMDS_DATA_P,
output 				[2:0]								O_TMDS_DATA_N,output 													O_TMDS_CLK_P,
output 													O_TMDS_CLK_N);
// | ====================================   模块内部信号声明   ====================================wire 				[9:0]								W_ENCODE_DATA_10BIT[2:0];
wire 				[3:0]								W_ODDR_Q;
// 数据分配
wire 				[4:0]								W_TMDS_CH0_D1;
wire 				[4:0]								W_TMDS_CH0_D2;wire 				[4:0]								W_TMDS_CH1_D1;
wire 				[4:0]								W_TMDS_CH1_D2;wire 				[4:0]								W_TMDS_CH2_D1;
wire 				[4:0]								W_TMDS_CH2_D2;wire 				[4:0]								W_TMDS_CH3_D1;
wire 				[4:0]								W_TMDS_CH3_D2;
// 移位寄存
reg 				[4:0]								R_TMDS_CH0_D1;
reg 				[4:0]								R_TMDS_CH0_D2;reg 				[4:0]								R_TMDS_CH1_D1;
reg 				[4:0]								R_TMDS_CH1_D2;reg 				[4:0]								R_TMDS_CH2_D1;
reg 				[4:0]								R_TMDS_CH2_D2;reg 				[4:0]								R_TMDS_CH3_D1;
reg 				[4:0]								R_TMDS_CH3_D2;// 计数
reg 				[2:0]								R_CNT ;// | ====================================   模块内部逻辑设计   ====================================assign 				W_TMDS_CH0_D1			=	{W_ENCODE_DATA_10BIT[0][8],W_ENCODE_DATA_10BIT[0][6],W_ENCODE_DATA_10BIT[0][4],W_ENCODE_DATA_10BIT[0][2],W_ENCODE_DATA_10BIT[0][0]};
assign 				W_TMDS_CH0_D2			=	{W_ENCODE_DATA_10BIT[0][9],W_ENCODE_DATA_10BIT[0][7],W_ENCODE_DATA_10BIT[0][5],W_ENCODE_DATA_10BIT[0][3],W_ENCODE_DATA_10BIT[0][1]};assign 				W_TMDS_CH1_D1			=	{W_ENCODE_DATA_10BIT[1][8],W_ENCODE_DATA_10BIT[1][6],W_ENCODE_DATA_10BIT[1][4],W_ENCODE_DATA_10BIT[1][2],W_ENCODE_DATA_10BIT[1][0]};
assign 				W_TMDS_CH1_D2			=	{W_ENCODE_DATA_10BIT[1][9],W_ENCODE_DATA_10BIT[1][7],W_ENCODE_DATA_10BIT[1][5],W_ENCODE_DATA_10BIT[1][3],W_ENCODE_DATA_10BIT[1][1]};assign 				W_TMDS_CH2_D1			=	{W_ENCODE_DATA_10BIT[2][8],W_ENCODE_DATA_10BIT[2][6],W_ENCODE_DATA_10BIT[2][4],W_ENCODE_DATA_10BIT[2][2],W_ENCODE_DATA_10BIT[2][0]};
assign 				W_TMDS_CH2_D2			=	{W_ENCODE_DATA_10BIT[2][9],W_ENCODE_DATA_10BIT[2][7],W_ENCODE_DATA_10BIT[2][5],W_ENCODE_DATA_10BIT[2][3],W_ENCODE_DATA_10BIT[2][1]};assign 				W_TMDS_CH3_D1			=	{1'b1,1'b1,1'b0,1'b0,1'b0};
assign 				W_TMDS_CH3_D2			=	{1'b1,1'b1,1'b1,1'b0,1'b0};always @ (posedge I_CLK_371M25)
beginif(~I_SYS_RSTN)beginR_TMDS_CH0_D1  <=  5'd0;R_TMDS_CH0_D2  <=  5'd0;R_TMDS_CH1_D1  <=  5'd0;R_TMDS_CH1_D2  <=  5'd0;R_TMDS_CH2_D1  <=  5'd0;R_TMDS_CH2_D2  <=  5'd0;R_TMDS_CH3_D1  <=  5'd0;R_TMDS_CH3_D2  <=  5'd0;R_CNT          <=  3'd0;endelsebeginR_CNT		    <= R_CNT[2] ? 3'd0 : R_CNT + 3'd1;R_TMDS_CH0_D1 <= R_CNT[2] ? W_TMDS_CH0_D1 : {1'b0,R_TMDS_CH0_D1[4:1]};R_TMDS_CH0_D2 <= R_CNT[2] ? W_TMDS_CH0_D2 : {1'b0,R_TMDS_CH0_D2[4:1]};R_TMDS_CH1_D1 <= R_CNT[2] ? W_TMDS_CH1_D1 : {1'b0,R_TMDS_CH1_D1[4:1]};R_TMDS_CH1_D2 <= R_CNT[2] ? W_TMDS_CH1_D2 : {1'b0,R_TMDS_CH1_D2[4:1]};R_TMDS_CH2_D1 <= R_CNT[2] ? W_TMDS_CH2_D1 : {1'b0,R_TMDS_CH2_D1[4:1]};R_TMDS_CH2_D2 <= R_CNT[2] ? W_TMDS_CH2_D2 : {1'b0,R_TMDS_CH2_D2[4:1]};R_TMDS_CH3_D1 <= R_CNT[2] ? W_TMDS_CH3_D1 : {1'b0,R_TMDS_CH3_D1[4:1]};R_TMDS_CH3_D2 <= R_CNT[2] ? W_TMDS_CH3_D2 : {1'b0,R_TMDS_CH3_D2[4:1]};end
end// | ====================================   模块内部模块例化   ====================================
// | TMDS 编码模块TMDS_ENCODE_MDL INST_TMDS_ENCODE_MDL_R(.I_PIXEL_CLK   (I_CLK_74M25),.I_SYS_RSTN    (I_SYS_RSTN),.I_DATA_EN     (I_PIXEL_DATA_EN),.I_PIXEL_DATA  (I_PIXEL_DATA_R),.I_CTRL0       (1'b0),.I_CTRL1       (1'b0),.O_ENCODE_DATA (W_ENCODE_DATA_10BIT[2]));TMDS_ENCODE_MDL INST_TMDS_ENCODE_MDL_G(.I_PIXEL_CLK   (I_CLK_74M25),.I_SYS_RSTN    (I_SYS_RSTN),.I_DATA_EN     (I_PIXEL_DATA_EN),.I_PIXEL_DATA  (I_PIXEL_DATA_G),.I_CTRL0       (1'b0),.I_CTRL1       (1'b0),.O_ENCODE_DATA (W_ENCODE_DATA_10BIT[1]));TMDS_ENCODE_MDL INST_TMDS_ENCODE_MDL_B(.I_PIXEL_CLK   (I_CLK_74M25),.I_SYS_RSTN    (I_SYS_RSTN),.I_DATA_EN     (I_PIXEL_DATA_EN),.I_PIXEL_DATA  (I_PIXEL_DATA_B),.I_CTRL0       (I_HSYNC),.I_CTRL1       (I_VSYNC),.O_ENCODE_DATA (W_ENCODE_DATA_10BIT[0]));
// | ODDR OBUFDS 原语例化
// CH0ODDR #(.DDR_CLK_EDGE("SAME_EDGE"),  // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT        (1'b0       ),  // Initial value of Q: 1'b0 or 1'b1.SRTYPE      ("SYNC"     )   // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_CH0 (.Q  (W_ODDR_Q[0]      ),     // 1-bit DDR output.C  (I_CLK_371M25     ),     // 1-bit clock input.CE (1'b1             ),     // 1-bit clock enable input.D1 (R_TMDS_CH0_D1[0] ),     // 1-bit data input (positive edge).D2 (R_TMDS_CH0_D2[0] ),     // 1-bit data input (negative edge).R  (1'b0             ),     // 1-bit reset.S  (1'b0             )      // 1-bit set);OBUFDS #(.IOSTANDARD("DEFAULT"),      // Specify the output I/O standard.SLEW      ("SLOW"   )       // Specify the output slew rate) OBUFDS_CH0 (.O  (O_TMDS_DATA_P[0]),      // Diff_p output (connect directly to top-level port).OB (O_TMDS_DATA_N[0]),      // Diff_n output (connect directly to top-level port).I  (W_ODDR_Q[0]     )       // Buffer input);// CH1ODDR #(.DDR_CLK_EDGE("SAME_EDGE"),  // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT        (1'b0       ),  // Initial value of Q: 1'b0 or 1'b1.SRTYPE      ("SYNC"     )   // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_CH1 (.Q  (W_ODDR_Q[1]      ),     // 1-bit DDR output.C  (I_CLK_371M25     ),     // 1-bit clock input.CE (1'b1             ),     // 1-bit clock enable input.D1 (R_TMDS_CH1_D1[0] ),     // 1-bit data input (positive edge).D2 (R_TMDS_CH1_D2[0] ),     // 1-bit data input (negative edge).R  (1'b0             ),     // 1-bit reset.S  (1'b0             )      // 1-bit set);OBUFDS #(.IOSTANDARD("DEFAULT"),      // Specify the output I/O standard.SLEW      ("SLOW"   )       // Specify the output slew rate) OBUFDS_CH1 (.O  (O_TMDS_DATA_P[1]),      // Diff_p output (connect directly to top-level port).OB (O_TMDS_DATA_N[1]),      // Diff_n output (connect directly to top-level port).I  (W_ODDR_Q[1]     )       // Buffer input);// CH2ODDR #(.DDR_CLK_EDGE("SAME_EDGE"),  // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT        (1'b0       ),  // Initial value of Q: 1'b0 or 1'b1.SRTYPE      ("SYNC"     )   // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_CH2 (.Q  (W_ODDR_Q[2]      ),     // 1-bit DDR output.C  (I_CLK_371M25     ),     // 1-bit clock input.CE (1'b1             ),     // 1-bit clock enable input.D1 (R_TMDS_CH2_D1[0] ),     // 1-bit data input (positive edge).D2 (R_TMDS_CH2_D2[0] ),     // 1-bit data input (negative edge).R  (1'b0             ),     // 1-bit reset.S  (1'b0             )      // 1-bit set);OBUFDS #(.IOSTANDARD("DEFAULT"),      // Specify the output I/O standard.SLEW      ("SLOW"   )       // Specify the output slew rate) OBUFDS_CH2 (.O  (O_TMDS_DATA_P[2]),      // Diff_p output (connect directly to top-level port).OB (O_TMDS_DATA_N[2]),      // Diff_n output (connect directly to top-level port).I  (W_ODDR_Q[2]     )       // Buffer input);// CH3ODDR #(.DDR_CLK_EDGE("SAME_EDGE"),  // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT        (1'b0       ),  // Initial value of Q: 1'b0 or 1'b1.SRTYPE      ("SYNC"     )   // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_CH3 (.Q  (W_ODDR_Q[3]      ),     // 1-bit DDR output.C  (I_CLK_371M25     ),     // 1-bit clock input.CE (1'b1             ),     // 1-bit clock enable input.D1 (R_TMDS_CH3_D1[0] ),     // 1-bit data input (positive edge).D2 (R_TMDS_CH3_D2[0] ),     // 1-bit data input (negative edge).R  (1'b0             ),     // 1-bit reset.S  (1'b0             )      // 1-bit set);OBUFDS #(.IOSTANDARD("DEFAULT"),      // Specify the output I/O standard.SLEW      ("SLOW"   )       // Specify the output slew rate) OBUFDS_CH3 (.O  (O_TMDS_CLK_P),          // Diff_p output (connect directly to top-level port).OB (O_TMDS_CLK_N),          // Diff_n output (connect directly to top-level port).I  (W_ODDR_Q[3] )           // Buffer input);
endmodule

 

HDMI显示方法

思路

共有两种演示模式,由拨码开关来切换。

开关为 1:彩条轮播;

开关为 0:显示图片;

图片为:(忽略水印)

图片尺寸大小:160*277 (由于FPGA的ROM资源有限,此处显示小图片)

ROM配置 : 位宽 24位,深度 160*277

像素时钟:74.25MHz

串行发送时钟:371.25MHz

分辨率:1280*720@60Hz

实现

工程结构

源代码分享

测试顶层:

// | ===================================================---------------------------===================================================
// | --------------------------------------------------- HDMI 数据发送测试顶层模块 ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-12-22
// | 完成时间 : 2022-12-22
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// |			-1- 仅包含视频数据发送(无音频)
// |			-2- 1280 * 720 分辨率
// | 			-3- 
// |
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:// Resolution_640x480    //时钟为25.175MHz
// Resolution_800x480    //时钟为33MHz,可兼容TFT5.0
// Resolution_800x600    //时钟为40MHz
// Resolution_1024x600   //时钟为51MHz
// Resolution_1024x768   //时钟为65MHz
// Resolution_1280x720   //时钟为74.25MHz
// Resolution_1920x1080  //时钟为148.5MHz// 宏定义
`define DEF_H_Total_Time    12'd1650
`define DEF_H_Right_Border  12'd0
`define DEF_H_Front_Porch   12'd110
`define DEF_H_Sync_Time     12'd40
`define DEF_H_Back_Porch    12'd220
`define DEF_H_Left_Border   12'd0`define DEF_V_Total_Time    12'd750
`define DEF_V_Bottom_Border 12'd0
`define DEF_V_Front_Porch   12'd5
`define DEF_V_Sync_Time     12'd5
`define DEF_V_Back_Porch    12'd20
`define DEF_V_Top_Border    12'd0module HDMI_DISP_TEST_MDL(
// | ==================================== 模块输入输出端口声明 ==================================== 
input 												   	I_SYS_CLK  ,
input												   	I_SYS_RSTN ,input 													I_DISP_SEL,output 													O_HDMI_EN,
output 				[2:0]								O_TMDS_DATA_P,
output 				[2:0]								O_TMDS_DATA_N,
output 													O_TMDS_CLK_P,
output 													O_TMDS_CLK_N);// | ====================================   模块内部参数声明   ====================================
localparam 			LP_DISP_WIDTH			=			1280;
localparam 			LP_DISP_HEIGHT          =			720;//颜色编码
localparam 			LP_BLACK    			= 			24'h000000; //黑色
localparam 			LP_BLUE     			= 			24'h0000FF; //蓝色
localparam 			LP_RED      			= 			24'hFF0000; //红色
localparam 			LP_PURPPLE  			= 			24'hFF00FF; //紫色
localparam 			LP_GREEN    			= 			24'h00FF00; //绿色
localparam 			LP_CYAN     			= 			24'h00FFFF; //青色
localparam 			LP_YELLOW   			= 			24'hFFFF00; //黄色
localparam 			LP_WHITE    			= 			24'hFFFFFF; //白色localparam 			LP_H_DATA_BEGIN   		=			`DEF_H_Sync_Time  + `DEF_H_Back_Porch    + `DEF_H_Left_Border - 1'b1;
localparam 			LP_H_DATA_END     		=			`DEF_H_Total_Time - `DEF_H_Right_Border  - `DEF_H_Front_Porch - 1'b1;
localparam 			LP_V_DATA_BEGIN   		=			`DEF_V_Sync_Time  + `DEF_V_Back_Porch    + `DEF_V_Top_Border  - 1'b1;
localparam 			LP_V_DATA_END     		=			`DEF_V_Total_Time - `DEF_V_Bottom_Border - `DEF_V_Front_Porch - 1'b1;// | ====================================   模块内部信号声明   ====================================
wire 													W_CLK_100M;
wire													W_CLK_74M25;
wire 													W_CLK_371M25;wire 													W_MMCM_LOCKED1;
wire													W_MMCM_LOCKED2;wire 				[11:0]								W_H_ADDR;
wire 				[11:0]								W_V_ADDR;
reg 				[11:0]								R_H_SCAN_CNT;
reg 				[11:0]								R_V_SCAN_CNT;
wire 													W_H_CNT_OVER;
wire 													W_V_CNT_OVER;
reg 													R_PIXEL_DATA_REQ;
reg 				[23:0]								R_PIXEL_DATA;
reg 													R_H_SYNC;
reg 													R_V_SYNC;
reg 				[7:0]								R_PIXEL_DATA_R;
reg 				[7:0]								R_PIXEL_DATA_G;
reg 				[7:0]								R_PIXEL_DATA_B;wire 													W_R0_FLAG;//第 0 行标志位 
wire 													W_R1_FLAG;//第 1 行标志位 
wire 													W_R2_FLAG;//第 2 行标志位 
wire 													W_R3_FLAG;//第 3 行标志位wire 													W_C0_FLAG;//第 0 列标志位
wire 													W_C1_FLAG;//第 1 列标志位
wire 													W_C2_FLAG;//第 0 列标志位
wire 													W_C3_FLAG;//第 1 列标志位reg 				[8*24-1:0]							R_COLOR;
reg 				[24:0]								R_CNT_25M;
wire 													W_PULSE;// ROM
wire 				[15:0]								W_ROM_ADDR;
wire 				[23:0]								W_ROM_DATA;// | ====================================   模块内部逻辑设计   ====================================
// 行标志
assign 		W_R0_FLAG 		 = (W_V_ADDR >= 0                 ) && (W_V_ADDR < LP_DISP_HEIGHT/4  );   
assign 		W_R1_FLAG 		 = (W_V_ADDR >= LP_DISP_HEIGHT/4  ) && (W_V_ADDR < LP_DISP_HEIGHT/2  );   
assign 		W_R2_FLAG 		 = (W_V_ADDR >= LP_DISP_HEIGHT/2  ) && (W_V_ADDR < LP_DISP_HEIGHT/4*3);   
assign 		W_R3_FLAG 		 = (W_V_ADDR >= LP_DISP_HEIGHT/4*3) && (W_V_ADDR < LP_DISP_HEIGHT    );   
// 列标志
assign 		W_C0_FLAG 		 = (W_H_ADDR >= 0                 ) && (W_H_ADDR < LP_DISP_WIDTH/4   );   
assign 		W_C1_FLAG 		 = (W_H_ADDR >= LP_DISP_WIDTH/4   ) && (W_H_ADDR < LP_DISP_WIDTH/2   );  
assign 		W_C2_FLAG 		 = (W_H_ADDR >= LP_DISP_WIDTH/2   ) && (W_H_ADDR < LP_DISP_WIDTH/4*3 );   
assign 		W_C3_FLAG 		 = (W_H_ADDR >= LP_DISP_WIDTH/4*3 ) && (W_H_ADDR < LP_DISP_WIDTH     ); always @ (posedge I_SYS_CLK)
beginif(~I_SYS_RSTN)beginR_CNT_25M <= 25'd0;R_COLOR   <= {LP_BLACK,LP_BLUE,LP_RED,LP_PURPPLE,LP_GREEN,LP_CYAN,LP_YELLOW,LP_WHITE};endelsebeginif(R_CNT_25M == 25'd25_000_000)beginR_CNT_25M <= 25'd0;endelsebeginR_CNT_25M <= R_CNT_25M + 1;endif(W_PULSE)beginR_COLOR <= {R_COLOR[0+:7*24],R_COLOR[(8*24-1)-:24]};endelsebeginR_COLOR <= R_COLOR;endend
endassign W_PULSE = (R_CNT_25M == 25'd25_000_000);
// 像素数据
always@(*)
beginif(~I_SYS_RSTN)beginR_PIXEL_DATA = 24'd0;endelse if(R_PIXEL_DATA_REQ)beginif(I_DISP_SEL)begincase({W_R0_FLAG,W_R1_FLAG,W_R2_FLAG,W_R3_FLAG})4'b1000:beginR_PIXEL_DATA = W_C0_FLAG ? R_COLOR[4*24+:24] : W_C1_FLAG ? R_COLOR[0*24+:24] : W_C2_FLAG ? R_COLOR[0*24+:24] : W_C3_FLAG ? R_COLOR[4*24+:24] :  R_COLOR[0*24+:24];end4'b0100:beginR_PIXEL_DATA = W_C0_FLAG ? R_COLOR[0*24+:24] : W_C1_FLAG ? R_COLOR[7*24+:24] : W_C2_FLAG ? R_COLOR[7*24+:24] : W_C3_FLAG ? R_COLOR[0*24+:24] :  R_COLOR[0*24+:24];end4'b0010:beginR_PIXEL_DATA = W_C0_FLAG ? R_COLOR[0*24+:24] : W_C1_FLAG ? R_COLOR[7*24+:24] : W_C2_FLAG ? R_COLOR[7*24+:24] : W_C3_FLAG ? R_COLOR[0*24+:24] :  R_COLOR[0*24+:24];end4'b0001:beginR_PIXEL_DATA = W_C0_FLAG ? R_COLOR[4*24+:24] : W_C1_FLAG ? R_COLOR[0*24+:24] : W_C2_FLAG ? R_COLOR[0*24+:24] : W_C3_FLAG ? R_COLOR[4*24+:24] :  R_COLOR[0*24+:24];enddefault:beginR_PIXEL_DATA = 24'hFFFFFF;endendcaseendelsebeginif((W_H_ADDR >= 12'd499) && (W_H_ADDR <= 12'd776) && (W_V_ADDR >= 12'd279) && (W_V_ADDR <= 12'd439))beginR_PIXEL_DATA = W_ROM_DATA;endelsebeginR_PIXEL_DATA = 24'hFFFFFF;endendendelsebeginR_PIXEL_DATA = 24'hFFFFFF;end
endassign W_ROM_ADDR = ((W_H_ADDR >= 12'd499) && (W_H_ADDR <= 12'd776) && (W_V_ADDR >= 12'd279) && (W_V_ADDR <= 12'd439)) ?((W_H_ADDR-12'd499) * 160 + (W_V_ADDR - 12'd279)) : 16'd0;
// 行扫描
always @ (posedge W_CLK_74M25)
beginif(~W_MMCM_LOCKED2)beginR_H_SCAN_CNT <= 12'd0;endelse if(W_H_CNT_OVER)beginR_H_SCAN_CNT <= 12'd0;endelsebeginR_H_SCAN_CNT <= R_H_SCAN_CNT + 1;endendassign W_H_CNT_OVER = (R_H_SCAN_CNT >= (`DEF_H_Total_Time - 1));// 场扫描
always @ (posedge W_CLK_74M25)
beginif(~W_MMCM_LOCKED2)beginR_V_SCAN_CNT <= 12'd0;endelse if(W_H_CNT_OVER)beginif(W_V_CNT_OVER)beginR_V_SCAN_CNT <= 12'd0;endelsebeginR_V_SCAN_CNT <= R_V_SCAN_CNT + 1;endendelsebeginR_V_SCAN_CNT <= R_V_SCAN_CNT ;endendassign W_V_CNT_OVER = (R_V_SCAN_CNT >= (`DEF_V_Total_Time - 1));// 行同步
always @ (posedge W_CLK_74M25)
beginif(~W_MMCM_LOCKED2)beginR_H_SYNC <= 1'b0;endelsebeginR_H_SYNC <= (R_H_SCAN_CNT > (`DEF_H_Sync_Time - 1));end 
end
// 场同步
always @ (posedge W_CLK_74M25)
beginif(~W_MMCM_LOCKED2)beginR_V_SYNC <= 1'b0;endelsebeginR_V_SYNC <= (R_V_SCAN_CNT > (`DEF_V_Sync_Time - 1));end 
end
// RGB 数据
always @ (posedge W_CLK_74M25)
beginif(~W_MMCM_LOCKED2)beginR_PIXEL_DATA_R <= 8'd0;R_PIXEL_DATA_G <= 8'd0;R_PIXEL_DATA_B <= 8'd0;endelse if(R_PIXEL_DATA_REQ)beginR_PIXEL_DATA_R <= R_PIXEL_DATA[16+:8];R_PIXEL_DATA_G <= R_PIXEL_DATA[8+:8];R_PIXEL_DATA_B <= R_PIXEL_DATA[0+:8];end elsebeginR_PIXEL_DATA_R <= 8'd0;R_PIXEL_DATA_G <= 8'd0;R_PIXEL_DATA_B <= 8'd0;		end
end
// 数据使能
always @ (posedge W_CLK_74M25)
beginif(~W_MMCM_LOCKED2)beginR_PIXEL_DATA_REQ <= 1'b0;endelsebeginR_PIXEL_DATA_REQ <= (R_H_SCAN_CNT >= LP_H_DATA_BEGIN) && (R_H_SCAN_CNT < LP_H_DATA_END)&&(R_V_SCAN_CNT >= LP_V_DATA_BEGIN) && (R_V_SCAN_CNT < LP_V_DATA_END);end 
end// 行 场 地址
assign W_H_ADDR = R_PIXEL_DATA_REQ ? (R_H_SCAN_CNT - LP_H_DATA_BEGIN) : 12'd0;
assign W_V_ADDR = R_PIXEL_DATA_REQ ? (R_V_SCAN_CNT - LP_V_DATA_BEGIN) : 12'd0;assign O_HDMI_EN = 1'b1;// | ====================================   模块内部模块例化   ====================================
// | 时钟 IP例化MMCM_HDMI INST_MMCM_HDMI(// Clock out ports.O_CLK_74M25(W_CLK_74M25),     // output O_CLK_74M25.O_CLK_371M25(W_CLK_371M25),     // output O_CLK_371M25// Status and control signals.reset(!I_SYS_RSTN ), // input reset.locked(W_MMCM_LOCKED2),       // output locked// Clock in ports.I_CLK_50M(I_SYS_CLK));      // input I_CLK_100M// | HDMI 数据发送模块例化HDMI_DATA_TX_MDL INST_HDMI_DATA_TX_MDL(.I_CLK_74M25     (W_CLK_74M25),.I_CLK_371M25    (W_CLK_371M25),.I_SYS_RSTN      (W_MMCM_LOCKED2),.I_PIXEL_DATA_EN (R_PIXEL_DATA_REQ),.I_PIXEL_DATA_R  (R_PIXEL_DATA_R),.I_PIXEL_DATA_G  (R_PIXEL_DATA_G),.I_PIXEL_DATA_B  (R_PIXEL_DATA_B),.I_HSYNC         (R_H_SYNC),.I_VSYNC         (R_V_SYNC),.O_TMDS_DATA_P   (O_TMDS_DATA_P),.O_TMDS_DATA_N   (O_TMDS_DATA_N),.O_TMDS_CLK_P    (O_TMDS_CLK_P),.O_TMDS_CLK_N    (O_TMDS_CLK_N));
// ROM
ROM_RGB_DATA INST_ROM_RGB_DATA (.clka(W_CLK_74M25),    // input wire clka.ena(1'b1),      // input wire ena.addra(W_ROM_ADDR),  // input wire [15 : 0] addra.douta(W_ROM_DATA)  // output wire [23 : 0] douta
);
endmodule

ROM 初始化文件 .coe 生成 MATLAB源码 

%% -------- 1280 * 720 像素数据提取 存入 .coe文件 --------
% 作    者:Xu Y. B.(CSDN 用户名:在路上,正出发)
% 创建日期:2022-12-22
% 完成日期:2022-12-22%% 准备
clc;
clearvars;
close all;%% 图片读取
File_Path = "D:\VIVADO_WORK_SPACE\XC7A35T_DESIGN\Figure\";
Figure4_Name = "Figure4.png";Figure_Data = imread(strcat(File_Path,Figure4_Name));imshow(Figure_Data(:,:,:))DATA_R = uint32(reshape(Figure_Data(:,:,1),[],1));
DATA_G = uint32(reshape(Figure_Data(:,:,2),[],1));
DATA_B = uint32(reshape(Figure_Data(:,:,3),[],1));DATA_COE = uint32(DATA_R * 256^2 + DATA_G * 256 + DATA_B);str='D:\VIVADO_WORK_SPACE\XC7A35T_DESIGN\COE_FILE\FIGURE_DATA.coe';%该字符串为文件的路径
fid_coe=fopen(str,'w+');%打开文件获得ID
fprintf(fid_coe,'memory_initialization_radix=10;\n');%写入第一行
fprintf(fid_coe,'memory_initialization_vector=\n');%写入第二行
fprintf(fid_coe,'%d,\n',DATA_COE((1:end-1),1));%写入数据并以逗号隔开
fprintf(fid_coe,'%d;',DATA_COE(end,1));%写入最后一个数据以分号结束

板级调试视频

基于FPGA的TMDS编码以及HDMI驱动 演示视频icon-default.png?t=M85Bhttps://live.csdn.net/v/264920



相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...