以下是筆者將ST的Custom_HID例程修改為“自定義USB設備”例程時總結出來的,因為筆者也是剛剛學USB開發不久,某些方面理解錯誤在所難免,請各位大蝦指正。
一、usb_desc.c文件
根據你程序使用的通信方式修改。usb_desc.h文件中定義要根據usb_desc.c文件中的數組的大小;ConfigDescriptor[SIZ_CONFIG_DESC]下添加需要處理的端點;根據需要添加或刪除報告描述符(主要用於HID)和CDC接口描述符(主要用於實現USB轉串口)等。具體方法可以下載個“電腦圈圈”使用D12編寫的例子。
二、Usb_conf.h文件:
1、修改需要處理那些中斷
CNTR_CTRM 處理數據正確傳輸後控制,比如說響應主機
CNTR_DOVRM /* DMA OVeR/underrun Mask */
CNTR_ERRM /* ERRor Mask */
CNTR_WKUPM 0 /* WaKe UP Mask */
CNTR_SUSPM /* SUSPend Mask */
CNTR_RESETM 主要處理USB復位後進行一些初始化任務
CNTR_SOFM /* Start Of Frame Mask */
CNTR_ESOFM /* Expected Start Of Frame Mask */
如:
usb_conf.h中的#define IMR_MSK (CNTR_CTRM | CNTR_SOFM | CNTR_RESETM )是決定USB_CNTR寄存器中的那個USB相關中斷啟動還是屏蔽。
2、根據需要增加端點緩存地址,要根據緩存區的地址修改,防止數據重疊
如下為根據每個緩沖區的大小為64字節修改:
#define ENDP1_TXADDR (0xC0)
#define ENDP1_RXADDR (0xD0)
#define ENDP2_TXADDR (0x100)
#define ENDP2_RXADDR (0x140)
#define ENDP3_TXADDR (0x180)
#define ENDP3_RXADDR (0x1C0)
3、修改/* CTR service routines */下的EPX_IN_Callback和EPX_OUT_Callback。注釋掉需要處理的函數。NOP_Process表示不處理。
三usb_prop.c文件
1、修改void XX_Reset(void)(如:void Joystick_Reset(void))
一般/* Initialize Endpoint 0 */的不用修改,如下為舉例說明端點1的初始化,其他端口原理一樣。
SetEPType(ENDP1, EP_INTERRUPT);//設置端點1類型
/*EP_BULK 批量端點
EP_CONTROL 控制端點
EP_ISOCHRNOUS 同步端點
EP_INTERRUPT 中斷端點*/
SetEPTxAddr(ENDP1, ENDP1_TXADDR); //設置端點1緩沖區基地址
SetEPTxCount(ENDP1, 64);// 配置Tx 緩沖計數器
SetEPRxStatus(ENDP1, EP_RX_DIS);// //設置端點接收關閉
SetEPTxStatus(ENDP1, EP_TX_NAK);// //設置端點1發送不應答
/*
#define EP_RX_DIS (0x0000) // EndPoint RX DISabled 端點接收關閉
#define EP_RX_STALL (0x1000) // EndPoint RX STALLed 端點接收延遲
#define EP_RX_NAK (0x2000) // EndPoint RX NAKed 端點接收不應答
#define EP_RX_VALID (0x3000) // EndPoint RX VALID端點接收有效
#define EP_TX_DIS (0x0000) //EndPoint TX DISabled
#define EP_TX_STALL (0x0010) // EndPoint TX STALLed
#define EP_TX_NAK (0x0020) // EndPoint TX NAKed
#define EP_TX_VALID (0x0030) // EndPoint TX VALID */
2、刪除不相干的描述符等。
如自定義的USB設備就不需要以下結構體初始化:
ONE_DESCRIPTOR Joystick_Report_Descriptor
ONE_DESCRIPTOR Mouse_Hid_Descriptor
3、修改RESULT XX_Data_Setup(u8 RequestNo)的數據類請求處理。
如Custom_HID例程修改為“自定義USB設備”例程時可以將以下代碼刪除
if ((RequestNo == GET_DESCRIPTOR)
&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
&& (pInformation->USBwIndex0 == 0))
{
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)
{
CopyRoutine = Joystick_GetReportDescriptor;
}
else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)
{
CopyRoutine = Joystick_GetHIDDescriptor;
}
}
4、刪除不相干的獲得描述符返回函數
如自定義的USB設備就不需要以下函數:
Joystick_GetReportDescriptor
Joystick_GetHIDDescriptor
四、usb_endp.c文件
1、增加之前定義的中斷數據處理函數
如:
void EP1_OUT_Callback(void)
{
這些寫接收代碼
}
五、數據發送和接收,舉例說明
1、數據接收
u8 DataLen;
DataLen = GetEPRxCount(ENDP1);
PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);
SetEPRxValid(ENDP1);
USART1_Send(DataLen);
count_out = 1;
2、數據發送
UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);
SetEPTxCount(ENDP1, 64);
SetEPTxValid(ENDP1);
===========================================================================
匯總2:STM32 USB 程序將BULK EP改成雙緩沖機制後,一直狂飚到了1MB/S!來自:http://www.powermcu.com/bbs/viewthread.php?tid=693
前天測試自己編寫的USB驅動程序時候發現從主機到STM32的OUT傳輸(主機到設備)速率竟然只有最高33KB/S,實在是暈死了。經過研究後發現是驅動程序中設置的PIPE MaxTransferSize參數的關系,原先設置64只能33KB/S,後參考其他USB設備驅動程序的值,設置成了65535,再測試USB OUT的速度,達到了500KB/S,終於解決了驅動程序的瓶頸。不過算下USB 2.0全速的通訊速率是12Mb/S,排除掉CRC、令牌、SOF等等開銷怎麼也應該不止最大500KB/S啊。到網上看了看,基本上應該能達到600KB/S~700KB/S以上,我現在的速度應該還有很大的提升才是。
看看程序,發現
void EP3_OUT_Callback(void)//EP3 OUT的回調函數,當EP3接收到數據時候中斷調用該函數
{
count_out = GetEPRxCount(ENDP3);//獲得接收到的數據長度
PMAToUserBufferCopy(buffer_out, ENDP3_RXADDR, count_out);//將數據從USB EP3 RX的緩沖區拷貝到用戶指定的數組中
SetEPRxValid(ENDP3); //完成拷貝後置有效狀態,從而EP3發送ACK主機可以進行下一個數據包的發送
}
試著將PMAToUserBufferCopy這句注釋掉(這樣STM32就不處理接收到的數據了)後再測試速度,驚奇地發現速度竟然達到了997KB/S!晚上仔細想了想,數據肯定是要使用的,這個數據拷貝的過程的時間消費總是少不了的;由於通常情況下USB設備BULK數據接收的步驟就是:接收到數據,置NAK->將緩沖區數據拷貝到用戶區(用戶處理過程)->發ACK通知主機完成了完整的接收可以發送下一個->主機發送下一個,按照以上的步驟USB接收一步步的進行,只要STM32不完成數據處理,狀態就一直是NAK,主機就會不停地發送該數據包,浪費了帶寬,因此就會導致我上面最大速度500KB/S難以再增加的情況!不甘心啊~~
昨天晚上又仔細研究了STM32的技術參考手冊的USB章節內容,裡面提到BULK可以采用雙緩沖機制(PING-PONG)進行處理,正好可以解決上面的情況。雙緩沖機制的原理就是分配2塊接收緩沖,STM32的用戶處理和USB接口可以分別交替占用2個緩沖區,當USB端點接收數據寫其中一個緩沖區的時候,用戶的應用程序可以同時處理另一個緩沖區,這樣緩沖區依次交換占有者,只要用戶處理程序在USB端點接收的時間片段內完成處理,就能夠完全不影響USB的通訊速度!
程序部分修改
一、EP3_OUT的設置修改,
//ZYP:修改EP3為BULK雙緩沖方式-------------------------
SetEPType(ENDP3, EP_BULK);
SetEPDoubleBuff(ENDP3);
SetEPDblBuffAddr(ENDP3, ENDP3_BUF0Addr, ENDP3_BUF1Addr);
SetEPDblBuffCount(ENDP3, EP_DBUF_OUT, VIRTUAL_COM_PORT_DATA_SIZE);
ClearDTOG_RX(ENDP3);
ClearDTOG_TX(ENDP3);
ToggleDTOG_TX(ENDP3);
SetEPRxStatus(ENDP3, EP_RX_VALID);
SetEPTxStatus(ENDP3, EP_TX_DIS);
//------------------------------------------------------
二、EP3_OUT回調函數的修改
void EP3_OUT_Callback(void)
{
//ZYP:以下是修改成EP3雙緩沖OUT後的處理函數
if (GetENDPOINT(ENDP3) & EP_DTOG_TX)//先判斷本次接收到的數據是放在哪塊緩沖區的
{
FreeUserBuffer(ENDP3, EP_DBUF_OUT); //先釋放用戶對緩沖區的占有,這樣的話USB的下一個接收過程可以立刻進行,同時用戶並行進行下面處理
count_out = GetEPDblBuf0Count(ENDP3);//讀取接收到的字節數
PMAToUserBufferCopy(buffer_out, ENDP3_BUF0Addr, count_out);
}
else
{
FreeUserBuffer(ENDP3, EP_DBUF_OUT);
count_out = GetEPDblBuf1Count(ENDP3);
PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);
}
}