你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32 USB相关知识扫盲

[复制链接]
STMCU小助手 发布时间:2022-4-9 23:28
1、基础知识, @- m1 S- ]; \0 }! z# ]' U: ~
等级划分:
. _( z* E$ [" z  K/ n/ @. a: W4 F; I6 v# l$ E+ y* G
I@{P8IVPU$H)DO`BUMJPU6G.png
1 @1 D0 R, ]+ J  S9 V5 A6 s& L" g5 B0 s* I, @; e- P0 r, o. K! N
接口类型:
1 K4 x* m: K7 }

" _  h% P+ }  Z- ]9 u* |: j PKUMM88ZPT]3(IMZKF{T72B.png 3 |, E. Z) |) q- W4 w

) t* {5 ~0 q/ f( hSTM32基础型(F1系列)所带的USB是全速。
' U# T( T1 m% m' z8 [) N/ y& e  x0 i; r3 U$ l
2、电气属性
) [3 g/ S, o! @USB的通信都是由主机发起的,这一点与IIC协议是类似的。
$ \* [3 k9 v9 q7 \6 ~; [5 P* X3 t) Z1 q
2.1 数据线

& X/ B# x8 n3 c4 O: g1 F2 c$ AUSB使用差分传输模式,有两条数据线,分别是:
3 |6 R. y, S" @$ ?2 g- u! X2 y. s7 @
USB数据正信号线,USB Data Positive,即USB-DP线,简写为D+
( \8 Q5 @9 |8 u1 m) sUSB数据负信号线,USB Data Minus, 即USB-DM线,简写为D-7 e: E/ p1 p( W
) k! G  g) i  j9 \* w% ]" K
剩下的就是电源线(5V-Vbus)和地线(GND)。& o( p7 M& j+ a( E+ b9 ^& X

/ s' D* e) `7 |! f. C1 Q2.2 USB主机是如何识别设备是高速设备/全速设备/低速设备?2 e$ e2 S: E; o! ]& O8 k, J2 G" n
主机的D+和D-都接有15K下拉电阻。# j& y! l7 W  f% L5 c

5 y  r/ b$ X7 C% M8 f全速USB设备的数据线D+接有1.5K的上拉电阻,一旦接入主机,主机的D+被拉高
, z* F) k' j* {7 {+ ?低速USB设备的数据线D-接有1.5K的上拉电阻,一旦接入主机,主机的D-会被拉高3 e2 q& }/ d) R) f  j2 T

' @/ G. ?( a$ D& P$ x& O: w5 ~, o
) |; i: S: Y1 S因此,主机就可以根据检测到自己的D+为高还是D-为高,从而判断接入的设备是一个全速还是低速设备。
+ g" y* w' B- Q1 N4 Z1 G& G% m& q
所以你可以看到STM32板子上的USB口D+有一个上拉电阻,而且是必须有的:; k; I* U4 A  r; M( I# W% Z

7 |( ]; @! ^4 Q; U( ?. G OG}IT_SY%QK)L68[KA_(9{0.png
$ a0 }" u! F! e8 d6 H& l7 \* p% n& C2 \
3、USB设备分类2 K; m, @3 L) j- d. }. Q
2 j0 b$ }, @1 H* p; {1 l3 H
BQKVM4710I9F5B8F(M7]A%U.png # f, w5 ?: z' ^- l* }$ ?
! H6 e' z9 S6 T9 ]" k2 b
看一个CDC虚拟串口的设备分类:
$ Y" U  k2 _$ I" K$ z$ H* s8 \* l% s& b. C8 Q: ]
9AFHVVM1``3(N}K}T$OV(X4.png . Z; d; W  i3 V+ }- w4 B
# E5 o. o/ i# k2 a, f' n7 d, y
在看一个MSC的(模拟U盘):' n( {" C+ G3 q6 r( c- y& M

