TMDS编码原理以及Verilog实现HDMI接口


一、HDMI简介

  之前我们了解并且实现了VGA视频接口协议;VGA接口是早期使用的模拟视频接口,传输的是模拟信号。但是随着电子设备集成度越来越高,视频分辨率越来越高,VGA已经不能满足需求,因此视频接口发展到现在,HDMI接口成为了主流。
  HDMI(High-Definition Multimedia Interface)是一种高清多媒体接口,它是一种全数字的音视频传输接口,用于连接各种音视频设备,如电视、显示器、音响系统、游戏机、电脑等。

  HDMI接口相对于VGA接口的一些主要优势:

  1. 数字信号传输:HDMI传输的是数字信号,而VGA传输的是模拟信号。数字信号在传输过程中更稳定,不易受干扰,能提供更清晰的图像质量。

  2. 高清视频支持:HDMI接口支持高清视频格式,如1080p、4K、8K等,而VGA接口最高仅支持到1080p,并且通常用于较低分辨率的视频传输。

  3. 音频传输:HDMI接口可以同时传输音频和视频信号,而VGA接口仅传输视频信号。这意味着使用HDMI接口时,不需要另外的音频线缆。

  4. 双向通信:HDMI接口支持CEC(Consumer Electronics Control)等双向通信协议,可以实现设备间的智能控制和交互,而VGA接口不支持。

  5. 接口体积小:HDMI接口比VGA接口体积小了许多,适合现在集成度越来越高的产品

二、HDMI接口以及引脚定义

  根据《HDMI协议规范1.4b》可知,一共有五种类型的接口:HDMI A Type、HDMI B Type、HDMI C Type、HDMI D Type、HDMI E Type。不同类型的HDMI接口使用范围不同,分别如下:
在这里插入图片描述

  1. A型:标准全尺寸HDMI接口,是日常生活中最常见的类型;广泛用于电视、电脑、投影仪、游戏机、蓝光播放器、AV接收器和许多其他消费电子设备。
  2. B型:支持双链路的接口,能够提供更高的带宽和分辨率;较少使用,主要针对高分辨率显示器和专业设备。
  3. C型:也称为Mini HDMI接口,体积比A型小,适用于需要节省空间的设备;常用于便携式设备如平板电脑、便携式摄像机、部分笔记本电脑和一些较小的高清摄像设备。
  4. D型:也称为Micro HDMI接口,比C型更小,适用于超便携设备;广泛用于智能手机、超薄平板电脑、小型摄像设备和其他超便携设备。
  5. E型:专为汽车内部使用设计,具有增强的锁定机制和抗震性;用于汽车信息娱乐系统、车载显示器和其他汽车电子设备。

  因为A型HDMI接口使用范围更广,因此本章只讲A型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时钟+
  • TMDS通道:引脚1 ~ 引脚12,负责发送音频、视频及各种辅助数据;
  • DDC通道:引脚15、16、17。DDC全文为Display Data Channel,发送端与接收端可利用 DDC 沟道得知彼此的发送与接收能力,但 HDMI 仅需单向获知接收端(显示器)的能力;DDC 通道使用 100kHz 时钟频率的 I²C 信号,发送数据结构为EDID。
  • CEC通道:引脚13、17。CEC通道为必须预留线路,但可以不必实现,作用是用来发送工业规格的 AV Link 协议信号。
  • 其他通道:引脚14位保留引脚,无连接;引脚18为+5V电源;引脚19位热插拔检测引脚。

三、HDMI传输原理

  HDMI的系统架构有发射源和接收器组成,给定的设备可能有一个或以上HDMI输入和一个或以上的 HDMI输出。所有的 HDMI输入都要满足 HDMI 接收器的规则,所有的 HDMI输出都要满足 HDMI发射器的规则。系统架构如下:

