
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: void ISTR_CTR(void); ) z8 r y6 H# U" B6 |, A void ISTR_SOF(void); . i. ^% I1 o7 Z/ z& e6 D* j void ISTR_ESOF(void); void ISTR_DOVR(void); 9 L/ {) r) S; a- L& ~( w R void ISTR_ERROR(void); void ISTR_RESET(void); void ISTR_WAKEUP(void); void ISTR_SUSPEND(void); 这些处理函数使能由定义CNTR_MASK决定: // CNTR mask control #define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \ ' f! m1 T! I; R$ i3 g$ b CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \ ) F( l: n8 A, Q; c9 }" N 其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。 # Z5 P1 M7 X3 F+ V& L/ k8 W ; U- V- m. O! K ^3 r1 i9 o // ***************************************************************************** 5 o& p8 _- Q- |2 Y7 h8 s1 K) e: S // Function Name : INT_ISTR_RESET \# x& j4 ]- G3 F& R7 b/ p# x // Description : ISTR Reset Interrupt service routines. // Input : : [/ D4 H" E2 f! ~" x // Output : // Return : // ***************************************************************************** ' D$ q: z6 o( ]# y void INT_ISTR_RESET(void) { 5 U) L; z' Y2 G5 h // Set the buffer table address 6 g* d4 _$ c- N, x6 S SetBTABLE(BASEADDR_BTABLE); ( ]/ t( @; [8 z2 Z L # p3 E' p3 f& a* `3 g // Set the endpoint type: ENDP0 & `' s8 n9 W( u7 @8 \! v SetEPR_Type(ENDP0, EP_CONTROL); ) o8 c% C0 i4 O- j: ] Clr_StateOut(ENDP0); $ e1 X+ ]0 l3 |9 {: g" V. z 4 D* f# f7 g6 G) j8 v" z // Set the endpoint data buffer address: ENDP0 RX 9 E/ K3 T$ t" X' G SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR); 7 l2 j2 m( C1 S1 Y4 Q: M4 h // Set the endpoint data buffer address: ENDP0 TX SetBuffDescTable_TXCount(ENDP0, 0); ! O" I0 z# R$ j; T! g: u SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR); 8 U, D4 u i4 M, ]% K: g D7 J // Initialize the RX/TX status: ENDP0 SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_NAK); " y4 n. a: @3 K6 I2 L# T/ b( R) Q // Set the endpoint address: ENDP0 ' G0 }: {: s- d9 Y, g3 h SetEPR_Address(ENDP0, ENDP0); // --------------------------------------------------------------------- // TODO: Add you code here " `: X3 D( g/ s4 D: t3 ^- ^ // --------------------------------------------------------------------- 2 C( l1 S" y( @3 p7 ^& f // Set the endpoint type: ENDP1 SetEPR_Type(ENDP1, EP_INTERRUPT); Clr_StateOut(ENDP1); 9 H3 U( _5 _* ^) C( N# { // Set the endpoint data buffer address: ENDP1 RX ( J9 ], P* N8 p" q SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); ) L) }. o0 Z! l a5 x W SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR); // Set the endpoint data buffer address: ENDP1 TX SetBuffDescTable_TXCount(ENDP1, 0); 1 i+ l1 G" Q, i' e+ o SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR); # F) A. T& T% A% G) r' J$ L // Initialize the RX/TX status: ENDP1 SetEPR_RXStatus(ENDP1, EP_RX_VALID); ( Z1 s1 i- ]* H. D SetEPR_TXStatus(ENDP1, EP_TX_DIS); ; v/ H6 \' K& a1 J' s6 { " q% R# `( f1 V // Set the endpoint address: ENDP1 - r) d$ h3 m( F, v- n8 B SetEPR_Address(ENDP1, ENDP1); * |8 {& A3 C% [6 s : }+ v Q9 l' `+ E" Z6 p- J SetEPR_Type(ENDP2, EP_INTERRUPT); , D2 r8 O6 a1 e Clr_StateOut(ENDP2); . m0 D$ S$ \8 x% H6 m. c2 U3 ?# g2 D3 r // Set the endpoint data buffer address: ENDP2 RX SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR); 4 e2 m1 O2 ` F0 h // Set the endpoint data buffer address: ENDP2 TX SetBuffDescTable_TXCount(ENDP2, 0); / i7 k5 J/ c+ D7 n7 q SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR); ) [# q% _% V; s5 v1 F2 o* B // Initialize the RX/TX status: ENDP2 # w+ c7 e0 a( {4 y" P* Q SetEPR_RXStatus(ENDP2, EP_RX_DIS); SetEPR_TXStatus(ENDP2, EP_TX_VALID); $ q( h. S% }( D 1 s( k: [- A( x% Y+ U! v! | // Set the endpoint address: ENDP2 SetEPR_Address(ENDP2, ENDP2); + `4 O# R* Z" S+ V / @% n5 H- s2 G+ {* y# H9 V // --------------------------------------------------------------------- . K: T7 ~2 {) ^& o* E) w% v: N // End of you code + P, j* P% A$ K- z T, X7 x // --------------------------------------------------------------------- SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); % A0 P: e8 t9 c5 Z% U3 \/ {5 B7 M vsDeviceInfo.eDeviceState = DS_DEFAULT; vsDeviceInfo.bCurrentFeature = 0x00; vsDeviceInfo.bCurrentConfiguration = 0x00; vsDeviceInfo.bCurrentInterface = 0x00; vsDeviceInfo.bCurrentAlternateSetting = 0x00; vsDeviceInfo.uStatusInfo.w = 0x0000; } 在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。 ) v v' o1 Z: A 值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下: // USB_IP Packet Memory Area base address + ^1 y t" V3 N+ _1 N #define PMAAddr (0x40006000L) n0 @8 V$ [) y# p% t 4 S! \5 r- n: B, Q/ j6 T // Buffer Table address register ( F! l6 ` P2 }8 W/ ~% }& N/ h #define BTABLE ((volatile unsigned *)(RegBase + 0x50)) " j) X, K; _- {4 e0 F( \ // ***************************************************************************** // Packet memory area: Total 512Bytes // ***************************************************************************** Q( z% a& w+ @ Q1 D; q #define BASEADDR_BTABLE 0x0000 // ***************************************************************************** // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT . ?# B" a5 U1 b# q4 d2 Q // PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT // / V6 P$ _6 ^1 x/ Q- \& b, b# V // PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR 3 d2 P4 \( ~ z/ j // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT + |- t$ X% b, t7 V; b // + J) c9 O" m0 t! M/ x7 t // PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR ; M+ e8 G) C2 R: f) x$ g // PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT ; u$ }9 c. ?; {8 e // // PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT f5 H& r. U! b; {; p- U // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR ' s( Q& ]" @1 i; x5 s' t3 { // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT // , l. V! [# |+ ~% q' F8 ^! A" \ // PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT " y4 R' u/ V& N; N, Q // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT , ]$ s2 x% J) n( ^- w5 o8 Q // // PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR 7 m& x2 j+ q% A: I // PMAAddr + BASEADDR_BTABLE + 0x0000002A : EP5_TX_COUNT 5 n% t- E: [! i5 E& N // PMAAddr + BASEADDR_BTABLE + 0x0000002C : EP5_RX_ADDR + e: C8 Y! m- b$ s // PMAAddr + BASEADDR_BTABLE + 0x0000002E : EP5_RX_COUNT // 7 c% f; S) O4 K, A1 w. z4 Z2 H // PMAAddr + BASEADDR_BTABLE + 0x00000030 : EP6_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR 5 \$ Y3 k. q' A4 s // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR - B4 T; D r8 n9 p' `, O" H // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR & r# g5 x& v2 n$ l- _2 r. ~( n% w& b" y& z // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT / p/ p- T [* E. u, s* `* f$ F, P1 j // ***************************************************************************** / p' F$ A0 I, R# A, x# ? // // PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer : K2 k6 n" @/ q // // ***************************************************************************** % P4 t( D1 l6 {. k #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) 8 V3 L( b/ A) j* f# | 1 b# {8 W; _; g: H t // ENP0 #define ENDP0_PACKETSIZE 0x40 #define ENDP0_RXADDR BASEADDR_DATA $ ~+ X" B0 @; _- A. i6 D! W0 P% C7 [ #define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) ; Z% t. w2 Z; H6 @( t$ c$ T // ENP1 #define ENDP1_PACKETSIZE 0x40 7 g3 e6 }0 h- P$ n+ x5 K #define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) % t X, j3 p% P2 }0 S2 e #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) ' i% a, r( D2 T( h4 ~2 q // ENP2 0 q. h" ~4 j1 r7 L% K #define ENDP2_PACKETSIZE 0x40 + q* L$ N& X* v+ B #define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) ( A3 g' ^% E' \2 m2 t( P2 `% K5 l 4 i* }6 C2 o& B# a. W // ENP3 #define ENDP3_PACKETSIZE 0x40 #define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) * L) K6 n2 a5 X& r // ENP4 #define ENDP4_PACKETSIZE 0x40 #define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) 1 F1 A& F9 T0 A6 |1 Q #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) ! W: Y% }: g- w& i- } // ENP5 4 }$ B/ b0 R8 k: w' ~$ X1 t( o/ m/ P #define ENDP5_PACKETSIZE 0x40 #define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) 2 O2 }1 k/ {8 O5 ?+ e4 C* Q ) |" p- D- d* _0 H4 m" ^1 u% w+ `+ ^$ ]% l // ENP6 #define ENDP6_PACKETSIZE 0x40 #define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) % A3 v9 C# g2 D7 m // ENP7 #define ENDP7_PACKETSIZE 0x40 #define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE) 9 X% O* t3 z8 V" ` 这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。 * V- F1 w& H3 G% X b2 c. ~ : g& j0 \5 \, R2 @1 e // ***************************************************************************** // Function Name : INT_ISTR_CTR $ T; X& w8 m! s5 `3 Z // Description : ISTR Correct Transfer Interrupt service routine. 8 L% j$ N, K$ O! A // Input : // Output : // Return : // ***************************************************************************** 0 @! ?* g+ e5 K; t. } void INT_ISTR_CTR(void) ' T9 u1 z/ ?/ x. n2 ^7 c1 ]: I { 5 O1 T- a* Y9 p' T5 z unsigned short wEPIndex; 9 o8 ^- l# h/ D- I3 S unsigned short wValISTR; 1 W* x; r" ` f1 W Q3 p* a! D4 g. B unsigned short wValENDP; while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) ; u" R: a& {* D9 d7 e3 ~ { 0 G" v2 I8 F; w // Get the index number of the endpoints wEPIndex = wValISTR & ISTR_EP_ID; 3 v- x3 p& S; [3 @ if(wEPIndex == 0) 0 m% \) c0 ]4 e& z+ N0 I+ E( Z { ' w G3 u1 s# N1 R1 V // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) * t3 U: z G2 X/ v SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_NAK); . t# U0 |$ x% d& K // Transfer direction if((wValISTR & ISTR_DIR) == 0) { * p# L" w+ o$ R% l( k3 s0 ^ Z // DIR=0: IN // DIR=0 implies that EP_CTR_TX always 1 ClrEPR_CTR_TX(ENDP0); 5 A& Y4 z! P B: u' a* V+ t- b/ V CTR_IN0(); 4 M0 w' P6 E, e2 B return; } else { // DIR=1: SETUP or OUT // DIR=1 implies that CTR_TX or CTR_RX always 1 & e, y y* \ b# t wValENDP = GetEPR(ENDP0); if((wValENDP & EP_CTR_TX) != 0) - M8 w3 r' i! ^8 } [ { ; i6 H+ |7 Q5 v3 T. B+ e. }9 f ClrEPR_CTR_TX(ENDP0); 2 X% ^) k1 m) @6 r2 J# b5 I) S! W CTR_IN0(); & q4 B" |, T B9 v0 A" e& _ return; } else if((wValENDP & EP_SETUP) != 0) { ClrEPR_CTR_RX(ENDP0); CTR_SETUP0(); return; * P! ^5 C- H0 Q } else if((wValENDP & EP_CTR_RX) != 0) : v1 ~" z/ K: L( r% \- ` { 4 e7 e% r' s7 _. U! G ClrEPR_CTR_RX(ENDP0); # }$ w( G; B0 | CTR_OUT0(); return; } } & G: ^- r% ?6 ? U0 h } * ~! D4 i% ?+ \8 w9 z // Other endpoints - [# o0 P* V0 d7 s/ S else . [6 C0 T8 t9 G+ b { wValENDP = GetEPR(wEPIndex); SetEPR_RXStatus(wEPIndex, EP_RX_NAK); L! ^# G% j- l9 w: ] SetEPR_TXStatus(wEPIndex, EP_TX_NAK); if((wValENDP & EP_CTR_TX) != 0) { ClrEPR_CTR_TX(wEPIndex); ! c$ Y9 S; [2 D0 ? switch(wEPIndex) { ( p) Z! b( U* t R4 f case ENDP1: CTR_IN1(); break; + W. B8 k2 @6 k& d case ENDP2: CTR_IN2(); break; $ {; L! }7 l# b% d case ENDP3: CTR_IN3(); break; 7 T7 D& Y' M' E9 }+ f case ENDP4: CTR_IN4(); break; case ENDP5: CTR_IN5(); break; - Y) T; W9 V( G9 [& D case ENDP6: CTR_IN6(); break; case ENDP7: CTR_IN7(); break; default: break; } " m d# [, \1 {: S8 K- } } 8 M+ Y: f) ?$ ?* _$ |7 @, I( ?7 Y% V if((wValENDP & EP_CTR_RX) != 0) { ClrEPR_CTR_RX(wEPIndex); 5 u0 y0 s3 w" j- C" i switch(wEPIndex) + Y M. R8 g" b- A9 t7 m6 C: a { : s* h1 y$ V; j- B& z case ENDP1: CTR_OUT1(); break; case ENDP2: CTR_OUT2(); break; ! z9 R1 T2 |) I2 ^) e/ A case ENDP3: CTR_OUT3(); break; / }/ f9 m5 n1 K5 [# _ case ENDP4: CTR_OUT4(); break; case ENDP5: CTR_OUT5(); break; & s8 G) F/ N: z* l2 O" b' [ case ENDP6: CTR_OUT6(); break; 2 T' k& w0 l. z6 \2 d case ENDP7: CTR_OUT7(); break; % ?& W1 [/ h9 @5 h default: break; % J( ^- G: e( A0 c0 a: K+ y9 J } ) _" }; t( {! U6 l1 w, ]# I } } } % [. t1 t- ^5 g( 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固件函数的一些介绍