HDMI-FMC子卡的使用(基于VC707)(中)

子卡到FPGA引脚对应关系

上一篇大致学习了HDMI-FMC子卡的相关配置,发现配置过程比想象中复杂,原因是子卡使用了多个IIC接口分别对各个器件进行配置,这一节主要探讨一下这些端口在“子卡 - FMC - VC707”上的对应关系。

“子卡-FMC”的对应关系

在HDMI-FMC提供的官方demo中,可以看到一共使用了4组IIC传输线(SCL、SDA),如下图所示。
在这里插入图片描述
通过查阅电路原理图和demo中的引脚约束文件,明确信号和FMC引脚之间的对应关系:

序号信号连接器件FMC引脚
1i2c_scl / i2c_sdaADV7619HA00_P_CC / HA01_P_CC
2hdmi_tx_fmc_i2c_scl / hdmi_tx_fmc_i2c_sdaSII9136LA24_N / LA26_P
3rx0_edid_i2c_scl / rx0_edid_i2c_sdaRX0_EDID (DDC)HB19_P / HB19_N
4rx1_edid_i2c_scl / rx1_edid_i2c_sdaRX1_EDID (DDC)LA31_P / LA31_N

“FMC-FPGA”的对应关系

同理,查阅VC707的原理图,可得到FMC引脚和FPGA引脚的对印关系:

序号信号FMC引脚FPGA引脚
1i2c_scl / i2c_sdaHA00_P_CC / HA01_P_CCE34 / D35
2hdmi_tx_fmc_i2c_scl / hdmi_tx_fmc_i2c_sdaLA24_N / LA26_PP31 / J30
3rx0_edid_i2c_scl / rx0_edid_i2c_sdaHB19_P / HB19_NL25 / L26
4rx1_edid_i2c_scl / rx1_edid_i2c_sdaLA31_P / LA31_NM28 / M29

有了以上对应关系,我们就可以在FPGA中通过引脚对FMC子卡上的芯片进行初始化了。
同时,在初始化时还需要用到一些其他的信号(如中断、复位、时钟等),可以用同样的方式把信号对应到VC707的FPGA引脚上。

工程搭建

硬件连接

使用了4个AXI IIC IP连接到FMC接口
使用1个UART和主机通信
使用1个GPIO用于按键控制
5路中断连接到microblaze
使用100M时钟发生器
连接图如下所示:
在这里插入图片描述
之后是约束引脚,按照上面表格的对应关系进行约束;同时,由于VC707提供给FMC接口的标准电平为1.8V,这里的引脚电平也约束为1.8V。

生成比特流。

SDK

在一开始测试的时候,发现ADV7619不能正常写入,现象是:

  1. 使用AXI IIC IP核无法从ADV7619的寄存器中正常读取数据
  2. 在对0x98FF的地址写入0x80数据后,无法继续写入其他数据(任何地址)
    纠结了一段时间,一度怀疑是芯片坏了,结果还找人更换了芯片,结果发现还是会出现同样的问题,网上查阅一番后,我认为可能的原因是:
  3. ADV7619在一次错误的i2c传输后会复原到正常状态,不会返回ack信号;
  4. 而AXI IIC IP核却会傻傻地等待ack信号的到来,否则无法正常执行下一次读写指令
    所以,在这种情况下,如果我往ADV7619发送了一条有问题的i2c指令,那么ADV芯片不会返回应答信号,而IP核这边又一直在等待应答信号,因此程序无法再继续往下执行。
    下面附上写入的i2c指令(来自hdmi-fmc官方例程):
