当前位置:   article > 正文

FPGA实现HDMI接口_fpga hdmi

fpga hdmi

1.HDMI简介

HDMI( High-Definition Multimedia Interface),即高清多媒体接口。它能够同时传输视频和音频,简化了设备的接口和连线;同时提供了更高的数据传输带宽, 可以传输无压缩的数字音频及高分辨率视频信号。 HDMI 接口在物理层使用 TMDS 标准传输音视频数据。在这里插入图片描述

2.LCD驱动

点亮液晶显示屏需要按照一定的时序,示意图如下。使用FPGA驱动LCD屏幕时,是按照一行一行的形式点亮屏幕的,一个像素时钟点亮一个像素。其中,黑色部分传输控制数据或者其他数据,白色部分传输有效的像素数据。以下图为例,传输一幅图像的过程为:1.VSYNC拉高表示开始传输;;2. 等待VBP行的扫描时间;3.进入有效显示区域,HSYNC拉高开始一行数据的扫描,这一行的时间包括三个部分的:HBP+DE+HFP;4.扫完600行,即完成有效区域的显示,然后等待VFP行的扫描时间结束一帧的扫描。
在这里插入图片描述
LCD 行显示时序
在有效数据区域,LCD行显示时序如下图,其中参数有:
.HSYNC:行同步信号,当此信号有效的时候就表示开始显示新的一行数据,图示为低电平有效。
HSPW:行同步间隔,也就是 HSYNC 信号持续时间。行与行之间不是连续的,中间有间隔。单位是像素时钟。
HBP::行显示后沿(或后肩),单位是 像素时钟。
HOZVAL:行有效显示区域,即显示一行数据所需的时间,假如屏幕分辨率为 1024*600,那么 HOZVAL就是 1024,单位为像素时钟。

在这里插入图片描述
LCD 帧显示时序
传输一整幅图像的时序如下图,其中参数有:
VSYNC:帧(场)同步信号,当此信号有效的时候就表示开始显示新的一帧数据。图示为低电平有效。
VSPW:帧同步信号宽度,也就是 VSYNC 信号持续时间,单位为 1 行的时间。
VBP:帧显示后沿(或后肩),单位为 1 行的时间。
LINE:帧有效显示区域,即显示一帧数据所需的时间,假如屏幕分辨率为 1024*600,那么 LINE 就是600 行的时间。
VFP:帧显示前沿(或前肩),单位为 1 行的时间。
因此,显示一帧的总时间就可以计算为:VSPW+VBP+LINE+VFP 个行时间,总的时间T:T = (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)(含义就是行乘列个像素时钟)
在这里插入图片描述

3.FPGA实现HDMI接口

首先我们需要明确一定,使用HDMI接口传输数据时,数据以TMDS的电平标准传输,实际上它是一种串行的数据格式。但是在FPGA中处理的数据是以RGB888的形式的并行数据,所以需要进行并串转换。在转换之前,需要将RGB888数据进8b/10b编码,这么做的目的主要是为了避免连续出现0或者1,来保证DC平衡。同时,每个编码后的 10-bit字符中状态跳转(“由 1 到 0”或者“由 0 到 1” ) 的次数将被限制在五次以内。
8b/10b编码
对于8b/10b编码,XILINX官方提供了编码模块流程以及代码,不过多解释了:
在这里插入图片描述
并串转换
并串转换使用XILINX FPGA提供的并转串的IO逻辑资源OSERDES,调用方式是使用原语OSERDESE2。一个OSERDES只能实现8:1的并串转换,若要实现10:1的并串转换则需要使用两个OSERDES级联实现。需要注意的是,两个OSERDES级联时,第二个OSERDES输入数据端口只能从D3开始,D1、D2不可用。
在这里插入图片描述
差分输出
经过并转串的数据并不能直接输出,还需要转为差分形式的信号。这时候需要使用到FPGA的IO资源中的IOB,两个IOB可以配置成差分信号输出,调用方法是使用原语OBUFDS。
在这里插入图片描述

4.实验——HDMI传图驱动LCD显示

工程的整体框图如下,主要由四个模块组成,分别是分频模块、视频显示模块、视频驱动模块、数据传输模块。分频模块产生像素时钟和五倍的像素时钟用于其他模块;视频显示模块主要完成ROM中图片数据的读取,由视频驱动模块驱动并输出RGB888数据给视频驱动模块;视频驱动模块完成LCD扫描的过程以及将读取的像素数据输出给数据传输模块;数据传输模块完成8b/10b编码、并转串输出和差分信号的形成。
在这里插入图片描述
视频显示模块
视频显示模块主要完成要显示图片的像素数据输出。图片数据存于例化的单端ROM中,单个数据位宽为24bit,图片大小200*200一共40000像素。LCD使用的是1280×720的分辨率,因此除了显示图片的地方外,其他位置显示白色。该模块代码如下:

module  video_display(
    input                pixel_clk,
    input                sys_rst_n,
    
    input        [10:0]  pixel_xpos,  //像素点横坐标
    input        [10:0]  pixel_ypos,  //像素点纵坐标
    output  reg  [23:0]  pixel_data   //像素点数据
);

//parameter define
parameter  H_DISP = 11'd1280;                       //分辨率——行
parameter  V_DISP = 11'd720;                        //分辨率——列

localparam WHITE  = 24'b11111111_11111111_11111111;  //RGB888 白色

//*****************************************************
//**                    main code
//*****************************************************
wire ena;
wire [23:0]rom_out;
reg [15:0]cnt;

assign ena = 1;