在这里插入图片描述
  如图所示,HDMI 发送器和接收器有四个差分线对组成TMDS数据通道和时钟通道,这些通道用于传递视频,音频和辅助数据,TMDS时钟通道始终发送与 TMDS 数据通道上的像素数据相一致的采样时钟;另外, HDMI 提供一个DDC 通道,DDC 是用于配置和在一个单独的信源端和一个单独的接收端交换状态;可选择的 CEC 在用户的各种不同的音视频产品中, 提供高水平的控制功能; 可选择的 HDMI 以太网和音频返回(HEAC),在连接的设备中提供以太网兼容的网络数据和一个和 TMDS 相对方向的音频回返通道;此外还有热插拔检测信号 HDP,当显示器等 HDMI 接口的显示设备通过 HDMI 接口与 HDMI 信源端相连或断开连接时,HDMI 信源端能够通过 HPD 引脚检测出这一事件,并做出响应。

四、TMDS编码规则以及实现

4.1 TMDS编码框图

在这里插入图片描述
  分对8bit的R、G、B数据编码,输出串行的差分信号。行场同步信号HSYNC,VSYNC作为B通道的控制信号也进行编码。4位的音频信号也同步编码(本文没有讲解音频编码,因此可以忽略)。

4.2 TMDS编码流程图

在这里插入图片描述

D 待编码的8bit视频信号
DE视频数据有效信号
C0,C1控制信号
N1{X}数据X中1的个数
N0{X}数据X中0的个数
q_out编码后的数据输出

  由流程图可以知道,第一个判断框通过判断输入数据中1的个数然后对数据进行异或编码,或者异或非编码成9位数据。第二第三个判断框再根据编码后q_m中0 和1的个数再次编码来保证直流均衡。

4.3 Verilog实现TMDS编码

  根据TMDS编码流程图编写Verilog代码,注意时序对齐的问题。

