基于FPGA的 TMDS 编码 及 HDMI 显示
目录
引言
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 位 的像素数据
C0 和 C1 为 控制信号
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驱动 演示视频https://live.csdn.net/v/264920
基于FPGA的 TMDS 编码 及 HDMI 显示相关推荐
- FPGA虚拟三阶魔方(HDMI显示版)
目录 编辑 一.设计概述 二.设计模块 1.三阶魔方建模 (1)魔方基本操作: (2)魔方建模: 2.魔方转动控制: (1)控制概述: (2)按键消抖: (3)魔方控制: 3.HDMI显示模块: ( ...
- 半定制器件课程设计——基于FPGA的PS2键盘人机输入显示系统
基于FPGA的PS2键盘人机输入显示系统 第一部分 课程设计概述 1 课程设计的目的与任务 2 课程设计题目 3 设计功能要求 4 设计实现提示 5 课程设计的内容与要求 5.1 设计内容 5.2 设 ...
- 基于FPGA的 DS18B20多功能温度显示
基于FPGA的 DS18B20多功能温度显示 实现功能: 1.实时温度显示在数码管上,更新速率1-2s一次 2.按下按键显示最近30s内的最高温和最低温 不包含板子, 3.按下按键可以存储当前温度,最 ...
- FPGA设计——CMOS摄像与HDMI显示(DVP版)
1. 概述 本设计采用FPGA技术,将CMOS摄像头(DVP接口)的视频数据经过采集.存储.处理.帧率转换,最终通过HDMI接口显示在电视屏幕上. 2. 硬件系统框图 CMOS采用分辨率为1080p的 ...
- 基于FPGA的数码管动态扫描显示(含代码)
数码管的显示有静态显示和动态扫描显示.一个数码管通常有8个段码,当要控制数码管的数量在两个及以上时,动态扫描无疑是一个最好的选择,可以大量节约IO资源. 静态显示很好理解,8个段码拼成了数字8.,当我 ...
- 【至简设计案例系列】基于FPGA的曼彻斯特编码解码设计
作者:小周 实验简述 将输入的数据流经过曼彻斯特编码器编码后经过解码器恢复为原来的输入序列. 一.曼彻斯特码简介 曼彻斯特码(Manchester code),又称数字双向码.分相码或相位编码(PE) ...
- 基于FPGA的LED 16×16点阵汉字显示设计
使用VHDL语言设计 在QuartusII下创建,也可在vivado,ISE下创建 总体设计方案 方案一:本设计所使用的16×16的点阵,EDA实验箱上有其接口电路,列选信号为SEL0,SEL1,SE ...
- 基于FPGA的VGA显示对贪吃蛇游戏的设计
基于FPGA的VGA显示对贪吃蛇游戏的设计 摘要 目前,电子数码产品已经进入了人生活的方方面面,而大多数电子产品都依靠显示屏来传递信息,由此可见用电路对显示屏进行控制的研究有很大的实用价值和市场需求. ...
- 基于FPGA实现Camera Link接口
1 Camera Link 应用领域 1)图像采集是数字图像处理.图像识别和机器视觉的基础,其应用领域非常广泛.主要采用CCD或CMOS等光电转换器件把光学影像转化为数字信号,然后利用相应的接口将数据 ...
最新文章
- SpringMVC中,前台jsp封装参数,绑定参数,传递参数到后台controller的过程详解
- Shell脚本中的并发(2)
- CentOS 初体验十二:wget下载文件
- Condition中的transferForSignal()方法的不解
- C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻
- sparkSQL操作hiveSQL
- map/vector erase
- Unity3d高频率面试题目(选择题)
- 进程间能否传递指针?
- python网络编程linux pdf_Python网络编程:Linux epoll
- PHP和ajax请求_「jQuery+PHP」ajax请求以及接口PHP响应教程
- 开发打开设置洁面_用了两到三年的华为手机,一键打开开发者选项,帮助性能加速...
- 【小工具】用js自动生成pdf目录索引
- 德州大学达拉斯分校计算机工程,“你的留学故事”| 德克萨斯大学达拉斯分校交换生感悟...
- BZOJ-2242 计算器 快速幂+拓展欧几里得+BSGS(数论三合一)
- 龙芯+复旦微FPGA全国产VPX高速数据采集卡解决方案
- spring框架_03
- 2021水利水电安全员模拟考试案例题库及答案
- 二叉树遍历之前序遍历,中序遍历,后序遍历
- jasypt对yml文件中密码加密