在OHCI的體系下,判斷數據是否傳輸完畢是需要通過中斷程序來判斷的,當USB主機設置了HcControl和HcCommandStatus寄存器開始傳輸數據後,AM9200
自動開始數據傳輸,並且定期的檢查HcDoneHead寄存器的內容,並且將其轉移到HCCA.DoneHead。然後產生中斷,觸發中斷處理程序。
在中斷處理程序中,需要檢查HcInterruptStatus寄存器的內容,判斷WDH位是否為1,以便確定是否有TD被處理完畢。一般來說,其余的中斷狀態位不用理會。當發現有TD被處理完畢,則還需要判斷已經完成的TD是否是當前傳輸命令的最後一個TD,如果是則標志命令執行結束,上層程序可以進行後續處理。
/**
* OHCI中斷處理程序
*/
void AT91F_UHP_Handler(void)
{
unsigned int status;
unsigned char idx;
unsigned char cc;
//unsigned
int control;
//得到HcInterruptStatus寄存器的內容
status = ohciGetIntrStatus();
//檢查WDH位,判斷是否有TD傳輸完畢
if ((status & OHCI_HC_INTR_WDH) != 0)
{
//根據當前執行的命令類型,確定TD的數量
switch(usbCmdState.cmdType)
{
case USB_CMD_TYPE_BULK_WRITE:
case USB_CMD_TYPE_BULK_READ:
idx = 3;
break;
case USB_CMD_TYPE_CTRL_READ:
idx = 2;
break;
case USB_CMD_TYPE_CTRL_WRITE:
idx = 1;
break;
default:
usbCmdState.state = USB_CMD_OVER;
ohciClearIntrStatus();
return ;
}
//取得當前完成的TD的Complete Code值
cc = getTdCC(ohciGetHccaDoneHead());
//判斷當前完成的TD是否是命令的最後一個TD
if (ohciGetHccaDoneHead() ==
usbGetLastTdAddr(idx))
{
usbCmdState.cmdResult = cc;
usbCmdState.state = USB_CMD_OVER;
}
else
{
//當前TD不是最後一個TD,但是執行失敗,不會繼續處理TD列表,因此需要返回
if (cc)
{
usbCmdState.cmdResult = cc;
usbCmdState.state = USB_CMD_OVER;
}
else
//當前TD不是最後一個TD,等待繼續處理
usbCmdState.state++;
}
}
//清除HcInterruptStatus寄存器的內容,以便能夠產生新的中斷
ohciClearIntrStatus();
}
在最初的代碼中,不是通過中斷來判斷TD數據是否處理完畢的,而是直接調用ohciGetIntrStatus()函數並判斷返回值的,但是實際調試時發現這樣不能正確得到TD數據處理完畢的信息。通過對中斷程序的實際調試發現,因為ED會帶有多個TD,AM9200在處理的時候可能是處理速度的原因,會產生1個或多個中斷,因此在中斷處理程序中需要判斷當前結束的TD是否是當前命令的最後一個TD,這樣才能確保整個ED處理完畢。
說實話,我不認為上面的判斷ED隊列執行完畢的方法是好的方法,本來我一直以為會有一個寄存器,在ED隊列處理完畢的時候會跳出來告訴我說隊列執行完畢了,可是找了半天也沒有找到,只好采用這個笨方法了。
本文出自 “rainman” 博客,請務必保留此出處http://lancelot.blog.51cto.com/393579/261181