
接着上篇,详细情况可以查看usb_core(.c/.h),STM32 USB中断事件为以下几种: ; K$ d- J s8 q9 d. R, M8 P, j void ISTR_CTR(void); void ISTR_SOF(void); void ISTR_ESOF(void); void ISTR_DOVR(void); void ISTR_ERROR(void); void ISTR_RESET(void); ! q/ }/ Z/ s! R9 a& W9 q2 H$ R9 ~: t0 C void ISTR_WAKEUP(void); void ISTR_SUSPEND(void); " h: [6 `: {/ e/ c: L* C' q/ F, z 这些处理函数使能由定义CNTR_MASK决定: I- g3 x. |3 ] // CNTR mask control #define CNTR_MASK CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | \ 1 R( p) k3 |- u& x/ n6 U9 d CNTR_SOFM | CNTR_ESOFM | CNTR_RESETM | CNTR_DOVRM \ + g G4 W6 m, @ J6 B) g 其中着重说明的是ISTR_RESET()和ISTR_CTR()函数,ISTR_RESET()主要处理USB复位后进行一些初始化任务,ISTR_CTR()则是处理数据正确传输后控制,比如说响应主机。 , w8 ~3 p( Z+ t9 k/ F2 ~. P % j! C( R E% X2 P$ O1 W" S; M // ***************************************************************************** // Function Name : INT_ISTR_RESET n4 b: G5 Y0 F/ | // Description : ISTR Reset Interrupt service routines. - X; w1 m* |% h; c2 w1 w/ v // Input : // Output : , \: S, V% ~; y" Y; ]0 E // Return : 4 w% P0 B1 C9 q9 D0 o // ***************************************************************************** void INT_ISTR_RESET(void) { % ]) U, K# k1 x$ c; G) }% j // Set the buffer table address / a. k1 t7 t% `0 }, z/ L SetBTABLE(BASEADDR_BTABLE); // Set the endpoint type: ENDP0 SetEPR_Type(ENDP0, EP_CONTROL); # R# H: e7 w$ P# x# |% f0 S Clr_StateOut(ENDP0); 2 _( r8 n K. \' m' b7 } // Set the endpoint data buffer address: ENDP0 RX # M8 q& R% G9 I SetBuffDescTable_RXCount(ENDP0, ENDP0_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP0, ENDP0_RXADDR); " Z$ l( ]+ x6 H) y3 W 0 i- B, ], R% \7 n6 [: @ // Set the endpoint data buffer address: ENDP0 TX SetBuffDescTable_TXCount(ENDP0, 0); : w0 _0 W0 J! R SetBuffDescTable_TXAddr(ENDP0, ENDP0_TXADDR); a3 `5 r& H" P* T7 w7 f // Initialize the RX/TX status: ENDP0 SetEPR_RXStatus(ENDP0, EP_RX_VALID); SetEPR_TXStatus(ENDP0, EP_TX_NAK); $ ~( x% ?9 u/ ]0 Y6 u. s1 X* ^ // Set the endpoint address: ENDP0 SetEPR_Address(ENDP0, ENDP0); ( s p7 S1 {" Y9 k* P; s. e // --------------------------------------------------------------------- 3 A9 D' c6 i$ C' Z! Q. A // TODO: Add you code here // --------------------------------------------------------------------- * Z% h& O9 T& _. N7 [, N // Set the endpoint type: ENDP1 SetEPR_Type(ENDP1, EP_INTERRUPT); 9 a5 A/ f- f2 A( o& ~& e Clr_StateOut(ENDP1); // Set the endpoint data buffer address: ENDP1 RX 0 p3 ?) A$ M% r SetBuffDescTable_RXCount(ENDP1, ENDP1_PACKETSIZE); ( J& I( D1 {- L; n) ] SetBuffDescTable_RXAddr(ENDP1, ENDP1_RXADDR); // Set the endpoint data buffer address: ENDP1 TX SetBuffDescTable_TXCount(ENDP1, 0); SetBuffDescTable_TXAddr(ENDP1, ENDP1_TXADDR); / l8 b/ s4 [* d4 C1 q // Initialize the RX/TX status: ENDP1 SetEPR_RXStatus(ENDP1, EP_RX_VALID); SetEPR_TXStatus(ENDP1, EP_TX_DIS); " W: Z0 Q, g6 b; Q8 ?8 u // Set the endpoint address: ENDP1 * s3 q$ p4 a. u$ K/ y SetEPR_Address(ENDP1, ENDP1); SetEPR_Type(ENDP2, EP_INTERRUPT); Clr_StateOut(ENDP2); + l# X7 [" Z& D9 @. z // Set the endpoint data buffer address: ENDP2 RX SetBuffDescTable_RXCount(ENDP2, ENDP2_PACKETSIZE); SetBuffDescTable_RXAddr(ENDP2, ENDP2_RXADDR); , K! W o q0 \! Z/ j4 _% Q; n9 U ; |7 i( S+ I( K) Z! [/ k8 N/ w' | // Set the endpoint data buffer address: ENDP2 TX 8 \8 _' Q9 }$ F2 ~ SetBuffDescTable_TXCount(ENDP2, 0); SetBuffDescTable_TXAddr(ENDP2, ENDP2_TXADDR); 9 w+ L" G- {# ?* L) t, h7 | // Initialize the RX/TX status: ENDP2 SetEPR_RXStatus(ENDP2, EP_RX_DIS); # Y# F/ b2 i5 J SetEPR_TXStatus(ENDP2, EP_TX_VALID); 8 p6 f1 ]5 H: l5 k* I % J4 j" P/ r( W% `' u4 o$ q( J // Set the endpoint address: ENDP2 SetEPR_Address(ENDP2, ENDP2); $ T) Q( V& p; g, P0 h K7 g J // --------------------------------------------------------------------- $ w; N6 l+ h# w9 L: D* q2 Q7 o // End of you code + l& [# Z! z* H& k" l- P // --------------------------------------------------------------------- SetDADDR(0x0080 | vsDeviceInfo.bDeviceAddress); vsDeviceInfo.eDeviceState = DS_DEFAULT; vsDeviceInfo.bCurrentFeature = 0x00; vsDeviceInfo.bCurrentConfiguration = 0x00; vsDeviceInfo.bCurrentInterface = 0x00; 4 B: t: l# h4 F2 H1 h9 _) O vsDeviceInfo.bCurrentAlternateSetting = 0x00; p( {" F/ {4 i0 v+ y vsDeviceInfo.uStatusInfo.w = 0x0000; : P4 E* Y& C3 S9 I } 在这个ISTR_CTR()函数中,定义了EP0、1、2的传输方式以及各自的缓冲描述符,其中EP0是默认端口,负责完成USB设备的枚举,一般情况是不需要更改的。其他端点配置则需根据实际应用而决定,如何设置请仔细理解STM32的参考手册。 0 D2 f) \1 Y: _: n 值得说明的是STM32的端点RX/TX缓冲描述表是定义在PMA中的,他是基于分组缓冲区描述报表寄存器(BTABLE)而定位的,各端点RX/TX缓冲描述表说明是数据存储地址以及大小,这个概念需要了解,ST提供的固件很含糊,为此,我在usb_regs.h文件中进行了重新定义,如下: // USB_IP Packet Memory Area base address #define PMAAddr (0x40006000L) * @3 S" w$ d9 k& V% V; h, @& i& s // Buffer Table address register #define BTABLE ((volatile unsigned *)(RegBase + 0x50)) 5 u. D- K h/ | // ***************************************************************************** 8 E6 e: c. E- n; @+ P$ } // Packet memory area: Total 512Bytes 0 p) z O" k/ X; o6 V // ***************************************************************************** 6 Y& ~' R5 G7 \8 h #define BASEADDR_BTABLE 0x0000 5 H9 H% z+ D1 J1 V6 j5 D // ***************************************************************************** 1 ? b& c1 P4 @" u9 m7 F0 x // PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT ' R7 D9 u+ c/ { v6 i/ X5 U // PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR % ~9 K0 q# K ] o9 ]) N // PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT // 1 o! ]9 ~2 E# j8 K9 G* i! ` // PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT & c# a% ^9 ]. r* {4 }6 s9 j // PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR " T. Z. p3 C! Q8 q8 y // PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT // ; ], G6 ]% w! Q" [ // PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR 3 X+ z* l& q5 W8 X2 m // PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT 8 P9 e; t9 U5 W$ i8 H9 a3 N5 @ // PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR & y- u5 Y( I6 [" \6 h3 z // PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT ! V" U1 k, z; ]4 L // 3 \8 a9 h. z- {# q // PMAAddr + BASEADDR_BTABLE + 0x00000018 : EP3_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000001A : EP3_TX_COUNT , g# P9 j4 r5 G8 C/ I // PMAAddr + BASEADDR_BTABLE + 0x0000001C : EP3_RX_ADDR 9 w* u' H$ l( [1 I0 E // PMAAddr + BASEADDR_BTABLE + 0x0000001E : EP3_RX_COUNT // 2 s0 n: r5 K; P+ E+ ^4 j // PMAAddr + BASEADDR_BTABLE + 0x00000020 : EP4_TX_ADDR ( m% n# g, h) O: T // PMAAddr + BASEADDR_BTABLE + 0x00000022 : EP4_TX_COUNT % s* l1 C! M0 O4 C/ {% `: O0 P // PMAAddr + BASEADDR_BTABLE + 0x00000024 : EP4_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x00000026 : EP4_RX_COUNT 3 ?* \9 y5 d5 F; G$ A // // PMAAddr + BASEADDR_BTABLE + 0x00000028 : EP5_TX_ADDR + l$ O) N2 O x0 y6 U$ W // 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 // PMAAddr + BASEADDR_BTABLE + 0x00000034 : EP6_RX_ADDR , m: S1 J( v6 _$ R5 ~% ?9 g // PMAAddr + BASEADDR_BTABLE + 0x00000036 : EP6_RX_COUNT * v. @( x( C/ D" f; ~ // // PMAAddr + BASEADDR_BTABLE + 0x00000038 : EP7_TX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003A : EP7_TX_COUNT // PMAAddr + BASEADDR_BTABLE + 0x0000003C : EP7_RX_ADDR // PMAAddr + BASEADDR_BTABLE + 0x0000003E : EP7_RX_COUNT 3 c% S$ f: n3 `+ Q! P+ ` // ***************************************************************************** // 6 \: w/ M v) @7 T4 O W. M! f // PMAAddr + BASEADDR_BTABLE + (0x00000040 - 0x000001FF) : assigned to data buffer 4 G+ o6 X" W& X3 G& f L // // ***************************************************************************** #define BASEADDR_DATA (BASEADDR_BTABLE + 0x00000040) // ENP0 ; U% }" H% N/ y3 x( p( g- T: N #define ENDP0_PACKETSIZE 0x40 #define ENDP0_RXADDR BASEADDR_DATA #define ENDP0_TXADDR (ENDP0_RXADDR + ENDP0_PACKETSIZE) ! ^+ V( z; S) ?) g3 I0 [7 j* B // ENP1 ! e/ P$ `6 ^+ e2 V0 @/ v #define ENDP1_PACKETSIZE 0x40 #define ENDP1_RXADDR (ENDP0_TXADDR + ENDP0_PACKETSIZE) * r9 Y7 X8 J0 K3 Z" {7 x #define ENDP1_TXADDR (ENDP1_RXADDR + ENDP1_PACKETSIZE) W" T! i* K6 i. U // ENP2 #define ENDP2_PACKETSIZE 0x40 # H. o, Y" q4 d4 [0 X# v: f #define ENDP2_RXADDR (ENDP1_TXADDR + ENDP1_PACKETSIZE) # [) h* D( V1 u+ N; r #define ENDP2_TXADDR (ENDP2_RXADDR + ENDP2_PACKETSIZE) 4 \+ Y+ i7 s$ X- ~4 J7 W2 R5 T h, c) M" b7 e' k5 \5 r* u, { // ENP3 3 K" r4 e" `& ?# I4 G$ c6 l #define ENDP3_PACKETSIZE 0x40 ) o8 C7 |3 \5 J$ J7 r5 T #define ENDP3_RXADDR (ENDP2_TXADDR + ENDP2_PACKETSIZE) 1 k' s! i5 y7 |4 `+ B #define ENDP3_TXADDR (ENDP3_RXADDR + ENDP3_PACKETSIZE) 7 ~: L0 i& n: z- `* v# J2 i1 R // ENP4 1 `8 x% N7 A% g1 s8 ] #define ENDP4_PACKETSIZE 0x40 3 N9 p( y- T' U" T; N2 S #define ENDP4_RXADDR (ENDP3_TXADDR + ENDP3_PACKETSIZE) #define ENDP4_TXADDR (ENDP4_RXADDR + ENDP4_PACKETSIZE) 7 @; I4 H+ ]; G; ~. e- o8 P4 }9 | // ENP5 ; `* H1 s, L: t4 ^; y+ X( w #define ENDP5_PACKETSIZE 0x40 4 }, x5 P/ O0 V4 [ #define ENDP5_RXADDR (ENDP4_TXADDR + ENDP4_PACKETSIZE) #define ENDP5_TXADDR (ENDP5_RXADDR + ENDP5_PACKETSIZE) // ENP6 #define ENDP6_PACKETSIZE 0x40 #define ENDP6_RXADDR (ENDP5_TXADDR + ENDP5_PACKETSIZE) #define ENDP6_TXADDR (ENDP6_RXADDR + ENDP6_PACKETSIZE) // ENP7 #define ENDP7_PACKETSIZE 0x40 #define ENDP7_RXADDR (ENDP6_TXADDR + ENDP6_PACKETSIZE) + f3 J" Q8 Q7 ?5 X #define ENDP7_TXADDR (ENDP7_RXADDR + ENDP7_PACKETSIZE) 这样,一般只要在PMA的大小区域内(512Bytes),修改端点EPnR的数据包大小就可以了,当然,实际情况可以根据需要进行更改。 $ e" n7 W& s$ k' e ^ 2 X1 p5 c6 j0 u" d* l // ***************************************************************************** // Function Name : INT_ISTR_CTR // Description : ISTR Correct Transfer Interrupt service routine. // Input : // Output : // Return : , g5 d# _, Y2 e4 f" |5 V$ z5 |- D // ***************************************************************************** void INT_ISTR_CTR(void) { unsigned short wEPIndex; 8 s, Q5 T& J6 U, w9 `( w. l unsigned short wValISTR; unsigned short wValENDP; 8 R4 O: m8 h/ x+ K+ K9 _ while( ((wValISTR=GetISTR()) & ISTR_CTR) != 0 ) { ' u, u! F1 `( k4 J2 z- i // Get the index number of the endpoints , F2 \% x$ L7 G } wEPIndex = wValISTR & ISTR_EP_ID; ! O9 L' f1 G; E# r) c& X( K if(wEPIndex == 0) 3 ]; b9 a. y6 n- d# b- K { 3 ?" d& y4 Z/ L$ ~0 D' e // Set endpoint0 RX/TX status: NAK (Negative-Acknowlegment) , F( H2 g6 i7 o3 J! s SetEPR_RXStatus(ENDP0, EP_RX_NAK); SetEPR_TXStatus(ENDP0, EP_TX_NAK); // Transfer direction : ^ Z E$ I; Q) b: D/ d% R1 V if((wValISTR & ISTR_DIR) == 0) # H9 Y: b/ c! M" K8 p/ o8 l { // DIR=0: IN * V+ f# T1 [- ]" n* F! E) ]+ ` // DIR=0 implies that EP_CTR_TX always 1 ClrEPR_CTR_TX(ENDP0); / o: y- ]( a) Q+ \2 D8 w CTR_IN0(); 9 Y7 y3 p7 J& O% g4 R, x7 T return; } ! O( N! k2 L- J7 t3 q else ' ^! u5 t4 r# e2 j { " X# S5 `+ B* _/ N // DIR=1: SETUP or OUT // DIR=1 implies that CTR_TX or CTR_RX always 1 wValENDP = GetEPR(ENDP0); / G- u9 F! H0 y" I3 V! L5 c, R if((wValENDP & EP_CTR_TX) != 0) * D8 @* p! t3 ~: p; e+ D { 6 f1 B9 |6 P7 L: u. L9 u ClrEPR_CTR_TX(ENDP0); CTR_IN0(); return; 8 ^# \" ^! Z1 d! L. \* o4 | } * C9 E) ~+ x' E7 L6 Z$ N/ y else if((wValENDP & EP_SETUP) != 0) { ) X; Y, Y1 R7 @6 s8 N: g* N ClrEPR_CTR_RX(ENDP0); * \3 O0 `5 p- p" m! ~' i' v" w CTR_SETUP0(); return; 5 V9 m4 A j \; f6 X5 W } else if((wValENDP & EP_CTR_RX) != 0) { ]+ `9 J* W4 \ ClrEPR_CTR_RX(ENDP0); # b/ k# Z1 R( n) C CTR_OUT0(); return; : e# V: S" a7 u1 `4 |$ k$ c4 f } } " n. m- v. }* G2 F4 V } // Other endpoints else { wValENDP = GetEPR(wEPIndex); 1 X5 z, F1 R( f7 r- h SetEPR_RXStatus(wEPIndex, EP_RX_NAK); ( u4 @* k: x8 u SetEPR_TXStatus(wEPIndex, EP_TX_NAK); if((wValENDP & EP_CTR_TX) != 0) ! J. H; i- o/ r/ W3 |! d* L; P9 k0 b7 m { ClrEPR_CTR_TX(wEPIndex); switch(wEPIndex) . G) z0 `/ V: \8 s5 \4 X { case ENDP1: CTR_IN1(); break; 6 L! [9 v. V; i case ENDP2: CTR_IN2(); break; case ENDP3: CTR_IN3(); break; case ENDP4: CTR_IN4(); break; + a- ]/ V1 _( ^, Z( O( M8 i case ENDP5: CTR_IN5(); break; ; d% B, J4 }" ?1 d3 k# t* }2 Y case ENDP6: CTR_IN6(); break; case ENDP7: CTR_IN7(); break; default: break; } } " }/ c$ S w0 j% E2 D" {+ T if((wValENDP & EP_CTR_RX) != 0) $ G6 R. ?2 t% w. o' t" ^. v { L% ]2 j5 B; b/ E ClrEPR_CTR_RX(wEPIndex); switch(wEPIndex) 1 T5 x1 B: J& S+ \ { case ENDP1: CTR_OUT1(); break; 3 ~; K; @) M3 Q case ENDP2: CTR_OUT2(); break; case ENDP3: CTR_OUT3(); break; case ENDP4: CTR_OUT4(); break; 1 N9 V3 a' p- g case ENDP5: CTR_OUT5(); break; case ENDP6: CTR_OUT6(); break; 7 P$ i7 U! W$ ?) |% P: Q4 I case ENDP7: CTR_OUT7(); break; default: break; $ t5 |6 t( \- D- [ M$ Z } } } } / F" F* C5 }. x+ ^& P } 5 Y+ m) [- T. C% R . J$ Y! N/ o, E! x 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固件函数的一些介绍