: ~" F  Q# E6 G9 F$ P6 [ 6`B)XRM]W5OTE5~LG$WPDN3.png
8 I6 I6 c0 K6 \7 {9 \
: f0 t) }& R, K* p; r9 o不同的类有不同的用途;不同的应用场合对应不同的产品形态;不同的产品形态可能会有自己特殊的描述符,比如: HID类有报告描述符、CDC类有ACM、Union描述符等。, F+ Y2 `  F+ v8 u

, J  V' K8 {6 E% @( H4、描述符详解

" _  J& _# Q" ~% N以STM32里的MSC设备为例,MSC类所需要的描述符有:设备描述符+配置描述符+接口描述符(数量由配置描述符里的bNumInterfaces字段决定)+端点描述符(数量由配置描述符里的bNumEndpoints决定),所以MSC类结构就是这样的:
) a) B5 ^* H1 A  U; Z9 F
% A0 I& g( B, L$ e" Z& {' ~
  1. 配置描述符
    : r& h# B$ w( M( ]% L8 A2 d9 q
  2. {
    / l5 T! M7 ~0 p1 J0 l0 }- y  Z  K
  3.         接口描述符1
    # _) X. {8 p5 Y; l5 i/ i) l% ?. e" J
  4.         {5 H3 [5 K  A! J
  5.                 端点描述符15 S& `' x6 f) ]- A7 m( O4 g1 j
  6.                 {
    , I- S  w: _6 E* ?
  7.                         
    ! z/ g3 _  h' F, o
  8.                 }9 Y3 G" F. @4 c
  9.                 端点描述符2+ K. [; C3 n( r' X
  10.                 {
    ) S/ Z- h- z' [" G" P
  11.                         8 m2 x0 G$ o' F: O  l  V
  12.                 }
    * A/ F  h" J% ]
  13.         }5 T% M1 q5 l: W, n6 }; S
  14. }
