
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: void ISTR_CTR(void); void ISTR_SOF(void); ( ^% R; ]* C2 [ void ISTR_ESOF(void); 7 `$ Y6 S# c) }5 h void ISTR_DOVR(void); void ISTR_ERROR(void); " H7 A' ~5 u0 t4 k void ISTR_RESET(void); 8 A+ @9 T8 {1 `. } void ISTR_WAKEUP(void); : o: x0 g2 ^5 r( S. h$ h% A8 r void ISTR_SUSPEND(void); + j6 ?6 \' i: R 这些处理函数使能由定义CNTR_MASK决定: // CNTR mask control #define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \ CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \ 3 o, R# ~+ L+ I( H8 M# n/ R- v- ?; H* m( A 其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。 " G, G; Q. M% y9 x* l // ***************************************************************************** // Function Name : INT_ISTR_RESET ( e3 K* P5 | P } ] // Description : ISTR Reset Interrupt service routines. // Input : // Output : // Return : 2 }/ J. ]" W5 ^) {- D& y: Z // ***************************************************************************** " K5 s0 D' K' Z& g void INT_ISTR_RESET(void) { - _: V* T8 d3 N* Y2 b // Set the buffer table address SetBTABLE(BASEADDR_BTABLE); . u7 r$ F9 U8 ~( m; W* N, l4 \% f" ? // Set the endpoint type: ENDP0 SetEPR_Type(ENDP0, EP_CONTROL); Clr_StateOut(ENDP0); // Set the endpoint data buffer address: ENDP0 RX SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); 4 D$ x; | B7 V0 a5 i' g- l. F SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR); // Set the endpoint data buffer address: ENDP0 TX SetBuffDescTable_TXCount(ENDP0, 0); SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR); / c& X: v1 M" y3 h' y // Initialize the RX/TX status: ENDP0 / z, v- T R, y. U% |5 V/ R SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_NAK); 9 `/ A1 w- S1 Y& e& o: }( U // Set the endpoint address: ENDP0 2 w! Y& v# Z5 h6 l- Q6 s2 ~ SetEPR_Address(ENDP0, ENDP0); 4 U" Y" w7 ]4 C) k/ o& A1 B // --------------------------------------------------------------------- & V) T) q1 j O+ R7 ]8 j3 s* k // TODO: Add you code here " ?9 V. ^. B, l. q8 j% F$ Y; S // --------------------------------------------------------------------- % s8 ?) h( d( T" b2 u/ B // Set the endpoint type: ENDP1 1 m4 ~# M: _4 Z SetEPR_Type(ENDP1, EP_INTERRUPT); Clr_StateOut(ENDP1); * @$ l ^4 y1 p& v# P& v7 n ) M5 s# _; N+ J // Set the endpoint data buffer address: ENDP1 RX 6 q1 {4 ]$ x3 d$ } SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); " r" _2 h, {: d- h+ Q5 c SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR); ) T4 o( y/ e* g1 u$ c: Z- W. \4 H7 w // Set the endpoint data buffer address: ENDP1 TX SetBuffDescTable_TXCount(ENDP1, 0); SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR); // Initialize the RX/TX status: ENDP1 9 V3 [* G* G, Y& D/ } SetEPR_RXStatus(ENDP1, EP_RX_VALID); 9 @3 z7 `, i# } SetEPR_TXStatus(ENDP1, EP_TX_DIS); ! R: X4 ~4 I! W* }4 g // Set the endpoint address: ENDP1 SetEPR_Address(ENDP1, ENDP1); : s4 E% I2 z, @) l3 H5 |4 i 2 j3 ]% V9 d8 c0 g& }3 _ SetEPR_Type(ENDP2, EP_INTERRUPT); 6 N# q! b7 P* e+ f- d4 Z- s Clr_StateOut(ENDP2); / W- x+ @' S; s( b " I9 g s, ?0 I \8 p3 m2 { // Set the endpoint data buffer address: ENDP2 RX SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR); j {( w" ~! w m- C6 r8 }) X // Set the endpoint data buffer address: ENDP2 TX 4 I5 [. {" h) M2 u SetBuffDescTable_TXCount(ENDP2, 0); SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR); // Initialize the RX/TX status: ENDP2 SetEPR_RXStatus(ENDP2, EP_RX_DIS); 6 [9 v. w; |6 r- r7 v' i SetEPR_TXStatus(ENDP2, EP_TX_VALID); - \% T: j( ^' u! V+ m2 |1 C // Set the endpoint address: ENDP2 SetEPR_Address(ENDP2, ENDP2); ( [) t- [; I. Z; [3 H( a ( |8 G4 K& ^3 ?2 [ // --------------------------------------------------------------------- // End of you code ! A/ |+ r2 L+ e9 X* k) t" C // --------------------------------------------------------------------- - Q8 {& L* E( g7 q7 |7 e6 ` SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); vsDeviceInfo.eDeviceState = DS_DEFAULT; , m: \& {, c4 L* O* i- Z# L vsDeviceInfo.bCurrentFeature = 0x00; vsDeviceInfo.bCurrentConfiguration = 0x00; , ~; C. j; }9 G |6 O; a- y vsDeviceInfo.bCurrentInterface = 0x00; . a. G! |1 y! M) b. H# c1 m) D$ a q vsDeviceInfo.bCurrentAlternateSetting = 0x00; vsDeviceInfo.uStatusInfo.w = 0x0000; } 在这个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 % \! T/ i+ d# g/ u+ i. d #define PMAAddr (0x40006000L) 7 u- y- N4 s4 _' R // Buffer Table address register : P! S; Z7 c$ c& F #define BTABLE ((volatile unsigned *)(RegBase + 0x50)) # \0 D4 p2 [' M- w7 R- |4 D1 R 4 G$ Z& Z+ Y+ B9 }, | // ***************************************************************************** // Packet memory area: Total 512Bytes . H2 ]& r& U; ^% J( | // ***************************************************************************** #define BASEADDR_BTABLE 0x0000 // ***************************************************************************** . @4 k( }9 Z6 [ // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR 1 r, u- a* D$ s5 D // 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 . g, G) e2 v- y% w5 N // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR 2 Q4 b5 a# q. n // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT 8 E' w h$ o" g$ {* \ c' |2 |. b // 5 p2 r1 M/ }, E* h // PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT // 2 _# \/ J/ ~$ [. p$ C' y // PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR + _1 @2 P, e: p3 S, `0 j8 x1 T1 H // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT # c' _0 q; A! b/ o/ ~ // // PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR 1 O; D8 E' Q: g1 f! O. M) p; |* u% P' R // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT / v% }: s3 X; g3 l! D q // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR 4 b) |& Z/ c- q3 N4 M! g3 u+ _' S // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT // $ S2 |: h5 [2 O( Q // PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000002A : EP5_TX_COUNT ; I0 v: m8 E/ [; i: r, I) h; o4 ? // PMAAddr + BASEADDR_BTABLE + 0x0000002C : EP5_RX_ADDR 3 x% v' _. v `) ^" X // PMAAddr + BASEADDR_BTABLE + 0x0000002E : EP5_RX_COUNT & o: `0 Y2 w& @4 L0 c // // PMAAddr + BASEADDR_BTABLE + 0x00000030 : EP6_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000032 : EP6_TX_COUNT + W% |# I( Q9 L // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR $ p6 ?/ I5 z E3 a // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT // // PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR 3 A5 K' o( `1 u, v5 ` // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT $ R4 G+ I- V' y5 x- l7 j // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT 5 d' q( s+ V. c$ o/ P // ***************************************************************************** . ~5 q4 ^/ G! g/ Y7 y" s5 |* B // ' b7 ]* g V1 y: o // PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer * M- e3 [/ o7 S( j' L. B // ! O3 {7 b) X/ R0 _ C$ ?% l // ***************************************************************************** #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) // ENP0 x' x# A9 X! W: n! X #define ENDP0_PACKETSIZE 0x40 7 X; r5 d+ X& ^ #define ENDP0_RXADDR BASEADDR_DATA 7 |- C' b- I1 P' k8 j% { #define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) + R! k) H% ]( c8 `8 O! z+ y // ENP1 #define ENDP1_PACKETSIZE 0x40 #define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) 5 X9 Q. `( @2 I4 c0 H! ? 7 B3 P1 {8 @/ R! f2 f // ENP2 $ [9 a- p- j) @3 M1 A #define ENDP2_PACKETSIZE 0x40 #define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) # t8 Y' }( m u% c% K2 Q #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) 5 L ]3 g; l& }+ j) v2 H! z // ENP3 - B, s0 t. [( d" b$ n9 M #define ENDP3_PACKETSIZE 0x40 7 T& ^$ b( H4 W #define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) 6 d' b+ a/ y) l #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) ) }) }8 \& h! X5 x4 z' @ // ENP4 ; d5 ~3 y' W1 }9 n: }" b' C+ P #define ENDP4_PACKETSIZE 0x40 #define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) ( ^' n: F$ k9 \6 [ |5 S+ M' A9 O #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) * M/ \+ W- @3 f* {* G // ENP5 & N. W9 ?* Z* ]0 ?( `1 o1 E/ q #define ENDP5_PACKETSIZE 0x40 #define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) $ A: T; A) h- }' b // ENP6 #define ENDP6_PACKETSIZE 0x40 3 K4 z/ @5 J4 [ #define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) ; H. C: {$ O6 d4 Y #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) 4 Q/ I" n' b7 R, M5 c5 s // ENP7 #define ENDP7_PACKETSIZE 0x40 #define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE) 这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。 0 i) [1 f* S$ U e2 ~9 a4 G ; Z& P, j4 F. \; g+ J) _- E9 h // ***************************************************************************** // Function Name : INT_ISTR_CTR // Description : ISTR Correct Transfer Interrupt service routine. 9 n$ |0 y& m$ x. r( L$ B // Input : // Output : 5 i! x' j% K. V // Return : ' _- G; }1 S2 \2 x // ***************************************************************************** void INT_ISTR_CTR(void) { $ k* N4 X E; ?( d* M+ g& X. ~5 ~ unsigned short wEPIndex; ! r, Z$ j6 j0 _6 v9 {1 b- g [ unsigned short wValISTR; unsigned short wValENDP; # ^3 P- z( I# } while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) 8 h9 W- ~$ y" k4 M w/ W2 g { // Get the index number of the endpoints wEPIndex = wValISTR & ISTR_EP_ID; 5 [) M& E8 Q; K if(wEPIndex == 0) { ; f4 I) s8 Z+ w: c1 Z // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) SetEPR_RXStatus(ENDP0, EP_RX_NAK); " W) y( \# p' Y* [0 I SetEPR_TXStatus(ENDP0, EP_TX_NAK); ) D. x' Z! E( z6 o( k // Transfer direction if((wValISTR & ISTR_DIR) == 0) + j2 ]) S$ e' H1 L# | { // DIR=0: IN // DIR=0 implies that EP_CTR_TX always 1 ClrEPR_CTR_TX(ENDP0); CTR_IN0(); return; * D- A- W# l. _" }; Y+ i } 7 D: X# }. t+ k: | else { // DIR=1: SETUP or OUT 5 G [; s/ ?" E' {$ G9 |3 r // DIR=1 implies that CTR_TX or CTR_RX always 1 wValENDP = GetEPR(ENDP0); $ g- { G) q- L4 @# S if((wValENDP & EP_CTR_TX) != 0) { ClrEPR_CTR_TX(ENDP0); . q' Z8 z% i! w( b2 j. `$ D9 d; a CTR_IN0(); return; ; c6 u4 v2 h, U6 t9 Y } else if((wValENDP & EP_SETUP) != 0) { + R3 O) Z/ S5 E! Y; ^" _- K ClrEPR_CTR_RX(ENDP0); - @2 W9 u' z9 i. J! p CTR_SETUP0(); / V8 C+ l9 j# N- A! k3 E6 o4 l return; 9 x+ R% F3 p# {+ ]; p } else if((wValENDP & EP_CTR_RX) != 0) { . o$ o. B6 \& @) y ClrEPR_CTR_RX(ENDP0); ; A2 P' q' a- X5 L. B CTR_OUT0(); 8 ]* n7 ~' M5 Z6 u) ~& m2 w return; } } $ \7 K# C* b" P l6 x: K- D, U } // Other endpoints * \ S& d1 H! ^3 k# X; O else { wValENDP = GetEPR(wEPIndex); 0 K' Z4 {2 b n1 _$ D1 G1 n' j SetEPR_RXStatus(wEPIndex, EP_RX_NAK); 9 b& K0 C6 _) u+ ` e3 U SetEPR_TXStatus(wEPIndex, EP_TX_NAK); " [$ C3 Z! @! f / c9 E4 ^+ C9 ? if((wValENDP & EP_CTR_TX) != 0) { 4 u% V7 O, W5 ^, y" G6 u( h; g ClrEPR_CTR_TX(wEPIndex); switch(wEPIndex) { 1 I' R) i! h I% h9 k9 Z case ENDP1: CTR_IN1(); break; case ENDP2: CTR_IN2(); break; : ]( C9 h; y/ T6 G; p4 I# g case ENDP3: CTR_IN3(); break; \& d! }8 Q& K4 s case ENDP4: CTR_IN4(); break; case ENDP5: CTR_IN5(); break; z3 i' O0 t& B7 A case ENDP6: CTR_IN6(); break; case ENDP7: CTR_IN7(); break; default: break; / b: v) K" N; u! O( Z' O2 F+ o } ' l7 L" u: L, b- M: i/ i7 M M } : g B! {- p0 a- C- M! a + @. {3 D, t0 d" i. x if((wValENDP & EP_CTR_RX) != 0) { * o4 q o1 _0 L' h* L& S% r+ ?+ A ClrEPR_CTR_RX(wEPIndex); 9 ^3 w3 a; B5 K switch(wEPIndex) { case ENDP1: CTR_OUT1(); break; case ENDP2: CTR_OUT2(); break; case ENDP3: CTR_OUT3(); break; case ENDP4: CTR_OUT4(); break; case ENDP5: CTR_OUT5(); break; case ENDP6: CTR_OUT6(); break; case ENDP7: CTR_OUT7(); break; ( _3 b( R) m8 V4 a: t default: break; } } } } } " }3 Y- C- t. q0 k j7 f! G { 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固件函数的一些介绍