最近在调试Camera驱动时候,发现通过IIC读写Camera设备时,总是出现问题。跟踪调试发现,对Camera设备的写操作基本不会出现问题,但是读操作有时候正常,有时候不正常。从串口输入调试信息发现,读操作总是出现“ACK NOT received”,在分析这部分源码时,发现此处的逻辑有些问题,同时从网上查找到了有人也遇到了这个问题,不过对于网上的分析,本人有一些不同的看法,这里描述一下。如果有不正确的地方,望指正。
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

       先给出网络上对这一问题分析的文章《s<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />3c6410 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));
}
    在 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==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;
 
       问题二:读操作时常出现失败

       上面问题一提到了,虽然总是提示 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;
}
       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);
}
       这样可以避免前面 Master_receive 情况下,多次执行 SetEvent(g_hTransferDone) 函数导致的不合理。