alt_32 regData[]={
		0x98FF80, //I2C reset
		0x98F480, //CEC
		0x98F57C, //INFOFRAME
		0x98F84C, //Set DPLL Slave Address 0x4C
		0x98F964, //Set KSV(repeater) Slave Address 0x64
		0x98FA6C, //Set EDID Map Slave Address 0x6C
		0x98FB68, //Set HDMI Map Slave Addres 0x68
		0x98FD44, //Set CP Map Slave Addres 0x44
		0x68C003, //ADI Required Write
		0x980019, //Set VID_STD
		0x980105, //Prim_Mode =101b HDMI-Comp
		0x9802F2, //Auto CSC, RGB out, Set op_656 bit  (Address 0x02[1]=1, RGB output)
		0x980354, //2x24 bit SDR 444 interleaved mode 0
		0x980528, //AV Codes Off
		0x9806A0, //No inversion on VS,HS pins
		0x980C42, //Power up part
		0x981580, //Disable Tristate of Pins
		0x981980, //LLC DLL phase
		0x983340, //LLC DLL MUX enable
		0x98DD00, //ADI Required Write
		0x98E704, //ADI Required Write
		0x4CB501, //Setting MCLK to 256Fs
		0x4CC380, //ADI Required Write
		0x4CCF03, //ADI Required Write
		0x4CDB80, //ADI Required Write
		0x68C003, //ADI Required write
		0x680008, //Set HDMI Input Port A (BG_MEAS_PORT_SEL = 001b)
		0x680203, //ALL BG Ports enabled
		0x680398, //ADI Required Write
		0x6810A5, //ADI Required Write
		0x681B00, //ADI Required Write
		0x684504, //ADI Required Write
		0x6897C0, //ADI Required Write
		0x683E69, //ADI Required Write
		0x683F46, //ADI Required Write
		0x684EFE, //ADI Required Write
		0x684F08, //ADI Required Write
		0x685000, //ADI Required Write
		0x6857A3, //ADI Required Write
		0x685807, //ADI Required Write
		0x686F08, //ADI Required Write
		0x6883FC, //Enable clock terminators for port A & B
		0x688403, //FP MODE
		0x688510, //ADI Required Write
		0x68869B, //ADI Required Write
		0x688903, //HF Gain
		0x689B03, //ADI Required Write
		0x689303, //ADI Required Write
		0x685A80, //ADI Required Write
		0x689C80, //ADI Required Write
		0x689CC0, //ADI Required Write
		0x689C00, //ADI Required Write
};

后续

至此,还有一个重要的问题没解决,就是ip核无法正常读取数据,因此,下面的计划是花点时间重做i2c的控制,看能否解决这个问题。

补充

书接上文,我在这段时间用verilog重写了i2c驱动部分的代码,试图直接往ADV7619芯片中写入数据,目前初步验证成功,感兴趣的同学可以参考我另一篇博客《用verilog 实现的 i2c控制模块》
在测试的过程中,我发现这块芯片跟写eeprom有一点不同:ADV7619对时序的要求更加严格。
现象是:我原本在设计中使用了ILA用于观察读到的数据,这样的方法在对EEPROM进行读写时没有任何的问题;但是当我使用同样的方法对ADV7619进行读写时,却发现ILA无法正常启动,并且done信号(LED)也一直为false,后来我改为用led观察输出数据才解决了这个问题。因此,我初步认为是时序约束的问题,下面贴出测试的结果:

    3'd1:     begin mode <= 1'b1; devaddr <= 8'h98; subaddr<= 8'hEA; data_in<= 8'h00; end
    3'd2:     begin mode <= 1'b1; devaddr <= 8'h98; subaddr<= 8'hEB; data_in<= 8'h00; end
    3'd3:     begin mode <= 1'b0; devaddr <= 8'h98; subaddr<= 8'hF4; data_in<= 8'h80; end
    3'd4:     begin mode <= 1'b1; devaddr <= 8'h98; subaddr<= 8'hF4; data_in<= 8'h00; end

上面这段代码是我用于测试的代码块,其内容是:

  1. 读取0x98设备中0xEA地址寄存器的值
  2. 读取0x98设备中0xEB地址寄存器的值
  3. 往0x98设备的0xF4地址寄存器写入数据0x80
  4. 读取0x98设备中0xF4地址寄存器的值

0x98是ADV7619芯片的i2c设备地址,后面的是寄存器地址,观察结果如下:
0xEA值为0x20在这里插入图片描述

0xEB值为0xC1在这里插入图片描述

0xF4值为0x80在这里插入图片描述
从测试结果来看,基本可以认为读回的数据是正确的。

封装AXI IP

把代码封装到AXI IP中,替换原来的IP,接入到microblaze
vivado 提供了 简便的AXI IP封装支持,只需要把需要的寄存器slv_regx的值连接到自定义verilog模块的输入输出端口,就可以通过读写AXI设备寄存器的方式对IP进行各种控制。我的例化方式如下:

	always @(*)
	begin
	      // Address decoding for reading registers
	      case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
	        2'h0   : reg_data_out <= slv_reg0;
	        2'h1   : reg_data_out <= slv_reg1;
	        2'h2   : reg_data_out <= {16'd0,tran_done, data_out, tran_state, i2c_state}; //slv_reg2
	        // 16'd0 + 1 + 8 + 4 + 3
	        2'h3   : reg_data_out <= slv_reg3;
	        default : reg_data_out <= 0;
	      endcase
	end
	......
	// Add user logic here
    i2c_tran u1(
    .clk_50M(S_AXI_ACLK), 
    .rst_n(S_AXI_ARESETN),
    .data_valid(slv_reg0[0]),
    .mode(slv_reg0[2]),
    .devaddr(slv_reg1[31:24]),
    .subaddr1(slv_reg1[23:16]),
    .subaddr2(slv_reg1[15:8]),
    .data_in(slv_reg1[7:0]),
    .data_out(data_out),
    .tran_done(tran_done),
    .scl(scl), .sda(sda), 

    .tran_state(tran_state), .i2c_state(i2c_state)
    );
	// User logic ends
	.....

