
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: + t7 c: T8 _, V void ISTR_CTR(void); void ISTR_SOF(void); 6 K8 F! y u+ {2 P5 e9 }5 j void ISTR_ESOF(void); 5 |& |$ Z" Y& l ~ void ISTR_DOVR(void); 9 J: O; N2 r. M! H% @7 Y void ISTR_ERROR(void); 4 t. h# I- Z5 q* |/ M6 W& i) S& O void ISTR_RESET(void); void ISTR_WAKEUP(void); 6 x' Q& Q4 M' |* W4 O2 E void ISTR_SUSPEND(void); " Q5 c" Y+ ^) ^2 N! t9 x3 y+ ?9 B 这些处理函数使能由定义CNTR_MASK决定: 8 H- x/ v0 }6 a" C# v // CNTR mask control #define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \ P( c, M' A) Q8 P; w2 Q ?5 g CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \ 其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。 7 g+ X. u! i, Q: k1 h# z ( I# k: h+ F9 i // ***************************************************************************** 2 ?' c, C) T3 u: J7 a' N // Function Name : INT_ISTR_RESET // Description : ISTR Reset Interrupt service routines. // Input : // Output : // Return : // ***************************************************************************** , T1 n/ I! w- V' K8 q void INT_ISTR_RESET(void) { // Set the buffer table address 4 j5 w5 f" y+ t, k+ y* g SetBTABLE(BASEADDR_BTABLE); + m6 n% S Y6 P$ x // Set the endpoint type: ENDP0 SetEPR_Type(ENDP0, EP_CONTROL); ) i9 i5 k: O2 Y Clr_StateOut(ENDP0); d1 w* d/ n9 m // Set the endpoint data buffer address: ENDP0 RX SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); + }+ {+ ]" T A" p' p SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR); 3 Q+ b3 N0 W- Y" v1 S# L7 I // Set the endpoint data buffer address: ENDP0 TX SetBuffDescTable_TXCount(ENDP0, 0); SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR); $ U& w4 U8 l' L r0 n // Initialize the RX/TX status: ENDP0 SetEPR_RXStatus(ENDP0, EP_RX_VALID); 1 C6 X3 [" {! q6 g0 t5 Z SetEPR_TXStatus(ENDP0, EP_TX_NAK); ( x; O/ I7 B2 h @4 u# t: K // Set the endpoint address: ENDP0 SetEPR_Address(ENDP0, ENDP0); ; {* p) ?# `/ f/ e , Y5 H: w% x2 V // --------------------------------------------------------------------- // TODO: Add you code here : p e b# w; S( ^: T* Z- P) c+ A$ | // --------------------------------------------------------------------- // Set the endpoint type: ENDP1 : V% z% Q- ?0 `* T3 e% W" R7 v% k' z5 P SetEPR_Type(ENDP1, EP_INTERRUPT); Clr_StateOut(ENDP1); // Set the endpoint data buffer address: ENDP1 RX 9 ~9 |9 @$ y) R) q1 A SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR); ) z+ F+ G4 }# J0 w& ^# u: j 2 h% y- d& l ^/ |& k# Z // Set the endpoint data buffer address: ENDP1 TX SetBuffDescTable_TXCount(ENDP1, 0); 2 i+ U$ \1 h9 x7 U+ e5 M SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR); & z% C2 M* k, a" T! J9 b: U // Initialize the RX/TX status: ENDP1 SetEPR_RXStatus(ENDP1, EP_RX_VALID); SetEPR_TXStatus(ENDP1, EP_TX_DIS); 6 j+ a7 M! ?6 y H* |6 | // Set the endpoint address: ENDP1 7 W! t- \. r* P2 E SetEPR_Address(ENDP1, ENDP1); 7 H5 f- H; b n( F4 f# _: f$ \8 T 9 u4 T& a. k2 L SetEPR_Type(ENDP2, EP_INTERRUPT); & p" }- M1 d+ x Clr_StateOut(ENDP2); 7 r' y# y, E) w8 `7 W // Set the endpoint data buffer address: ENDP2 RX - q# U3 j# a5 c6 j" J: \ q1 z# ^ SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR); // Set the endpoint data buffer address: ENDP2 TX SetBuffDescTable_TXCount(ENDP2, 0); SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR); ! S; c# `& T' X! _. c ) w4 G; u' j, I" R# [ z // Initialize the RX/TX status: ENDP2 SetEPR_RXStatus(ENDP2, EP_RX_DIS); SetEPR_TXStatus(ENDP2, EP_TX_VALID); // Set the endpoint address: ENDP2 2 a. _% B2 ]: P, ~ D( h2 A; a. p SetEPR_Address(ENDP2, ENDP2); , J( [6 P1 f' y4 J6 Q. G7 ? * m+ e! i3 j) Y" n F ; Q, c' _& r+ N& v0 E8 w @5 [ // --------------------------------------------------------------------- // End of you code // --------------------------------------------------------------------- - Y( O3 l8 F2 u* _ SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); vsDeviceInfo.eDeviceState = DS_DEFAULT; vsDeviceInfo.bCurrentFeature = 0x00; vsDeviceInfo.bCurrentConfiguration = 0x00; ' M w; z1 z4 \; _6 C4 ~, x vsDeviceInfo.bCurrentInterface = 0x00; vsDeviceInfo.bCurrentAlternateSetting = 0x00; vsDeviceInfo.uStatusInfo.w = 0x0000; } 在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。 / O6 j3 B4 K7 }4 [ 值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下: // USB_IP Packet Memory Area base address #define PMAAddr (0x40006000L) % s; t( L* h6 w/ ?, V2 n 6 W; m( `# m; m- x: o- }' y // Buffer Table address register #define BTABLE ((volatile unsigned *)(RegBase + 0x50)) // ***************************************************************************** , f$ _* K1 G6 ]- L, ~& A // Packet memory area: Total 512Bytes & {% t( O/ E( u8 y6 O // ***************************************************************************** 1 [# I3 I) B! f2 D #define BASEADDR_BTABLE 0x0000 // ***************************************************************************** & X2 P( v1 ?) P // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR 9 r B4 g/ y" ~+ W6 I/ \ // PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT * c5 a( z# @9 k, R/ m; h) z- W: x/ d ~ // PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR / w2 v# {/ U1 ^3 e) _: Y7 I- P // PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT $ r, I( B. P3 |8 W5 ]& v // - B# @ M; q7 f- S // PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR ) J. E. s) @$ N6 b# X! S // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR 3 F- z9 t9 a- e5 V; O R // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT 4 C; O" R: N- j4 o w' z // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR # @$ n& x! ?* U // PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT ! U0 R/ z: q- D0 t% J // // PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR . O m8 `& u4 W) I8 O( y // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT // 2 w0 D& `4 _9 y, Z: m // PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR / j9 e, S6 e) n/ r3 h9 Q // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT // `6 O6 Y* a# Z6 e // PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR 9 H( D4 r/ X+ |; s // 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 // PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT ( z9 d3 a& V9 d$ l' v // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR & t. M6 x9 G! {8 Y // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT // D7 ^/ A; ?$ ^ v0 t! ` // PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR ( X$ @# y' N/ O/ f+ e+ G // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT // ***************************************************************************** // // PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer // 1 O9 t% u5 G, c1 L/ Y+ M! R // ***************************************************************************** #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) / G% k) Z8 c' }& K) X) b" q1 Z' ]$ ] // ENP0 & e& O* D+ Y3 j4 e' i, o: ] #define ENDP0_PACKETSIZE 0x40 #define ENDP0_RXADDR BASEADDR_DATA #define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) : e$ K: `* u n& I0 w- n . O$ T6 L* e$ h3 I! n& A+ ~2 D4 B // ENP1 #define ENDP1_PACKETSIZE 0x40 3 L/ w0 j$ X( Y% E0 e: Z; Q3 b; U #define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) " S; J. g: P. \: k 4 ~* |: }, N& C3 e) ] // ENP2 . b7 W: F0 b+ T1 _# \6 Q1 L #define ENDP2_PACKETSIZE 0x40 #define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) : D+ ^8 k5 U$ I7 ^& |2 @7 B9 R9 [3 D // ENP3 ! R7 V' \+ s" e$ ^; c% }) x8 G( ^ #define ENDP3_PACKETSIZE 0x40 #define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) // ENP4 ) t) `5 d& B$ N/ c) o #define ENDP4_PACKETSIZE 0x40 #define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) $ m% v- P+ X8 W) t" K# j #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) 3 {' S/ M) j' ~9 { " h) u% u' Q/ U* b // ENP5 % c: p# r. ]" n9 M #define ENDP5_PACKETSIZE 0x40 #define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) V' W6 O$ s2 S6 O) ^. ` #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) $ n5 n1 u0 M# E4 \. s. X // ENP6 * W+ w5 V3 Q! D; }* b7 u8 l #define ENDP6_PACKETSIZE 0x40 0 Y/ m* g% Q& @ #define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) ( O! V4 M+ x# \1 A* l #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) z- V3 T5 ]+ w. [$ \ // ENP7 , f+ m* ~$ O6 \( B) b #define ENDP7_PACKETSIZE 0x40 ! \! Q6 Q# d" f #define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) + \0 ^9 M9 o Q: d- ?* N #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE) - A0 [3 I1 M+ z 这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。 ! o J5 g" D( }: N( O " c9 L! K+ G C$ e$ _5 @" f1 ^ // ***************************************************************************** // Function Name : INT_ISTR_CTR // Description : ISTR Correct Transfer Interrupt service routine. // Input : 0 t1 n, `- Y: p8 A- P; M // Output : // Return : // ***************************************************************************** & P! k7 S/ I. c+ r; P/ J void INT_ISTR_CTR(void) 5 T6 J1 F% A) j { 9 q% i. S+ y. v unsigned short wEPIndex; unsigned short wValISTR; unsigned short wValENDP; , J! G8 D" k r5 `, ~- t; i$ R' [1 m while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) { // Get the index number of the endpoints , V( v, T0 \* L/ |1 t- U; e wEPIndex = wValISTR & ISTR_EP_ID; if(wEPIndex == 0) { % n. s u+ [7 W // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_NAK); & q2 f0 p1 K8 |* { ! y6 o i' ]# [ // Transfer direction 9 [7 [3 G% I/ B9 M4 X# ^ if((wValISTR & ISTR_DIR) == 0) { 6 N5 M# D: d6 m8 _. U3 h0 W // DIR=0: IN ! ] w* U; L. M2 _ // DIR=0 implies that EP_CTR_TX always 1 ClrEPR_CTR_TX(ENDP0); , S% j; N- `, d' G* I0 v3 r CTR_IN0(); return; } / A3 F* i6 Z3 A: T else { [ o+ c! ~; I# Z // DIR=1: SETUP or OUT // DIR=1 implies that CTR_TX or CTR_RX always 1 9 k/ k" A: B$ c m wValENDP = GetEPR(ENDP0); if((wValENDP & EP_CTR_TX) != 0) 3 A4 h3 F i; f { ) R& T- B V2 G0 w2 } T E& O9 @% P ClrEPR_CTR_TX(ENDP0); 7 a. U# B; h2 k, R1 m" m, [ CTR_IN0(); ) _/ d* p" b! z return; " j7 o% W3 G6 @; e+ k+ i. L } 0 x4 W s8 V8 Q6 G7 N. f else if((wValENDP & EP_SETUP) != 0) , p! E6 X4 {1 ?, m% q { 2 u7 K1 u3 Y% o; { ClrEPR_CTR_RX(ENDP0); CTR_SETUP0(); 5 \! @* U! p& e. h. f; z return; } 4 r s" g% Z0 q& L else if((wValENDP & EP_CTR_RX) != 0) % o3 a/ ]8 x! M { ClrEPR_CTR_RX(ENDP0); ( {' q7 R' X6 w CTR_OUT0(); 5 r% T! {, c. H return; + d/ I$ _; u4 x6 H V2 D4 j+ Z" M& l+ ` } 8 v/ K! V3 m, _8 V } I: T% {: f& h: t: O0 K } // Other endpoints else : k- N! J* {! r { ' z5 f; |* m% z% ?& h- T' e& J. O wValENDP = GetEPR(wEPIndex); * G% ~4 g6 ^2 a2 P z $ F- | ~" f4 ~4 v SetEPR_RXStatus(wEPIndex, EP_RX_NAK); + H1 M' a& ` I, I7 e# ~* o SetEPR_TXStatus(wEPIndex, EP_TX_NAK); 6 d" b8 G) D7 ~$ p" H if((wValENDP & EP_CTR_TX) != 0) { ClrEPR_CTR_TX(wEPIndex); % I% I C, K- k; g- T switch(wEPIndex) M4 K0 V5 W! L' ~( \ { , p: _; i5 t, C4 O0 M case ENDP1: CTR_IN1(); break; case ENDP2: CTR_IN2(); break; 8 d4 Q" c$ @+ j1 z2 E; A* v case ENDP3: CTR_IN3(); break; ; y! W0 U0 K% N3 c& W4 \ case ENDP4: CTR_IN4(); break; 6 h4 j6 y3 r# ~7 ] case ENDP5: CTR_IN5(); break; j- r5 s3 ?" l- t& U case ENDP6: CTR_IN6(); break; * V% u0 u- y' o2 I2 z- O case ENDP7: CTR_IN7(); break; % Y3 t/ z% @: ?& l default: break; } } / e& j1 l$ L( r& Y) q- [1 t* a8 h1 ?( Z if((wValENDP & EP_CTR_RX) != 0) ! S8 ~" y+ W" S3 N3 } { ClrEPR_CTR_RX(wEPIndex); switch(wEPIndex) , _& O: y4 ?9 x3 S1 N( }1 M6 N { case ENDP1: CTR_OUT1(); break; : [" @7 N; N! j! g( w case ENDP2: CTR_OUT2(); break; 3 g2 u. P t h* B- D case ENDP3: CTR_OUT3(); break; ' d( D7 r" A! u case ENDP4: CTR_OUT4(); break; 3 S$ ]3 n" P& _! ~0 I5 I case ENDP5: CTR_OUT5(); break; # W4 G) m/ o7 z, l case ENDP6: CTR_OUT6(); break; 4 [2 E/ ^: E% n0 i0 X1 D0 ]! q case ENDP7: CTR_OUT7(); break; default: break; } } : T1 r) ` J) A. @4 _% R } } } + w. B; c9 |* Y, n( v, ^- \ INT_ISTR_CTR()函数将各自响应事件提取出来,默认端点EP0也是最为复杂的,这个需要查看STM32的参考手册以及USB协议才能更好了解为何如此。到这里STM32 USB里数据传输事件就指向了各个对应的端点。下篇着重说明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程序开发笔记(三) ——STM32 USB固件函数的一些介绍