新聞中心

STM32 USB 問(wèn)題匯總

作者: 時(shí)間:2016-11-10 來(lái)源:網(wǎng)絡(luò) 收藏
以下是筆者將ST的Custom_HID例程修改為“自定義USB設(shè)備”例程時(shí)總結(jié)出來(lái)的,因?yàn)楣P者也是剛剛學(xué)USB開(kāi)發(fā)不久,某些方面理解錯(cuò)誤在所難免,請(qǐng)各位大蝦指正。

一、usb_desc.c文件

本文引用地址:http://butianyuan.cn/article/201611/317102.htm

根據(jù)你程序使用的通信方式修改。usb_desc.h文件中定義要根據(jù)usb_desc.c文件中的數(shù)組的大??;ConfigDescriptor[SIZ_CONFIG_DESC]下添加需要處理的端點(diǎn);根據(jù)需要添加或刪除報(bào)告描述符(主要用于HID)和CDC接口描述符(主要用于實(shí)現(xiàn)USB轉(zhuǎn)串口)等。具體方法可以下載個(gè)“電腦圈圈”使用D12編寫(xiě)的例子。

二、Usb_conf.h文件:

1、修改需要處理那些中斷

CNTR_CTRM 處理數(shù)據(jù)正確傳輸后控制,比如說(shuō)響應(yīng)主機(jī)

CNTR_DOVRM /* DMA OVeR/underrun Mask */

CNTR_ERRM /* ERRor Mask */

CNTR_WKUPM 0 /* WaKe UP Mask */

CNTR_SUSPM /* SUSPend Mask */

CNTR_RESETM 主要處理USB復(fù)位后進(jìn)行一些初始化任務(wù)

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寄存器中的那個(gè)USB相關(guān)中斷啟動(dòng)還是屏蔽。

2、根據(jù)需要增加端點(diǎn)緩存地址,要根據(jù)緩存區(qū)的地址修改,防止數(shù)據(jù)重疊

如下為根據(jù)每個(gè)緩沖區(qū)的大小為64字節(jié)修改:

#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。注釋掉需要處理的函數(shù)。NOP_Process表示不處理。

三usb_prop.c文件

1、修改void XX_Reset(void)(如:void Joystick_Reset(void))

一般/* Initialize Endpoint 0 */的不用修改,如下為舉例說(shuō)明端點(diǎn)1的初始化,其他端口原理一樣。

SetEPType(ENDP1, EP_INTERRUPT);//設(shè)置端點(diǎn)1類(lèi)型

/*EP_BULK 批量端點(diǎn)

EP_CONTROL 控制端點(diǎn)

EP_ISOCHRNOUS 同步端點(diǎn)

EP_INTERRUPT 中斷端點(diǎn)*/

SetEPTxAddr(ENDP1, ENDP1_TXADDR); //設(shè)置端點(diǎn)1緩沖區(qū)基地址

SetEPTxCount(ENDP1, 64);// 配置Tx 緩沖計(jì)數(shù)器

SetEPRxStatus(ENDP1, EP_RX_DIS);// //設(shè)置端點(diǎn)接收關(guān)閉

SetEPTxStatus(ENDP1, EP_TX_NAK);// //設(shè)置端點(diǎn)1發(fā)送不應(yīng)答