复制代码
% O- X; b" G. M
而CDC类是这样的:9 ?& s8 E* T2 A* V0 S4 i
" G% n( ]* T( H* v' b
  1. 配置描述符
    5 |: V' D/ R  O  b7 o4 S- K, L
  2. {& E; G/ _0 Q, X8 d% B$ m
  3.         接口描述符1(通信接口)
    : @, I: w# }# G3 p0 Q/ x! w
  4.         {8 k: N6 I! S1 b) X" C& U8 A
  5.                 其他描述符(特殊描述符)2 A4 w0 o1 ^- p; x7 {. }5 r
  6.                 {$ c0 i% I3 v, d8 T3 s8 E6 F
  7.                         /*Header Functional Descriptor*/
    : [1 n' ]; C! M( @/ a
  8.                         /*Call Management Functional Descriptor*/
    4 {( z6 W0 k. B+ B  w- ^5 \
  9.                         /*ACM Functional Descriptor*/
    & u+ z( @9 J- ^/ x5 |: S
  10.                         /*Union Functional Descriptor*/
    - G$ Y( ]8 T# e: g8 \
  11.                 }! |2 d5 b( @1 q, A
  12.                 端点描述符(命令端点)# y, z7 a' T# ]1 @
  13.                 {1 I! R$ V8 f  M& s* h0 U1 [
  14.                         
    & S1 D" Q3 P3 \9 @
  15.                 }
    $ U- l! j) A5 t- E/ V" S% \
  16.         }9 F2 [. \9 [+ ?
  17.         接口描述符2(数据接口)2 _( V# X# a5 W. L$ O2 p7 }% Q
  18.         {
    + S; r/ ]! [8 f; R
  19.                 端点描述符1(输出端口)
    ; G# u2 G9 d/ o. Z5 y
  20.                 {& o$ F3 q, |3 ?- V7 G( \) h; L! r
  21.                         6 r6 Y0 U8 z) q) S$ A; e
  22.                 }
    # L( \3 M& _2 _
  23.                 端点描述符2(输入端口)
    8 J, M2 X* f9 d3 ?- p0 q; o
  24.                 {
    ; l" C1 ?# b. s# B
  25.                         / A: e, w% J; E/ j1 H9 p
  26.                 }
    * E& y, y# k# ^& z" w; w) q+ l% g
  27.         }
    * j# U* K& O% P
  28. }. K9 j2 H7 ^* `: p$ ?
复制代码

9 g- W3 T; l1 R6 u3 A4.1 设备描述符
7 D6 x" v! W. g7 N每个USB设备都必须且只有一个设备描述符,摘取一下STM32的MSC设备里的实例代码:
$ s+ D! X: w$ m# p- P# m
2 I. Y- _# K; ~6 H3 }% Z( z3 F Q4O0XI)3DGAR{L{CZVJO6%R.png
: y3 m+ ^( p  A$ |7 ]1 r% F. ]( `- m/ V
每个字段的含义:
, v! j3 R- F/ V8 ^6 ^bLength:描述符大小.固定为0x12;
- T+ Q7 Q. R- [8 \1 |bDescriptorType:设备描述符类型.固定为0x01;) i( h) m1 {$ L  s& v+ y
bcdUSB:USB 规范发布号.表示了本设备能适用于那种协议,如2.0=0200,1.1=0110;  k2 ^; z7 J* X1 g
bDeviceClass:类型代码(由USB指定)。当它的值是0时,表示所有接口在配置描述符里,并且所有接口是独立的。当它的值是1到FEH时,表示不同的接口关联的。当它的值是FFH时,它是厂商自己定义的;
- }4 K9 P# C, J7 a. j- IbDeviceSubClass:子类型代码(由USB分配),如果 bDeviceClass值是0,一定要设置为0。其它情况就跟据USB-IF组织定义的编码;# K9 _# x6 L1 W# E5 x+ L% g9 o0 E
bDeviceProtocol:协议代码(由USB分配),如果使用USB-IF组织定义的协议,就需要设置这里的值,否则直接设置为0。如果厂商自己定义的可以设置为FFH;  d4 O: J0 t6 s
bMaxPacketSize0:端点0最大分组大小(只有8,16,32,64有效);! |* J  v" Y, n* m
idVendor:供应商ID(由USB分配);
% V7 u; B8 k7 B# O0 `% [7 iidProduct : 产品ID(由厂商分配),由供应商ID和产品ID,就可以让操作系统加载不同的驱动程序
' \' ?. R" m: nbcdDevice :设备出产编码.由厂家自行设置;# Y, Q; J- @: D% {# D2 \: w5 E3 u
iManufacturer :厂商描述符字符串索引.索引到对应的字符串描述符. 为0则表示没有;- F: j* w! W+ c; M9 y3 I9 [
iProduct :产品描述符字符串索引.同上;/ h! I! N* y. N& c
iSerialNumber:设备序列号字符串索引.同上;$ j; R/ k9 ]4 y, B( H
bNumConfigurations :可能的配置数.指配置字符串的个数。
; i  A. L* q4 X7 ~$ ?4 }% @# A! g. Q" S! P2 D
4.2 配置描述符
. s2 s- W; {; Z6 V8 A+ h9 ?配置描述符定义了设备的配置信息,一个设备可以有多个配置描述符。摘一个STM32的MSC设备的配置描述符:
! F+ G4 ^6 G" X( o
0 h! ]! }$ G8 o3 L% _& y0 ] QV6[X)C[7`J@}LIASS9U7OW.png 3 h3 ^1 `7 L- I: B2 T$ C

* F9 j! h3 U/ Z/ ]2 i, [用C语言组合就是这样的一个结构:
' \6 T% x' n! {7 e' f, a5 a6 H2 C- n; d) j7 {1 E
  1. typedef struct _USB_CONFIGURATION_DESCRIPTOR_
    , H% @0 w' M1 w/ r- Y- F
  2. {
    4 @8 ?* Q, P6 H5 v' x" ~1 i
  3.     BYTE      bLength,
    ' S- L1 l. y" {. ~+ e% H2 R" b4 [' x  L
  4.     BYTE      bDescriptorType,
    6 v9 U8 l, I! d% B7 }* p
  5.     uint16_t  wTotalLength,
    ' _* A1 c/ M' ]/ b8 w  |7 z# a
  6.     BYTE      bNumInterfaces,) u: l+ \$ M7 T- d) o
  7.     BYTE      bConfigurationValue,2 k. _- o- R0 O  B6 E" j* G" p
  8.     BYTE      iConfiguration,
    % X# X$ J6 w  z# H1 h
  9.     BYTE      bmAttributes,
    " h1 e; W4 q# ~5 J
  10.     BYTE      MaxPower* Q6 ^2 g6 i2 F
  11. }USB_CONFIGURATION_DESCRIPTOR;
    ) _  @9 \8 n6 ~+ E  v
