SAM9G45之USB學習筆記
最近一直在學習SAM9G45的USB功能,資料大部分都是從網上找的,還有就是官方給的庫,只是把自己除錯過程中遇到的問題記錄下來,因為沒有除錯完,所以會持續更新。
問題一(其實也不是問題),SAM9G45只有7個端點(包括端點0),我開始除錯的時候以為不帶0,哎,鬱悶。
問題二(待求證),不過從我目前的除錯來看,結論是對的。SAM9G45的端點不是全雙工了,一個端點不能同時配置為IN和OUT,但是手冊又有自相矛盾的地方。
從表38-1來看它的每個端點都支援ctrl傳輸模式,根據USB協議規定,這個傳輸模式要求端點必須是全雙工的,因為ctrl的端點必須支援messsge pipe型別。
從表38-2也能看出,CTRL模式是雙向的;
從表38-3的注意1來看,它說control transfer必須是1bank,從38-1看,bank1只能是端點0;從這裡來看其它端點是不能支援ctrl模式的。和我的實驗結論一致,也可能是我不會設定端點地址,如果哪位網友正好知道,請給我留言,謝謝。
三、Bus Hound配置問題
這個東西用的不熟悉,抓包發現只能抓取32的位元組的IN指令包,以為是裝置問題,最後發現是busHound設定問題,鬱悶。
四、這個是目前遇到最鬱悶的問題
剛開始的Demo是hid_transfer,我在這個基礎上改的CCID,並且開始只是用一個裝置+一個interface除錯的,很順利,很快就識別出來了。應用需求,需要兩個interface,改過之後怎麼也識別不了第二個裝置。Bus Hound上面,也沒有第二個裝置的報文。鬱悶,以為是配置問題,結果把配置複製帶interface0上面,正常。鬱悶了,所有的操作函式都是一套,通過介面傳引數,來確定是操作那個裝置,難道個別的函式有錯誤,引數沒有改過來?於是把所有的函式全部對了一遍,沒有錯誤啊?這是怎麼回事。最後找到原因了,原來是windows系統的驅動問題。如果一個裝置的V_ID和P_ID不修改,之前已經插入過電腦,並且正確設別並安裝驅動的話,如果再次插入(即使配置付改變了)驅動不會從新安裝,這個時候裝置出來的還是原來的裝置。
解決方法:
1、修改P_ID或者V_ID的值;
2、或者在登錄檔裡面刪掉對應的配置資訊;
我修改了P_ID值,正確識別了兩個裝置:
《-------------------------------------------------------------------------------------------------------》
官方USB協議幀學習筆記:
先上傳幾張圖片,回頭再整理:
圖1 協議棧的結構
圖2 USB的狀態機
圖3 USB的協議棧回撥函式架構
首先要知道USB的驅動狀態:
USB device states
/// The device is currently suspended.
#define USBD_STATE_SUSPENDED 0
/// USB cable is plugged into the device.
#define USBD_STATE_ATTACHED 1
/// Host is providing +5V through the USB cable.
#define USBD_STATE_POWERED 2
/// Device has been reset.
#define USBD_STATE_DEFAULT 3
/// The device has been given an address on the bus.
#define USBD_STATE_ADDRESS 4
/// A valid configuration has been selected.
#define USBD_STATE_CONFIGURED 5
deviceState(狀態變數)
其次USB的API返回值
USB device API return values
/// Indicates the operation was successful.
#define USBD_STATUS_SUCCESS 0
/// Endpoint/device is already busy.
#define USBD_STATUS_LOCKED 1
/// Operation has been aborted.
#define USBD_STATUS_ABORTED 2
/// Operation has been aborted because the device has been reset.
#define USBD_STATUS_RESET 3
/// Operation failed because parameter error
#define USBD_STATUS_INVALID_PARAMETER 4
/// Operation failed because HW not supported
#define USBD_STATUS_HW_NOT_SUPPORTED 5
端點的狀態:
Endpoint states
/// Endpoint states: Endpoint is disabled
#define UDP_ENDPOINT_DISABLED 0
/// Endpoint states: Endpoint is halted (i.e. STALLs every request)
#define UDP_ENDPOINT_HALTED 1
/// Endpoint states: Endpoint is idle (i.e. ready for transmission)
#define UDP_ENDPOINT_IDLE 2
/// Endpoint states: Endpoint is sending data
#define UDP_ENDPOINT_SENDING 3
/// Endpoint states: Endpoint is receiving data
#define UDP_ENDPOINT_RECEIVING 4
1、CCIDDriver_Initialize()
CCIDDriver_Initialize()->USBD_Init()->USBDCallbacks_Initialized()->IRQ_ConfigureIT(AT91C_ID_UDPHS, 0, UDPD_IrqHandler);//, UDPD_IrqHandler);
把UDPD_IrqHandler()配置為USB的中斷函式;
UDPD_IrqHandler()呼叫UDPHS_EndpointHandler()和UDPHS_DmaHandler()
其中:
UDPHS_EndpointHandler()->USBDCallbacks_RequestReceived()->CCID_RequestHandler()->CCID
處理和USBDDriver_RequestHandler()
UDPHS_EndpointHandler()和UDPHS_DmaHandler()都會呼叫UDPHS_EndOfTransfer()
來看下UDPHS_EndOfTransfer()
//------------------------------------------------------------------------------
/// Handles a completed transfer on the given endpoint, invoking the
/// configured callback if any.
/// \param bEndpoint Number of the endpoint for which the transfer has completed.
/// \param bStatus Status code returned by the transfer operation
//------------------------------------------------------------------------------
static void UDPHS_EndOfTransfer( unsigned char bEndpoint, char bStatus )
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = &(pEndpoint->transfer);
// Check that endpoint was sending or receiving data
if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING)
|| (pEndpoint->state == UDP_ENDPOINT_IDLE)
|| (pEndpoint->state == UDP_ENDPOINT_SENDING) ) {
TRACE_DEBUG_WP("UDPHS_EndOfTransfer ");
if(pEndpoint->state == UDP_ENDPOINT_SENDING) {
pEndpoint->sendZLP = 0;
}
// Endpoint returns in Idle state
//if(pEndpoint->state == UDP_ENDPOINT_SENDING){
pEndpoint->state = UDP_ENDPOINT_IDLE;
//}
// Invoke callback is present
if (pTransfer->fCallback != 0) {
((TransferCallback) pTransfer->fCallback)
(pTransfer->pArgument,
bStatus,
pTransfer->transferred,
pTransfer->remaining + pTransfer->buffered);
}
else {
TRACE_DEBUG_WP("No callBack\n\r");
}
}
}
當接收完成或者傳送完成,就會呼叫一個callback,這個callback是哪裡來的呢?繼續看
void CCID_SmartCardRequest_V1( uint8_t chCcidNum )
{
unsigned char bStatus;
if(chCcidNum >= CCID_INTERFACE_NUM){
TRACE_ERROR("CCID_SmartCardRequest chCcidNum >= CCID_INTERFACE_NUM\n\r");
return;
}
//do{
bStatus = CCID_Read_V1((void*)&ccidDriver.tCcidStruct[chCcidNum].sCcidCommand,
sizeof(S_ccid_bulk_out_header),
(TransferCallback)&CCIDCommandDispatcher_V1,
(void*)chCcidNum );//直接把值變為地址傳輸
//}while (bStatus != USBD_STATUS_SUCCESS);
}
這個是API層的CCID呼叫函式,//------------------------------------------------------------------------------
/// Reads data from the Data OUT endpoint
/// \param pBuffer Buffer to store the received data
/// \param dLength data buffer length
/// \param fCallback Optional callback function
/// \param pArgument Optional parameter for the callback function
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
/*
unsigned char CCID_Read(void *pBuffer,
unsigned int dLength,
TransferCallback fCallback,
void *pArgument)
{
return USBD_Read(CCID_EPT_DATA_OUT, pBuffer, dLength, fCallback, pArgument);
}*/
unsigned char CCID_Read_V1( void *pBuffer,
unsigned int dLength,
TransferCallback fCallback,
void *pArgument)
{
uint8_t chEndpointOut = 0;
if(((uint8_t)pArgument) >= CCID_INTERFACE_NUM){
TRACE_ERROR("CCID_Read chCcidNum >= CCID_INTERFACE_NUM\n\r");
return USBD_STATUS_INVALID_PARAMETER;
}
switch((uint8_t)pArgument){
#if (CCID_INTERFACE_NUM>0)
case 0:
chEndpointOut = CCID_INTERFACE0_EPT_DATA_OUT;
break;
#endif
#if (CCID_INTERFACE_NUM>1)
case 1:
chEndpointOut = CCID_INTERFACE1_EPT_DATA_OUT;
break;
#endif
#if (CCID_INTERFACE_NUM>2)
case 2:
chEndpointOut = CCID_INTERFACE2_EPT_DATA_OUT;
break;
#endif
#if (CCID_INTERFACE_NUM>3)
case 3:
chEndpointOut = CCID_INTERFACE3_EPT_DATA_OUT;
break;
#endif
}
return USBD_Read(chEndpointOut, pBuffer, dLength, fCallback, pArgument);
}
繼續看:
//------------------------------------------------------------------------------
/// Reads incoming data on an USB endpoint (OUT)
/// \param bEndpoint Index of endpoint
/// \param *pData Data to be readen
/// \param dLength Data length to be receive
/// \param fCallback Callback to be call after the success command
/// \param *pArgument Callback argument
/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS
//------------------------------------------------------------------------------
char USBD_Read( unsigned char bEndpoint,
void *pData,
unsigned int dLength,
TransferCallback fCallback,
void *pArgument )
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = &(pEndpoint->transfer);
// Return if the endpoint is not in IDLE state
if (pEndpoint->state != UDP_ENDPOINT_IDLE) {
return USBD_STATUS_LOCKED;
}
TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength);
// Endpoint enters Receiving state
pEndpoint->state = UDP_ENDPOINT_RECEIVING;
// Set the transfer descriptor
pTransfer->pData = pData;
pTransfer->remaining = dLength;
pTransfer->buffered = 0;
pTransfer->transferred = 0;
pTransfer->fCallback = fCallback;
pTransfer->pArgument = pArgument;
#ifdef DMA
// Test if endpoint type control
if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) {
#endif
// Control endpoint
// Enable endpoint IT
AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint);
AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_RX_BK_RDY;
#ifdef DMA
}
else {
TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength);
// Others endpoints (not control)
if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) {
// Transfer the max
pTransfer->buffered = DMA_MAX_FIFO_SIZE;
}
else {
// Transfer the good size
pTransfer->buffered = pTransfer->remaining;
}
AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = (unsigned int)(pTransfer->pData);
// Clear unwanted interrupts
AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS;
// Enable DMA endpoint interrupt
AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint);
TRACE_DEBUG_WP("\n\r_RR:%d ", pTransfer->remaining );
TRACE_DEBUG_WP("B:%d ", pTransfer->buffered );
TRACE_DEBUG_WP("T:%d ", pTransfer->transferred );
// DMA config
AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz
AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL =
( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT)
| AT91C_UDPHS_END_TR_EN
| AT91C_UDPHS_END_TR_IT
| AT91C_UDPHS_END_B_EN
| AT91C_UDPHS_END_BUFFIT
| AT91C_UDPHS_CHANN_ENB );
}
#endif
return USBD_STATUS_SUCCESS;
}
到這看明白了吧,是上層的CCID_SmartCardRequest_V1()通過usb_read()設定的看下端點的結構體:
//------------------------------------------------------------------------------
/// Describes the state of an endpoint of the UDP controller.
//------------------------------------------------------------------------------
typedef struct
{
/// Current endpoint state.
volatile unsigned char state;
/// Current reception bank (0 or 1).
unsigned char bank;
/// Maximum packet size for the endpoint.
unsigned short size;
/// Describes an ongoing transfer (if current state is either
/// <UDP_ENDPOINT_SENDING> or <UDP_ENDPOINT_RECEIVING>)
Transfer transfer;
/// Special case for send a ZLP
unsigned char sendZLP;
} Endpoint;
/// Describes an ongoing transfer on a UDP endpoint.
typedef struct
{
/// Pointer to a data buffer used for emission/reception.
char *pData;
/// Number of bytes which have been written into the UDP internal FIFO
/// buffers.
volatile int buffered;
/// Number of bytes which have been sent/received.
volatile int transferred;
/// Number of bytes which have not been buffered/transferred yet.
volatile int remaining;
/// Optional callback to invoke when the transfer completes.
volatile TransferCallback fCallback;
/// Optional argument to the callback function.
void *pArgument;
} Transfer;
至此,我們把自己每個端點的處理函式CCIDCommandDispatcher_V1()繫結到了相應的端點上。
問題:
1、到目前為止,有一點我不是很理解,為什麼每次收到後,需要對相應的DMA重新初始化,相應的端點也同樣做了初始化,我檢視了HID、CDC歷程,都是這麼處理的。因此在CCIDCommandDispatcher_V1()裡面我們需要呼叫usb_read()做相應初始化,或者利用查詢方式,在應用層初始化。
相關文章
- STM32-USB學習筆記(一) USB基礎筆記
- node學習筆記之39筆記
- 學習筆記之測試筆記
- Swoft 學習筆記之配置筆記
- Boltdb學習筆記之〇--概述筆記
- flask學習筆記之blueprintFlask筆記
- JS學習筆記之this指向JS筆記
- Java學習筆記之staticJava筆記
- oracle之awr學習筆記Oracle筆記
- Web之http學習筆記WebHTTP筆記
- 《筆記》之學習高併發筆記
- Vue(1)之—— Vuex學習筆記Vue筆記
- swoft 學習筆記之 response 篇筆記
- JVM學習筆記之棧區JVM筆記
- hive學習筆記之十一:UDTFHive筆記
- Golang學習筆記之方法(method)Golang筆記
- 學習筆記:DOM之appendChild筆記APP
- Java學習筆記之I/OJava筆記
- C++ 學習筆記之 引用C++筆記
- Android學習筆記之IntentAndroid筆記Intent
- Netty學習筆記之ChannelHandlerNetty筆記
- Java學習筆記之檔案Java筆記
- 安卓學習筆記之Activity(一)安卓筆記
- Java學習筆記之陣列Java筆記陣列
- Java學習筆記之繼承Java筆記繼承
- MD5之學習筆記筆記
- swift 學習筆記之陣列Swift筆記陣列
- CUDA 學習筆記之程式棧筆記
- Python學習筆記之序列Python筆記
- robot framework學習筆記之九-雜記Framework筆記
- numpy的學習筆記\pandas學習筆記筆記
- Vue學習筆記之Webpack的使用Vue筆記Web
- MongoDB 學習筆記之常用 shell 命令MongoDB筆記
- ReactNative學習筆記十一之FlatListReact筆記
- Swoft 學習筆記之 request 請求筆記
- swoft 學習筆記之驗證器筆記
- Swoft 學習筆記之控制器筆記
- go 學習筆記之工作空間Go筆記