
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: ) ~" p* p" ]; c1 q. L" u3 t2 L void ISTR_CTR(void); 6 ^# l' @. }* H( G1 ^( Z: T void ISTR_SOF(void); & v' T& A! j; U0 z0 g1 K& g void ISTR_ESOF(void); 3 w5 Y* v# v3 [$ Y0 u9 j/ f* H1 e void ISTR_DOVR(void); void ISTR_ERROR(void); + @+ e) @* Q; X9 E$ o void ISTR_RESET(void); void ISTR_WAKEUP(void); 8 `/ |/ N7 t' X0 s void ISTR_SUSPEND(void); & G" ]4 Q+ j# d. z8 o6 I9 Y 这些处理函数使能由定义CNTR_MASK决定: // CNTR mask control 6 A+ }: z9 g5 X. U0 m #define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \ " U) g* e. y l' J# H' O8 m- l- z CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \ * `( R- ]+ s* O( U; H: Q, } 其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。 9 ?- m; [. \( L, B+ O- l // ***************************************************************************** " s) d* m# e! j# h# f4 o( b // Function Name : INT_ISTR_RESET // Description : ISTR Reset Interrupt service routines. 3 H" J+ c0 N( S5 Q/ f" s+ ?& }7 o // Input : // Output : // Return : ! d2 s) I/ w9 Z // ***************************************************************************** void INT_ISTR_RESET(void) " _3 H9 Y. b7 v6 F, i7 B$ f' b { $ l0 c k1 u, J0 ^2 x7 @ // Set the buffer table address * S; T2 d6 p) q# E* x7 c SetBTABLE(BASEADDR_BTABLE); // Set the endpoint type: ENDP0 SetEPR_Type(ENDP0, EP_CONTROL); Clr_StateOut(ENDP0); 1 O/ S. d) _$ J, @# A // Set the endpoint data buffer address: ENDP0 RX SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); , z4 I9 C: n1 s V: y SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR); : N2 g. g" e- F2 r1 i% y: \% t6 v // Set the endpoint data buffer address: ENDP0 TX 0 t _# ]. V) ?& l, b" |/ r SetBuffDescTable_TXCount(ENDP0, 0); SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR); % b2 f5 Q) W! [% _6 t // Initialize the RX/TX status: ENDP0 SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_NAK); // Set the endpoint address: ENDP0 SetEPR_Address(ENDP0, ENDP0); 5 q4 z3 ]2 s" J. p4 S 8 D) M) I# u+ K; I h6 M9 \# [ // --------------------------------------------------------------------- 3 Q: Y% `, J$ M- {7 \9 _( Q O // TODO: Add you code here // --------------------------------------------------------------------- // Set the endpoint type: ENDP1 SetEPR_Type(ENDP1, EP_INTERRUPT); Clr_StateOut(ENDP1); ) @- c5 x1 X( c: }9 i& }2 F* P // Set the endpoint data buffer address: ENDP1 RX 9 W7 G0 g' x' D9 y; E SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); . F( ?; J% }$ X! Q0 R# H SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR); : r0 P3 s; `7 I! x2 K7 N // Set the endpoint data buffer address: ENDP1 TX SetBuffDescTable_TXCount(ENDP1, 0); " L" t# v/ _+ c6 b \" t9 l SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR); 4 U: h0 I2 p% u& r; W( H( C$ C 6 O, D, q- L& p6 g. _ // Initialize the RX/TX status: ENDP1 SetEPR_RXStatus(ENDP1, EP_RX_VALID); ) K3 {5 k7 t; l5 ? SetEPR_TXStatus(ENDP1, EP_TX_DIS); - d; {1 x! _" Q( m1 [ // Set the endpoint address: ENDP1 ' h4 m' }, q/ x8 }6 V SetEPR_Address(ENDP1, ENDP1); + ?1 o) o3 _! G; A2 s SetEPR_Type(ENDP2, EP_INTERRUPT); 0 {( ?$ i% W7 L9 E8 ?- u$ j# G- \ Clr_StateOut(ENDP2); // Set the endpoint data buffer address: ENDP2 RX 2 W- g6 [0 n% F8 ?3 e SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); ' F) w) \5 l( G6 {+ x SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR); // Set the endpoint data buffer address: ENDP2 TX $ {/ `7 x5 _; ^$ ?/ | SetBuffDescTable_TXCount(ENDP2, 0); SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR); // Initialize the RX/TX status: ENDP2 SetEPR_RXStatus(ENDP2, EP_RX_DIS); / l! }2 T1 w( U$ B6 J SetEPR_TXStatus(ENDP2, EP_TX_VALID); 2 l) G4 S: E$ V) V3 k, ?5 l / r3 }* v2 L8 x. y( k) j1 n4 c // Set the endpoint address: ENDP2 ( }. J) O* W5 A: u2 { SetEPR_Address(ENDP2, ENDP2); ! y3 q+ @1 u9 _1 t9 K p // --------------------------------------------------------------------- + y& o. a+ G" E" d+ x% ^, _ // End of you code // --------------------------------------------------------------------- ; q2 W% F2 H& c- A# A) C n8 s 6 x W& a; T$ W: A* p SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); 8 x' O/ |' F0 D" b7 ?2 ]: ~- y vsDeviceInfo.eDeviceState = DS_DEFAULT; vsDeviceInfo.bCurrentFeature = 0x00; ' c( p" n8 [3 g0 x7 `$ I vsDeviceInfo.bCurrentConfiguration = 0x00; : q$ W9 |6 w5 k vsDeviceInfo.bCurrentInterface = 0x00; % m( [& T, O! M2 M n% d vsDeviceInfo.bCurrentAlternateSetting = 0x00; , ]8 v9 I* H2 f5 |! Z% L! ^ vsDeviceInfo.uStatusInfo.w = 0x0000; / g; i0 E: [6 {$ |# ~6 C! ^ } 1 Y1 \0 i! R, L7 a1 s- | 在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。 值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下: // USB_IP Packet Memory Area base address ( F# q2 [/ c { #define PMAAddr (0x40006000L) 2 P& R5 A9 e( K# \" C // Buffer Table address register #define BTABLE ((volatile unsigned *)(RegBase + 0x50)) i% G" q, ], E // ***************************************************************************** // Packet memory area: Total 512Bytes ( Y' T! l( \4 V! J // ***************************************************************************** # i) V9 o: J, J9 h. \" h# \ #define BASEADDR_BTABLE 0x0000 // ***************************************************************************** // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR * V& K. x5 Z0 X9 K- F // PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR 9 C, S) C- b( Y' g1 A9 c* C // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT : x( l" o+ M" p6 M // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR 0 o! C6 R4 J4 Z$ N // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR % x/ ?/ m' s' [, _$ i5 i // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT & z' S) Y" K: D: R& S4 w2 c$ ` // ; g. ?! u1 k2 `' g4 F // PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR ) T7 E9 Q! ]. ~2 |1 i& N // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT ! F+ q3 n; S7 w // // PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR ) T$ R" ^! w8 D5 L // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT ; g4 Z6 u+ H5 K // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000002A : EP5_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000002C : EP5_RX_ADDR 1 c6 U" H, M. W7 D: X9 O' |! B* W // PMAAddr + BASEADDR_BTABLE + 0x0000002E : EP5_RX_COUNT 1 |3 c" E" M- Y6 o, ^& p. q // // PMAAddr + BASEADDR_BTABLE + 0x00000030 : EP6_TX_ADDR $ Q8 e* ^/ J; ~5 N2 p6 J: L // PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT 0 q5 i3 Q; j/ F3 P // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT 6 X# s8 M( E- |7 F3 \ // // PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT ( K5 e. q) Q4 a; b$ D: q/ {- o // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT // ***************************************************************************** ( X5 M7 D+ ^, M( A // % O, ~0 L! _1 `2 s) }2 i // PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer $ ~: n; N: P+ D, j: x6 f // 5 K$ P6 X9 Y8 y; q. c // ***************************************************************************** #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) ) e. @. }( y- B- x3 V# T# V // ENP0 #define ENDP0_PACKETSIZE 0x40 / N$ e8 n: B% B$ r #define ENDP0_RXADDR BASEADDR_DATA " c" O# g5 b# a #define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) ; h3 @3 N0 t. P+ Q: N" A ]9 p ( s+ ^- h' e1 f+ [2 s9 {8 Y$ O // ENP1 #define ENDP1_PACKETSIZE 0x40 4 ?# U9 ?3 N" ]! r* e #define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) C y w6 d/ j // ENP2 #define ENDP2_PACKETSIZE 0x40 #define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) ' n8 S0 \3 n( g! v7 T* K; v; G // ENP3 & w% y5 G( o; b, L #define ENDP3_PACKETSIZE 0x40 8 i( y8 k4 J- ^6 [7 j #define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) 5 h: T5 G0 S9 Q$ H7 I& y // ENP4 #define ENDP4_PACKETSIZE 0x40 #define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) 5 q0 \( J9 ^! O" V #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) // ENP5 7 q! O+ j) X# s4 t% K! B #define ENDP5_PACKETSIZE 0x40 #define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) # x8 F* C; M% F // ENP6 " n6 t% e4 [0 ~# s: \) n #define ENDP6_PACKETSIZE 0x40 #define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) * N# n6 f' d! [ #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) // ENP7 #define ENDP7_PACKETSIZE 0x40 ! Q, j+ S2 E, h4 Z5 T& Y, W" E #define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE) ) b, n, F) p9 E2 m+ V ; A% i$ G* U- S9 O( Y$ _" f0 } 这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。 + b0 ]) e) ~! V8 a3 ]# e 9 K5 @9 @" v* H/ v1 P! q1 l // ***************************************************************************** // Function Name : INT_ISTR_CTR // Description : ISTR Correct Transfer Interrupt service routine. // Input : # F5 {: K5 S) u5 z% q // Output : // Return : // ***************************************************************************** void INT_ISTR_CTR(void) , `8 t4 u6 Y0 G) e$ c- o! ^, p { unsigned short wEPIndex; i# X: U/ e+ A unsigned short wValISTR; unsigned short wValENDP; + _* V: t6 W y+ y1 C while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) { // Get the index number of the endpoints wEPIndex = wValISTR & ISTR_EP_ID; 7 m. L l4 G+ x if(wEPIndex == 0) { // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) 0 L, U% T3 ^, C3 `$ u/ Z# ^( `1 l SetEPR_RXStatus(ENDP0, EP_RX_NAK); " }$ v( Z) g R0 e, d SetEPR_TXStatus(ENDP0, EP_TX_NAK); // Transfer direction if((wValISTR & ISTR_DIR) == 0) % \ b. c/ D7 O, Z1 u4 e { // DIR=0: IN // DIR=0 implies that EP_CTR_TX always 1 ClrEPR_CTR_TX(ENDP0); CTR_IN0(); - Z! h _6 e# Q% ?( g return; 0 b& D" T: p$ r" _* p4 L+ A } else { 9 S. j) S; l4 e5 ^/ Q) O // DIR=1: SETUP or OUT // DIR=1 implies that CTR_TX or CTR_RX always 1 4 |3 }* N$ N6 }/ F wValENDP = GetEPR(ENDP0); if((wValENDP & EP_CTR_TX) != 0) { / t G$ d9 G* W ClrEPR_CTR_TX(ENDP0); 2 I0 j/ ]# A# ~4 D% t CTR_IN0(); return; } 4 x6 h9 d8 }6 N8 o& }/ \0 W W else if((wValENDP & EP_SETUP) != 0) 2 A+ S+ p! `% Y* n0 R+ a' R0 O { $ D( T7 K2 Y4 A# L ClrEPR_CTR_RX(ENDP0); CTR_SETUP0(); return; } 8 v: K5 g" q8 L) ]% }1 f( C1 v+ U! [: D else if((wValENDP & EP_CTR_RX) != 0) 8 P/ G2 s2 l* m8 V" G6 [9 j/ u$ t { $ Y# w& Y8 ~! G8 X3 y2 s ClrEPR_CTR_RX(ENDP0); CTR_OUT0(); return; } 3 j! o* L+ M! k9 i! b, |) b) ? } } // Other endpoints N! ?9 B9 E# | else / y, s L' f5 ^6 N5 B9 d { wValENDP = GetEPR(wEPIndex); SetEPR_RXStatus(wEPIndex, EP_RX_NAK); SetEPR_TXStatus(wEPIndex, EP_TX_NAK); ! |; r) p) [% d0 X if((wValENDP & EP_CTR_TX) != 0) { ClrEPR_CTR_TX(wEPIndex); # S, y6 H4 y) x switch(wEPIndex) { case ENDP1: CTR_IN1(); break; case ENDP2: CTR_IN2(); break; case ENDP3: CTR_IN3(); break; case ENDP4: CTR_IN4(); break; % P9 X9 V0 | O case ENDP5: CTR_IN5(); break; case ENDP6: CTR_IN6(); break; case ENDP7: CTR_IN7(); break; default: break; - f$ Y+ ~0 S, Z7 b% G" P, L } } % l; A8 r+ } X# ?. P if((wValENDP & EP_CTR_RX) != 0) { ClrEPR_CTR_RX(wEPIndex); + `( [! Q2 T% N switch(wEPIndex) { 7 \! @4 L6 L* B, _3 }3 s' X2 j case ENDP1: CTR_OUT1(); break; 9 f! c2 d3 |- _5 F7 F ~, ]/ s! V case ENDP2: CTR_OUT2(); break; case ENDP3: CTR_OUT3(); break; case ENDP4: CTR_OUT4(); break; case ENDP5: CTR_OUT5(); break; 4 H" d: K4 P" T* Q8 `) B case ENDP6: CTR_OUT6(); break; case ENDP7: CTR_OUT7(); break; ( A' d) l$ b0 g4 I4 |9 X# k8 x6 f default: break; 3 I" n4 |$ e, q } % ^6 {& w1 U( b' K7 h; p; c } 8 T& B% i" R" J6 | z0 B/ ~ } } 1 E. I6 k9 O" ]5 V% H- P9 L! r' U. i } 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固件函数的一些介绍