本原创文章由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处(www.meyesemi.com)
1. 实验简介
实验目的:
完成 HDMI 回环实验
实验环境:
Window11
PDS2022.2-SP6.4
硬件环境:
MES2L676-100HP
2. 实验原理
HDMI 输入接口采用宏晶微 MS7200 HMDI 接收芯片,HDMI 输出接口采用宏晶微 MS7210 HMDI 发送芯片。 芯片兼容 HDMI1.4b 及以下标准视频的 3D 传输格式,最高分辨率高达 4K@30Hz,最高采样率达到 300MHz, 支持 YUV 和 RGB 之间的色彩空间转换,数字接口支持 YUV 及 RGB 格式。
MS7200 和 MS7210 的 IIC 配置接口与 FPGA 的 IO 相连,通过 FPGA 的编程来对芯片进行初始化和配置操作。
MES2L100HP 开发板上将 MS7200 的 SA 管脚下拉到地,故 IIC 的 ID 地址为 0x56,将 MS7210 的 SA 管脚 上拉到电源电压,故 IIC 的 ID 地址为 0xB2。
2.1. 显示原理
下图表示一个 8*8 像素的画面,图中每个格子表示一个像素点,显示图像时像素点快速点亮的过程按表格中 编号的顺序逐个点亮,从左到右,从上到下,按图中箭头方向的“Z”字形顺序。
以上图为例,每行 8 个像素点,每完成一行信号的传输,会转到下一行信号传输,直到完成第 8 行数据的传输, 就完成了一个画面的数据传输了,一个画面也称为一场或一帧,显示每秒中刷新的帧数称为帧率。比如 1920*1080P 像素,就是 1 行有效像素点 1920,一场有效行为 1080 行。
每个像素点的像素值数据,对应每个像素点的颜色。常见的像素值表示格式比如:RGB888,RGB 分别代表: 红 R,绿 G,蓝 B,888 是指 R、G、B 分别有 8bit,也就是 R、G、B 每一色光有 28=256 级阶调,通过 RGB 三色光的 不同组合,一个像素上最多可显示 24 位的 256*256*256 =16,777,216 色。
HDMI 显示的数据源采用 verilog 编写的显示时序产生模块 sync_vg 实现上图的时序,彩条生成模块 pattern_vg 根据像素点所在位置,即列数和行数确定像素值,实现彩条图案。
2.2. HDMI_PHY 配置
MS7200 为 HMDI 接收芯片,MS7210 为 HMDI 发送芯片,芯片的 IIC 配置接口已与 FPGA 的 IO 相连,芯片 正常使用需要通过 FPGA 的对芯片进行初始化和配置操作。寄存器配置切忌修改。具体配置可以参考 demo。
3. 接口列表
顶层模块接口如下所示:
4. 工程说明
系统时钟 sys_clk 通过 pll 锁相环 IP 分出 cfg_clk 给到 ms72xx_ctl 模块,该模块通过 IIC 配置 MS7200 与 MS7210 芯片。HDMI 输入的数据经过 MS7200 芯片解码后转成标准 RGB 格式的数据输入到 FPGA 中。再将该数 据输出到 MS7210 芯片。MS7210 将 RGB 格式的数据编码成 TMDS 信号对外输出。
5. 代码模块说明
Ms72xx_ctl.v 模块
module ms72xx_ctl(input clk,input rst_n,output init_over,output iic_tx_scl,inout iic_tx_sda,output iic_scl,inout iic_sda
);reg rstn_temp1, rstn_temp2;reg rstn;always @(posedge clk or negedge rst_n) beginif (!rst_n)rstn_temp1 <= 1'b0;elserstn_temp1 <= rst_n;endalways @(posedge clk) beginrstn_temp2 <= rstn_temp1;rstn <= rstn_temp2;endwire init_over_rx;wire [7:0] device_id_rx;wire iic_trig_rx;wire w_r_rx;wire [15:0] addr_rx /*synthesis PAP_MARK_DEBUG="true"*/;wire [7:0] data_in_rx;wire busy_rx;wire [7:0] data_out_rx;wire byte_over_rx;wire [7:0] device_id_tx;wire iic_trig_tx;wire w_r_tx;wire [15:0] addr_tx /*synthesis PAP_MARK_DEBUG="true"*/;wire [7:0] data_in_tx;wire busy_tx;wire [7:0] data_out_tx;wire byte_over_tx;ms7200_ctl ms7200_ctl(.clk (clk), // input.rstn (rstn), // input.init_over(init_over_rx), // output reg.device_id(device_id_rx), // output [7:0].iic_trig (iic_trig_rx), // output reg.w_r (w_r_rx), // output reg.addr (addr_rx), // output reg [15:0].data_in (data_in_rx), // output reg [7:0].busy (busy_rx), // input.data_out (data_out_rx), // input [7:0].byte_over(byte_over_rx) // input);ms7210_ctl ms7210_ctl(.clk (clk), // input.rstn (init_over_rx), // input.init_over(init_over), // output reg.device_id(device_id_tx), // output [7:0].iic_trig (iic_trig_tx), // output reg.w_r (w_r_tx), // output reg.addr (addr_tx), // output reg [15:0].data_in (data_in_tx), // output reg [7:0].busy (busy_tx), // input.data_out (data_out_tx), // input [7:0].byte_over(byte_over_tx) // input);wire sda_in /*synthesis PAP_MARK_DEBUG="true"*/;wire sda_out /*synthesis PAP_MARK_DEBUG="true"*/;wire sda_out_en /*synthesis PAP_MARK_DEBUG="true"*/;iic_dri #(.CLK_FRE (27'd10_000_000), // system clock frequency.IIC_FREQ (20'd400_000), // I2c clock frequency.T_WR (10'd1), // I2c transmit delay ms.ADDR_BYTE (2'd2), // I2C addr byte number.LEN_WIDTH (8'd3), // I2C transmit byte width.DATA_BYTE (2'd1) // I2C data byte number) iic_dri_rx (.clk (clk),.rstn (rstn),.device_id (device_id_rx),.pluse (iic_trig_rx), // I2C transmit trigger.w_r (w_r_rx), // I2C transmit direction 1:send 0:receive.byte_len (4'd1), // I2C transmit data byte length.addr (addr_rx), // I2C transmit addr.data_in (data_in_rx), // I2C send data.busy (busy_rx), // I2C bus status.byte_over (byte_over_rx), // I2C byte transmit over flag.data_out (data_out_rx), // I2C receive data.scl (iic_scl),.sda_in (sda_in),.sda_out (sda_out),.sda_out_en (sda_out_en));assign iic_sda = sda_out_en ? sda_out : 1'bz;assign sda_in = iic_sda;wire sda_tx_in /*synthesis PAP_MARK_DEBUG="true"*/;wire sda_tx_out /*synthesis PAP_MARK_DEBUG="true"*/;wire sda_tx_out_en /*synthesis PAP_MARK_DEBUG="true"*/;iic_dri #(.CLK_FRE (27'd10_000_000), // system clock frequency.IIC_FREQ (20'd400_000), // I2c clock frequency.T_WR (10'd1), // I2c transmit delay ms.ADDR_BYTE (2'd2), // I2C addr byte number.LEN_WIDTH (8'd3), // I2C transmit byte width.DATA_BYTE (2'd1) // I2C data byte number) iic_dri_tx (.clk (clk),.rstn (rstn),.device_id (device_id_tx),.pluse (iic_trig_tx), // I2C transmit trigger.w_r (w_r_tx), // I2C transmit direction 1:send 0:receive.byte_len (4'd1), // I2C transmit data byte length.addr (addr_tx), // I2C transmit addr.data_in (data_in_tx), // I2C send data.busy (busy_tx), // I2C bus status.byte_over (byte_over_tx), // I2C byte transmit over flag.data_out (data_out_tx), // I2C receive data.scl (iic_tx_scl),.sda_in (sda_tx_in),.sda_out (sda_tx_out),.sda_out_en (sda_tx_out_en));assign iic_tx_sda = sda_tx_out_en ? sda_tx_out : 1'bz;assign sda_tx_in = iic_tx_sda;endmodule
该模块主要完成对 ms7210 和 ms7200 芯片的 IIC 配置。具体代码不详细分析,按照 IIC 协议去完成即可, 然后就是配置 ms7210 和 ms7200 的寄存器即可。以下给出 IIC 协议的时序说明,如下图所示:
当 SDA 和 SCL 都处于高电平时,表示空闲状态,因此在硬件设计时其实也要通过上拉电阻将信号拉高,表示 空闲。当 SCL 保持高电平时,SDA 拉低时表示 IIC 总线启动,标志一次数据传输的开始,在开始前,IIC 总线必须处 于空闲状态。在数据传输过程中,SDA 上逐位串行传输每一位数据,在 SCL 高电平期间,SDA 必须保持稳定。当 SCL 为低电平时,SDA 才允许改变。
IIC 总线每次传输一个字节,也就是传输 8 个 bit,每 8bit 后要收到一个来自接收方反馈的 ACK(应答信号), 该应答信号为高电平时,表示接收方接收字节失败。为低电平时,表示有效。之后,在 SCL 高电平期间,SDA 重新 拉高,则表示传输结束,IIC 总线回到空闲状态。
6. 实验现象
连接好 MES2L100HP 开发板、视频源和显示器;
注意视频源必须为 1920*1080P@60,下图为设置分辨率步骤,下载程序,可以看到显示器显示与视频源一致的图像。
上图为 FPGA 的 HDMI_OUT 输出的图像。
注意事项:
需要插上 HDMI 接口,板子上电时才会对 HDMI 芯片进行配置,否则将配置失败。
我们对 HDMI 芯片配置时使用的是 RGB888 协议,如果视频源的颜色格式不是 RGB888,会出现一些问题。 如上图,视频源的颜色格式是 YCbCr444,回环实验中,在显示器上显示视频源的画面,显示器上的画面颜色会偏绿。