`timescale 1ns / 1ns
module TMDS_encode(
    input                                               sys_clk,        //输入系统时钟
    input                                               sys_rstn,       //系统复位
    input                                               c0  ,           //控制信号c0
    input                                               c1  ,           //控制信号c1
    input                                               de  ,           //数据有效信号
    input           [7:0]                               data_in ,       //8bit像素
    output  reg     [9:0]                               data_out        //编码后的10bit数据
);

//定义输入控制信号时,输出的数据
    parameter                                           CTRLTOKEN0  = 10'b1101010100;   
    parameter                                           CTRLTOKEN1  = 10'b0010101011; 
    parameter                                           CTRLTOKEN2  = 10'b0101010100; 
    parameter                                           CTRLTOKEN3  = 10'b1010101011; 


// 第一级流水线信号
    reg             [3:0]                               n1_din  ;       //统计输入8bit数据中1的个数
    reg             [7:0]                               data_in_d1  ;   
    reg                                                 de_d1   ;
    reg                                                 c0_d1   ;
    reg                                                 c1_d1   ;
    wire                                                decision1   ;   //第一个判断条件
    wire            [8:0]                               q_m ;           //第一级流水线输出的中间值
//第二级流水线信号
    reg             [3:0]                               n1_qm  ;        //统计qm中1的个数
    reg             [3:0]                               n0_qm  ;        //统计qm中0的个数
    wire                                                decision2   ;   //第二个判断条件
    wire                                                decision3   ;   //第三个判断条件
    reg             [4:0]                               cnt ;           //视差计数器,之前编码后数据0-1个数差别,最高位为符号位
    reg             [8:0]                               qm_d1  ;   
    reg                                                 de_d2   ;
    reg                                                 c0_d2   ;
    reg                                                 c1_d2   ;

//处理第一级流水线
always @(posedge sys_clk or negedge sys_rstn) begin
    if(sys_rstn == 1'b0)
        n1_din <= 4'd0;
    else
        n1_din <= data_in[0] + data_in[1] + data_in[2] + data_in[3] + data_in[4] + data_in[5] + data_in[6] + data_in[7];
end

always @(posedge sys_clk) begin
    data_in_d1 <= data_in;
    de_d1 <= de;
    c0_d1 <= c0;
    c1_d1 <= c1;
end

assign decision1 = (n1_din > 4'd4) || ((n1_din == 4'd4)&&(data_in_d1[0] == 1'b0));

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

//处理第二级流水线
always @(posedge sys_clk or negedge sys_rstn) begin
    if(sys_rstn == 1'b0)begin
        n1_qm <= 4'd0;
        n0_qm <= 4'd0;
    end
    else begin
        n1_qm <= q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
        n0_qm <= 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
end

always @(posedge sys_clk) begin
    qm_d1 <= q_m;
    de_d2 <= de_d1;
    c0_d2 <= c0_d1;
    c1_d2 <= c1_d1;
end

assign decision2 = (cnt == 5'd0) || (n1_qm == n0_qm);
//decision3中通过符号位判断cnt是否大于0
assign decision3 = ((~cnt[4] == 1'b1) && (n1_qm > n0_qm)) || ((cnt[4] == 1'b1) && (n1_qm < n0_qm));

always @(posedge sys_clk or negedge sys_rstn) begin
    if(sys_rstn == 1'b0)begin
        cnt <= 5'd0;
        data_out <= 10'd0;
    end
    else begin
        if(de_d2 == 1'b1)begin
            if(decision2 == 1'b1)begin
                data_out[9] <= ~ qm_d1[8];
                data_out[8] <= qm_d1[8];
                data_out[7:0] <= (qm_d1[8]) ? qm_d1[7:0] : ~qm_d1[7:0];
                cnt <= (~qm_d1[8]) ? (cnt + n0_qm - n1_qm) : (cnt + n1_qm - n0_qm);
            end
            else begin
                if(decision3 == 1'b1)begin
                    data_out[9] <= 1'b1;
                    data_out[8] <= qm_d1[8];
                    data_out[7:0] <= ~qm_d1[7:0];
                    cnt <= cnt + {qm_d1[8],1'b0} + n0_qm - n1_qm;
                end
                else begin
                    data_out[9] <= 1'b0;
                    data_out[8] <= qm_d1[8];
                    data_out[7:0] <= qm_d1[7:0];
                    cnt <= cnt - {~qm_d1[8],1'b0} + n1_qm - n0_qm;
                end
            end
        end
        else begin
            cnt <= 5'd0;
            case ({c1_d2,c0_d2})
                2'b00: data_out <= CTRLTOKEN0;
                2'b01: data_out <= CTRLTOKEN1;
                2'b10: data_out <= CTRLTOKEN2;
                default: data_out <= CTRLTOKEN3;
            endcase
        end
    end
end

endmodule


五、OSERDESE2原语介绍以及使用

  由于TMDS编码后的是10bit并行数据,而实际HDMI传输的是串行差分信号。因此需要将10bit编码后的数据转换成串行数据,再转换成差分信号,这里并转串使用的是Xilinx提供的OSERDESE2原语 。
  OSERDESE2 原语是 Xilinx 7 系列 FPGA 中的一种并行到串行转换器(Parallel-to-Serial Converter),能够在源同步接口中高效地将并行数据转换为串行数据。

  • 支持单数据速率(SDR)和双数据速率(DDR)模式。在 DDR 模式下,数据可以在每个时钟周期的上升沿和下降沿传输,从而实现数据传输速率的翻倍。
  • OSERDESE2 可以处理从 2 到 8 位的并行数据输入,并将其转换为串行输出。在某些情况下,可以通过宽度扩展(Width Expansion)技术,使用两个 OSERDESE2 模块来支持更大的数据宽度,如 10 位或 14 位。
  • OSERDESE2 使用两个时钟信号,CLK 和 CLKDIV。CLK 是高速串行时钟,而 CLKDIV 是分频后的并行时钟。这两个时钟信号必须在相位上对齐。

5.1 OSERDESE2内部框图

  OSERDESE2原语输入输出端口如下:

在这里插入图片描述

  OSERDESE2原语内部框图如下:
在这里插入图片描述
  OSERDESE2原语通过级联的系统框图如下:

在这里插入图片描述

5.2 OSERDESE2 输入输出管脚信号说明

输入端口 输出端口
CLK并串转后的串行数据伴随时钟OFB输出反馈端口,结合使用原语ISERDESE2或者ODELAYE2的时候会用到
CLKDIVCLK时钟的分频,为并转串前并行数据的伴随时钟OB串行数据输出端口
D1-D8待转换的8位并行数据,可配置为 2 至 8 位。使用 SLAVE 模式下的第二个 OSERDES 可以支持大于 6 位(级联后最多 14 位)的位宽SHIFTOUT1/SHIFTOUT2级联输出信号,连接至MASTER的SHIFTIN1/SHIFTIN2端口
OCE高电平有效的时钟使能信号TBYTEOUT字节组三态输出至IOB
RST复位信号,只需CLKDIV 频域时序(与 CLKDIV 同步)一个周期的复位脉冲TFB发送到 ODELAYE2 的 OSERDES 模块的 3 态控制输出。使用时,此端口将 3 态并行到串行转换器的输出连接到 ODELAYE2 的控制/3 态输入
SHIFTIN1 /SHIFTIN2级联输入信号,用于扩展数据输入,连接至SLAVE的SHIFTOUT1/SHIFTOUT2TQ OSERDES 模块的 3 态控制输出。使用时,此端口将 3 态并行到串行转换器的输出连接到 IOB 的控制/3 态输入
T1-T4三态控制模块的输入端口可配置1,2,4位
TBYTEIN字节组三态输入
TCE高电平有效的3态控制模块时钟使能信号

5.3 OSERDESE2 配置属性信号说明

端口名称端口说明
DATA_RATE_OQ定义数据是以单倍数据速率 (SDR) 还是双倍数据速率 (DDR) 进行处理
DATA_RATE_TQ定义是否将三态控制处理为单数据速率 (SDR) 还是双数据速率 (DDR)
DATA_WIDTH定义并串转换器的并行数据输入宽度。此属性的可能值取决于 DATA_RATE_OQ 属性。当 DATA_RATE_OQ 设置为 SDR 时,DATA_WIDTH 属性的可能值为 2、3、4、5、6、7 和 8。当 DATA_RATE_OQ 设置为 DDR 时,DATA_WIDTH 属性的可能值为 4、6、8、10 和 14。当 DATA_WIDTH 设置为大于 8 的宽度时,必须将一对 OSERDES 配置为主从配置
INIT_OQ定义 OQ 输出的初始值
INIT_OQ定义 TQ 输出的初始值
SERDES_MODE定义使用宽度扩展时 OSERDES 模块是主模块还是从模块
SRVAL_OQ定义调用 SR 时OQ输出的值
SRVAL_TQ调用 SR 时 TQ 输出的值
TBYTE_CTL DDR3 模式启用三态 BYTE 操作。这允许三态信号从作为源的三态输出之一获取值
TBYTE_SRC启用 OSERDES 作为 DDR3 模式下三态字节操作的源
TRISTATE_WIDTH定义 3 态控制并串转换器的并行 3 态输入宽度。此属性的可能值取决于 DATA_RATE_TQ 属性。当 DATA_RATE_TQ 设置为 SDR 或 BUF 时,TRISTATE_WIDTH 属性只能设置为 1。 当 DATA_RATE_TQ 设置为 DDR 时,TRISTATE_WIDTH 属性的可能值为 4。 TRISTATE_WIDTH 不能设置为大于 4 的宽度。当 DATA_WIDTH 大于 4 时,请将 TRISTATE_WIDTH 设置为 1。

5.4 OSERDESE2 例化模板

  例化模板如下:

OSERDESE2 #(
      .DATA_RATE_OQ("DDR"),   // DDR, SDR
      .DATA_RATE_TQ("DDR"),   // DDR, BUF, SDR
      .DATA_WIDTH(4),         // Parallel data width (2-8,10,14)
      .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
      .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
      .SERDES_MODE("MASTER"), // MASTER, SLAVE
      .SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
      .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
      .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
      .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
      .TRISTATE_WIDTH(4)      // 3-state converter width (1,4)
   )
   OSERDESE2_inst (
      .OFB(OFB),             // 1-bit output: Feedback path for data
      .OQ(OQ),               // 1-bit output: Data path output
      // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
      .SHIFTOUT1(SHIFTOUT1),
      .SHIFTOUT2(SHIFTOUT2),
      .TBYTEOUT(TBYTEOUT),   // 1-bit output: Byte group tristate
      .TFB(TFB),             // 1-bit output: 3-state control
      .TQ(TQ),               // 1-bit output: 3-state control
      .CLK(CLK),             // 1-bit input: High speed clock
      .CLKDIV(CLKDIV),       // 1-bit input: Divided clock
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(D1),
      .D2(D2),
      .D3(D3),
      .D4(D4),
      .D5(D5),
      .D6(D6),
      .D7(D7),
      .D8(D8),
      .OCE(OCE),             // 1-bit input: Output data clock enable
      .RST(RST),             // 1-bit input: Reset
      // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
      .SHIFTIN1(SHIFTIN1),
      .SHIFTIN2(SHIFTIN2),
      // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
      .T1(T1),
      .T2(T2),
      .T3(T3),
      .T4(T4),
      .TBYTEIN(TBYTEIN),     // 1-bit input: Byte group tristate
      .TCE(TCE)              // 1-bit input: 3-state clock enable
   );

5.5 调用OSERDESE2级联原语

module oserdese2_10to1 (
    input                                               sys_rstn    ,   //系统复位
    input                                               clk ,           //并行数据时钟
    input                                               clk_5x  ,       //串行数据时钟,因为10位转1位,使用DDR模式所以是5倍并行时钟
    input           [9:0]                               data_in ,       //需要转换的10bit数据
    output                                              data_out        //转换后的串行数据
);
    
    wire                                                SLAVE_SHIFTOUT1   ;   //级联信号1
    wire                                                SLAVE_SHIFTOUT2   ;   //级联信号2


OSERDESE2 #(
      .DATA_RATE_OQ("DDR"),   // DDR, SDR :设置单倍速率或者双倍速率
      .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR
      .DATA_WIDTH(10),         // 并行数据位宽
      .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
      .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
      .SERDES_MODE("MASTER"), // MASTER, SLAVE
      .SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
      .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
      .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
      .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
   )
   OSERDESE2_master (
      .OFB(),                   // 1-bit output: Feedback path for data
      .OQ(data_out),            // 1-bit output: Data path output
      // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
      .SHIFTOUT1(),
      .SHIFTOUT2(),
      .TBYTEOUT(),              // 1-bit output: Byte group tristate
      .TFB(),                   // 1-bit output: 3-state control
      .TQ(),                    // 1-bit output: 3-state control
      .CLK(clk_5x),             // 1-bit input: High speed clock
      .CLKDIV(clk),             // 1-bit input: Divided clock
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(data_in[0]),
      .D2(data_in[1]),
      .D3(data_in[2]),
      .D4(data_in[3]),
      .D5(data_in[4]),
      .D6(data_in[5]),
      .D7(data_in[6]),
      .D8(data_in[7]),
      .OCE(1'b1),             // 1-bit input: Output data clock enable
      .RST(!sys_rstn),             // 1-bit input: Reset
      // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
      .SHIFTIN1(SLAVE_SHIFTOUT1),
      .SHIFTIN2(SLAVE_SHIFTOUT2),
      // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
      .T1(1'b0),
      .T2(1'b0),
      .T3(1'b0),
      .T4(1'b0),
      .TBYTEIN(1'b0),     // 1-bit input: Byte group tristate
      .TCE(1'b0)              // 1-bit input: 3-state clock enable
   );

   OSERDESE2 #(
      .DATA_RATE_OQ("DDR"),   // DDR, SDR :设置单倍速率或者双倍速率
      .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR
      .DATA_WIDTH(10),         // 并行数据位宽
      .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
      .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
      .SERDES_MODE("SLAVE"), // MASTER, SLAVE
      .SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
      .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
      .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
      .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
   )
   OSERDESE2_slave (
      .OFB(),                   // 1-bit output: Feedback path for data
      .OQ(),            // 1-bit output: Data path output
      // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
      .SHIFTOUT1(SLAVE_SHIFTOUT1),
      .SHIFTOUT2(SLAVE_SHIFTOUT2),
      .TBYTEOUT(),              // 1-bit output: Byte group tristate
      .TFB(),                   // 1-bit output: 3-state control
      .TQ(),                    // 1-bit output: 3-state control
      .CLK(clk_5x),             // 1-bit input: High speed clock
      .CLKDIV(clk),             // 1-bit input: Divided clock
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(1'b0),
      .D2(1'b0),
      .D3(data_in[8]),
      .D4(data_in[9]),
      .D5(1'b0),
      .D6(1'b0),
      .D7(1'b0),
      .D8(1'b0),
      .OCE(1'b1),                // 1-bit input: Output data clock enable
      .RST(!sys_rstn),          // 1-bit input: Reset
      // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
      .SHIFTIN1(),
      .SHIFTIN2(),
      // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
      .T1(1'b0),
      .T2(1'b0),
      .T3(1'b0),
      .T4(1'b0),
      .TBYTEIN(1'b0),     // 1-bit input: Byte group tristate
      .TCE(1'b0)              // 1-bit input: 3-state clock enable
   );

   // End of OSERDESE2_inst instantiation

endmodule

  接下来把上面所有模块都用顶层文件调用起来,框图如下:

在这里插入图片描述

六、HDMI显示系统

  以上我们实现了HDMI的发送接口,现在我们将前面的文章《VTC视频时序控制器原理以及Verilog实现》中的VTC模块调用过来,这样就能产生一个正确的视频时序给HDMI接口了,只有时序还是不够,没有数据,因此我们还需要一个TPG模块来产生想要显示的图像数据。

6.1 TPG模块编写

  本次让显示器显示16*16的黑白交替方格代码如下:

module tpg(
    input                                               clk ,
    input                                               rst_n   ,
    input                                               vs_in   ,
    input                                               hs_in   ,
    input                                               de_in   ,
    output  reg                                         vs_out  ,
    output  reg                                         hs_out  ,
    output  reg                                         de_out  ,
    output  reg     [23:0]                              data_out    
);


    reg                                                 vs_in_reg   ;   //对vs信号打一拍
    reg                                                 hs_in_reg   ;   //对hs信号打一拍
    reg                                                 de_in_reg   ;   //对de信号打一拍
    reg             [11:0]                              v_cnt   ; //视频垂直方向,行计数器
    reg             [11:0]                              h_cnt   ; //视频水平方向,列计数器
    reg             [7:0]                               grid_data   ;

//对vs hs de寄存一拍
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        vs_in_reg <= 1'b0;
        hs_in_reg <= 1'b0;
        de_in_reg <= 1'b0;
    end
    else begin
        vs_in_reg <= vs_in;
        hs_in_reg <= hs_in;
        de_in_reg <= de_in;
    end
end

//列计数器
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        h_cnt <= 12'd0;
    else if(de_in == 1'b1)
        h_cnt <= h_cnt + 1'b1;
    else
        h_cnt <= h_cnt;
end

//行计数器
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        v_cnt <= 12'd0;
    else if (vs_in_reg == 1'b1)
        v_cnt <= 12'd0;
    else if((hs_in == 1'b1)&&(hs_in_reg == 1'b0))
        v_cnt <= v_cnt + 1'b1;
    else
        v_cnt <= v_cnt;
end

always @(posedge clk)begin
	grid_data <= ((v_cnt[4]==1'b1) ^ (h_cnt[4]==1'b1)) ? 8'h00 : 8'hff; //方格大小16*16,黑白交替
end

always @(posedge clk)begin
	vs_out  <= vs_in_reg;
    hs_out  <= hs_in_reg;
    de_out  <= de_in_reg;
    data_out  <={grid_data,grid_data,grid_data};
end

endmodule

6.2 显示系统顶层模块

  参考文章《VTC视频时序控制器原理以及Verilog实现》里的常见信号长度表可知:如果要显示1920×1080P的画面,并行时钟就需要148.5M,TMDS编码需要5倍的时钟,则还需要生成一个742.5M时钟,显然这对FPGA来说有点难度。因此本次实验显示一个1280×720@60HZ的画面,这样只需要生成74.25M并行时钟以及371.25M的串行时钟。现在通过一个顶层文件,把PLL模块、VTC模块,TPG模块以及HDMI模块链接起来,代码如下:

module hdmi_display_top(
    input                                               sys_clk ,

    output                                              tmds_clk_p  ,    // TMDS 时钟通道
    output                                              tmds_clk_n  ,
    output          [2:0]                               tmds_data_p ,   // TMDS 数据通道
    output          [2:0]                               tmds_data_n 
);

    wire                                                locked  ;
    wire                                                vga_clk ;
    wire                                                vga_clk_5x  ;
    wire                                                vs_in   ;
    wire                                                hs_in   ;
    wire                                                de_in   ;
    wire                                                vs_out  ;
    wire                                                hs_out  ;
    wire                                                de_out  ;
    wire            [23:0]                              data_out    ;


clk_wiz_0 u_clk
(
    .clk_out1       (vga_clk        ),     
    .clk_out2       (vga_clk_5x     ),     
    .locked         (locked         ),       
    .clk_in1        (sys_clk        )
);      

tpg u_tpg(
    .clk            ( vga_clk       ),
    .rst_n          ( locked        ),
    .vs_in          ( vs_in         ),
    .hs_in          ( hs_in         ),
    .de_in          ( de_in         ),
    .vs_out         ( vs_out        ),
    .hs_out         ( hs_out        ),
    .de_out         ( de_out        ),
    .data_out       ( data_out      )
);

vtc#(
    .H_SYNC           ( 40          ),
    .H_BACK_PORCH     ( 220         ),
    .H_ACTIVE         ( 1280        ),
    .H_FRONT_PORCH    ( 110         ),
    .V_SYNC           ( 5           ),
    .V_BACK_PORCH     ( 20          ),
    .V_ACTIVE         ( 720         ),
    .V_FRONT_PORCH    ( 5           )
)u_vtc(   
    .clk              ( vga_clk      ),
    .rst_n            ( locked       ),
    .vs               ( vs_in        ),
    .hs               ( hs_in        ),
    .de               ( de_in        ),
    .h_end            (              )
);

hdmi_trans_top u_hdmi_trans_top(
    .clk          ( vga_clk             ),
    .clk_5x       ( vga_clk_5x          ),
    .sys_rstn     ( locked              ),
    .video_din    ( data_out            ),
    .video_hsync  ( hs_out              ),
    .video_vsync  ( vs_out              ),
    .video_de     ( de_out              ),
    .tmds_clk_p   ( tmds_clk_p          ),
    .tmds_clk_n   ( tmds_clk_n          ),
    .tmds_data_p  ( tmds_data_p         ),
    .tmds_data_n  ( tmds_data_n         )
);


endmodule

  连接后的系统框图如下:
在这里插入图片描述

七、显示结果

  经过全编译以及下板后,将HDMI接口连接到显示器可以看到如下画面:
在这里插入图片描述

  画面成功显示16*16的黑白交替方格,我们打开显示器的显示信息观察:
在这里插入图片描述

  显示器信息显示当前输入的是HDMI信号,分辨率为1280×720@60HZ,因此本次HDMI接口成功实现。再修改一下TPG的显示数据,让屏幕显示七色彩条,显示结果如下:

在这里插入图片描述
  显示色彩也没问题。


参考

《High-Definition Multimedia Interface Specification Version 1.4b 》
《UG471》

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱奔跑的虎子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值