
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: void ISTR_CTR(void); void ISTR_SOF(void); " I4 E# b' U: Q$ Q( i6 ] void ISTR_ESOF(void); ! c+ x9 M: x( z v; H4 w$ x void ISTR_DOVR(void); 0 M& U$ C: F1 l void ISTR_ERROR(void); void ISTR_RESET(void); void ISTR_WAKEUP(void); 4 `" U! p. j# G' d3 c# @/ m0 U! | void ISTR_SUSPEND(void); ! v: Q' \% x8 M 这些处理函数使能由定义CNTR_MASK决定: ( v8 o0 B) ~; y. u- i6 x! H' \ // CNTR mask control #define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \ CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \ 其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。 * b% x/ ]& d3 X! k ^" r; O9 w // ***************************************************************************** // Function Name : INT_ISTR_RESET 9 }4 [8 H. ?5 F* u // Description : ISTR Reset Interrupt service routines. // Input : # S _5 V8 }% D" {: G7 \ // Output : // Return : 5 U! a) p2 F3 @2 L) y // ***************************************************************************** void INT_ISTR_RESET(void) { // Set the buffer table address SetBTABLE(BASEADDR_BTABLE); 1 v+ F9 f) c) q& q5 D- I* g) Q // Set the endpoint type: ENDP0 SetEPR_Type(ENDP0, EP_CONTROL); Clr_StateOut(ENDP0); // Set the endpoint data buffer address: ENDP0 RX ' h: f, A% q6 q/ j/ n6 e SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR); 2 P$ r% f' W0 T7 Z; ? R . q( `$ e/ d- o- Y$ o // Set the endpoint data buffer address: ENDP0 TX - z3 e9 L& p1 H* \% U7 Z1 a& E SetBuffDescTable_TXCount(ENDP0, 0); SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR); // Initialize the RX/TX status: ENDP0 SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_NAK); & y5 |7 V& h. i7 w, c! N# r // Set the endpoint address: ENDP0 SetEPR_Address(ENDP0, ENDP0); 2 g) |% A5 c3 v // --------------------------------------------------------------------- // TODO: Add you code here // --------------------------------------------------------------------- % o, X6 Z4 K! m! _9 h; Q7 d0 l* D, d // Set the endpoint type: ENDP1 ) H" n$ W6 d y2 M, q/ N6 j SetEPR_Type(ENDP1, EP_INTERRUPT); - Y8 Y3 k5 U2 C; X7 o Clr_StateOut(ENDP1); // Set the endpoint data buffer address: ENDP1 RX - |5 j1 c: _0 {, R SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); $ G7 i! v! G5 }' ^0 ?0 T! i SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR); / ]' o1 U) n( S( S // Set the endpoint data buffer address: ENDP1 TX 7 C0 c7 V1 X6 w6 y+ _ SetBuffDescTable_TXCount(ENDP1, 0); SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR); 8 A s+ |" i; K1 y* r2 l$ n, c // Initialize the RX/TX status: ENDP1 t8 s5 Y) V$ y5 d+ @ SetEPR_RXStatus(ENDP1, EP_RX_VALID); ; X2 F ?6 x- a% [/ H SetEPR_TXStatus(ENDP1, EP_TX_DIS); 8 d( y( N1 @; q // Set the endpoint address: ENDP1 * }; R5 w. x1 {9 v SetEPR_Address(ENDP1, ENDP1); ; w A- D% s- n6 Y 6 I! S# j6 q9 a( V0 D! ?1 t SetEPR_Type(ENDP2, EP_INTERRUPT); 2 B) a- e. Q8 h( N9 x4 V6 p) L Clr_StateOut(ENDP2); : b3 `- G1 |, Q. r // Set the endpoint data buffer address: ENDP2 RX ( h W: w) {; M6 M1 y9 b& U* M- l SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR); ; m2 o3 T0 K" z. d4 o7 T // Set the endpoint data buffer address: ENDP2 TX * ~0 E% D! o A! m* x+ E! }7 K SetBuffDescTable_TXCount(ENDP2, 0); : c5 N4 `$ \8 a: F- A SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR); 3 O) w; e% h% {# Z f$ U // Initialize the RX/TX status: ENDP2 0 ~" X4 |5 a0 R SetEPR_RXStatus(ENDP2, EP_RX_DIS); SetEPR_TXStatus(ENDP2, EP_TX_VALID); * z. C$ w: o( a% Z0 X - @4 L3 r" Q$ C8 y [" Z // Set the endpoint address: ENDP2 , T) k' U% {* W: j2 ]9 T4 ]- F& s SetEPR_Address(ENDP2, ENDP2); 5 e8 x8 R% R$ k0 K- Q" z2 I + d. ]2 v- Y6 p8 J5 y // --------------------------------------------------------------------- // End of you code // --------------------------------------------------------------------- 5 u$ Q& @) m& h, D* L. i' D* m SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); vsDeviceInfo.eDeviceState = DS_DEFAULT; ; g% v$ Q/ Y8 T vsDeviceInfo.bCurrentFeature = 0x00; vsDeviceInfo.bCurrentConfiguration = 0x00; : o. z8 e6 M/ b9 m) T, }/ Q* b vsDeviceInfo.bCurrentInterface = 0x00; 1 A8 ^8 l, i+ x2 u1 e, t! } vsDeviceInfo.bCurrentAlternateSetting = 0x00; 4 V& [9 b1 r$ G* ]) k* {; A# R vsDeviceInfo.uStatusInfo.w = 0x0000; % @9 M& g' t* n* u3 ^* h5 I6 W } 在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。 S) u9 S& r9 r6 u7 r0 w 值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下: // USB_IP Packet Memory Area base address : u# m; Y) D F% x; n #define PMAAddr (0x40006000L) // Buffer Table address register #define BTABLE ((volatile unsigned *)(RegBase + 0x50)) ( J4 `9 ~0 u, x // ***************************************************************************** // Packet memory area: Total 512Bytes , i" v* F% g) L; | // ***************************************************************************** 5 V2 s% d5 n: A, i! B' o #define BASEADDR_BTABLE 0x0000 0 ^$ A' P' X. n0 F- ` L, f // ***************************************************************************** / ~) f4 @0 j2 _9 c2 \' x" K // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT + M( h% c, _ K; }- F2 o# ? // PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT # q$ V; ^; i( Y4 N // % t3 C' m" l: A" [5 }5 m% x: e // PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR / q* Q! u) u- i* I+ s2 G, q // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR 5 b4 F1 X$ ^- G0 M; m // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR # B( M+ P2 x/ k5 o9 a+ n; K // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT + f3 D& ^. \1 l4 h. [, N // // PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT ! v6 x3 i P% n5 m' ? // // PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR 2 u% N, k: L k! B% q% F // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR ( K/ ]6 j) h5 J* E // 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 & t }9 C; j- `5 x // PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT 2 U% ^# H, k( ~2 Y1 u // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT 0 d9 N4 ]9 `& e+ k% g // ' V c- Q/ ]3 z1 E5 T a // PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR 0 E, N; T. Z6 J# G5 x // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT ' N( I8 [; g0 g7 [# | // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT % ?% ]+ h, a* G2 }$ J- E // ***************************************************************************** // 0 l& _0 @! u1 Z4 | // PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer 1 q* j O4 E: W: q) l9 z9 @ // N4 f1 g3 \% m$ v# b9 N% A // ***************************************************************************** #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) 6 ?4 e1 k4 Y' z% q% V: } // ENP0 - G% J" @' `9 {5 s #define ENDP0_PACKETSIZE 0x40 #define ENDP0_RXADDR BASEADDR_DATA + F5 Z6 A, m | #define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) // ENP1 - E$ e( s6 t W! l' Y #define ENDP1_PACKETSIZE 0x40 - D( @$ f: w4 u0 \ #define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) // ENP2 #define ENDP2_PACKETSIZE 0x40 * o- {; a" w5 a; t, l6 R- e% A$ ^ #define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) ) x" p8 v+ n: j% w // ENP3 # W+ F" B/ P0 j3 a2 d: U5 ~ #define ENDP3_PACKETSIZE 0x40 , B& w4 Q0 C) x2 {. { #define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) : n# h. m) S! p3 O1 ~% @ #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) " S1 }" @1 n* z* `: [ // ENP4 + U+ c4 O: M5 u# E( x #define ENDP4_PACKETSIZE 0x40 #define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) ( P( Q' ~" Y2 h- c* e7 [; ?8 ^ #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) ( y5 V0 {& R* Q4 u% d3 ~ 8 k9 j* B u0 Z% \, i7 t: { // ENP5 % B `( ~, s2 \( S2 j B3 P1 b #define ENDP5_PACKETSIZE 0x40 0 p5 T. G6 P* r c P [( ]- O #define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) 9 W& `9 i2 I, w1 O8 @7 y$ o ) o7 j4 `& {9 |. r3 Z' j8 j' W // ENP6 #define ENDP6_PACKETSIZE 0x40 #define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) ; W/ [ p, F( B: |' d0 b // ENP7 6 k7 M0 n" G7 S& d( c+ }9 _ #define ENDP7_PACKETSIZE 0x40 #define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) - M; h* P& F( U8 [! F8 q7 K) i #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE) * u: V6 Y( @0 t9 F5 ~ 这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。 ) i. N, v* R6 X$ F& i4 T9 I // ***************************************************************************** & x p8 Z9 g2 x9 D* r: T" z! L/ H // Function Name : INT_ISTR_CTR // Description : ISTR Correct Transfer Interrupt service routine. 9 }- ~8 R* C1 |& D5 P // Input : 0 r% S, N1 J- |- I7 L7 s // Output : // Return : ! `5 ^0 Q! A! w2 Z/ X // ***************************************************************************** 2 t9 n+ E+ I* d7 \ void INT_ISTR_CTR(void) ' n7 g3 D, s, q- [5 J% F { unsigned short wEPIndex; unsigned short wValISTR; $ L8 N7 p0 |, o% Y" j unsigned short wValENDP; . \% B$ u0 O! u. H) X: A1 ^: e while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) { // Get the index number of the endpoints " c( ^* ?1 q' j( h" f6 E/ F; Q) s wEPIndex = wValISTR & ISTR_EP_ID; 1 Q9 h1 p# y6 n2 `1 D 8 a e' @$ \0 ]% c if(wEPIndex == 0) { 4 K& k. |4 o9 C3 ] // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_NAK); / O V- q+ G/ M2 k // Transfer direction if((wValISTR & ISTR_DIR) == 0) { // DIR=0: IN // DIR=0 implies that EP_CTR_TX always 1 ClrEPR_CTR_TX(ENDP0); ' n- x" |# N8 J5 E( q CTR_IN0(); . B: e& d+ t w( y7 D( q return; } else { 6 C3 ^& K6 m, v7 h // DIR=1: SETUP or OUT // DIR=1 implies that CTR_TX or CTR_RX always 1 wValENDP = GetEPR(ENDP0); 1 \6 c) p* a7 v v* q if((wValENDP & EP_CTR_TX) != 0) 9 {+ w' p# e* d9 b2 g) ~% |/ H: @! H { ) [2 N: I" r& ^- [1 j7 f* n0 P ClrEPR_CTR_TX(ENDP0); " A( K# E! U3 x- E) K CTR_IN0(); # l( S6 |& g* M; v3 O% @ return; } else if((wValENDP & EP_SETUP) != 0) 0 [/ |/ r' F" Z1 L) e { ClrEPR_CTR_RX(ENDP0); CTR_SETUP0(); ; f- P7 p: u. K9 z7 ?/ U return; . a% e! I4 R2 T9 k( a } 3 m2 L5 @, D# @- H else if((wValENDP & EP_CTR_RX) != 0) { ClrEPR_CTR_RX(ENDP0); * T( K% K6 p2 z& {: ?) s" F CTR_OUT0(); ) c: u8 @7 U* u, r( J return; } } , N" D! L( C1 k* W! K4 }- y } ) c% N- j7 v- q1 n // Other endpoints 4 S% c' H# Q* m5 _ else ; Z8 x& Y* i% t; I { 0 ~0 C& V$ Z8 S. E$ I' X( g8 I. V wValENDP = GetEPR(wEPIndex); / z. i0 Q% Q( ?9 o5 _2 Y% E SetEPR_RXStatus(wEPIndex, EP_RX_NAK); SetEPR_TXStatus(wEPIndex, EP_TX_NAK); ; M3 C- z1 C4 K if((wValENDP & EP_CTR_TX) != 0) { 4 ]* c ]7 v( V) m8 W ClrEPR_CTR_TX(wEPIndex); 6 G' V/ {$ t9 U" u% N- d switch(wEPIndex) + A3 m/ w+ l" g8 U! O9 { { , v$ `/ g, I$ j* O case ENDP1: CTR_IN1(); break; & ^& ^: [6 e* k2 x- a* S case ENDP2: CTR_IN2(); break; case ENDP3: CTR_IN3(); break; 5 n# \) a; `- G/ W3 Z& H. F case ENDP4: CTR_IN4(); break; 3 ?( J. m& w# o+ f) Z& J case ENDP5: CTR_IN5(); break; case ENDP6: CTR_IN6(); break; case ENDP7: CTR_IN7(); break; default: break; } & \# `; n; f3 |8 A } ; W2 }3 h8 r h _ ; Z5 K0 j$ r' P5 u9 |( p% y if((wValENDP & EP_CTR_RX) != 0) - E+ u3 C4 {/ |+ T! [6 a% {" M { ClrEPR_CTR_RX(wEPIndex); switch(wEPIndex) 2 a# I2 d" r! Q" A; H+ F/ o, R { % @; b/ |" r) z! B case ENDP1: CTR_OUT1(); break; case ENDP2: CTR_OUT2(); break; - r5 ?0 `$ X& i V# a" I6 |6 w case ENDP3: CTR_OUT3(); break; case ENDP4: CTR_OUT4(); break; case ENDP5: CTR_OUT5(); break; * [( @5 t4 ^! B D# J% {% U case ENDP6: CTR_OUT6(); break; . x8 F, x5 I# |" @ case ENDP7: CTR_OUT7(); break; # H" R0 L: S% S/ R default: break; } } } 1 {) A5 o3 l+ _4 y' y: X } # L5 \( E+ P( A3 [, T+ _ } 4 V3 A6 R0 m) x8 Q 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固件函数的一些介绍