做图像处理最终结果要么保存到文件内,要么通过一个设备显示出来,HDMI实现了显示渠道的接口,在MYD-CZU3EG这块板卡上,使用了SiI9022这款HDMI发送器,充当了将RGB数据转成HDMI信号发送出去的功能,SiI9022是基于IIC接口进行配置操作,具体有以下几种操作流程:
Zynq MPSoc使用I2C共有3种模式:
1. PS I2C Controller+MIO;
2. PS I2CController+EMIO(PL IO);
3. PL Logic Driver;
本文基于第二种进行操作,使用EMIO不仅仅可以利用到PS已有的硬核,还可以使用PL端的Chipscope进行波形的抓取,相对于MIO,在内部多了一个逻辑分析仪,相对于PL逻辑实现,无需占用PL逻辑资源;
配置SiI9022前提要先配置MPSoc的I2C controller,Xilinx提供的库函数太过臃肿,故此使用直接寄存器操作:
一般I2C控制器设计的操作流程是:初始化控制器->设置从机地址、数据->启动传输 ->查询状态;MPSoc的初始化控制器流程如下:
1. 备份Interrupt Mask寄存器
2. 设置Interrupt Disabled寄存器为0x2FF
3. 初始化FIFO,清除Transfer Size;
4. 清除Interrupt Status寄存器
5. 设置Mask备份值到Interrupt Enable寄存器;
6. 设置超时周期;
7. 清除Interrupt Status寄存器
8. 设置SCL时钟,SCL时钟根据Control寄存器的DivisorA和DivisorB值计算的,计算公式:I2C系统时钟/22U * (Div_a + 1U) * (Div_b + 1U)
MPSoc通过I2C写数据到从机,一般情况都使用3Phase模式,即从机地址+从机寄存器地址+数据,这种情况下的流程:
1. 等待总线空闲;
2. 设置是否处理应答(ACK_EN)、寻址模式(NEA)、主机模式还是从机模式(MS)、读还是写(RW)、是否需要重启动(HOLD)(Control寄存器)
3. 初始化FIFO,清除Transfer Size;
4. 禁用所有中断,设置Interrupt Disabled寄存器为0x2FF
5. 清除Interrupt Status寄存器
6. 写入从机寄存器地址(寄存器偏移0xC);
7. 写入从机寄存器地址需要写入的值(寄存器偏移0xC);(这一步可选)
8. 写入从机地址(寄存器偏移0x8)(在执行这一步写入后,传输已经开始);
9. 读取ISR寄存器的Complete位,来轮询传输是否已经结束;
10. 判断ISR的ARB_LOST、TX_OVF、NACK是否激活,如果都没有激活,表示传输过程没有出现错误
11. 等待总线空闲;
MPSoc通过I2C从从机读数据,一般有两种模式,
l 先执行写操作,但不是真正写,只是设置从机寄存器地址,然后执行Current Read,即在上次写的从机寄存器地址上读数据(此种方式取决于器件是否支持,根据手册,SiI9022是支持这种操作)
l 先写从机寄存器地址、重启动、读数据,此种方式不太方便利用之前I2C写的流程;
本文使用第一种,读流程如下:
1. 执行I2C写操作;
2. 设置是否处理应答(ACK_EN=1)、寻址模式(NEA=1)、主机模式还是从机模式(MS=1)、读还是写(RW=1)、不需要重启动(HOLD=0)(Control寄存器)
3. 清除Interrupt Status寄存器
4. 设置Transfer Size寄存器为1,只读一个Byte数据;
5. 写入从机地址(寄存器偏移0x8)(在执行这一步写入后,传输已经开始);
6. 读取ISR寄存器的RX_DV位,来轮询传输是否已经结束,或者延迟1ms;
7. 判断ISR的ARB_LOST、RX_OVF、RX_UNF、NACK是否激活,如果都没有激活,表示传输过程没有出现错误
8. 等待总线空闲;
使用上述流程对SiI9022操作,操作步骤如下:
- 设置I2C Brige连通channel1;
- 写寄存器0xC7值0,进行复位;(从机地址居然是0x3b,手册上是0x76,好奇怪)
- 读寄存器0x1B-0x1D;
读取SiI9022 ID如下图所示,根据手册,发现器件类型是9022A;
使用EMIO可以利用Vivado的Chipscope对从PS过来的信号进行捕捉,EMIO并不是PS直接到PL引脚上,而是到PL逻辑入口,具体怎么连接到哪,是否会做某些处理,完全取决于PL Logic;
Vivado的Chipscope捕捉分为Trigger和Capture,Trigger是捕捉到一次匹配条件,抓取设定好的前后多少个数据,而Capture是在满足一次匹配,捕捉一次,有多少个匹配,就会产生多少个点。捕捉低速信号,例如I2C,可以使用Capture,不过需要有scl的N倍频时钟作为捕捉条件;Trigger还有多窗口捕捉,可以实现多次捕捉,一次只捕捉设定好的点数;
下图是捕捉本例的结果:
写从机地址0x70,数据2,2Phase模式
写从机地址0x3b,寄存器地址0xC7,数据0,3Phase模式
读从机地址0x3b,寄存器地址0x1B
读从机地址0x3b,寄存器地址0x1C
读从机地址0x3b,寄存器地址0x1D
Main Code:
- #include
- #include "platform.h"
- #include "xil_printf.h"
- #include "xil_io.h"
- #include "sleep.h"
- #define CRL_APB 0xFF5E0000
- #define RST_LPD_IOU2 0x238
- #define I2C0_BASE 0xFF020000
- #define I2C1_BASE 0xFF030000
- static u32 i2cc_addr = I2C0_BASE;
- u32 Zu_I2c_Init(void)
- {
- u32 IntrMaskReg;
- u32 IntrStatusReg;
- IntrMaskReg = Xil_In32(i2cc_addr+0x20);
- Xil_Out32(i2cc_addr+0x28, 0x2FF);
- Xil_Out32(i2cc_addr, 0x40);
- IntrStatusReg = Xil_In32(i2cc_addr+0x10);
- Xil_Out32(i2cc_addr+0x10, IntrStatusReg);
- IntrMaskReg = (u32)0x2FF & (~IntrMaskReg);
- Xil_Out32(i2cc_addr+0x24, IntrMaskReg);
- Xil_Out32(i2cc_addr, 0);
- Xil_Out32(i2cc_addr+0x1C, 0xFF);
- Xil_Out32(i2cc_addr+0x28, 0x2FF);
- }
- s32 Zu_I2c_BusIsBusy()
- {
- u32 ControlReg;
- u32 StatusReg;
- s32 Status;
- ControlReg = Xil_In32(i2cc_addr);
- StatusReg = Xil_In32(i2cc_addr+0x4);
- if (((StatusReg & 0x100) != 0x0U) && ((ControlReg&0x10)==0)) {
- Status = (s32)TRUE;
- }else {
- Status = (s32)FALSE;
- }
- return Status;
- }
- u32 Zu_I2c_Write(u32 phy_addr, u32 reg_addr, u32 *reg_data)
- {
- u32 ControlReg;
- u32 Intrs;
- u32 IntrStatusReg;
- u32 Status;
- if(Zu_I2c_BusIsBusy())
- {
- xil_printf("Bus is Busynr");
- return 1;
- }
- ControlReg = Xil_In32(i2cc_addr)&(~0x4F);
- ControlReg |= (u32)0x4E;
- Xil_Out32(i2cc_addr, ControlReg);
- Xil_Out32(i2cc_addr+0x28, 0x2FF);// Disabled all interrupt
- Intrs = (u32)0x240;//0x244;
- IntrStatusReg = Xil_In32(i2cc_addr+0x10);
- Xil_Out32(i2cc_addr+0x10, IntrStatusReg);
- Xil_Out32(i2cc_addr+0xC,(u32)(reg_addr));
- if(reg_data != NULL)
- Xil_Out32(i2cc_addr+0xC,(u32)(*reg_data));
- // Xil_Out32(i2cc_addr+0x14,1);
- Xil_Out32(i2cc_addr+0x8, (u32)phy_addr);
- IntrStatusReg = Xil_In32(i2cc_addr+0x10);
- while ((IntrStatusReg & 1) != 1){ //Check Complete
- IntrStatusReg = Xil_In32(i2cc_addr+0x10);
- if ((IntrStatusReg & Intrs) != 0U) {
- break; //Capture Error
- }
- }
- if ((IntrStatusReg & Intrs) != 0U) {
- if (IntrStatusReg & 0x200) {
- Status = (s32) 1089;
- } else {
- Status = (s32)1;
- }
- } else {
- Status = (s32)0;
- while(Zu_I2c_BusIsBusy());
- }
- return Status;
- }
- u32 Zu_I2c_Read(u32 phy_addr, u32 reg_addr, u32 *reg_data)
- {
- u32 IntrStatusReg = 0;
- u32 Intrs = 0;
- u32 StatusReg = 0;
- u32 Status = 0;
- u32 ControlReg = 0;
- Status = Zu_I2c_Write(phy_addr, reg_addr, NULL);
- if(Status != 0)
- {
- xil_printf("Write Status: %dnr", Status);
- return Status;
- }
- usleep(250000);
- ControlReg = Xil_In32(i2cc_addr);
- ControlReg |= (u32)0x4F;
- Xil_Out32(i2cc_addr, ControlReg);
- IntrStatusReg = Xil_In32(i2cc_addr+0x10);
- Xil_Out32(i2cc_addr+0x10, IntrStatusReg);
- Xil_Out32(i2cc_addr+0x14,1);
- Xil_Out32(i2cc_addr+0x8, (u32)phy_addr);
- usleep(1000);
- Intrs = (u32)0x2a4;
- IntrStatusReg = Xil_In32(i2cc_addr+0x10);
- if ((IntrStatusReg & Intrs) == 0U) {
- StatusReg = Xil_In32(i2cc_addr+0x4);
- if((StatusReg & 0x20) != 0U) {
- *reg_data = Xil_In32(i2cc_addr+0x0C);
- StatusReg = Xil_In32(i2cc_addr+0x4);
- }
- else if((StatusReg & 0x40) != 0U) {
- xil_printf("RX FIFO Overflownr");
- *reg_data = Xil_In32(i2cc_addr+0x0C);
- StatusReg = Xil_In32(i2cc_addr+0x4);
- }
- // IntrStatusReg = Xil_In32(i2cc_addr+0x10);
- // Xil_Out32(i2cc_addr+0x10, IntrStatusReg);
- // Xil_Out32(i2cc_addr+0x08, phy_addr);
- // Xil_Out32(i2cc_addr+0x14,1);
- // IntrStatusReg = Xil_In32(i2cc_addr+0x0C);
- }
- // Xil_Out32(i2cc_addr+0x0C, Xil_In32(i2cc_addr+0x0) & (~0x10));
- if ((IntrStatusReg & Intrs) != 0U) {
- if (IntrStatusReg & 0x20) {
- Status = (s32) 1089;
- } else {
- Status = (s32)1;
- }
- } else {
- Status = (s32)0;
- while(Zu_I2c_BusIsBusy());
- }
- return Status;
- }
- void Zu_Set_Scl_Clock(u32 freq)
- {
- u32 Div_a;
- u32 Div_b;
- u32 ActualFscl;
- u32 Temp;
- u32 TempLimit;
- u32 LastError;
- u32 BestError;
- u32 CurrentError;
- u32 ControlReg;
- u32 CalcDivA;
- u32 CalcDivB;
- u32 BestDivA;
- u32 BestDivB;
- u32 FsclHzVar = 100000;
- Temp = XPAR_PSU_I2C_0_I2C_CLK_FREQ_HZ / ((u32)22U * FsclHzVar);
- /*
- * If frequency 400KHz is selected, 384.6KHz should be set.
- * If frequency 100KHz is selected, 90KHz should be set.
- * This is due to a hardware limitation.
- */
- if(FsclHzVar > (u32)384600U) {
- FsclHzVar = (u32)384600U;
- }
- if((FsclHzVar <= (u32)100000U) && (FsclHzVar > (u32)90000U)) {
- FsclHzVar = (u32)90000U;
- }
- TempLimit = ((XPAR_PSU_I2C_0_I2C_CLK_FREQ_HZ %
- ((u32)22 * FsclHzVar)) != (u32)0x0U) ?
- Temp + (u32)1U : Temp;
- BestError = FsclHzVar;
- BestDivA = 0U;
- BestDivB = 0U;
- for ( ; Temp <= TempLimit ; Temp++)
- {
- LastError = FsclHzVar;
- CalcDivA = 0U;
- CalcDivB = 0U;
- for (Div_b = 0U; Div_b < 64U; Div_b++) {
- Div_a = Temp / (Div_b + 1U);
- if (Div_a != 0U){
- Div_a = Div_a - (u32)1U;
- }
- if (Div_a > 3U){
- continue;
- }
- ActualFscl = XPAR_PSU_I2C_0_I2C_CLK_FREQ_HZ /
- (22U * (Div_a + 1U) * (Div_b + 1U));
- if (ActualFscl > FsclHzVar){
- CurrentError = (ActualFscl - FsclHzVar);}
- else{
- CurrentError = (FsclHzVar - ActualFscl);}
- if (LastError > CurrentError) {
- CalcDivA = Div_a;
- CalcDivB = Div_b;
- LastError = CurrentError;
- }
- }
- if (LastError < BestError) {
- BestError = LastError;
- BestDivA = CalcDivA;
- BestDivB = CalcDivB;
- }
- }
- ControlReg = Xil_In32(i2cc_addr);
- ControlReg &= ~((u32)0xC000 | (u32)0x3F00);
- ControlReg |= (BestDivA << 14) | (BestDivB << 8);
- Xil_Out32(i2cc_addr, ControlReg);
- }
- int main()
- {
- u32 dev_1 = 0 ;
- u32 dev_2 = 0;
- u32 dev_3 = 0 ;
- u32 dev_4 = 0;
- u32 sii_dev = 0;
- u32 reset = 0;
- u32 Status;
- u32 ov2640_addr = 0x3b;
- init_platform();
- Xil_Out32(0xFF0A0000 + 0x2C4,0x00000001);
- Xil_Out32(0xFF0A0000 + 0x2C8,0x00000001);
- Xil_Out32(0xFF0A0000 + 0x18,0xFFFE0001);
- usleep(2500);
- Zu_I2c_Init();
- //Xil_Out32(0xA0001000, 1);
- Zu_Set_Scl_Clock(100000);
- Zu_I2c_Write(0x70, 0x2, NULL);
- Zu_I2c_Write(0x3b, 0xC7, &reset);
- usleep(250000);
- Status = Zu_I2c_Read(0x3b, 0x1B, &dev_1);
- Status = Zu_I2c_Read(0x3b, 0x1C, &dev_2);
- Status = Zu_I2c_Read(0x3b, 0x1D, &dev_3);
- Status = Zu_I2c_Read(0x3b, 0x30, &dev_4);
- sii_dev = dev_1 | (dev_2 << 8) | (dev_3 << 16) | (dev_4 << 24);
- if(sii_dev== 0xB0)
- xil_printf("Detect Sii9022nr");
- if(sii_dev== 0x0302B0)
- xil_printf("Detect Sii9022Anr");
- if(sii_dev== 0x120000B0)
- xil_printf("Detect Sii9024nr");
- if(sii_dev== 0x120302B0)
- xil_printf("Detect Sii9024Anr");
- if(sii_dev== 0x123000B4)
- xil_printf("Detect Sii9136/9334nr");
- cleanup_platform();
- return 0;
- }