always @(posedge pixel_clk or negedge sys_rst_n ) begin
    if (!sys_rst_n)
        cnt <= 'b0;
	else if(pixel_ypos == 460)	
		cnt <= 'b0;
    else if((pixel_xpos >= 537) && (pixel_xpos < 737) && (pixel_ypos >= 260) && (pixel_ypos < 460))  //让图片显示在屏幕中央
		cnt <= cnt + 16'b1;	
end

always @(posedge pixel_clk ) begin
    if (!sys_rst_n)
		pixel_data <= 'b0;
	else if((pixel_xpos >= 539) && (pixel_xpos < 740) && (pixel_ypos >= 260) && (pixel_ypos < 460))
		pixel_data <= rom_out;
	else
		pixel_data <= WHITE;
end





blk_mem_gen_0 your_instance_name (
  .clka(pixel_clk),    // input wire clka
  .ena(ena),      // input wire ena
  .addra(cnt),  // input wire [15 : 0] addra
  .douta(rom_out)  // output wire [23 : 0] douta
);
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

视频驱动模块
视频驱动模块一方面产生像素点的坐标用于读取视频显示模块中的像素数据,一方面输出像素数据和行场同步信号给编码模块。此外,显示屏分辨率参数是1280*720。模块代码如下:

module video_driver(
    input           	pixel_clk	,
    input           	sys_rst_n	,
		
    //RGB接口	
    output          	video_hs	,     //行同步信号
    output          	video_vs	,     //场同步信号
    output          	video_de	,     //数据使能
    output  	[23:0]  video_rgb	,    //RGB888颜色数据
    output	reg			data_req 	,
	
    input   	[23:0]  pixel_data	,   //像素点数据
    output  reg	[10:0]  pixel_xpos	,   //像素点横坐标
    output  reg	[10:0]  pixel_ypos    //像素点纵坐标
);

//parameter define

//1280*720 分辨率时序参数
parameter  H_SYNC   =  11'd40;   //行同步
parameter  H_BACK   =  11'd220;  //行显示后沿
parameter  H_DISP   =  11'd1280; //行有效数据
parameter  H_FRONT  =  11'd110;  //行显示前沿
parameter  H_TOTAL  =  11'd1650; //行扫描周期

parameter  V_SYNC   =  11'd5;    //场同步
parameter  V_BACK   =  11'd20;   //场显示后沿
parameter  V_DISP   =  11'd720;  //场有效数据
parameter  V_FRONT  =  11'd5;    //场显示前沿
parameter  V_TOTAL  =  11'd750;  //场扫描周期

//reg define
reg  [11:0] cnt_h;
reg  [11:0] cnt_v;
reg       	video_en;

//*****************************************************
//**                    main code
//*****************************************************

assign video_de  = video_en;
assign video_hs  = ( cnt_h < H_SYNC ) ? 1'b0 : 1'b1;  //行同步信号赋值
assign video_vs  = ( cnt_v < V_SYNC ) ? 1'b0 : 1'b1;  //场同步信号赋值

//使能RGB数据输出
always @(posedge pixel_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		video_en <= 1'b0;
	else
		video_en <= data_req;
end

//RGB888数据输出
assign video_rgb = video_de ? pixel_data : 24'd0;

//请求像素点颜色数据输入
always @(posedge pixel_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		data_req <= 1'b0;
	else if(((cnt_h >= H_SYNC + H_BACK - 2'd2) && (cnt_h < H_SYNC + H_BACK + H_DISP - 2'd2))
                  && ((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK+V_DISP)))
		data_req <= 1'b1;
	else
		data_req <= 1'b0;
end

//像素点x坐标
always@ (posedge pixel_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        pixel_xpos <= 11'd0;
    else if(data_req)
        pixel_xpos <= cnt_h + 2'd2 - H_SYNC - H_BACK ;
    else 
        pixel_xpos <= 11'd0;
end
    
//像素点y坐标	
always@ (posedge pixel_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        pixel_ypos <= 11'd0;
    else if((cnt_v >= (V_SYNC + V_BACK)) && (cnt_v < (V_SYNC + V_BACK + V_DISP)))
        pixel_ypos <= cnt_v + 1'b1 - (V_SYNC + V_BACK) ;
    else 
        pixel_ypos <= 11'd0;
end

//行计数器对像素时钟计数
always @(posedge pixel_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt_h <= 11'd0;
    else begin
        if(cnt_h < H_TOTAL - 1'b1)
            cnt_h <= cnt_h + 1'b1;
        else 
            cnt_h <= 11'd0;
    end
end

//场计数器对行计数
always @(posedge pixel_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt_v <= 11'd0;
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)
            cnt_v <= cnt_v + 1'b1;
        else 
            cnt_v <= 11'd0;
    end
end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111

数据传输模块
编码模块将输入的并行数据编码、并转串数据和差分输出。
编码框图如下所示,主要完成RGB888的8b/10b编码,同时行场同步信号HSYNC、VSYNC由蓝色通道进行编码,表示控制信号,接在蓝色通道的C0、C1端口,在传输数据的消隐期传送。其余通道的控制信号未使用到,均赋值为0。
在这里插入图片描述
并转串使用OSERDESE2原语实现。HDMI接口中传输四路数据,分别是R、G、B和时钟,因此需要四路并转串。OSERDESE2使用DDR模式,也就是每个时钟可以转换2个数据,因此OSERDESE2转换10bit数据只需要五个时钟,这就是要用PLL产生5倍像素时钟的原因。时钟的并转串采用硬编码的形式,直接输入10‘b11111_00000的数据。最后由OBUFDS产生四差分信号输出。该部分的整体框图如下:
做了一个低电平复位转高电平复位的逻辑,原因是这些原语都是使用的高电平复位,但是其他模块都是低电平复位。
在这里插入图片描述
结果展示
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/370205
推荐阅读
相关标签
  

闽ICP备14008679号