
SETUP事件正确接收后,根据该事件提供的请求类型进行对主机的响应。SETUP数据结构的wLength字段说明的是请求返回或者提供的数据长度。 ) w& b" @. E9 T: n" b 如果判断出的请求信息错误或者说不被支持,STM32 USB设备需要中断此次请求: 7 z! [! ] ]* \( A1 j4 B) ? c SetEPR_RXStatus(ENDP0, EP_RX_VALID); " p0 i: s/ h& T9 u3 o SetEPR_TXStatus(ENDP0, EP_TX_STALL); 6 q3 n" H* ]2 b4 V: {* ? 正确获取到请求信息后,如果wLength为0,设备需要发送一个0长度数据包以响应主机: $ C8 |$ r1 T4 t* f // ***************************************************************************** // Function Name : SETUP0_Trans0Data + l V2 J6 b; N& t. c // Description : // Input : ' U( b0 Q2 l2 V/ ?6 H4 c' l/ s // Output : 0 f0 A# I- U- ~; ]3 d. m( R // Return : // ***************************************************************************** ! k9 E: j+ b2 p+ }: t RESULT SETUP0_Trans0Data(void) { // Send 0-length data frame as ACK to host SetBuffDescTable_TXCount(ENDP0, 0); SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_VALID); 9 Z! o0 R4 C3 q7 e" }( a% j, a return RESULT_SUCCESS; } " k1 d7 |. F8 t* k3 Z4 Z 如果wLength不为0,设备则需要根据请求的数据长度发送数据包以响应主机: // ***************************************************************************** // Function Name : SETUP0_TransData // Description : // Input : 1 w) J q8 w. O0 ?5 H Q9 Z( G; C // Output : ' ?5 H) b6 v& _! a // Return : & k3 X7 d/ O& A4 l1 B! V // ***************************************************************************** , y+ P3 W4 w5 _& k i RESULT SETUP0_TransData(void) / L, s8 n9 O1 Q; x4 W+ T4 G) V* } { * T! o4 y6 g( W Z unsigned short wLength = vsDeviceInfo.TransInfo.wLength; unsigned short wOffset = vsDeviceInfo.TransInfo.wOffset; 6 L/ S% F% h" x% s8 j K2 G unsigned short wMaxSize = vsDeviceInfo.TransInfo.wPacketSize; if(wLength) / @) q: ?+ U, i9 D; X( u { 3 d Z; J b5 w if(wLength > wMaxSize) 1 F7 J+ `0 t$ X* D7 ] { wLength = wMaxSize; ( o( M. R5 `0 ]- N5 i r } 3 S s- q3 F0 J& F7 s) t // Copy the transfer buffer to the endpoint0's buffer BufferCopy_UserToPMA( vsDeviceInfo.TransInfo.pBuffer+wOffset, // transfer buffer & v8 W- Z8 c. i8 V GetBuffDescTable_TXAddr(ENDP0), // endpoint 0 TX address . W: Q$ o" N7 w0 Y7 J wLength); SetBuffDescTable_TXCount(ENDP0, wLength); 0 I( |% S9 l' z+ | SetEPR_RXStatus(ENDP0, EP_RX_NAK); 2 h/ E: `9 Q: M' s. _ SetEPR_TXStatus(ENDP0, EP_TX_VALID); // Update the data lengths vsDeviceInfo.TransInfo.wLength -= wLength; " ^' } d8 M8 Z6 _# y6 p6 t4 s2 X vsDeviceInfo.TransInfo.wOffset += wLength; 8 J H+ d8 D9 `; b; x* d' n7 O return RESULT_LASTDATA; # z7 w7 B" u( R- R- w3 L' c } 1 e8 I4 _/ i* T7 [) R8 L+ _( d. M/ P" Z - j2 ?, z4 U% Q: L3 {3 t( ] return RESULT_SUCCESS; . y$ [/ @. {0 @+ E) P* c0 C( {4 k& S } 如果发送的数据长度大于端点设置的最大数据包长度,数据将分割为若干次发送,记录发送数据的状态包含在结构体TRANSFER_INFO中: // ***************************************************************************** 1 J+ ~) ^/ O* A6 P# t# H. j; m // TRANSFER_INFO & n# a! M1 q, F# P" W/ _ // ***************************************************************************** typedef struct _TRANSFER_INFO 9 K% h: R; d5 ~0 y { : h7 n+ a1 O9 k. T6 L/ u unsigned short wLength; // total lengths data will be transmit unsigned short wOffset; // number of data be transmited unsigned short wPacketSize; // endpoints packet max size 0 _. H8 e7 n! Q unsigned char* pBuffer; // address of data buffer 8 q3 u' `+ g/ H( K# G( X } % C* G& o. M, O/ T. }2 d3 X: u TRANSFER_INFO, *PTRANSFER_INFO; TRANSFER_INFO.wLength记录发送的数据长度,如果非0,表示有数据需要被发送。 7 h4 Q" c1 X. G% N& u, B, M TRANSFER_INFO.wOffset记录已发送的数据长度,用以确定数据缓冲TRANSFER_INFO.pBuffer的偏移量。 " ]4 {9 C4 A ]8 T" u 需要了解的一点:USB主机向USB设备正确发送一请求后(这部分的处理由硬件完成),USB主机将间隔若干次的向USB设备索取响应数据,STM32 USB TX状态为NAK说明不响应USB主机,USB主机在超时后退出此次请求;TX状态为STLL说明中断此次请求,USB主机将无条件退出请求;TX状态为 VALID说明设备已准备好数据发送,USB主机将从USB设备读取数据。 以非0长度数据请求的GET_DESCRIPTOR请求为例的响应过程: CTR_SETUP0()->SETUP0_Data()->SR_GetDescriptor()->SETUP0_TransData() RESULT SR_GetDescriptor(void) { // RequestType: device->host, standard request and device recipient if(vsDeviceInfo.SetupData.bmRequestType == RT_D2H_STANDARD_DEVICE) { . A, n: w7 l& F9 ]* }7 z% Y, j3 v // SetupData.wValue.b.MSB: descriptor type 4 n5 F- \4 }0 Z9 j* a // SetupData.wValue.b.LSB: descriptor index switch(vsDeviceInfo.SetupData.wValue.b.MSB) { case DESCRIPTOR_DEVICE: return SR_GetDescriptor_Device(); case DESCRIPTOR_CONFIG: return SR_GetDescriptor_Config(); case DESCRIPTOR_STRING: return SR_GetDescriptor_String(); - J& h, M; x- j6 H- \6 r: k6 R6 r default: return RESULT_UNSUPPORT; } 5 L1 T: t) h) i# p" L9 {0 W } 8 l) O) A$ _# s return RESULT_UNSUPPORT; 2 Z8 S3 h6 _0 N! L& m* @ } GET_DESCRIPTOR请求属于USB协议中的标准请求(standard request)并且数据方向为设备至主机(device->host),分设备描述符、配置描述符、字符串描述符三种。已设备描述符为例: ' C* e/ h+ q6 p9 r- \' X RESULT SR_GetDescriptor_Device(void) ! U' L; }4 n# v4 F { // Assigned the device descriptor to the transfer 2 c/ ^) h b5 l' Z5 v# O vsDeviceInfo.TransInfo.wOffset = 0; 8 _' I; T8 w% j1 a ? vsDeviceInfo.TransInfo.wPacketSize = ENDP0_PACKETSIZE; 3 R1 U- I( v3 v: Y- b3 B# X5 t7 X vsDeviceInfo.TransInfo.pBuffer = DescBuffer_Device.pBuff; & {9 J1 T9 A7 y2 m# n+ l8 h1 @ vsDeviceInfo.TransInfo.wLength = DescBuffer_Device.wLen; vsDeviceInfo.eControlState = CS_GET_DESCRIPTOR; ) y( z( x8 U' t4 c9 X if(vsDeviceInfo.TransInfo.wLength > vsDeviceInfo.SetupData.wLength.w) 9 X1 x1 y7 _, K4 u6 q# N6 Z { " O7 w6 {1 b8 L$ ^& }' l vsDeviceInfo.TransInfo.wLength = vsDeviceInfo.SetupData.wLength.w; } & Q" W- h- n6 `2 O6 T7 h" x return SETUP0_TransData(); } # p% H3 k2 l; d7 O! c( r/ j- F 这里说明了发送数据的长度、缓冲、偏移、端点包大小以及当前的控制状态,并说明了如果发送的数据长度超出请求的数据长度,则将舍弃超出的部分。数据配置好后,调用SETUP0_TransData()进行数据发送。 $ R( F% r3 ?- f% P# y 在USB主机查询到USB设备准备就绪后,将读取出这些数据,完成后,USB设备将产生IN事件,此时将响应CTR_IN0()函数: // ***************************************************************************** // Function Name : CTR_IN , A# R; k; ]/ w+ N0 v) i // Description : % W* U& W$ L. Z- f) b. g // Input : " D& I( y' p6 W4 R6 s2 }6 l // Output : 8 K2 q3 H3 L% `) R3 D! ^3 i0 x& [' U% w // Return : 7 F/ b" x& G1 I; D- w // ***************************************************************************** void CTR_IN0(void) { switch(vsDeviceInfo.eControlState) , m7 @/ a" h$ z) P6 v# i* J0 Y' S { case CS_GET_DESCRIPTOR: if(SETUP0_TransData() == RESULT_SUCCESS) { 1 [7 x$ i2 c1 u8 D/ J9 @, p% C SetEPR_TXStatus(ENDP0, EP_TX_NAK); 6 s# m2 l z1 U" v$ v7 \5 n SetEPR_RXStatus(ENDP0, EP_RX_VALID); 8 Q0 `7 Z8 M9 t. _) z } break; 5 x4 O7 v: W& u# ~ case CS_SET_ADDRESS: $ v% g3 j7 d6 r) I9 R SetEPR_TXStatus(ENDP0, EP_TX_NAK); SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); " o; e$ d" u8 `" j6 ^+ t vsDeviceInfo.eDeviceState = DS_ADDRESSED; + d* ~' p% U. V; Q$ S# B break; case CS_SET_CONFIGURATION: * d7 j: m! x y& ~& q+ U c' d+ } b SetEPR_TXStatus(ENDP0, EP_TX_NAK); SetEPR_RXStatus(ENDP0, EP_RX_VALID); 4 V3 V5 y( D8 d' Y$ q % d( m8 B; k* m2 K; I' J vsDeviceInfo.eDeviceState = DS_CONFIGURED; break; default: 1 R8 o: |, {$ ^0 ?( B break; } , ~8 g6 L' {! y, a6 \ } 再这如果响应GET_DESCRIPTOR请求发送的数据如果全部发送完毕,SETUP0_TransData()返回RESULT_SUCCESS,并设置TX状态为NAK;否则返回RESULT_LASTDATA,将继续发送剩余的数据直到数据全部被发送。至此,整个的GET_DESCRIPTOR请求过程完成。 : F2 L0 _; F/ w, Q- o" x+ y 0长度的数据请求在发送0长度数据响应后,因为不存在可能还未传送的数据,因而IN事件后直接结束此次请求。 % W7 \8 X! U s 在数据方向为USB主机->USB设备时,如果正确接收到数据,将响应CTR_OUT0()函数,处理过程类同CTR_IN0()函数。 在USB设备的枚举过程中,USB的一些描述符数据结构需要了解,具体在USB协议中有详细的说明,在usb_desc(.c/.h)文件中,定义了这些结构,这些结构是特定的: 设备描述符:长度、格式固定,其中VENDOR_ID与PRODUCT_ID决定上位机驱动的识别。设备分属类别决定了设备的性质,如果为自定义USB设备,设备分属类别值为0,同时上位机驱动必须配合编写;如果为标准USB设备,则必须使用这些标准设备的驱动、数据结构等等,条件是你必须了解这些标准设备的一些信息,好处是省去一些麻烦的驱动编写。 const unsigned char cbDescriptor_Device[DESC_SIZE_DEVICE] = { $ e# i7 Q _ t# U% m% } DESC_SIZE_DEVICE, // bLength: 18 DESCRIPTOR_DEVICE, // descriptor type $ \! D+ S- d b* P9 e 0x00, // bcdUSB LSB: USB release number -> USB2.0 0x02, // bcdUSB MSB: USB release number -> USB2.0 0x00, // bDeviceClass: Class information in the interface descriptors 0x00, // bDeviceSubClass: 0x00, // bDeviceProtocol: 0x40, // bMaxPacketSize0: LowS(8), FullS(8,16,32,64), HighS(64) - o* }# P2 X8 l/ ]2 U; w 1 Z$ X. a9 \- d- v2 D; K6 I LOWORD(VENDOR_ID), // idVendor LSB: Z* q# `5 p# ~2 j HIWORD(VENDOR_ID), // idVendor MSB: LOWORD(PRODUCT_ID), // idProduct LSB: 1 w' o: M8 R9 `: Y% b- L3 Q HIWORD(PRODUCT_ID), // idProduct MSB: & |% p _7 t0 P5 \4 Q0 O9 R7 @ LOWORD(DEVICE_VERSION), // bcdDevice LSB: HIWORD(DEVICE_VERSION), // bcdDevice MSB: & ?) K% e S% Q6 J ^ 0x01, // iManufacturer: Index of string descriptor describing manufacturer 0x02, // iProduct: Index of string descriptor describing product 9 A& D: h4 @$ b5 n- u/ g 0x03, // iSerialNumber: Index of string descriptor describing the device serial number 6 ^; z1 r+ J) h6 b. s( D( o7 w$ ^ 0x01 // bNumConfigurations: number of configurations $ E: @ q, {. x; Z( f: j: L }; # O+ m/ Y6 g. M \. m) X1 [- W ) L+ L8 D. y' S 配置描述符:前9个字节格式固定,后面紧跟的各种描述结构跟实际配置有关,每增加一种描述结构,该描述结构的第一字节说明了结构的长度,第二直接说明了结构的类型。在配置描述符中一般包含配置描述、接口描述、端点描述,如果需要同样可增加自定义的描述。使用标准USB设备类别时,配置描述符的结构也必须满足此类标准设备的数据结构。 Y& Q! N' c+ U. F% |9 T( h const unsigned char cbDescriptor_Config[DESC_SIZE_CONFIG] = { 2 ?* p2 j7 R; k+ @ // Descriptor of configuration , J) s7 c9 ?5 P5 O 0x09, // lengths DESCRIPTOR_CONFIG, // descriptor type 8 z5 B; l) n( z. A ]' D ) _ T: b: v" o8 f+ F DESC_SIZE_CONFIG, // Total configuration descriptor lengths LSB 0x00, // Total configuration descriptor lengths MSB ; a& G/ p- n B3 A# U% R" m6 Z2 E S/ p; G ! _8 Y" V, O4 H) ?" Q 0x01, // bNumInterfaces: Total number of interfaces 4 z' Z9 t& ~( Q: z, | 0x01, // bConfigurationValue: Configuration value 6 r, v& w# k; S7 u- e5 D4 H. t 0x00, // iConfiguration: Index of string descriptor describing the configuration ( K, G1 a( ?9 X4 I ~) U# n- ^ 0xA0, // bmAttributes: bus powered // bit 4...0 : Reserved, set to 0 ! h$ q* P) X/ P3 p$ X9 _ // bit 5 : Remote wakeup (1:yes) : h8 W. \3 ?* d# v) \3 D0 z // bit 6 : Self power (1:yes) 7 H+ q2 H: E$ i; A // bit 7 : Reserved, set to 1 0x32, // bMaxPower: this current is used for detecting Vbus = 100mA - M6 J' L/ F5 K3 n // Descriptor of interface 0x09, DESCRIPTOR_INTERFACE, 0x00, // bInterfaceNumber: Number of Interface ' e7 N# E0 \2 A; K2 r9 F" P9 [9 o 0x00, // bAlternateSetting: Alternate setting $ U. N4 J# T% b4 L/ Q- e 7 l, I" X( ]( Z& j 0x02, // bNumEndpoints: Number of endpoints except EP0 0x00, // bInterfaceClass: " @% p/ T/ `; U0 I 0x00, // bInterfaceSubClass: 6 Z5 Z k8 h; q 0x00, // nInterfaceProtocol: & P) t- k8 d8 z$ g. D" T 0x00, // iInterface: Index of string descriptor describing the interface % y6 p3 P9 Q# ~( t1 { // Descriptor of endpoint1 OUT 3 M7 g4 z- v7 Q1 ] 0x07, DESCRIPTOR_ENDPOINT, 9 j; | r9 U" r 4 T7 c& h Z- Z: q; e: ~6 U( z 0x01, // bEndpointAddress + V0 e* f. z, v" K# c' D // bit 3...0 : the endpoint number // bit 6...4 : reserved // bit 7 : 0(OUT), 1(IN) 0x03, // bmAttributes // bit 1...0 : Transfer type // 00(CONTROL), 01(ISOCHRONOUS), 10(BULK), 11(INTERRUPT) // bit 3...2 : Synchronization type // 00(No Synch), 01(Asynchronous), 10(Adaptive), 11(Synchronous) // bit 5...4 : Endpoint Usage type 0 S1 K; f3 P) B8 `7 R1 | // 00(data), 01(Feedback), 10(Implicit feedback data endpoint), 11(Reserved) // bit 7...6 : Reserved, must be zero ! T: j4 q& K9 ] 4 w) U& P1 k8 |: c% E, d8 e 0x40, // packet size LSB 9 a$ _" j: e! ^7 P& r4 ` 0x00, // packet size MSB ' L6 x N0 w5 W" p# w" N 0x20, // polling interval time: 32ms // Descriptor of endpoint2 IN . T3 Z: j/ S/ _ 0x07, : L. W. F/ s0 r, L6 Z a' V2 u DESCRIPTOR_ENDPOINT, 0x82, // bEndpointAddress // bit 3...0 : the endpoint number // bit 6...4 : reserved 7 D( Y4 N# @0 ]& C9 G% s( m) h // bit 7 : 0(OUT), 1(IN) % U8 Y( Y' N, I: }8 Z- \ 0x03, // bmAttributes // bit 1...0 : Transfer type 5 k& |9 ~9 w, L' g9 J* O% i // 00(CONTROL), 01(ISOCHRONOUS), 10(BULK), 11(INTERRUPT) // bit 3...2 : Synchronization type 0 [, z/ m! I9 m7 N // 00(No Synch), 01(Asynchronous), 10(Adaptive), 11(Synchronous) : P1 d$ D5 h6 h: y/ {% U // bit 5...4 : Endpoint Usage type // 00(data), 01(Feedback), 10(Implicit feedback data endpoint), 11(Reserved) + C2 y- P8 i& n/ ^; }2 X // bit 7...6 : Reserved, must be zero 0x40, // packet size LSB ' N2 r, K! ~( ^ 0x00, // packet size MSB ' c* S4 o7 P5 K. y x. J 0x20 // polling interval time: 32ms }; 1 } B3 C; x( h# C) J7 l 字符串描述符:定义了与设备有关的一些信息,常见的为以下四种,如果有需要,同样可以定义自己的字符串描述符。 const unsigned char cbDescriptor_StringLangID[DESC_SIZE_STRING_LANGID] = 2 Z+ z, B4 i& x1 Z { DESC_SIZE_STRING_LANGID, // bLength $ o2 Z1 q3 t! e7 @! G ^- v6 Y8 u# F DESCRIPTOR_STRING, // bDescriptorType = String Descriptor - y7 P; K8 y; F0 S4 A3 ?! ~; w0 @ 0x09, // LangID LSB: 0x04 // LangID MSB: 0x0409(U.S. English) - C% K+ c; Q% r }; & m" H* ~+ s: `9 V! X const unsigned char cbDescriptor_StringVendor[DESC_SIZE_STRING_VENDOR] = ! G( X& O4 I1 x5 W# a { DESC_SIZE_STRING_VENDOR, // bLength R' W0 |+ Q9 s; r DESCRIPTOR_STRING, // bDescriptorType = String Descriptor // String: "LaBiXiaoXiaoXin" - m# {+ _9 p. S, w& L0 g 'L',0, 'a',0, 'B',0, 'i',0, 'X',0, 'i',0, 'a',0, 'o',0, - x+ `& N9 w" D& k9 z$ Q 'X',0, 'i',0, 'a',0, 'o',0, 'X',0, 'i',0, 'n',0 }; + V, c' |1 ~: n- J const unsigned char cbDescriptor_StringProduct[DESC_SIZE_STRING_PRODUCT] = { DESC_SIZE_STRING_PRODUCT, // bLength A' c6 C3 H( F* k# r DESCRIPTOR_STRING, // bDescriptorType = String Descriptor 4 o) S- l" X# p4 U; [+ T R# C . S5 D4 v% A. t2 O( v // String: "STM32 ezUSB-CORE V1.01" 'S',0, 'T',0, 'M',0, '3',0, '2',0, ' ',0, 'e',0, 'z',0, 'U',0, 'S',0, 'B',0, '-',0, 'C',0, 'O',0, 'R',0, 'E',0, ' ',0, 'V',0, '1',0, '.',0, '0',0, '1',0 }; 1 \( E+ r8 u9 e0 o * l9 B( [" ~! K1 T# k0 T const unsigned char cbDescriptor_StringSerial[DESC_SIZE_STRING_SERIAL] = ; }" K4 j3 |3 b/ o! C/ q2 M. o, } { ( d( o) l+ G5 k9 C& c9 I' [6 V @ DESC_SIZE_STRING_SERIAL, // bLength # q; A3 H7 |2 o' }! C# w DESCRIPTOR_STRING, // bDescriptorType = String Descriptor 4 u) J# P- y: [9 e( W3 R // String: "ezUSB-CORE Demo 2008/11/18" 'e',0, 'z',0, 'U',0, 'S',0, 'B',0, '-',0, 'C',0, 'O',0, 'R',0, 'E',0, ' ',0, / {- g/ @- H3 s; o( L% ? 'D',0, 'e',0, 'm',0, 'o',0, ' ',0, '2',0, '0',0, '0',0, '8',0, '/',0, '1',0, '1',0, '/',0, '1',0, '8',0 ( Y& Z/ X4 i( X) C$ n, u }; 了解这些描述符的用法以及作用,最好的方法的是编写自定义的USB上位机驱动以及应用程序,这样你可以深刻了解USB设备与主机间的数据交换方式以及实现手段,下篇将开始介绍USB上位机驱动以及应用程序的编写以及开发环境的建立,STM32 USB设备的固件程序如有什么疑问,请朋友们多花几分种时间留言、讨论,共同学习与进步。 |
最全USB HID开发资料,悉心整理一个月,亲自测试
实战经验 | 选择USBX模块生成USB CDC ACM无PD的项目
STM32 USB HID键盘例程
刘氓兔的杂谈【001】-片上USB 高速PHY
【经验分享】在进行 USB CDC 类开发时,无法发送 64整数倍的数据
【源码】STLINK-V3MINI 高速USB仿真器,成功改刷【高速CMSIS-DAP】
在线直播|无需编写任何代码即可在STM32上实现USB-C Power Delivery
STM32 USB CDC 虚拟多串口
圈圈发布USB图书第二版有感,以及分享一些我学习USB过程...
USB Audio设计与实现
回复:基于STM32的USB程序开发笔记(五) ——USB设备的枚举(下)
RE:基于STM32的USB程序开发笔记(五) ——USB设备的枚举(下)
RE:基于STM32的USB程序开发笔记(五) ——USB设备的枚举(下)