复制代码
8 q, e$ Y6 f- D- I- T: U
每个字段含义如下:2 `$ P5 |4 M) e3 N
bLength:描述符大小,固定为0x09.+ L2 E7 [4 v/ U3 T6 Y2 f2 }- U
bDescriptorType:配置描述符类型,固定为0x02.! ^- [3 @7 j. t1 y! _: d+ V% U( p/ V
wTotalLength:返回整个数据的长度,指此配置返回的配置描述符,接口描述符以及端点描述符的全部大小0 e8 R3 M  N0 n4 f1 @6 F
bNumInterfaces:配置所支持的接口数。指该配置配备的接口数量也表示该配置下接口描述符数量
* I, w* X( I& hbConfigurationValue:作为Set Configuration的一个参数选择配置值
" E" Z8 V3 A2 B9 v1 p( U3 SiConfiguration:用于描述该配置字符串描述符的索引
. O# F( c& E0 _& `6 _$ u) [bmAttributes:供电模式选择,Bit4-0保留,D7:总线供电,D6:自供电,D5:远程唤醒5 S$ w8 T' B7 O4 ?5 _8 B
MaxPower:总线供电的USB设备的最大消耗电流.以2mA为单位" |( G; i" q6 h; G

. o/ \; a" ?) C' s4.3 接口描述符, I7 F" H5 T- G# ?8 s; ^4 C; g6 g
接口描述符说明了接口所提供的配置,一个配置所拥有的接口数量通过配置描述符的bNumInterfaces决定,摘取STM32的MSC设备类的接口描述符:! U* K8 R0 L0 A! ]" v# T

