最近在調試Camera驅動時候,發現通過IIC讀寫Camera裝置時,總是出現問題。跟蹤調試發現,對Camera裝置的寫操作基本不會出現問題,但是讀操作有時候正常,有時候不正常。從序列槽輸入調試資訊發現,讀操作總是出現“ACK NOT received”,在分析這部分源碼時,發現此處的邏輯有些問題,同時從網上查找到了有人也遇到了這個問題,不過對于網上的分析,本人有一些不同的看法,這裡描述一下。如果有不正确的地方,望指正。
其次給出本人的平台環境: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即可,如下:
//g_uIIC_PT++; //delete by jazka 2011.10.31
g_uIIC_PT++; //add by jazka 2011.10.31
問題二:讀操作時常出現失敗
上面問題一提到了,雖然總是提示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")));
CleanUp:
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)函數導緻的不合理。
本文轉自jazka 51CTO部落格,原文連結:http://blog.51cto.com/jazka/702584,如需轉載請自行聯系原作者