1、枚舉第二步:設置地址
(1)重新從復位狀態(tài)開始
在第一次獲取設備描述符后,程序使端點0的發(fā)送和接收都無效,狀態(tài)也設置為STALLED,所以主機先發(fā)一個復位,使得端點0接收有效。雖然說在NAK和STALL狀態(tài)下,端點仍然可以響應和接收SETUP包。
(2)設置地址的建立階段:
主機先發(fā)一個SETUP令牌包,設備端EP0的SETUP標志置位。然后主機發(fā)了一個OUT包,共8個字節(jié),里面包含設置地址的要求。
設備在檢驗數據后,發(fā)一個ACK握手包。同時CTR_RX置位,CTR置位。數據已經保存到RxADDR所指向的緩沖區(qū)。此時USB產生數據接收中斷。
由于CTR_RX和SETUP同時置位,終端處理程序調用Setup0_Process(),所做的工作仍然是先填充pInformation結構,獲取請求特征碼、請求代碼和數據長度。
由于設置地址不會攜帶數據,所以接下來調用NoData_Setup0()。執(zhí)行以下代碼:
else if (RequestNo == SET_ADDRESS)
{
Result = USB_SUCCESS;
}
說明設置地址沒有做任何工作。
ControlState = WAIT_STATUS_IN;/* After no data stage SETUP */
USB_StatusIn(); //這句話是一個關鍵,它是一個宏,實際是準備好發(fā)送0字節(jié)的狀態(tài)數據包。因為地址設置沒有數據過程,建立階段后直接進入狀態(tài)階段,主機發(fā)IN令牌包,設備返回0字節(jié)數據包,主機再ACK。
它對應的宏是這樣的:
#define USB_StatusIn() Send0LengthData() //準備發(fā)送0字節(jié)數據
#define Send0LengthData() { _SetEPTxCount(ENDP0, 0); \
vSetEPTxStatus(EP_TX_VALID); \ //設置發(fā)送有效,發(fā)送字節(jié)數為0
}
(3)設置地址的狀態(tài)階段:
而前面把狀態(tài)設置為WAIT_STATUS_IN是給IN令牌包的處理提供指示。因為建立階段結束以后,主機接著發(fā)一個IN令牌包,設備返回0字節(jié)數據包后,進入中斷。
本次中斷由IN0_Process()函數來處理,追蹤進入,它執(zhí)行以下代碼:
else if (ControlState == WAIT_STATUS_IN)
{
if ((pInformation->USBbRequest == SET_ADDRESS) &&
(Type_Recipient==(STANDARD_REQUEST|DEVICE_RECIPIENT)))
{
SetDeviceAddress(pInformation->USBwValue0);
pUser_Standard_Requests->User_SetDeviceAddress(); //這個函數就一個賦值語句,bDeviceState = ADDRESSED。
}
(*pProperty->Process_Status_IN)(); //這是一個空函數。
ControlState = STALLED;
}
執(zhí)行設置地址操作、采用新地址后,把設備的狀態(tài)改為STALLED。而在處理的出口中調用Post0_Process()函數,這個所做的工作是:
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
//將端點0的緩沖區(qū)大小設置為64字節(jié)
if (pInformation->ControlState == STALLED)
{
vSetEPRxStatus(EP_RX_STALL);
vSetEPTxStatus(EP_TX_STALL);
}
將端點0的發(fā)送和接收都設置為:STALL,這種狀態(tài)下只接受SETUP令牌包。
2、枚舉第三步:從新地址獲取設備描述符
(1)上一階段末尾的狀態(tài)
端點0的發(fā)送和接收都設置為:STALL,只接收SETUP令牌包。
(2)建立階段:主機發(fā)令牌包、數據包、設備ACK
產生數據接收中斷,且端點0的SETUP置位,調用Setup0_Process()函數進行處理。
在Setup0_Process()中,因為主機發(fā)送了請求數據8個字節(jié)。由調用Data_Setup0()函數進行處理。首先是獲取設備描述符的長度,描述符的起始地址,傳送的最大字節(jié)數,根據這些參數確定本次能夠傳輸的字節(jié)數,然后調用DataStageIn()函數進行實際的數據傳輸操作,設備描述符必須在本次中斷中就寫入發(fā)送緩沖區(qū),因為很快就要進入數據階段了。
在函數處理的最后:
vSetEPTxStatus(EP_TX_VALID);
USB_StatusOut();/*本來期待IN令牌包,但用戶可以取消數據階段,一般不會用到*/
(3)數據階段:主機發(fā)IN包,設備返回數據,主機ACK
本次操作會產生數據發(fā)送完成中斷,由In0_Process(void)來處理中斷,它也調用DataStageIn()函數來進行處理。
如果數據已經發(fā)送完:
ControlState = WAIT_STATUS_OUT;
vSetEPTxStatus(EP_TX_STALL);
//轉入狀態(tài)階段。
有可能的話:
Send0LengthData();
ControlState = LAST_IN_DATA;
Data_Mul_MaxPacketSize = FALSE; //這一次發(fā)送0個字節(jié),狀態(tài)轉為最后輸入階段。
否則,繼續(xù)準備數據,調整剩余字節(jié)數、發(fā)送指針位置,等待主機的下一個IN令牌包。
(4)狀態(tài)階段:主機發(fā)OUT包、0字節(jié)包,設備ACK
數據發(fā)送完成中斷,調用Out0_Process(void)函數進行處理,由于在數據階段的末尾已經設置設備狀態(tài)為:WAIT_STATUS_OUT,所以處理函數基本上沒有做什么事,就退出了。并將狀態(tài)設為STALLED。
3、對配置描述符、字符串描述符獲取過程進行簡單跟蹤,過程就不再一一敘述了。
4、主機設置配置。
建立階段:主機發(fā)SETUP包、發(fā)請求數據包(DATA0包)、用戶ACK。
進入CTR中斷,用戶調用Setup0_Process()函數進行處理,取得請求數據后,由于沒有數據傳輸階段,該函數調用NoData_Setup0()函數進行處理。
判斷為設置配置后,調用Standard_SetInterface()函數將設備狀態(tài)結構體的當前配置改為主機數據中的配置參數。同時調用用戶的設置配置函數,將設備狀態(tài)改為“configured”。
退出時,將控制傳輸狀態(tài)改為:ControlState = WAIT_STATUS_IN,進入狀態(tài)階段。設備期待主機的IN令牌包,返回狀態(tài)數據。
狀態(tài)階段:主機發(fā)IN令牌、設備返回0[size=12p]Setup0_Process()函數進行處理,取得請求數據后,由于沒有數據傳輸階段,該函數調用NoData_Setup0()函數進行處理。
設置空閑時一個類特殊請求,其特征碼為0x21,2表示類請求而不是標準請求,1表示接收對象是接口而不是設備。
USB的底層并不支持類特殊請求,它將調用上層函數提供的函數:
if (Result != USB_SUCCESS)
{
Result = (*pProperty->Class_NoData_Setup)(RequestNo); //這里就是調用用戶提供的類特殊請求的處理函數。結果發(fā)現用戶提供的類特殊請求(針對無數據情況)只支持SET_PROTOCOL。針對有數據情況只支持:GET_PROTOCOL。
if ((Type_Recipient==(CLASS_REQUEST | INTERFACE_RECIPIENT))
&& (RequestNo == SET_PROTOCOL))
{
return Joystick_SetProtocol();
}
}
6、主機獲取報告描述符
建立階段:主機發(fā)SETUP包、發(fā)請求數據包(DATA0包)、用戶ACK。
進入CTR中斷,獲取描述符是一個標準請求,但是報告描述符并不是需要通用實現的,所以在底層函數中沒有實現。跟蹤Setup0_Process(void)——進入Data_Setup(void)函數,它是這么處理的:
if (Request_No == GET_DESCRIPTOR)
{
if(Type_Recipient==(STANDARD_REQUEST| EVICE_RECIPIENT))
{
u8 wValue1 = pInformation->USBwValue1;
if (wValue1 == DEVICE_DESCRIPTOR)
{
CopyRoutine = pProperty->GetDeviceDescriptor;
}
else if (wValue1 == CONFIG_DESCRIPTOR)
{
CopyRoutine = pProperty->GetConfigDescriptor;
}
else if (wValue1 == STRING_DESCRIPTOR)
{
CopyRoutine = pProperty->GetStringDescriptor;
}
/* End of GET_DESCRIPTOR */
}
}
可見核心函數只支持設備描述符、配置描述符以及字符串描述符。最終該函數將調用:
Result= (*pProperty->Class_Data_Setup)(pInformation->USBbRequest);
調用用戶的類特殊實現來獲取報告描述符,同時HID類描述符也是通過這種方式取得的。
7、主機從中斷端點讀取鼠標操作數據
主機會輪詢設備,設備數據的準備在主函數中,用Joystick_Send(JoyState())函數來實現。
Mouse_Buffer[1] = X;
Mouse_Buffer[2] = Y;
/*copy mouse position info in ENDP1 Tx Packet Memory Area*/
UserToPMABufferCopy(Mouse_Buffer, GetEPTxAddr(ENDP1), 4);
/* enable endpoint for transmission */
SetEPTxValid(ENDP1);
使能端點1的發(fā)送,當主機的IN令牌包來的時候,SIE將數據返回給主機。同時產生CTR中斷。
在中斷處理程序中,執(zhí)行下列代碼:
if ((wEPVal & EP_CTR_TX) != 0)
{
/* clear int flag */
_ClearEP_CTR_TX(EPindex);
(*pEpInt_IN[EPindex-1])();
} /* if((wEPVal & EP_CTR_TX) != 0) */
這是在函數指針數組中調用函數,跟蹤進入:發(fā)現這個函數什么也沒有做。
聯系客服