$ d+ k. d4 z# V4 p6 ?1 y N[Y@)]AS%`~62DO@AS8ST$X.png
9 r" [# c) e! i& D( J/ m* |8 u" {' ]9 K
用C语言组合就是这样的一个结构:
) J9 }. ?7 I7 }3 K6 d, b% p# K% `! }  L3 i2 W
  1. typedef struct _USB_INTERFACE_DESCRIPTOR_1 M' o1 r$ P* r
  2. {
    % l' d! e3 G  k* N  c
  3.     BYTE      bLength,2 P/ g% _) @: n% a' `! @
  4.     BYTE      bDescriptorType,
    + D" @* c, y* m8 G  h
  5.     BYTE      bInterfaceNumber,5 v: F( Z! H" r& z/ P  ^: J
  6.     BYTE      bAlternateSetting,
    6 [4 J2 O# x4 k* a( z
  7.     BYTE      bNumEndpoint,
    , K; Y0 v* z  ^
  8.     BYTE      bInterfaceClass,! X  A! u( s  o' W7 @
  9.     BYTE      bInterfaceSubClass,( H6 f' ?7 q( T& d
  10.     BYTE      bInterfaceProtocol,
    1 x7 ~: x5 ^6 F6 T$ y- P
  11.     BYTE      iInterface
    : R# Y! ^, V5 K
  12. }USB_INTERFACE_DESCRIPTOR;, C  X2 W7 Q% i6 Z5 W8 @
复制代码
, x' w; p* ?4 W3 I
每个字段含义如下:

7 q# m1 k8 q& B/ \1 J' v" y- S7 p: H6 ?, v7 U
bLength : 描述符大小.固定为0x09.
1 ]4 z; c! S: ibDescriptorType : 接口描述符类型.固定为0x04.9 N0 G- k  `) Q5 l0 T4 f/ h
bInterfaceNumber: 该接口的编号.6 y# L5 v" B  a# f$ D
bAlternateSetting : 用于为上一个字段选择可供替换的位置.即备用的接口描述符标号.
; b" i% N: t9 S* l2 h- u6 @bNumEndpoint : 使用的端点数目.端点0除外.
: E3 i7 }$ t$ F& RbInterfaceClass : 类型代码(由USB分配).
3 Y" g4 l' @2 L, ubInterfaceSubClass : 子类型代码(由USB分配).
. g9 }4 f0 D6 t5 ibInterfaceProtocol : 协议代码(由USB分配).4 d4 p) ~: S. V$ O9 H% w4 h6 j% G" X
iInterface : 字符串描述符的索引
8 Y0 _4 i- ^" F+ S) {& [8 V2 F1 B3 t4 J
4.4 端点描述符
8 |& G5 V! P  T7 d" sUSB设备中的每个端点都有自己的端点描述符,由接口描述符中的bNumEndpoint决定其数量。摘取STM32的MSC设备类的端口描述符:
. S; P8 |% c, X& ^. X
' F- T7 d/ V* Z/ M+ }' z7 Y( I' A9 j3 h 20210423144822577.png ) \) V/ n2 L6 l1 Q& D  A

; g# {6 `1 k  m4 B/ D0 L用C语言组合就是这样的一个结构:3 Q) C9 V: [" L7 l4 C! v7 r$ ~9 ?. y

% R) Q3 _- @. c7 c/ e7 v
  1. typedef struct _USB_ENDPOINT_DESCRIPTOR_9 \7 N# K" p! N. u. x
  2. {
    3 e4 V/ o! K  E: p1 r0 O& I, _
  3.     BYTE        bLength,
    ; r0 \9 N0 M! A4 R( {5 ?! R
  4.     BYTE        bDescriptorType,. k# |+ R. }, y
  5.     BYTE        bEndpointAddress,% l$ y2 ^" ?. Z5 o" d% z
  6.     BYTE        bmAttributes,
    / y- v! x" A8 ?5 K' I: E4 j$ R4 j
  7.     uint16_t    wMaxPacketSize,$ g9 J( H% Z0 s3 p4 [
  8.     BYTE        bInterval$ e, W6 M* L8 |1 s1 X1 a5 f, l
  9. }USB_ENDPOINT_DESCRIPTOR;3 p1 g, f7 r4 `3 p# E% }
复制代码

2 f' K" _' A) K  i- c' Q/ T) \# E每个字段含义如下:
1 J' ~2 O; t% N% A
/ Q$ H( x$ Y9 k1 i
bLength : 描述符大小.固定为0x07, v- L2 l* x. {/ ?# _
bDescriptorType : 接口描述符类型.固定为0x050 t# O: ?, B2 H% h9 N; H
bEndpointType : USB设备的端点地址.Bit7决定方向,1为IN端点,0为OUT端点,对于控制端点可以忽略;Bit6-4,保留;BIt3-0:端点号
- Y( {! S1 |$ dbmAttributes : 端点属性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中断5 w3 _% @) @+ X3 b7 N
wMaxPacketSize : 本端点接收或发送的最大信息包大小- J, f- `- h" k% A2 U
bInterval : 轮训数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255
* T* u) Q0 Y' e( K2 M0 s7 W4 }0 ], _" D$ {' `) G/ t' l9 z, p* ~) I
# n/ }2 C1 R2 K/ k+ `. q8 j
4.5 字符串描述符
& c" I4 c& A3 d% K+ y, Z字符串描述符是可选的,如果不支持字符串描述符,其设备描述符、配置描述符、接口描述符内的所有字符串描述符索引都必须为0。
' i, P3 p) r% p- ]
7 \- X8 e$ I8 C% j字符串描述符结构如下:/ i- p0 Y9 `. m# H- N
1 b) T  n, ^9 C0 X) H7 c6 ?
  1. typedef struct _USB_STRING_DESCRIPTION_$ j( o2 C3 O! y! c1 X+ M- F
  2. {
    + y; {2 f: A/ o) c+ o+ h: p" a. A
  3.     BYTE      bLength,
    8 V, y  P' }9 K; m- l7 v. ?% p, Q
  4.     BYTE      bDescriptionType,
    0 k1 h6 W1 u  I
  5.     BYTE      bString[1];
    ; }& f; d/ g; {/ f$ U8 {* P( i
  6. }USB_STRING_DESCRIPTION;
复制代码
* v: ~. M/ D3 C2 h) ~3 U5 w
各个字段含义:
5 U2 [% ~7 _5 c  t) s$ U: I. d4 }6 N
$ i' i# X2 Q  P( t8 XbLength : 描述符大小.由整个字符串的长度加上bLength和bDescriptorType的长度决定.  r0 ]) e0 U4 B: Z/ s5 t5 t
bDescriptorType : 接口描述符类型,固定为0x03.
3 z4 B* Q4 y1 `; H/ M% G! CbString[1] : Unicode编码字符串5 a6 F0 {4 q0 r. _3 `' F/ u. E- u

( F: ?+ G' f/ E4 F$ U% U  y1 R4.6 IAD描述符) ^) z$ j- |% b$ T* Y, w% J. H
USB组合设备一般用Interface Association Descriptor(IAD)实现,就是在要合并的接口前加上IAD描述符。例如你想用一个硬件USB接口实现两个功能,又能到U盘又能当虚拟串口,那么在USB配置描述符中就需要加上IAD描述符来指明。* f$ G7 z- ]5 k2 c8 i0 T( n* `! H
+ z- N' [5 Q! f8 Q  q4 s. q* `
  1. typedef struct _USBInterfaceAssociationDescriptor
    4 ^0 V2 m3 \( B- M
  2. {
    9 t: U, L6 S; ~5 K* t; {4 p
  3.     BYTE  bLength:                  0x08        //描述符大小,固定
    9 k( u6 Z& ]2 R, r* B5 `5 _5 F2 G
  4.     BYTE  bDescriptorType:          0x0B        //IAD描述符类型,固定" O5 D- q& C& ?7 \) N5 Q
  5.     BYTE  bFirstInterface:          0x00        //起始接口编号, o4 F% D$ t0 K' a+ C& a# a
  6.     BYTE  bInterfaceCount:          0x02        //本个IAD下设备类的接口数量
    ( y) U6 r# m, w! a! q4 _; p* y
  7.     BYTE  bFunctionClass:           0x0E        //类型代码,本个IAD指示的是什么类型的设备,例如CDC是0X02,MSC是0X08
    # o9 }& M" t3 I. \' R' e% ^
  8.     BYTE  bFunctionSubClass:        0x03        //子类型代码6 J, p# ?4 E: d
  9.     BYTE  bFunctionProtocol:        0x00        //协议代码
    5 L1 q- K. ^0 x( K
  10.     BYTE  iFunction:                0x04        //描述字符串索引( ~4 j- J6 A! L# b
  11. }
复制代码

2 [: U4 ~0 M9 {* }; s) C  _以MSC+CDC为例,他的配置描述符结构就是这样的:) m5 ?- i4 f/ q. c

/ J+ x5 G3 K3 _: ?5 F/ `* P
  1. 配置描述符
    5 r5 S& E8 g8 v* D
  2. {
    4 d. A- _9 X: o$ w" ?/ S
  3.         IAD描述符1(CDC)
    + U1 X4 [! K7 \' x5 v0 f. Z5 `1 h
  4.         {
    ! V; [3 {& _; s% X" P* P
  5.                 接口描述符1(通信接口)
      k! g7 T8 n3 \8 v; P; S
  6.                 {
    ! L/ K% F, y8 e7 X
  7.                         其他描述符(特殊描述符)  o' X& p7 e( }% o  _( E; n& Z) ~0 M
  8.                         {+ i# G1 t. f- [( w
  9.                                 /*Header Functional Descriptor*/6 I' z& ^# w% c$ A) V) ~3 H4 P
  10.                                 /*Call Management Functional Descriptor*/0 K" X/ R' D  S+ ]
  11.                                 /*ACM Functional Descriptor*/7 R& d* Z& M9 d7 Y
  12.                                 /*Union Functional Descriptor*/
    * l- @- g! |5 C7 s0 c. ~3 y
  13.                         }8 W. M+ u% i9 O
  14.                         端点描述符(命令端点)
    # o2 t9 {9 t" h+ U% s% D
  15.                         {
    0 ~* a8 A; b: A+ v. k& n5 X8 t
  16.                                 
    6 c4 b) {+ J# N% ^0 `" g
  17.                         }
    7 G" z% a! C/ B8 O% }- c- y/ R
  18.                 }  Z0 j; x1 ^; s8 }/ e, q0 I; w2 j
  19.                 接口描述符2(数据接口)
    8 l, _! Q" A, ]
  20.                 {
    4 h' W1 R7 ]4 v8 c6 e' o0 \3 T! E
  21.                         端点描述符1(输出端口)
    , Z9 u" x& J# U  V
  22.                         {
    / U1 G0 H! k: \% ?
  23.                                 
    ) w/ D( A$ @) H; e' H0 L
  24.                         }
    6 v9 v/ ~/ {  m& ]4 f, o( D
  25.                         端点描述符2(输入端口)4 M7 R% [/ L) M6 E: G! V
  26.                         {  v/ @. S  ?) r9 h+ n! j1 N+ g
  27.                                 
    " e. w( t2 x5 M" H( G0 g) k3 k9 o
  28.                         }
    ( O4 X; b2 Z; ^3 `$ L
  29.                 }
    9 w7 A4 E4 S& G, J# X
  30.         }* l% O9 D' p0 _) ?' x# A5 R8 @; f

  31. ; ]2 p7 H$ Q" E- {6 A
  32.         IAD描述符(MSC)
    ; }2 c& T  i: e# L' H
  33.         {  H% d0 W5 r0 s- J0 H5 O1 m
  34.                 接口描述符10 v( y; T3 i1 k" D. H, c" E: _) L
  35.                 {
    % x$ F& \; E6 H
  36.                         端点描述符10 i1 u7 u( |# ^6 g1 k: K% }
  37.                         {0 u' i% F6 Y/ ~7 ^  ?6 p- u
  38.                                 
    % r. X" m! c  }! u, ^. d8 l/ Y
  39.                         }
    & |0 M- R5 q8 W6 I
  40.                         端点描述符23 l" J' r* d+ A( ?. l
  41.                         {
    8 E  ?$ w# I, K' s
  42.                                 
    & v% J' Z( Z3 M
  43.                         }
    $ O; s/ }& V: T0 [7 ~8 D
  44.                 }# {. T& E6 y$ A6 \- ?* j
  45.         }
    6 V8 |6 o3 Y( p9 ?0 `+ V
  46. }
复制代码
, F0 `. ~! t& L
5、STM32-USB详解
6 L$ d0 u; F; C& @+ K

- e; |+ @0 l2 v6 t/ t' B5 O 1C90HS%8{]RJ}G(XONA}WTR.png , `* Q  r" m8 P" ~9 G7 S

4 z5 `' h+ ]4 a- c8 C- D- z  V这个512字节SRAM叫做Packet Buffer Memory Area(简称PMA),这个很重要,后面会详细讲解。- T" I" k2 \/ r
* g3 D% y6 F. t9 ^2 k# t; W+ Y: f. i
20210423152245989.png 2 M7 j) N. F( r. _) Z
& D- [8 ]/ C2 J% e/ s1 G' H5 e
根据描述可以,一共有8个端点,16个寄存器,一个端点关联两个寄存器,所以我们可以将他们规划为8个输入端点(0x80-0X87)+8个输出端点(0X00-0X07)。, d2 |" ~9 Y0 I4 P8 D9 `$ q2 i
& p# n0 b* s6 E) `  B9 q
6、STM32-PMA详解
9 J+ U5 B9 X& L& l. [- `& l1 Q) o- i先说一下USB的数据包大小,全速设备的最大包大小为64字节,高速最大为1024字节。& u1 G( m+ @2 ~

, G8 j  Z! _- D% q( f& U, ^% zPacket Buffer Memory Area(简称PMA)" A. P) G' f3 L
6 S5 J' q0 i9 t  D2 ^; @
STM32F1/F3/L1系列都有且结构相同(其他系列暂未考证),译过来就是包数据缓存区,大小为512字节,按2字节进行寻址。
( M2 s- H5 `6 p0 R5 t# P( @/ \
5 h+ ^: ~' H4 w# k# [$ u; x这个PMA的作用就是USB设备模块用来实现MCU与主机进行数据通信的一个专门的数据缓冲区,我们称之为USB硬件缓冲区。
- S: X8 c2 q0 j' ?
0 W) Z0 ?( ^  o2 c说得具体点就是USB模块把来自主机的数据接收进来后先放到PMA,然后再被拷贝到用户数据缓存区;或者MCU要发送到主机的数据,先从用户数据缓存区拷贝进PMA,再通过USB模块负责发送给主机。
% X! M' k! z( ^( \/ y* W% {
- N* o, y3 L) y! c5 l$ @很多人利用ST官方的USB库修改自己的USB应用时候卡住,获取改完之后懵懵懂懂出现错误,估计大多数原因就在此处的修改!& F; ^/ b7 M7 `9 b
, D) ?1 u1 B+ A: p9 i. v
摘取一下STM32F1参考手册里的PMA描述表:6 [4 X9 g: n  T

5 }8 t  Z% S4 M1 R! y6 A2 O9 d4 v! P) c PZS$S`SZZ_`Z`36[P3{G7LA.png + E. W8 Y6 h6 z+ K0 g& d+ n7 j

5 o' C: O; F0 ^* g5 D名称含义:
8 b, N9 B/ Y' S7 N  Z: T* i( e6 g# S
ADDR0_TX:输出端点0发送缓冲区地址
2 F2 H/ B1 V+ l* U1 c+ L# gCOUNT0_TX:输出端点0发送缓冲区大小
( I4 `; j* ~" C. Y. u' `ADDR0_RX:输入端点0发送缓冲区地址/ S1 v3 x6 G3 f; F
COUNT0_RX:输入端点0发送缓冲区大小
1 k/ N8 l, @% u0 N
. \* K3 v: R& M' B0 V可以看到一个完整的端点描述包括:缓冲区地址+缓冲区大小。
3 N$ y; Y. u. c: `+ _- @! ^5 Y; L$ S4 h6 M
PMA的头部为端点的描述,每个端点占8个字节,实际使用了几个端点就有几个描述头,例如使用了0、1、2这三个连续端点(这里注意是连续端点),那么PMA头部的3x8=24(十六进制的0X18)字节就是描述,倘若你使用的是0、1、3这三个端点,其中编号为2的端点虽然没使用但是占用空间,那么PMA头部的端点描述就是4x8=32字节,编号为2的端点8字节的空间就浪费了(严格来说没浪费,就是不方便使用)。- J# e3 G# O0 M8 H& _) B
. \5 h8 [1 d2 O
头部的端点描述之后就是各个端点的缓冲区了,例如使用了0、1、2三个端点,占用了PMA头部3x8=24字节的空间,那么这三个端点的缓冲区地址就是从PMA偏移24字节开始的,当然只要是大于24就都可以,这里就是最关键的地方了,很多人修改ST官方库实现自己USB应用时候就是没改这里的地址,导致缓冲区的使用覆盖了PMA头部的端点描述从而出错!
& [& ~- e3 C4 @7 S* c) j8 i
/ z' l# [4 N/ F2 E0 N下面摘取一个STM32官方MSC设备的实例进行分析:  ~- s- O% L/ t- F5 J! t& [8 A
" ^% _7 \6 }& x( o$ \7 H
CEJODZ]EQIDCJYU4QBF@9M4.png
2 p  X) i/ J( }8 U" r, W8 X
7 F0 W* M4 \- ]: s% k: K( e可以看到一共用到了4个端点,分别是输入端点0X80和0X81,输出端点0X00和0X01,其中0X00端点和0X80端点是供USB使用必须有的,0X81和0X01端点则是MSC设备输入输出端点。
1 F  b3 O$ y6 D4 |
8 h( F; @8 M1 t. U9 _那么一共使用了4个端点,按理来说PMA头部的端点描述大小应该是4X8=32(十六进制的0X20)字节,0X20之后的才是各个端点缓冲区,但是ST这里的却是从0X18开始,也就是说使用了三个端点,这个地方我还没有搞明白为什么,欢迎各位补充!
: a9 n/ p# v  h0 I6 p% ?) G4 m" J: q2 T. G* j' t6 P6 Z
至于为什么0X18之后是0X58,是因为USB全速设备的最大包是64字节(十进制的0X40),所以这里PMA的划分就是:6 N- T, T* Z2 B- j4 _" Y1 Q
$ }( B& b# a" {3 U
头部0X18字节为各个端点的描述
6 B: U  `0 A( w! S' w6 L) `0X18地址开始的64字节为输出端点0的缓冲区0 T; ^! K4 K: r) N
0X58地址开始的64字节为输入端点0的缓冲区: m$ I7 X! e3 u* l: c# \
0X98地址开始的64字节为输出端点1的缓冲区
4 R& B* d- ~4 P$ d0XD8地址开始的64字节为输入端点1的缓冲区# S9 y7 C5 t" ?" i/ G

. |2 ?/ r, q) C+ A  e
, I! y3 ~" R  \* E7 x! p- L! b6 \这里注意一点,缓冲区分配好之后访问是不会溢出的,也就是说缓冲区之间完全隔离。
4 n) z+ Z+ C4 |5 S$ O/ c) W, S4 Z9 S1 z$ S+ S
收藏 1 评论0 发布时间:2022-4-9 23:28

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版