介绍:高清多媒体接口(High Definition Multimedia Interface,HDMI [1])是一种全数字化视频和声音发送接口,可以发送未压缩的音频及视频信号。
原理:
下面是对HDMI接口各引脚的简单介绍
|
运用:
结构框图:
TMDS差分传动技术是一种利用2个引脚间电压差来传送信号的技术。
HDMI 线缆和连接器提供四个差分线对,组成TMDS数据和时钟通道
HDMI中的TMDS 传输系统如下:
要实现TMDS通道传输,首先要将传入的8 位的并行数据进行编码、并/串转换,添加第9位编码
实验目的:可以将 “TFTLCD屏幕彩条显示” 通过HDMI接口正常显示在屏幕上
注意:ftf时钟与hdim传输时钟为10倍关系,在本实验中,ftf控制时钟为25Mhz,即像素时钟为25Mhz,根据计算公式 “单通道速率 = 像素时钟 × 10” 得到 hdim传输信号速率为250Mhz。
应该满足-hdim传输信号速率像素时钟频率*10
(本实验参考了野火的hdmi显示器驱动设计与验证)
链接:12. hdmi显示器驱动设计与验证 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档
模块框图:
模块代码
tft_colorbar模块代码,见我的这一篇文章:用FPGA设计TFTLCD液晶屏幕_基于fpga的tft液晶屏驱动电路-优快云博客
par_to_ser模块代码:
`timescale 1ns/1ps
module par_to_ser (
input wire clk_x5, // 5倍系统时钟
input wire [9:0] par_data,// 10位并行数据输入
output wire ser_data_p, // 串行差分输出正端
output wire ser_data_n // 串行差分输出负端
);
// 数据预处理:提取上升沿和下降沿数据位
wire [4:0] data_rise = {par_data[8], par_data[6], par_data[4], par_data[2], par_data[0]};
wire [4:0] data_fall = {par_data[9], par_data[7], par_data[5], par_data[3], par_data[1]};
// 移位寄存器控制逻辑
reg [4:0] data_rise_s = 5'b0;
reg [4:0] data_fall_s = 5'b0;
reg [2:0] cnt = 3'b0;
reg data_h_p, data_l_p; // 正端寄存器
reg data_h_n, data_l_n; // 负端寄存器
always @(posedge clk_x5) begin
cnt <= (cnt == 3'b100) ? 3'b0 : cnt + 1;
if (cnt == 3'b100) begin
// 加载新数据
data_rise_s <= data_rise;
data_fall_s <= data_fall;
end
else begin
// 右移操作
data_rise_s <= {1'b0, data_rise_s[4:1]};
data_fall_s <= {1'b0, data_fall_s[4:1]};
end
end
// 正端数据处理
always @(posedge ~clk_x5) data_h_p <= data_rise_s[0]; // 上升沿锁存
always @(negedge ~clk_x5) data_l_p <= data_fall_s[0]; // 下降沿锁存
// 负端数据处理(取反实现差分)
always @(posedge ~clk_x5) data_h_n <= ~data_rise_s[0]; // 上升沿锁存反相
always @(negedge ~clk_x5) data_l_n <= ~data_fall_s[0]; // 下降沿锁存反相
// 组合输出选择
assign ser_data_p = ~clk_x5 ? data_h_p : data_l_p;
assign ser_data_n = ~clk_x5 ? data_h_n : data_l_n;
endmodule
encode模块代码:
`timescale 1ns/1ns
module encode (
input wire sys_clk,
input wire sys_rst_n,
input wire [7:0] data_in,
input wire c0,
input wire c1,
input wire de,
output reg [9:0] data_out
);
// 参数定义
parameter DATA_OUT0 = 10'b1101010100,
DATA_OUT1 = 10'b0010101011,
DATA_OUT2 = 10'b0101010100,
DATA_OUT3 = 10'b1010101011;
// 内部信号声明
wire condition_1, condition_2, condition_3;
wire [8:0] q_m;
reg [3:0] data_in_n1;
reg [7:0] data_in_reg;
reg [3:0] q_m_n1, q_m_n0;
reg [4:0] cnt;
reg de_reg1, de_reg2;
reg c0_reg1, c0_reg2, c1_reg1, c1_reg2;
reg [8:0] q_m_reg;
// 同步逻辑:计算输入数据中1的个数
always @(posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n) 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 sys_clk or negedge sys_rst_n)
if (!sys_rst_n) data_in_reg <= 8'b0;
else data_in_reg <= data_in;
// 生成条件1:决定编码模式
assign condition_1 = (data_in_n1 > 4'd4) ||
((data_in_n1 == 4'd4) && !data_in_reg[0]);
// 生成q_m[8:0](完全展开的逻辑)
assign q_m[0] = data_in_reg[0];
assign q_m[1] = condition_1 ? (q_m[0] ~^ data_in_reg[1]) : (q_m[0] ^ data_in_reg[1]);
assign q_m[2] = condition_1 ? (q_m[1] ~^ data_in_reg[2]) : (q_m[1] ^ data_in_reg[2]);
assign q_m[3] = condition_1 ? (q_m[2] ~^ data_in_reg[3]) : (q_m[2] ^ data_in_reg[3]);
assign q_m[4] = condition_1 ? (q_m[3] ~^ data_in_reg[4]) : (q_m[3] ^ data_in_reg[4]);
assign q_m[5] = condition_1 ? (q_m[4] ~^ data_in_reg[5]) : (q_m[4] ^ data_in_reg[5]);
assign q_m[6] = condition_1 ? (q_m[5] ~^ data_in_reg[6]) : (q_m[5] ^ data_in_reg[6]);
assign q_m[7] = condition_1 ? (q_m[6] ~^ data_in_reg[7]) : (q_m[6] ^ data_in_reg[7]);
assign q_m[8] = condition_1 ? 1'b0 : 1'b1;
// 计算q_m中1和0的个数(完全展开的逻辑)
always @(posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n) 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 <= 8 - (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 condition_2 = (cnt == 5'd0) || (q_m_n1 == q_m_n0);
assign condition_3 = (!cnt[4] && (q_m_n1 > q_m_n0)) ||
(cnt[4] && (q_m_n0 > q_m_n1));
// 控制信号同步打拍
always @(posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n) begin
{de_reg2, de_reg1} <= 2'b0;
{c0_reg2, c0_reg1} <= 2'b0;
{c1_reg2, c1_reg1} <= 2'b0;
q_m_reg <= 9'b0;
end else begin
de_reg1 <= de;
de_reg2 <= de_reg1;
c0_reg1 <= c0;
c0_reg2 <= c0_reg1;
c1_reg1 <= c1;
c1_reg2 <= c1_reg1;
q_m_reg <= q_m;
end
// 数据编码输出逻辑
always @(posedge sys_clk or negedge sys_rst_n)
if (!sys_rst_n) begin
data_out <= 10'b0;
cnt <= 5'b0;
end else if (de_reg2) begin
if (condition_2) begin
// 模式1:直接反转
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 <= cnt + (q_m_reg[8] ? (q_m_n1 - q_m_n0) : (q_m_n0 - q_m_n1));
end else if (condition_3) begin
// 模式2:全反转+特殊符号
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
// 模式3:保持原样
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 else begin
// 控制信号输出模式
case ({c1_reg2, c0_reg2})
2'b00: data_out <= DATA_OUT0;
2'b01: data_out <= DATA_OUT1;
2'b10: data_out <= DATA_OUT2;
default: data_out <= DATA_OUT3;
endcase
cnt <= 5'b0;
end
endmodule
encode_par模块代码:
module encode_par (
input wire clk ,
input wire clk_x5 ,
input wire rst ,
input wire hsync ,
input wire vsync ,
input wire de ,
input wire [23:0] rgb ,
output wire clk_p ,
output wire clk_n ,
output wire r_p ,
output wire r_n ,
output wire g_p ,
output wire g_n ,
output wire b_p ,
output wire b_n
);
wire [9:0] blue;
wire [9:0] green;
wire [9:0] red;
encode encode_inst0
(
.sys_clk (clk ),
.sys_rst_n (rst ),
.data_in (rgb[7:0] ),
.c0 (hsync ),
.c1 (vsync ),
.de (de ),
.data_out (blue )
);
//------------- encode_inst1 -------------
encode encode_inst1
(
.sys_clk (clk ),
.sys_rst_n (rst ),
.data_in (rgb[15:8] ),
.c0 (hsync ),
.c1 (vsync ),
.de (de ),
.data_out (green )
);
//------------- encode_inst2 -------------
encode encode_inst2
(
.sys_clk (clk ),
.sys_rst_n (rst ),
.data_in (rgb[23:16] ),
.c0 (hsync ),
.c1 (vsync ),
.de (de ),
.data_out (red )
);
//------------- par_to_ser_inst0 -------------
par_to_ser par_to_ser_inst0
(
.clk_x5 (clk_x5 ),
.par_data (blue ),
.ser_data_p (b_p ),
.ser_data_n (b_n )
);
//------------- par_to_ser_inst1 -------------
par_to_ser par_to_ser_inst1
(
.clk_x5 (clk_x5 ),
.par_data (green ),
.ser_data_p (g_p ),
.ser_data_n (g_n )
);
//------------- par_to_ser_inst2 -------------
par_to_ser par_to_ser_inst2
(
.clk_x5 (clk_x5 ),
.par_data (red ),
.ser_data_p (r_p ),
.ser_data_n (r_n )
);
//------------- par_to_ser_inst3 -------------
par_to_ser par_to_ser_inst3
(
.clk_x5 (clk_x5 ),
.par_data (10'b1111100000),
.ser_data_p (clk_p ),
.ser_data_n (clk_n )
);
endmodule //encode_par
hdmi模块代码:
module hdmi (
input wire clk ,
input wire rst ,
output wire ddc_scl ,
output wire ddc_sda ,
output wire clk_p ,
output wire clk_n ,
output wire r_p ,
output wire r_n ,
output wire g_p ,
output wire g_n ,
output wire b_p ,
output wire b_n
);
assign ddc_scl = 1'b1;
assign ddc_sda = 1'b1;
wire hsync ;
wire vsync ;
wire tft_de ;
wire [23:0] rgb ;
wire locked;
wire clk_out1;
wire clk_out2;
assign rst_n = (rst & locked);
encode_par encode_par_inst
(
.clk (clk_out1 ) ,
.clk_x5 (clk_out2 ) ,
.rst (rst_n ) ,
.hsync (hsync ) ,
.vsync (vsync ) ,
.de (tft_de ) ,
.rgb (rgb ) ,
.clk_p (clk_p ) ,
.clk_n (clk_n ) ,
.r_p (r_p ) ,
.r_n (r_n ) ,
.g_p (g_p ) ,
.g_n (g_n ) ,
.b_p (b_p ) ,
.b_n (b_n )
);
tft_colorbar tft_colorbar_inst
(
.sys_clk ( clk ) , //输入工作时钟,频率50MHz
.sys_rst_n ( rst_n ) , //输入复位信号,低电平有效
.rgb_tft ( rgb ) , //输出像素信息
.hsync ( hsync ) , //输出行同步信号
.vsync ( vsync ) , //输出场同步信号
.tft_de ( tft_de ) //输出TFT使能信号
);
clk_wiz_1 clk_wiz_1inst
(
// Clock out ports
.clk_out1(clk_out1),//50mhz
.clk_out2(clk_out2),//250mhz
// Status and control signals
.reset(~rst),
.locked(locked),
// Clock in ports
.clk_in1(clk)
);
endmodule //hdmi