接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: void ISTR_CTR(void); void ISTR_SOF(void); void ISTR_ESOF(void); $ l% q* F2 M* c' O' }8 Z void ISTR_DOVR(void); . m, u7 P) H* ~0 H: | void ISTR_ERROR(void); 6 s3 M3 `/ s# \5 j r void ISTR_RESET(void); : H0 W6 c T: C0 m void ISTR_WAKEUP(void); " v* T9 V: {( }1 b, U void ISTR_SUSPEND(void); ' A I. Q! c+ U3 @" y& h) K1 [ ! V4 u6 A E9 K8 Y- B 这些处理函数使能由定义CNTR_MASK决定: - `* p, f/ |+ r$ F/ f# B // CNTR mask control #define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \ 2 P- f( W7 g% `! ^) D CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \ 3 ?- ^# v& T/ n 其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。 3 X9 Q( Z( ]6 ~( p" ^ 4 _4 F2 Z$ R" h; ^. z // ***************************************************************************** 3 w' T7 [. Z5 X5 }% L // Function Name : INT_ISTR_RESET / H/ X8 [7 d3 F // Description : ISTR Reset Interrupt service routines. 9 o; ?6 [! a5 P // Input : " f( ?. g9 E! ~( { // Output : 4 M$ v+ t4 @4 B; ^2 ~% V' t8 x // Return : // ***************************************************************************** " z* n" Q. k7 K: E3 r8 Y+ M void INT_ISTR_RESET(void) 3 ?9 I$ M9 W; _, H3 M { // Set the buffer table address SetBTABLE(BASEADDR_BTABLE); ; j+ n& U( F) f // Set the endpoint type: ENDP0 , x: p; v: _! z' Y8 O7 l# | SetEPR_Type(ENDP0, EP_CONTROL); I9 X9 U y. K- y Clr_StateOut(ENDP0); 2 ~. f9 ?' b$ h5 H* n' R 6 e7 Q2 F7 m. O+ p: S& P // Set the endpoint data buffer address: ENDP0 RX SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR); + f, E1 y+ D/ S4 m // Set the endpoint data buffer address: ENDP0 TX SetBuffDescTable_TXCount(ENDP0, 0); - p) c$ t$ h; d/ G SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR); // Initialize the RX/TX status: ENDP0 - o0 ^4 U+ X1 P* x, L+ C+ ~ SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_NAK); , r9 Q8 i9 b2 ]( P) i ( M, ~: W$ [; u) h; @* m. T // Set the endpoint address: ENDP0 SetEPR_Address(ENDP0, ENDP0); // --------------------------------------------------------------------- // TODO: Add you code here // --------------------------------------------------------------------- - h1 n: Z$ W( D7 ]. x% y { // Set the endpoint type: ENDP1 SetEPR_Type(ENDP1, EP_INTERRUPT); Clr_StateOut(ENDP1); 1 o5 C4 o2 N; U* h* y) S // Set the endpoint data buffer address: ENDP1 RX 0 f' X7 s( h" I4 n SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR); ! J* ]$ K, H# H* D" d+ h 9 s$ `( l M; o/ e& J3 @% b" K // Set the endpoint data buffer address: ENDP1 TX 6 n% k) m- ?: y5 y% q/ B1 E SetBuffDescTable_TXCount(ENDP1, 0); & l& G4 H9 r: j# K# K SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR); 1 B& o% Y4 l; \) U( f // Initialize the RX/TX status: ENDP1 6 I5 c7 B; X x8 l/ @7 D6 p SetEPR_RXStatus(ENDP1, EP_RX_VALID); $ a. V. w4 P2 R# O3 x, u SetEPR_TXStatus(ENDP1, EP_TX_DIS); - {- t3 o/ c+ l; X3 y" {8 V% W" l // Set the endpoint address: ENDP1 + N% D+ T6 M7 x2 P, N% S4 j SetEPR_Address(ENDP1, ENDP1); " L8 n% B: t" N) w: ^ SetEPR_Type(ENDP2, EP_INTERRUPT); Clr_StateOut(ENDP2); 6 T& ]; B5 ]$ ^0 W3 J2 g , ]: | M% F9 A8 w' X; | // Set the endpoint data buffer address: ENDP2 RX , c! x, @; t+ B( y \$ S+ x SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); : c0 k+ F+ i2 x5 N6 C SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR); * T1 y3 g7 z) E% r5 T, s G // Set the endpoint data buffer address: ENDP2 TX d, T+ n- b+ O/ k6 L5 L SetBuffDescTable_TXCount(ENDP2, 0); SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR); 1 w) K1 i. _: J$ {% f, | // Initialize the RX/TX status: ENDP2 SetEPR_RXStatus(ENDP2, EP_RX_DIS); SetEPR_TXStatus(ENDP2, EP_TX_VALID); ! L& G5 r: K& e# Q' D' I% j; g( U- ^ // Set the endpoint address: ENDP2 - @' D- ~0 x0 \! }+ g4 t k, H SetEPR_Address(ENDP2, ENDP2); $ q( t, @9 y2 s) K // --------------------------------------------------------------------- // End of you code " P4 _( q/ l" ] // --------------------------------------------------------------------- - W; B4 R( k/ j+ T2 A SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); 4 L% D7 D' e$ o4 y8 Y) i, f vsDeviceInfo.eDeviceState = DS_DEFAULT; vsDeviceInfo.bCurrentFeature = 0x00; % o6 Z* f6 I8 [" W; s+ F9 U; ] vsDeviceInfo.bCurrentConfiguration = 0x00; & e. V3 d4 f7 x$ T, l vsDeviceInfo.bCurrentInterface = 0x00; 5 \! w- p5 S8 C vsDeviceInfo.bCurrentAlternateSetting = 0x00; vsDeviceInfo.uStatusInfo.w = 0x0000; } 9 o# e$ J! w8 Y" Q- Q 在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。 s7 L9 s4 C9 Q 值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下: # n2 O# e" P4 A0 R7 U! D1 _. w; Z // USB_IP Packet Memory Area base address 9 }+ a& v/ K% v4 K+ w #define PMAAddr (0x40006000L) ( M& ^8 t0 E5 D/ a # W$ S- x, h& Q1 R3 k9 H* J // Buffer Table address register #define BTABLE ((volatile unsigned *)(RegBase + 0x50)) // ***************************************************************************** * G! {3 z L- t // Packet memory area: Total 512Bytes // ***************************************************************************** #define BASEADDR_BTABLE 0x0000 - Z# a5 T+ B; P K // ***************************************************************************** ) y2 p5 E0 y, G! V // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT ' T' D% Y p; l // PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT , S7 a2 N* J6 |, w // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR Y7 C: @/ Y+ f* k$ @ // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT ' b& h7 C0 E0 e5 F5 u // // PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR # j+ S$ t1 v; s // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT % \& k7 x- }, T. J. a // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT , y0 E. H1 e- ~+ O d- g+ `# | // 9 a7 [. C$ @4 y2 x! \! W8 P1 b // PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT 4 `( d. e; \( y // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT 0 L$ R; H$ t9 E$ L/ U, v // . f# f& A4 N6 J* R p$ L // PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000002A : EP5_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000002C : EP5_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000002E : EP5_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000030 : EP6_TX_ADDR V9 k N- c8 s9 g ~# }3 _: G8 m // PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT 7 F! }2 I1 v V+ D4 s; s3 ] // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR ' T6 ~ l# e4 V3 G/ n# F- A // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT 4 R& p2 Y- {# J7 [2 T // // PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR + ~+ n) \* n a, {9 q2 t // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR 6 O1 U$ c& _4 U3 E I // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT 2 y% \! R- S3 ~% ? // ***************************************************************************** // ) H: N, {+ ^' h* U% X( l // PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer // ( i; c/ x! J& i( b0 i // ***************************************************************************** 0 B" j5 |! I- Y. b7 M #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) 2 s4 v9 n5 ]& b4 }' h% O5 y // ENP0 #define ENDP0_PACKETSIZE 0x40 #define ENDP0_RXADDR BASEADDR_DATA #define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) % w& O, a w7 V4 H% J1 j* H // ENP1 #define ENDP1_PACKETSIZE 0x40 #define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) ) I% F% L$ M6 ]6 {/ i2 f4 P# v, v" R 1 N. a7 v- P0 Q% h, T: K9 j // ENP2 #define ENDP2_PACKETSIZE 0x40 / c' A# Y7 w' G4 Y# O #define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) ; h- E, R( ^8 y. r1 ?& U% Q4 f // ENP3 #define ENDP3_PACKETSIZE 0x40 8 f) Q7 C7 |, N2 r, A4 F #define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) // ENP4 % P6 ~" x8 O9 S# Y: z #define ENDP4_PACKETSIZE 0x40 2 e" R1 n n1 g: }" c$ w #define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) , y9 F, B0 p3 g* N2 T4 _ " Z+ p8 C3 J3 M; h, k& o // ENP5 #define ENDP5_PACKETSIZE 0x40 ( T4 w! k4 ~' O0 C& q# p% V #define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) 3 E1 z+ P# G' ]1 H. K/ b #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) 5 j$ K6 T4 a5 Q7 U. [* _1 S // ENP6 #define ENDP6_PACKETSIZE 0x40 # f' ]6 D3 p! z; \+ s) T' S% l #define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) ) p! j5 B3 V7 N; h // ENP7 #define ENDP7_PACKETSIZE 0x40 1 F: w( r n& J3 [- v8 l1 t& ? #define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) , {5 r. F' T. E" c3 Y* \9 k #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE) , ]: k6 [" T7 n5 g _. a 这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。 // ***************************************************************************** // Function Name : INT_ISTR_CTR // Description : ISTR Correct Transfer Interrupt service routine. // Input : // Output : // Return : , m* R& `- j. Z9 u; N$ E3 ^ // ***************************************************************************** 2 U& Q) j' F6 s# @* a2 ^% \ void INT_ISTR_CTR(void) - {! A( R$ Y# ]7 ~ { - |# B& j8 m8 ? unsigned short wEPIndex; unsigned short wValISTR; # J3 q+ `3 ^0 f( o- Q unsigned short wValENDP; % J# T& O o/ ~" m; k & h, O1 I5 S6 l while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) 1 y* V' d7 l# U8 { { & m v3 f( C; R6 E7 E // Get the index number of the endpoints wEPIndex = wValISTR & ISTR_EP_ID; * Y' V/ |6 |. l; I6 p$ F ) L, ]: ]8 J* C/ x4 F if(wEPIndex == 0) 3 K& \0 J- q* a% p0 I' L { . U0 \% F3 D# x // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) 6 C6 a: l8 m% Y8 ]( f0 B4 _* h SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_NAK); 6 z. d! D& y9 e2 d* m0 f5 ` 3 n7 y4 Z/ j4 X: { // Transfer direction @' s" B& N0 Y: ^$ A, P if((wValISTR & ISTR_DIR) == 0) ' u) H [# A: J { // DIR=0: IN 1 c( e# f) x2 O // DIR=0 implies that EP_CTR_TX always 1 6 P, e/ k( ]9 g0 m5 G- n ClrEPR_CTR_TX(ENDP0); % [" {9 q# m" [6 t CTR_IN0(); return; 0 g. x2 P1 @6 W: k5 _! j } else { // DIR=1: SETUP or OUT ) I# L! f# V" {$ |: p8 Z // DIR=1 implies that CTR_TX or CTR_RX always 1 wValENDP = GetEPR(ENDP0); if((wValENDP & EP_CTR_TX) != 0) ( U5 M" _! X3 h% [8 j5 g { ClrEPR_CTR_TX(ENDP0); CTR_IN0(); ( F) ?$ O/ E. e t" g J return; } else if((wValENDP & EP_SETUP) != 0) ) |; A, v1 V) F$ y1 M1 F& a4 g" C { 5 R, P) ~) D9 B2 ~( k3 s ClrEPR_CTR_RX(ENDP0); 2 }$ P' b: P4 l5 H4 b CTR_SETUP0(); 6 j9 ~ x4 W* _ return; 9 G3 b1 T% U% c4 E9 B3 { } & T: H+ { [3 u: T- j+ }& q" | else if((wValENDP & EP_CTR_RX) != 0) { # [7 E" w* c; m5 Q ClrEPR_CTR_RX(ENDP0); . Q8 K* P( P1 B" j4 q* R& l CTR_OUT0(); return; . e* |: m7 d6 M# ]: B } } ; H0 C+ h8 {/ S M* q6 ? } R. W( o& w; I; k // Other endpoints else 6 Z1 \: s: f/ K/ p* b$ ~ { ( J5 S9 d, ]. E wValENDP = GetEPR(wEPIndex); % q m7 M* p8 ]2 N$ }" i: H( }' v' t SetEPR_RXStatus(wEPIndex, EP_RX_NAK); SetEPR_TXStatus(wEPIndex, EP_TX_NAK); : Q$ W/ J( _: g! r& k if((wValENDP & EP_CTR_TX) != 0) { ClrEPR_CTR_TX(wEPIndex); switch(wEPIndex) { , b0 ]# j p: m0 W' k case ENDP1: CTR_IN1(); break; case ENDP2: CTR_IN2(); break; case ENDP3: CTR_IN3(); break; % d- U; i# }! S- x9 o8 H case ENDP4: CTR_IN4(); break; case ENDP5: CTR_IN5(); break; 4 I, e# p" \+ Q0 M3 K- S) e( ] case ENDP6: CTR_IN6(); break; # F$ ~6 X) O+ X" f v/ G9 i case ENDP7: CTR_IN7(); break; default: break; } } if((wValENDP & EP_CTR_RX) != 0) & q. f0 c% e" { G7 _- m! K' J- d { ClrEPR_CTR_RX(wEPIndex); * N G6 e( S4 T" R9 r) u switch(wEPIndex) { case ENDP1: CTR_OUT1(); break; case ENDP2: CTR_OUT2(); break; 5 V1 n: r7 g+ x" E' j6 } case ENDP3: CTR_OUT3(); break; ! {' Q3 R$ {3 E2 x! P case ENDP4: CTR_OUT4(); break; case ENDP5: CTR_OUT5(); break; case ENDP6: CTR_OUT6(); break; ) U" V6 N# X* d4 p2 ` case ENDP7: CTR_OUT7(); break; 6 o! n: L6 q$ ^4 C4 U/ |- J default: break; } 6 c. z+ s; |% c: W) N4 P7 K" { K; _% n } ) k E u5 X$ \ } 7 l0 o3 {2 O& h7 w+ e+ @ } } * X6 r6 ]* i) d 0 S( c: u" w( p! P) y8 S INT_ISTR_CTR()函数将各自响应事件提取出来,默认端点EP0也是最为复杂的,这个需要查看STM32的参考手册以及USB协议才能更好了解为何如此。到这里STM32 USB里数据传输事件就指向了各个对应的端点。下篇着重说明USB设备的枚举。 |
最全USB HID开发资料,悉心整理一个月,亲自测试
USB Audio设计与实现
【MCU实战经验】+STM32F107的USB使用
圈圈发布USB图书第二版有感,以及分享一些我学习USB过程...
STM32F4-DISC 实现USB主机(U盘)和USB设备(虚拟串口)自动切换
STM32 USB-HID通信移植步骤STM32 USB HID键盘例程
【经验分享】在进行 USB CDC 类开发时,无法发送 64整数倍的数据
如何让CDC类USB设备批量接收64字节以上数据
用STM32F4实现的USB摄像头UVC,配合上位机可识别车牌
STM32 USB CDC 虚拟多串口
回复:基于STM32的USB程序开发笔记(三) ——STM32 USB固件函数的一些介绍