【转载】也谈WinCE6.0+S3C6410 IIC驱动Bug
转载自:http://jazka.blog.51cto.com/809003/702584
最近在调试Camera驱动时候,发现通过IIC读写Camera设备时,总是出现问题。跟踪调试发现,对Camera设备的写操作基本不会出现问题,但是读操作有时候正常,有时候不正常。从串口输入调试信息发现,读操作总是出现“ACK NOT received”,在分析这部分源码时,发现此处的逻辑有些问题,同时从网上查找到了有人也遇到了这个问题,不过对于网上的分析,本人有一些不同的看法,这里描述一下。如果有不正确的地方,望指正。
先给出网络上对这一问题分析的文章《s3c6410 winCE6.0 IIC驱动BUG》,地址http://blog.csdn.net/knock/article/details/4758818。
其次给出本人的平台环境:Wince6.0+S3C6410+Android6410开发板。
问题一:为什么读操作会出现“ACK NOT received”
查看该提示出现的函数是IIC中断处理函数IIC_IST中,如下:
if (iicstat & ACK_NOT_RECEIVED)
{
DEBUGMSG(ZONE_ERROR,(TEXT("I2C_IST[0x%x, %d]: ACK NOT received \r\n"),
g_OwnerContext, g_uIIC_PT));
}
{
DEBUGMSG(ZONE_ERROR,(TEXT("I2C_IST[0x%x, %d]: ACK NOT received \r\n"),
g_OwnerContext, g_uIIC_PT));
}
在IIC_IST中断处理函数中,对于读操作的处理代码如下:
case Master_receive:
if (g_uIIC_PT>0)
{
bDone = FALSE;
g_pcIIC_BUFFER[g_uIIC_PT-1] = g_pIICReg->IICDS;
}
g_uIIC_PT++;
if (g_uIIC_PT>0)
{
bDone = FALSE;
g_pcIIC_BUFFER[g_uIIC_PT-1] = g_pIICReg->IICDS;
}
g_uIIC_PT++;
if (g_uIIC_PT==g_uIIC_DATALEN)
{
g_pIICReg->IICCON &= ~(1<<7);
}
else if (g_uIIC_PT > g_uIIC_DATALEN)
{
bDone = TRUE;
g_pIICReg->IICSTAT = MRX_STOP;
}
g_pIICReg->IICCON &= ~(1<<4);
break;
在这部分代码中加入调试信息会发现,在请求读取Camera设备8字节数据的过程中,共进入此处三次,也就是说明中断了三次。博文《s3c6410 winCE6.0 IIC驱动BUG》中对第二中断非正常的中断,本人不是很赞同这种说法。
Master在初始化读取配置之后,先是开始信号,之后是将slave address写入IICDS寄存器,在该地址传递给Camera设备之后,Camera设备回复ACK,同时Master发生中断,此乃第一次中断;之后Camera设备发送请求的数据,SDA上的数据移入IICDS寄存器,此时产生第二次中断,Master读取8字节的数据;读取之后,由于没有发送结束信号,所有SDA上数据依然移入IICDS寄存器,故产生第三次中断,此时发送结束信号。
结合上面的源码发现,逻辑确实有些问题,在第一次中断中,将IICCON寄存器的bit[7]清空,导致第二次中断时,if (iicstat & ACK_NOT_RECEIVED)语句成立,也就出现了ACK NOT received的提示,但是之后仍然能够将请求的数据读取到,所以应该是逻辑上的不合理,而不应该说是错误。
解决的方法就是将IICCON寄存器bit[7]清空的操作往后移一个Clock即可,如下:
case Master_receive:
if (g_uIIC_PT>0)
{
bDone = FALSE;
g_pcIIC_BUFFER[g_uIIC_PT-1] = g_pIICReg->IICDS;
}
//g_uIIC_PT++; //delete by jazka 2011.10.31
if (g_uIIC_PT==g_uIIC_DATALEN)
{
g_pIICReg->IICCON &= ~(1<<7);
}
else if (g_uIIC_PT > g_uIIC_DATALEN)
{
bDone = TRUE;
g_pIICReg->IICSTAT = MRX_STOP;
}
g_uIIC_PT++; //add by jazka 2011.10.31
g_pIICReg->IICCON &= ~(1<<4);
break;
if (g_uIIC_PT>0)
{
bDone = FALSE;
g_pcIIC_BUFFER[g_uIIC_PT-1] = g_pIICReg->IICDS;
}
//g_uIIC_PT++; //delete by jazka 2011.10.31
if (g_uIIC_PT==g_uIIC_DATALEN)
{
g_pIICReg->IICCON &= ~(1<<7);
}
else if (g_uIIC_PT > g_uIIC_DATALEN)
{
bDone = TRUE;
g_pIICReg->IICSTAT = MRX_STOP;
}
g_uIIC_PT++; //add by jazka 2011.10.31
g_pIICReg->IICCON &= ~(1<<4);
break;
问题二:读操作时常出现失败
上面问题一提到了,虽然总是提示ACK NOT received,但是不影响请求数据的读取,所以读操作偶尔失败,偶尔成功的现象是由其他地方引起的。写操作总是可以成功,所以需要看一下读操作和写操作的区别,最大的区别莫过于每次读操作之前都会进行一次写操作,如下代码所示:
BOOL HW_Read(PHW_OPEN_INFO pOpenContext, PIIC_IO_DESC pInData ,PIIC_IO_DESC pOutData)
{
BOOL retVal = TRUE; // Initialize to success
DEBUGMSG (ZONE_FUNCTION, (TEXT("+HW_Read(0x%X)\r\n"),pOpenContext));
HW_SetRegister(pOpenContext);
HW_Write(pOpenContext, pInData);
ResetEvent(g_hTransferDone);
//Wait until IIC bus is free.
if(!WaitForReg((PVOID)&(g_pIICReg->IICSTAT), (1<<5), 0x0, TIMEOUT_MS_RX))
{
DEBUGMSG(ZONE_ERROR,(TEXT("[IIC ERROR]IIS BUS is busy.\r\n")));
retVal = FALSE;
goto CleanUp;
}
g_pcIIC_BUFFER = pOutData->Data;
g_uIIC_PT = 0;
g_uIIC_DATALEN = pOutData->Count;
g_pIICReg->IICCON |= (1<<7); //Ack generation Enable
g_pIICReg->IICDS = pInData->SlaveAddress;
g_pIICReg->IICSTAT = MRX_START;
if(WaitForSingleObject(g_hTransferDone, TIMEOUT_MS_RX) == WAIT_TIMEOUT)
{
DEBUGMSG(ZONE_ERROR,(TEXT("[IIC ERROR]RX Time out.\r\n")));
retVal = FALSE;
}
CleanUp:
DEBUGMSG (ZONE_FUNCTION,
(TEXT("+HW_Read(0x%X)\r\n"),
pOpenContext));
return retVal;
}
BOOL retVal = TRUE; // Initialize to success
DEBUGMSG (ZONE_FUNCTION, (TEXT("+HW_Read(0x%X)\r\n"),pOpenContext));
HW_SetRegister(pOpenContext);
HW_Write(pOpenContext, pInData);
ResetEvent(g_hTransferDone);
//Wait until IIC bus is free.
if(!WaitForReg((PVOID)&(g_pIICReg->IICSTAT), (1<<5), 0x0, TIMEOUT_MS_RX))
{
DEBUGMSG(ZONE_ERROR,(TEXT("[IIC ERROR]IIS BUS is busy.\r\n")));
retVal = FALSE;
goto CleanUp;
}
g_pcIIC_BUFFER = pOutData->Data;
g_uIIC_PT = 0;
g_uIIC_DATALEN = pOutData->Count;
g_pIICReg->IICCON |= (1<<7); //Ack generation Enable
g_pIICReg->IICDS = pInData->SlaveAddress;
g_pIICReg->IICSTAT = MRX_START;
if(WaitForSingleObject(g_hTransferDone, TIMEOUT_MS_RX) == WAIT_TIMEOUT)
{
DEBUGMSG(ZONE_ERROR,(TEXT("[IIC ERROR]RX Time out.\r\n")));
retVal = FALSE;
}
CleanUp:
DEBUGMSG (ZONE_FUNCTION,
(TEXT("+HW_Read(0x%X)\r\n"),
pOpenContext));
return retVal;
}
在HW_Write之后,等待IIC总线空闲之后才开始真正的读操作。在其他代码处经常看到在读写操作之后都会Sleep几秒,所以猜测是不是在HW_Write之后没有Sleep操作的原因导致的。于是在HW_Write之后加入Sleep(1)之后变发现读操作正常了。可能时钟设置的不同,Sleep的时间也不同,这里猜测还是时序问题导致的,不过本人没有细究下去了。以后有机会再细研究。
问题三:IIC_IST函数中对g_hTransferDone的不合理
这个问题采用博文《s3c6410 winCE6.0 IIC驱动BUG》中的解决方法即可,修改的代码如下:
if (bDone)
{
DEBUGMSG(ZONE_INFO, (TEXT("SetEvent DONE\r\n")));
bDone = FALSE; //add by jazka 2011.10.31
SetEvent(g_hTransferDone);
}
{
DEBUGMSG(ZONE_INFO, (TEXT("SetEvent DONE\r\n")));
bDone = FALSE; //add by jazka 2011.10.31
SetEvent(g_hTransferDone);
}
这样可以避免前面Master_receive情况下,多次执行SetEvent(g_hTransferDone)函数导致的不合理。