手上有一块AC701,准备测试一下HDMI接口,查了一堆资料,ADI官方的实在太复杂,我就点个屏至于那么复杂吗,果断放弃。
adam-taylor大佬倒是有一个教程:
https://www.hackster.io/adam-taylor/ac701-hdmi-test-pattern-generation-9aa148
但是很遗憾的是图片看不清。
ADI还有个应用笔记AN-1270《基于ADV7511/ADV7511W/ADV7513的视频发生器》,我觉得这个比较简洁,直接CV大佬的代码。再加一个MB软核,挂载AXI IIC用来配置ADV7511。
XILINX官方也有个非常不错的视频系列教程:Xilinx Video Series and Blog Posts https://support.xilinx.com/s/question/0D52E00006hpsS0SAI/xilinx-video-series-and-blog-posts?language=en_US
我按照Xilinx Video Series and Blog Posts ,很快用ZEDBOARD上的HDMI输出了图像,但是把软件代码移植到AC701上,HDMI输出还是没显示,把初始化代码完全按照AN-1270《基于ADV7511/ADV7511W/ADV7513的视频发生器》,也不行。无意中发现,用ZEDBOARD的寄存器配置后,再运行AN-1270的配置,AC701的HDMI终于有输出了。
完整的初始化代码如下:
#include <stdio.h>
//#include "platform.h"
#include "xil_printf.h"
#include "xstatus.h"
#include "sleep.h"
#include "xiic_l.h"
#include "xil_io.h"
#include "xil_types.h"
#define PAGE_SIZE 16
#define IIC_BASE_ADDRESS XPAR_IIC_0_BASEADDR
#define EEPROM_TEST_START_ADDRESS 0x80
#define IIC_SWITCH_ADDRESS 0x74
#define IIC_ADV7511_ADDRESS 0x39
#define ADV7511_HPD_CTRL_MASK 0x40 // bit 6 = state of HPD
#define ADV7511_HDP_REG_ADDR 0x42
typedef u8 AddressType;
typedef struct {
u8 addr;
u8 data;
u8 init;
} HDMI_REG;
#define NUMBER_OF_HDMI_REGS 41
HDMI_REG hdmi_iic[NUMBER_OF_HDMI_REGS] = {
{0x41, 0x00, 0x10},
{0x98, 0x00, 0x03},
{0x9A, 0x00, 0xE0},
{0x9C, 0x00, 0x30},
{0x9D, 0x00, 0x61},
{0xA2, 0x00, 0xA4},
{0xA3, 0x00, 0xA4},
{0xE0, 0x00, 0xD0},
{0xF9, 0x00, 0x00},
{0x18, 0x00, 0xE7},
{0x55, 0x00, 0x00},
{0x56, 0x00, 0x28},
{0xD6, 0x00, 0xC0},
{0xAF, 0x00, 0x04},
{0xF9, 0x00, 0x00},
{0x01, 0x00, 0x00}, //Set N Value(6144)
{0x02, 0x00, 0x18}, //Set N Value(6144)
{0x03, 0x00, 0x00}, //Set N Value(6144)
{0x15, 0x00, 0x00}, //Input 444 (RGB or YCrCb) with Separate Syncs
{0x16, 0x00, 0x61}, //44.1kHz fs, YPrPb 444
{0x18, 0x00, 0x46}, //CSC disabled
{0x40, 0x00, 0x80}, //General Control Packet Enable
{0x41, 0x00, 0x10}, //Power Down control
{0x48, 0x00, 0x48}, //Reverse bus, Data right justified
{0x48, 0x00, 0xA8}, //Set Dither_mode - 12-to-10 bit
{0x4C, 0x00, 0x06}, //12 bit Output
{0x55, 0x00, 0x00}, //Set RGB444 in AVinfo Frame
{0x55, 0x00, 0x08}, //Set active format Aspect
{0x96, 0x00, 0x20}, //HPD Interrupt clear
{0x98, 0x00, 0x03}, //ADI required Write
{0x98, 0x00, 0x02}, //ADI required Write
{0x9C, 0x00, 0x30}, //ADI required Write
{0x9D, 0x00, 0x61}, //Set clock divide
{0xA2, 0x00, 0xA4}, //ADI required Write
{0x43, 0x00, 0xA4}, //ADI required Write
{0xAF, 0x00, 0x16}, //Set HDMI Mode
{0xBA, 0x00, 0x60}, //No clock delay
{0xDE, 0x00, 0x9C}, //ADI required write
{0xE4, 0x00, 0x60}, //ADI required Write
{0xFA, 0x00, 0x7D} //Nbr of times to search for good phase,,
};
u8 EepromIicAddr; /* Variable for storing Eeprom IIC address */
int IicLowLevelDynEeprom();
u8 EepromReadByte(AddressType Address, u8 *BufferPtr, u8 ByteCount);
u8 EepromWriteByte(AddressType Address, u8 *BufferPtr, u8 ByteCount);
int check_hdmi_hpd_status(void);
//HDMI IIC
int IicLowLevelDynEeprom()
{
u8 BytesRead;
u32 StatusReg;
u8 Index;
int Status;
u32 i;
u8 channel[1] = {0x20};
Status = XIic_DynInit(IIC_BASE_ADDRESS);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
xil_printf("\r\nAfter XIic_DynInit\r\n");
while (((StatusReg = XIic_ReadReg(IIC_BASE_ADDRESS,
XIIC_SR_REG_OFFSET)) &
(XIIC_SR_RX_FIFO_EMPTY_MASK |
XIIC_SR_TX_FIFO_EMPTY_MASK |
XIIC_SR_BUS_BUSY_MASK)) !=
(XIIC_SR_RX_FIFO_EMPTY_MASK |
XIIC_SR_TX_FIFO_EMPTY_MASK)) {
}
EepromIicAddr = IIC_SWITCH_ADDRESS;
XIic_DynSend(IIC_BASE_ADDRESS, EepromIicAddr,
channel, sizeof(channel), XIIC_STOP);
EepromIicAddr = IIC_ADV7511_ADDRESS;
for ( Index = 0; Index < NUMBER_OF_HDMI_REGS; Index++)
{
EepromWriteByte(hdmi_iic[Index].addr, &hdmi_iic[Index].init, 1);
}
for ( Index = 0; Index < NUMBER_OF_HDMI_REGS; Index++)
{
BytesRead = EepromReadByte(hdmi_iic[Index].addr, &hdmi_iic[Index].data, 1);
for(i=0;i<1000;i++) {}; // IIC delay
if (BytesRead != 1) {
return XST_FAILURE;
}
}
return XST_SUCCESS;
}
u8 EepromReadByte(AddressType Address, u8 *BufferPtr, u8 ByteCount)
{
u8 ReceivedByteCount;
u8 SentByteCount;
u16 StatusReg;
/*
* Position the Read pointer to specific location in the EEPROM.
*/
do {
StatusReg = XIic_ReadReg(IIC_BASE_ADDRESS, XIIC_SR_REG_OFFSET);
if (!(StatusReg & XIIC_SR_BUS_BUSY_MASK)) {
SentByteCount = XIic_DynSend(IIC_BASE_ADDRESS, EepromIicAddr,
(u8 *) &Address, sizeof(Address), XIIC_REPEATED_START);
}
} while (SentByteCount != sizeof(Address));
/*
* Receive the data.
*/
ReceivedByteCount = XIic_DynRecv(IIC_BASE_ADDRESS, EepromIicAddr,
BufferPtr, ByteCount);
/*
* Return the number of bytes received from the EEPROM.
*/
return ReceivedByteCount;
}
u8 EepromWriteByte(AddressType Address, u8 *BufferPtr, u8 ByteCount)
{
u8 SentByteCount;
u8 WriteBuffer[sizeof(Address) + PAGE_SIZE];
u8 Index;
/*
* A temporary write buffer must be used which contains both the address
* and the data to be written, put the address in first based upon the
* size of the address for the EEPROM
*/
if (sizeof(AddressType) == 2) {
WriteBuffer[0] = (u8) (Address >> 8);
WriteBuffer[1] = (u8) (Address);
} else if (sizeof(AddressType) == 1) {
WriteBuffer[0] = (u8) (Address);
EepromIicAddr |= (EEPROM_TEST_START_ADDRESS >> 8) & 0x7;
}
/*
* Put the data in the write buffer following the address.
*/
for (Index = 0; Index < ByteCount; Index++) {
WriteBuffer[sizeof(Address) + Index] = BufferPtr[Index];
}
/*
* Write a page of data at the specified address to the EEPROM.
*/
SentByteCount = XIic_DynSend(IIC_BASE_ADDRESS, EepromIicAddr,
WriteBuffer, sizeof(Address) + ByteCount,
XIIC_STOP);
/*
* Return the number of bytes written to the EEPROM.
*/
return SentByteCount - sizeof(Address);
}
int main()
{
// XVtc_Timing vtcTiming;
int Status;
// XVtc_SourceSelect/ SourceSelect;
// init_platf/orm();
print("Hello World\n\r");
print("Successfully ran Hello World application");
Status = IicLowLevelDynEeprom();
if (Status != XST_SUCCESS) {
xil_printf("ADV7511 IIC programming FAILED\r\n");
return XST_FAILURE;
}
xil_printf("ADV7511 IIC programming PASSED\r\n");
check_hdmi_hpd_status();
return 0;
}
int check_hdmi_hpd_status(void)
{
// int Status;
u8 data = 0x00;
u8 BytesRead;
//
// Status = iic_read(IicPs, Address, ADV7511_HDP_REG_ADDR, &data, 1);
// Status = iic_read2( IicPs, /*ZC702_HDMI_ADDR*/ 0x39, 0x42, &data, 1);
BytesRead = EepromReadByte(0x42, &data, 1);
if((data & ADV7511_HPD_CTRL_MASK) == ADV7511_HPD_CTRL_MASK) {
// Monitor Connected
xil_printf("Monitor Connected\n\r");
return 1;
}
else
{
// Monitor not connected
xil_printf("Monitor not Connected\n\r");
return 0;
}
}
verilog代码就不贴了,用AN-1270的,如果是1080P,给个148.5MHz的时钟就行了。
调试中还发现一个奇怪的问题,要把输出给ADV7511的信号,通过ILA抓波形才行,如果不加ILA,HDMI居然没输出,不知道是不是被优化掉了,算了,反正加个ILA有HDMI输出就行,不想折腾了。
// ila_0 ila_0_i (
// .clk(clk148p5mhz), // input wire clk
// .probe0(adv7511_hs), // input wire [0:0] probe0
// .probe1(adv7511_vs), // input wire [0:0] probe1
// .probe2(adv7511_de), // input wire [0:0] probe2
// .probe3(adv7511_d) // input wire [23:0] probe3
// );