/*

#define EP_RX_DIS (0x0000) // EndPoint RX DISabled 端點(diǎn)接收關(guān)閉

#define EP_RX_STALL (0x1000) // EndPoint RX STALLed 端點(diǎn)接收延遲

#define EP_RX_NAK (0x2000) // EndPoint RX NAKed 端點(diǎn)接收不應(yīng)答

#define EP_RX_VALID (0x3000) // EndPoint RX VALID端點(diǎn)接收有效

#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設(shè)備就不需要以下結(jié)構(gòu)體初始化:

ONE_DESCRIPTOR Joystick_Report_Descriptor

ONE_DESCRIPTOR Mouse_Hid_Descriptor

3、修改RESULT XX_Data_Setup(u8 RequestNo)的數(shù)據(jù)類(lèi)請(qǐng)求處理。

如Custom_HID例程修改為“自定義USB設(shè)備”例程時(shí)可以將以下代碼刪除

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、刪除不相干的獲得描述符返回函數(shù)

如自定義的USB設(shè)備就不需要以下函數(shù):

Joystick_GetReportDescriptor

Joystick_GetHIDDescriptor

四、usb_endp.c文件

1、增加之前定義的中斷數(shù)據(jù)處理函數(shù)

如:

void EP1_OUT_Callback(void)

{

這些寫(xiě)接收代碼

}

五、數(shù)據(jù)發(fā)送和接收,舉例說(shuō)明

1、數(shù)據(jù)接收

u8 DataLen;

DataLen = GetEPRxCount(ENDP1);

PMAToUserBufferCopy(TX1_buffer, ENDP1_RXADDR, DataLen);

SetEPRxValid(ENDP1);

USART1_Send(DataLen);

count_out = 1;

2、數(shù)據(jù)發(fā)送

UserToPMABufferCopy(InBuffer, GetEPTxAddr(ENDP1), 64);

SetEPTxCount(ENDP1, 64);

SetEPTxValid(ENDP1);

===========================================================================

匯總2:STM32 USB 程序?qū)ULK EP改成雙緩沖機(jī)制后,一直狂飚到了1MB/S!來(lái)自:http://www.powermcu.com/bbs/viewthread.php?tid=693

前天測(cè)試自己編寫(xiě)的USB驅(qū)動(dòng)程序時(shí)候發(fā)現(xiàn)從主機(jī)到STM32的OUT傳輸(主機(jī)到設(shè)備)速率竟然只有最高33KB/S,實(shí)在是暈死了。經(jīng)過(guò)研究后發(fā)現(xiàn)是驅(qū)動(dòng)程序中設(shè)置的PIPE MaxTransferSize參數(shù)的關(guān)系,原先設(shè)置64只能33KB/S,后參考其他USB設(shè)備驅(qū)動(dòng)程序的值,設(shè)置成了65535,再測(cè)試USB OUT的速度,達(dá)到了500KB/S,終于解決了驅(qū)動(dòng)程序的瓶頸。不過(guò)算下USB 2.0全速的通訊速率是12Mb/S,排除掉CRC、令牌、SOF等等開(kāi)銷(xiāo)怎么也應(yīng)該不止最大500KB/S啊。到網(wǎng)上看了看,基本上應(yīng)該能達(dá)到600KB/S~700KB/S以上,我現(xiàn)在的速度應(yīng)該還有很大的提升才是。
看看程序,發(fā)現(xiàn)
void EP3_OUT_Callback(void)//EP3 OUT的回調(diào)函數(shù),當(dāng)EP3接收到數(shù)據(jù)時(shí)候中斷調(diào)用該函數(shù)
{
count_out = GetEPRxCount(ENDP3);//獲得接收到的數(shù)據(jù)長(zhǎng)度
PMAToUserBufferCopy(buffer_out, ENDP3_RXADDR, count_out);//將數(shù)據(jù)從USB EP3 RX的緩沖區(qū)拷貝到用戶指定的數(shù)組中
SetEPRxValid(ENDP3); //完成拷貝后置有效狀態(tài),從而EP3發(fā)送ACK主機(jī)可以進(jìn)行下一個(gè)數(shù)據(jù)包的發(fā)送
}
試著將PMAToUserBufferCopy這句注釋掉(這樣STM32就不處理接收到的數(shù)據(jù)了)后再測(cè)試速度,驚奇地發(fā)現(xiàn)速度竟然達(dá)到了997KB/S!晚上仔細(xì)想了想,數(shù)據(jù)肯定是要使用的,這個(gè)數(shù)據(jù)拷貝的過(guò)程的時(shí)間消費(fèi)總是少不了的;由于通常情況下USB設(shè)備BULK數(shù)據(jù)接收的步驟就是:接收到數(shù)據(jù),置NAK->將緩沖區(qū)數(shù)據(jù)拷貝到用戶區(qū)(用戶處理過(guò)程)->發(fā)ACK通知主機(jī)完成了完整的接收可以發(fā)送下一個(gè)->主機(jī)發(fā)送下一個(gè),按照以上的步驟USB接收一步步的進(jìn)行,只要STM32不完成數(shù)據(jù)處理,狀態(tài)就一直是NAK,主機(jī)就會(huì)不停地發(fā)送該數(shù)據(jù)包,浪費(fèi)了帶寬,因此就會(huì)導(dǎo)致我上面最大速度500KB/S難以再增加的情況!不甘心啊~~
昨天晚上又仔細(xì)研究了STM32的技術(shù)參考手冊(cè)的USB章節(jié)內(nèi)容,里面提到BULK可以采用雙緩沖機(jī)制(PING-PONG)進(jìn)行處理,正好可以解決上面的情況。雙緩沖機(jī)制的原理就是分配2塊接收緩沖,STM32的用戶處理和USB接口可以分別交替占用2個(gè)緩沖區(qū),當(dāng)USB端點(diǎn)接收數(shù)據(jù)寫(xiě)其中一個(gè)緩沖區(qū)的時(shí)候,用戶的應(yīng)用程序可以同時(shí)處理另一個(gè)緩沖區(qū),這樣緩沖區(qū)依次交換占有者,只要用戶處理程序在USB端點(diǎn)接收的時(shí)間片段內(nèi)完成處理,就能夠完全不影響USB的通訊速度!
程序部分修改
一、EP3_OUT的設(shè)置修改,
//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回調(diào)函數(shù)的修改
void EP3_OUT_Callback(void)
{
//ZYP:以下是修改成EP3雙緩沖OUT后的處理函數(shù)
if (GetENDPOINT(ENDP3) & EP_DTOG_TX)//先判斷本次接收到的數(shù)據(jù)是放在哪塊緩沖區(qū)的
{
FreeUserBuffer(ENDP3, EP_DBUF_OUT); //先釋放用戶對(duì)緩沖區(qū)的占有,這樣的話USB的下一個(gè)接收過(guò)程可以立刻進(jìn)行,同時(shí)用戶并行進(jìn)行下面處理
count_out = GetEPDblBuf0Count(ENDP3);//讀取接收到的字節(jié)數(shù)
PMAToUserBufferCopy(buffer_out, ENDP3_BUF0Addr, count_out);
}
else
{
FreeUserBuffer(ENDP3, EP_DBUF_OUT);
count_out = GetEPDblBuf1Count(ENDP3);
PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);
}
}
經(jīng)過(guò)上面的修改,終于解決了STM32在處理接收數(shù)據(jù)時(shí)導(dǎo)致主機(jī)等待的情況,用BUS HOUND軟件測(cè)試了下

哈哈,這下終于爽了。
PS:上面的FreeUserBuffer(ENDP3, EP_DBUF_OUT); 這句話的上下位置是關(guān)鍵,如果放到函數(shù)的后面,則仍舊會(huì)有主機(jī)等待STM32處理數(shù)據(jù)的情況,速度仍然是500KB/S!
把這句話放在拷貝函數(shù)的前面的話就真正把雙緩沖PING-PONG機(jī)制用起來(lái)了。大致算了下PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);這句話當(dāng)count_out為最大值64的時(shí)候STM32執(zhí)行需要302個(gè)周期,72MHZ情況下約4.2微秒執(zhí)行時(shí)間,而USB傳輸按照12Mb/s的線速度傳輸64字節(jié)的數(shù)據(jù)至少也得40微秒,因此只要PMAToUserBufferCopy的時(shí)間不超過(guò)40微秒,就不會(huì)導(dǎo)致緩沖區(qū)競(jìng)爭(zhēng)的情況。

===============================================================================

匯總3:STM32的USB中斷說(shuō)明,來(lái)自:http://bbs.ednchina.com/BLOG_ARTICLE_238817.HTM

STM32的USB模塊可以產(chǎn)生三種中斷:USB喚醒中斷、USB高優(yōu)先級(jí)中斷和USB低優(yōu)先級(jí)中斷,在STM32的參考手冊(cè)中沒(méi)有詳細(xì)說(shuō)明這三種中斷對(duì)應(yīng)哪些事件,現(xiàn)說(shuō)明如下:


1)USB喚醒中斷:在中斷向量表中的位置是42。這個(gè)中斷在USB設(shè)備從暫停模式喚醒時(shí)產(chǎn)生,喚醒事件由USB_ISTR寄存器的WKUP位標(biāo)識(shí)。


2)USB高優(yōu)先級(jí)中斷:在中斷向量表中的位置是19。這個(gè)中斷僅由USB同步(Isochronous)模式傳輸或雙緩沖塊(Bulk)傳輸模式下的正確傳輸事件產(chǎn)生,正確傳輸事件由USB_ISTR寄存器的CTR位標(biāo)識(shí)。


3)USB低優(yōu)先級(jí)中斷:在中斷向量表中的位置是20。這個(gè)中斷由所有其它的USB事件產(chǎn)生,例如正確傳輸(不包括同步模式和雙緩沖塊模式)、USB復(fù)位等,事件標(biāo)志位在USB_ISTR寄存器中。


在STM32的USB開(kāi)發(fā)包的例子中包含了上述中斷的處理,例如在USB揚(yáng)聲器的例子中,CTR_HP函數(shù)處理USB高優(yōu)先級(jí)中斷;在所有例子中都有USB_Istr()函數(shù)處理USB低優(yōu)先級(jí)中斷

===============================================================================

匯總4:如何使用STM32的USB庫(kù)支持控制端點(diǎn)0,來(lái)自:http://bbs.ednchina.com/BLOG_ARTICLE_242276.HTM

首先我們先回顧一下控制端點(diǎn)的傳輸方式:

控制端點(diǎn)的傳輸有三個(gè)階段,SETUP階段、數(shù)據(jù)階段和狀態(tài)階段;數(shù)據(jù)階段又分為數(shù)據(jù)入(DATA IN)和數(shù)據(jù)出(DATA OUT),控制端點(diǎn)傳輸可以沒(méi)有數(shù)據(jù)階段;狀態(tài)階段有狀態(tài)入(STATUS IN)和狀態(tài)出(STATUS OUT)。

總結(jié)起來(lái),控制端點(diǎn)有如下三種可能的傳輸過(guò)程(以下括號(hào)中的0或1表示DATA0或DATA1傳輸):
一、 SETUPDATA_IN(0)DATA_IN(1)DATA_IN(0)......STATUS_OUT(1)
二、 SETUPDATA_OUT(0)DATA_OUT(1)DATA_OUT(0)...... STATUS_IN(1)
三、 SETUPSTATUS_IN(1)

這里做一個(gè)約定,把上述過(guò)程一定義為“數(shù)據(jù)入過(guò)程”,過(guò)程二定義為“數(shù)據(jù)出過(guò)程”,過(guò)程三定義為“無(wú)數(shù)據(jù)過(guò)程”。所有的USB控制端點(diǎn)的數(shù)據(jù)傳輸都可以而且只用這三種傳輸過(guò)程表示。HID的SET_REPORT是數(shù)據(jù)出過(guò)程,HID的GET_REPORT是數(shù)據(jù)入過(guò)程,USB的GET DEVICE DESCRIPTOR是數(shù)據(jù)入過(guò)程,USB的SET CONFIGURATION是無(wú)數(shù)據(jù)過(guò)程,等等。



接下來(lái),我們看看STM32的USB庫(kù)是如何處理控制端點(diǎn)0的傳輸。

根據(jù)USB協(xié)議,每個(gè)SETUP包都由8個(gè)字節(jié)構(gòu)成,用戶程序可以通過(guò)結(jié)構(gòu)體Device_Info(類(lèi)型DEVICE_INFO)訪問(wèn)SETUP包的數(shù)據(jù),因?yàn)樵谡麄€(gè)的USB處理中都要用到結(jié)構(gòu)體Device_Info的內(nèi)容,庫(kù)中定義了一個(gè)全局的指針pInformation指向這個(gè)結(jié)構(gòu)體,用戶可以通過(guò)這個(gè)指針訪問(wèn)結(jié)構(gòu)體的內(nèi)容。

對(duì)應(yīng)SETUP包的8個(gè)字節(jié),用戶可以用下述方式訪問(wèn):
pInformation->USBbmRequestType (字節(jié)類(lèi)型)
pInformation->USBbRequest(字節(jié)類(lèi)型)
pInformation->USBwValue(雙字節(jié)類(lèi)型)
pInformation->USBwIndex(雙字節(jié)類(lèi)型)
pInformation->USBwLength (雙字節(jié)類(lèi)型)
使用pInformation->USBwValue0訪問(wèn)wValue的低字節(jié),pInformation->USBwValue1訪問(wèn)wValue的高字節(jié)。
使用pInformation->USBwIndex0訪問(wèn)USBwIndex的低字節(jié),pInformation->USBwIndex1訪問(wèn)USBwIndex的高字節(jié)。
使用pInformation->USBwLength0訪問(wèn)USBwLength的低字節(jié),pInformation->USBwLength1訪問(wèn)USBwLength的高字節(jié)。

通過(guò)分析SETUP包的8個(gè)字節(jié),可以判斷出一個(gè)SETUP的傳輸過(guò)程是屬于數(shù)據(jù)入過(guò)程、數(shù)據(jù)出過(guò)程還是無(wú)數(shù)據(jù)過(guò)程。STM32的USB庫(kù)中處理了所有的USB協(xié)議文本中定義的標(biāo)準(zhǔn)SETUP命令,對(duì)于USB協(xié)議文本中未定義的命令,USB庫(kù)按照數(shù)據(jù)入過(guò)程、數(shù)據(jù)出過(guò)程或無(wú)數(shù)據(jù)過(guò)程通過(guò)回調(diào)函數(shù)交給用戶程序處理。

全局變量Device_Property(DEVICE_PROP類(lèi)型)封裝了所有的回調(diào)函數(shù),DEVICE_PROP定義如下:
typedef struct _DEVICE_PROP
{
void (*Init)(void);// 設(shè)備初始化回調(diào)函數(shù)
void (*Reset)(void);// USB復(fù)位回調(diào)函數(shù)

void (*Process_Status_IN)(void);// STATUS_IN階段處理回調(diào)函數(shù)
void (*Process_Status_OUT)(void); // STATUS_OUT階段處理回調(diào)函數(shù)

RESULT (*Class_Data_Setup)(u8 RequestNo);//數(shù)據(jù)入/出過(guò)程處理回調(diào)函數(shù)
RESULT (*Class_NoData_Setup)(u8 RequestNo); //無(wú)數(shù)據(jù)過(guò)程處理回調(diào)函數(shù)

RESULT(*Class_Get_Interface_Setting)(u8 Interface, u8 AlternateSetting); // GET_INTERFACE 回調(diào)函數(shù)

u8* (*GetDeviceDescriptor)(u16 Length); // GET_DEVICE_DESCRIPTION回調(diào)函數(shù)
u8* (*GetConfigDescriptor)(u16 Length); // GET_CONFIGURATION_DESCRIPTION回調(diào)函數(shù)
u8* (*GetStringDescriptor)(u16 Length); // GET_STRING_DESCRIPTION回調(diào)函數(shù)
u8 MaxPacketSize; // 最大包長(zhǎng)度
} DEVICE_PROP;

結(jié)合SETUP的三種傳輸過(guò)程,用戶通過(guò)實(shí)現(xiàn)不同的回調(diào)函數(shù)即可完成對(duì)各種USB類(lèi)命令的處理,下面以HID的SET REPORT為例說(shuō)明。




在介紹具體實(shí)現(xiàn)之前,先介紹一下另一個(gè)回調(diào)函數(shù)CopyRoutine的概念,這個(gè)函數(shù)的原型是:
u8 *CopyRoutine(u16 length);// 返回一個(gè)緩沖區(qū)指針

USB庫(kù)通過(guò)這個(gè)函數(shù)獲得用戶的數(shù)據(jù)緩沖區(qū)地址,從而可以在數(shù)據(jù)出過(guò)程中把收到的數(shù)據(jù)拷貝到用戶緩沖區(qū),或在數(shù)據(jù)入過(guò)程中把用戶緩沖區(qū)的數(shù)據(jù)拷貝到USB發(fā)送緩沖區(qū)。每個(gè)數(shù)據(jù)出過(guò)程可能有若干次DATA_OUT傳輸,USB庫(kù)每完成一次這樣的傳輸都會(huì)調(diào)用一次回調(diào)函數(shù)CopyRoutine,參數(shù)length是本次傳輸所收到的數(shù)據(jù)字節(jié)數(shù)目,CopyRoutine必須返回一個(gè)緩沖區(qū)指針,這個(gè)緩沖區(qū)必須能夠容納length字節(jié)的數(shù)據(jù),CopyRoutine返回到USB庫(kù)之后,USB庫(kù)將把收到的數(shù)據(jù)拷貝到用戶指定的緩沖區(qū)。同樣每個(gè)數(shù)據(jù)入過(guò)程也可能有若干次DATA_IN傳輸,每次需要向主機(jī)傳輸數(shù)據(jù)時(shí),USB庫(kù)都會(huì)調(diào)用一次回調(diào)函數(shù)CopyRoutine,參數(shù)length是本次傳輸所要發(fā)送的數(shù)據(jù)字節(jié)數(shù)目,CopyRoutine必須返回一個(gè)緩沖區(qū)指針,這個(gè)緩沖區(qū)中必須包含要求的數(shù)據(jù)字節(jié),USB庫(kù)將把用戶緩沖區(qū)的數(shù)據(jù)拷貝到USB緩沖區(qū)并擇機(jī)發(fā)送出去。

當(dāng)以length=0調(diào)用CopyRoutine時(shí),CopyRoutine需要返回用戶緩沖區(qū)的長(zhǎng)度,因?yàn)镃opyRoutine的返回類(lèi)型是一個(gè)指針,所以需要通過(guò)類(lèi)型的強(qiáng)制轉(zhuǎn)換返回緩沖區(qū)長(zhǎng)度。這個(gè)功能是為了處理用戶緩沖區(qū)的長(zhǎng)度與主機(jī)SETUP數(shù)據(jù)請(qǐng)求長(zhǎng)度不符的情況,而不至于造成用戶緩沖區(qū)的溢出。




介紹完上述若干概念和回調(diào)函數(shù),再看SET_REPORT的實(shí)現(xiàn)就很容易了。

SET_REPORT是一個(gè)數(shù)據(jù)出過(guò)程,因此需要實(shí)現(xiàn)一個(gè)Class_Data_Setup回調(diào)函數(shù),示例如下:

RESULT HID_Data_Setup(u8 RequestNo)
{
u8 *(*CopyRoutine)(u16 length);
CopyRoutine = NULL;
if (pInformation->USBbmRequestType == CLASS_REQUEST|INTERFACE_RECIPIENT
&& RequestNo == SET_REPORT)
CopyRoutine = My_Data_Request;

if (CopyRoutine == NULL)
return USB_UNSUPPORT;

pInformation->Ctrl_Info.CopyData = CopyRoutine;
pInformation->Ctrl_Info.Usb_wOffset = 0;
pInformation->Usb_wLength = (*CopyRoutine)(0);

return USB_SUCCESS;
} // End of HID_Data_Setup()

u8 My_Buffer[10];
u8 *My_Data_Request(u16 length)
{
if (length == 0)
return (u8*)10;// 假定你的REPORT長(zhǎng)度和Buffer長(zhǎng)度為10

return My_Buffer;
}



上面介紹的CopyRoutine用于把多次傳輸?shù)臄?shù)據(jù)包合并到一個(gè)完整的緩沖區(qū)中,因此只有到STATUS階段才能夠指導(dǎo)一次SETUP傳輸是否結(jié)束,所以用戶程序需要在回調(diào)函數(shù)Process_Status_IN中處理從SET_REPORT接收到的數(shù)據(jù)。因?yàn)樗械幕卣{(diào)函數(shù)都是USB中斷處理的一部分,所以更好的辦法是在Process_Status_IN中設(shè)置一個(gè)標(biāo)記,然后在用戶主程序中判斷這個(gè)標(biāo)記并做處理。



注意,STM32的USB庫(kù)設(shè)計(jì)成以回調(diào)函數(shù)處理用戶命令請(qǐng)求,包含類(lèi)命令請(qǐng)求,是為了能夠清晰地區(qū)分庫(kù)程序和用戶程序,使這兩者不會(huì)混在一起,這樣的好處是非常明顯的,當(dāng)USB庫(kù)需要更新升級(jí)時(shí),只需替換掉相應(yīng)的程序模塊,而不必修改用戶已經(jīng)完成的程序。

以上的介紹都可以在STM32 USB庫(kù)的說(shuō)明手冊(cè)中找到。




上述示意代碼是以My_Buffer長(zhǎng)度為10字節(jié)為例,而USB庫(kù)的默認(rèn)包長(zhǎng)度為16字節(jié),因此My_Data_Request并沒(méi)有多包的處理。

關(guān)于多包的緩沖區(qū)處理的示意代碼可以是這樣的:

u8 *My_Data_Request(u16 length)
{
if (length == 0)
return (u8*)100;// 假定你的REPORT長(zhǎng)度和Buffer長(zhǎng)度為100

return &My_Buffer[pInformation->Ctrl_Info.Usb_wOffset];
}

這里有一個(gè)庫(kù)中使用的變量pInformation->Ctrl_Info.Usb_wOffset,這個(gè)變量回在傳輸每個(gè)數(shù)據(jù)包時(shí)候由庫(kù)中的程序按數(shù)據(jù)包長(zhǎng)度增加,如最大包長(zhǎng)為16字節(jié)時(shí),第一次調(diào)用My_Data_Request時(shí)Usb_wOffset=0,第二次調(diào)用My_Data_Request時(shí)Usb_wOffset=16,第三次調(diào)用My_Data_Request時(shí)Usb_wOffset=32,依此類(lèi)推。這樣就可以使用Usb_wOffset作為My_Buffer的下標(biāo)從My_Data_Request返回。




對(duì)于提問(wèn)“如何傳遞length?在上面沒(méi)有看到這個(gè)參數(shù)的傳遞過(guò)程”的回答:


參數(shù)length是用于檢測(cè)緩沖區(qū)長(zhǎng)度是否足夠,如果你有足夠長(zhǎng)的緩沖區(qū),可以不必檢測(cè),上述示例中使用了一個(gè)固定的緩沖區(qū),所以不必使用參數(shù)length檢測(cè)緩沖區(qū)長(zhǎng)度。


關(guān)鍵詞: STM32USB問(wèn)題匯

評(píng)論


技術(shù)專(zhuān)區(qū)

關(guān)閉