1、枚舉第二步:設(shè)置地址 (1)重新從復(fù)位狀態(tài)開始 在第一次獲取設(shè)備描述符后,程序使端點(diǎn)0的發(fā)送和接收都無效,狀態(tài)也設(shè)置為STALLED,所以主機(jī)先發(fā)一個復(fù)位,使得端點(diǎn)0接收有效。雖然說在NAK和STALL狀態(tài)下,端點(diǎn)仍然可以響應(yīng)和接收SETUP包。 (2)設(shè)置地址的建立階段: 主機(jī)先發(fā)一個SETUP令牌包,設(shè)備端EP0的SETUP標(biāo)志置位。然后主機(jī)發(fā)了一個OUT包,共8個字節(jié),里面包含設(shè)置地址的要求。 設(shè)備在檢驗(yàn)數(shù)據(jù)后,發(fā)一個ACK握手包。同時CTR_RX置位,CTR置位。數(shù)據(jù)已經(jīng)保存到RxADDR所指向的緩沖區(qū)。此時USB產(chǎn)生數(shù)據(jù)接收中斷。 由于CTR_RX和SETUP同時置位,終端處理程序調(diào)用Setup0_Process(),所做的工作仍然是先填充pInformation結(jié)構(gòu),獲取請求特征碼、請求代碼和數(shù)據(jù)長度。 由于設(shè)置地址不會攜帶數(shù)據(jù),所以接下來調(diào)用NoData_Setup0()。執(zhí)行以下代碼:
else if (RequestNo ==SET_ADDRESS)
{
Result = USB_SUCCESS;
}
說明設(shè)置地址沒有做任何工作。
ControlState =WAIT_STATUS_IN;
USB_StatusIn(); //這句話是一個關(guān)鍵,它是一個宏,實(shí)際是準(zhǔn)備好發(fā)送0字節(jié)的狀態(tài)數(shù)據(jù)包。因?yàn)榈刂吩O(shè)置沒有數(shù)據(jù)過程,建立階段后直接進(jìn)入狀態(tài)階段,主機(jī)發(fā)IN令牌包,設(shè)備返回0字節(jié)數(shù)據(jù)包,主機(jī)再ACK。
它對應(yīng)的宏是這樣的: #defineUSB_StatusIn() Send0LengthData() //準(zhǔn)備發(fā)送0字節(jié)數(shù)據(jù) #defineSend0LengthData() { _SetEPTxCount(ENDP0, 0);\
vSetEPTxStatus(EP_TX_VALID); \//設(shè)置發(fā)送有效,發(fā)送字節(jié)數(shù)為0
}
(3)設(shè)置地址的狀態(tài)階段: 而前面把狀態(tài)設(shè)置為WAIT_STATUS_IN是給IN令牌包的處理提供指示。因?yàn)榻㈦A段結(jié)束以后,主機(jī)接著發(fā)一個IN令牌包,設(shè)備返回0字節(jié)數(shù)據(jù)包后,進(jìn)入中斷。 本次中斷由IN0_Process()函數(shù)來處理,追蹤進(jìn)入,它執(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();//這個函數(shù)就一個賦值語句,bDeviceState =ADDRESSED。
}
(*pProperty->Process_Status_IN)();//這是一個空函數(shù)。
ControlState = STALLED;
}
執(zhí)行設(shè)置地址操作、采用新地址后,把設(shè)備的狀態(tài)改為STALLED。而在處理的出口中調(diào)用Post0_Process()函數(shù),這個所做的工作是:
SetEPRxCount(ENDP0,Device_Property.MaxPacketSize);
//將端點(diǎn)0的緩沖區(qū)大小設(shè)置為64字節(jié)
if (pInformation->ControlState ==STALLED)
{
vSetEPRxStatus(EP_RX_STALL);
vSetEPTxStatus(EP_TX_STALL);
}
將端點(diǎn)0的發(fā)送和接收都設(shè)置為:STALL,這種狀態(tài)下只接受SETUP令牌包。 2、枚舉第三步:從新地址獲取設(shè)備描述符 (1)上一階段末尾的狀態(tài) 端點(diǎn)0的發(fā)送和接收都設(shè)置為:STALL,只接收SETUP令牌包。 (2)建立階段:主機(jī)發(fā)令牌包、數(shù)據(jù)包、設(shè)備ACK 產(chǎn)生數(shù)據(jù)接收中斷,且端點(diǎn)0的SETUP置位,調(diào)用Setup0_Process()函數(shù)進(jìn)行處理。 在Setup0_Process()中,因?yàn)橹鳈C(jī)發(fā)送了請求數(shù)據(jù)8個字節(jié)。由調(diào)用Data_Setup0()函數(shù)進(jìn)行處理。首先是獲取設(shè)備描述符的長度,描述符的起始地址,傳送的最大字節(jié)數(shù),根據(jù)這些參數(shù)確定本次能夠傳輸?shù)淖止?jié)數(shù),然后調(diào)用DataStageIn()函數(shù)進(jìn)行實(shí)際的數(shù)據(jù)傳輸操作,設(shè)備描述符必須在本次中斷中就寫入發(fā)送緩沖區(qū),因?yàn)楹芸炀鸵M(jìn)入數(shù)據(jù)階段了。 在函數(shù)處理的最后:
vSetEPTxStatus(EP_TX_VALID);
USB_StatusOut();
(3)數(shù)據(jù)階段:主機(jī)發(fā)IN包,設(shè)備返回?cái)?shù)據(jù),主機(jī)ACK 本次操作會產(chǎn)生數(shù)據(jù)發(fā)送完成中斷,由In0_Process(void)來處理中斷,它也調(diào)用DataStageIn()函數(shù)來進(jìn)行處理。 如果數(shù)據(jù)已經(jīng)發(fā)送完:
ControlState =WAIT_STATUS_OUT;
vSetEPTxStatus(EP_TX_STALL);
//轉(zhuǎn)入狀態(tài)階段。
有可能的話:
Send0LengthData();
ControlState =LAST_IN_DATA;
Data_Mul_MaxPacketSize = FALSE;//這一次發(fā)送0個字節(jié),狀態(tài)轉(zhuǎn)為最后輸入階段。
否則,繼續(xù)準(zhǔn)備數(shù)據(jù),調(diào)整剩余字節(jié)數(shù)、發(fā)送指針位置,等待主機(jī)的下一個IN令牌包。 (4)狀態(tài)階段:主機(jī)發(fā)OUT包、0字節(jié)包,設(shè)備ACK 數(shù)據(jù)發(fā)送完成中斷,調(diào)用Out0_Process(void)函數(shù)進(jìn)行處理,由于在數(shù)據(jù)階段的末尾已經(jīng)設(shè)置設(shè)備狀態(tài)為:WAIT_STATUS_OUT,所以處理函數(shù)基本上沒有做什么事,就退出了。并將狀態(tài)設(shè)為STALLED。 3、對配置描述符、字符串描述符獲取過程進(jìn)行簡單跟蹤,過程就不再一一敘述了。 4、主機(jī)設(shè)置配置。 建立階段:主機(jī)發(fā)SETUP包、發(fā)請求數(shù)據(jù)包(DATA0包)、用戶ACK。 進(jìn)入CTR中斷,用戶調(diào)用Setup0_Process()函數(shù)進(jìn)行處理,取得請求數(shù)據(jù)后,由于沒有數(shù)據(jù)傳輸階段,該函數(shù)調(diào)用NoData_Setup0()函數(shù)進(jìn)行處理。 判斷為設(shè)置配置后,調(diào)用Standard_SetInterface()函數(shù)將設(shè)備狀態(tài)結(jié)構(gòu)體的當(dāng)前配置改為主機(jī)數(shù)據(jù)中的配置參數(shù)。同時調(diào)用用戶的設(shè)置配置函數(shù),將設(shè)備狀態(tài)改為“configured”。 退出時,將控制傳輸狀態(tài)改為:ControlState =WAIT_STATUS_IN,進(jìn)入狀態(tài)階段。設(shè)備期待主機(jī)的IN令牌包,返回狀態(tài)數(shù)據(jù)。 狀態(tài)階段:主機(jī)發(fā)IN令牌、設(shè)備返回0[size=12p]Setup0_Process()函數(shù)進(jìn)行處理,取得請求數(shù)據(jù)后,由于沒有數(shù)據(jù)傳輸階段,該函數(shù)調(diào)用NoData_Setup0()函數(shù)進(jìn)行處理。 設(shè)置空閑時一個類特殊請求,其特征碼為0x21,2表示類請求而不是標(biāo)準(zhǔn)請求,1表示接收對象是接口而不是設(shè)備。 USB的底層并不支持類特殊請求,它將調(diào)用上層函數(shù)提供的函數(shù):
if (Result !=USB_SUCCESS)
{
Result =(*pProperty->Class_NoData_Setup)(RequestNo); //這里就是調(diào)用用戶提供的類特殊請求的處理函數(shù)。結(jié)果發(fā)現(xiàn)用戶提供的類特殊請求(針對無數(shù)據(jù)情況)只支持SET_PROTOCOL。針對有數(shù)據(jù)情況只支持:GET_PROTOCOL。
if ((Type_Recipient==(CLASS_REQUEST |INTERFACE_RECIPIENT))
&& (RequestNo== SET_PROTOCOL))
{
returnJoystick_SetProtocol();
}
}
6、主機(jī)獲取報(bào)告描述符 建立階段:主機(jī)發(fā)SETUP包、發(fā)請求數(shù)據(jù)包(DATA0包)、用戶ACK。 進(jìn)入CTR中斷,獲取描述符是一個標(biāo)準(zhǔn)請求,但是報(bào)告描述符并不是需要通用實(shí)現(xiàn)的,所以在底層函數(shù)中沒有實(shí)現(xiàn)。跟蹤Setup0_Process(void)——進(jìn)入Data_Setup(void)函數(shù),它是這么處理的:
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;
}
}
}
可見核心函數(shù)只支持設(shè)備描述符、配置描述符以及字符串描述符。最終該函數(shù)將調(diào)用:
Result=(*pProperty->Class_Data_Setup)(pInformation->USBbRequest);
調(diào)用用戶的類特殊實(shí)現(xiàn)來獲取報(bào)告描述符,同時HID類描述符也是通過這種方式取得的。 7、主機(jī)從中斷端點(diǎn)讀取鼠標(biāo)操作數(shù)據(jù) 主機(jī)會輪詢設(shè)備,設(shè)備數(shù)據(jù)的準(zhǔn)備在主函數(shù)中,用Joystick_Send(JoyState())函數(shù)來實(shí)現(xiàn)。
Mouse_Buffer[1] = X;
Mouse_Buffer[2] = Y;
UserToPMABufferCopy(Mouse_Buffer, GetEPTxAddr(ENDP1),4);
SetEPTxValid(ENDP1);
使能端點(diǎn)1的發(fā)送,當(dāng)主機(jī)的IN令牌包來的時候,SIE將數(shù)據(jù)返回給主機(jī)。同時產(chǎn)生 CTR中斷。 在中斷處理程序中,執(zhí)行下列代碼:
if ((wEPVal & EP_CTR_TX) !=0)
{
_ClearEP_CTR_TX(EPindex);
(*pEpInt_IN[EPindex-1])();
}
這是在函數(shù)指針數(shù)組中調(diào)用函數(shù),跟蹤進(jìn)入:發(fā)現(xiàn)這個函數(shù)什么也沒有做。 經(jīng)過對程序執(zhí)行過程的跟蹤和分析,我現(xiàn)在對USB設(shè)備HID類的工作有了大概的了解,對ST的USB庫的工作也有了初步的概念。把所有文件的源代碼粗略地瀏覽了一遍,心里大概有了些底。但現(xiàn)在我還不準(zhǔn)備閱讀源代碼,我先把例程在智林開發(fā)板上移植好,再詳細(xì)的閱讀一遍源代碼。
|