其中,使用了slv_reg1寄存器作为devaddr、subaddr1、subaddr2、data_in的输入;slv_reg0中的两位(0和2)作为data_valid和mode的输入;
而模块输出的数据data_out、tran_done、tran_state(DEBUG)、i2c_state(DEBUG)则组合替换掉原来的slv_reg3;同时,这些输出数据我也保留在了IP的外部端口上,这样可以直接连接到led上观察调试。
这样,通过在SDK中写slv_reg1和slv_reg0的值可以控制模块的输入,处理完毕后,可以从slv_reg3读回所需的数据。
在这里插入图片描述
之后重新烧比特流,在SDK中开发驱动,由于我的verilog部分已经实现了i2c读字节和写字节功能,因此只需要在AXI发送指令和数据(写寄存器),然后读回状态和返回的数据即可。SDK部分代码如下所示:

u8 read_from_device(u8 Devaddr, u8 Subaddr)
{
	u32 data_reg = 0;
	data_reg = (Devaddr << 24) + (Subaddr << 8);
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG1, data_reg);

	u32 ctrl_reg = 0;
	ctrl_reg = (u32) 0x00000004; // mode = 1, data_valid = 0
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG0, ctrl_reg);
	usleep(100);
	ctrl_reg = (u32) 0x00000005; // mode = 1, data_valid = 1
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG0, ctrl_reg);
	usleep(100);
	ctrl_reg = (u32) 0x00000004; // mode = 1, data_valid = 0
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG0, ctrl_reg);

	usleep(1000); // wait for process
	
	u32 data_out;
	data_out = I2C_AXI_mReadReg(ADV_BASE_ADDR, REG2);

	// read slv_reg3 and decode
	u8 i2c_state, tran_state, data_back, done;
	i2c_state = data_out & 0x00000007;
	tran_state = (data_out & 0x00000078) >> 3;
	data_back = (data_out & 0x00007F80) >> 7;
	done = (data_out & 0x00008000) >> 15;
	if ((done == 1) && (tran_state == 0) && (i2c_state == 0) )
		return data_back;
	else
		return 0;
}

u8 write_to_device(u8 Devaddr, u8 Subaddr, u8 Data)
{
	u32 data_reg = 0;
	data_reg = (Devaddr << 24) + (Subaddr << 8) + (Data);
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG1, data_reg);
	
	u32 ctrl_reg = 0;
	ctrl_reg = (u32) 0x00000000; // mode = 0, data_valid = 0
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG0, ctrl_reg);
	usleep(100);
	ctrl_reg = (u32) 0x00000001; // mode = 0, data_valid = 1
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG0, ctrl_reg);
	usleep(100);
	ctrl_reg = (u32) 0x00000000; // mode = 0, data_valid = 0
	I2C_AXI_mWriteReg(ADV_BASE_ADDR, REG0, ctrl_reg);
	
	usleep(1000);// wait for process
	
	u32 data_out;
	data_out = I2C_AXI_mReadReg(ADV_BASE_ADDR, REG2);
	
	// read slv_reg3 and decode
	u8 i2c_state, tran_state, data_back, done;
	i2c_state = data_out & 0x00000007;
	tran_state = (data_out & 0x00000078) >> 3;
	data_back = (data_out & 0x00007F80) >> 7;
	done = (data_out & 0x00008000) >> 15;
	if ((done == 1) && (tran_state == 0) && (i2c_state == 0) )
		return 1;
	else
		return 0;
}
    u8 read_data, state;
    read_data = read_from_device(0x98,0xEA);
    read_data = read_from_device(0x98,0xEB);
    read_data = read_from_device(0x98,0xF4);
    read_data = read_from_device(0x98,0xF5);
    state = write_to_device(0x98,0xF4,0x80);
    state = write_to_device(0x98,0xF5,0x7C);
    read_data = read_from_device(0x98,0xF4);
    read_data = read_from_device(0x98,0xF5);

其中,write_to_device和read_from_device分别是基于AXI IP原有驱动封装的两个API,能够往I2C设备中读写数据,经过调试,目前读写各项功能均正常。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值