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

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

[复制链接]
STMCU小助手 发布时间:2022-4-9 23:28
1、基础知识; I! ~5 Q- z) R" ?( ^' t% w6 }
等级划分:
8 h7 D* B/ P8 c# J2 r  j' Q; T+ C* A
I@{P8IVPU$H)DO`BUMJPU6G.png / K" m2 R& i# ^* h, m3 w3 v" @
- d% X* |* M2 N! L; e7 [0 m/ E
接口类型:
( a3 L0 t2 O, R* _0 i* `4 W, j  Z
. K# g% f, V5 a# e
PKUMM88ZPT]3(IMZKF{T72B.png   ?- v; D4 r4 e- D! t" o
9 V3 p8 G$ A$ I6 b. D3 _$ r" X# O
STM32基础型(F1系列)所带的USB是全速。0 n+ U' i4 ^9 C3 }' }% t7 h
. O9 x. Z: @: u0 i
2、电气属性0 z- B1 i4 s9 A4 t) N2 d- X+ l
USB的通信都是由主机发起的,这一点与IIC协议是类似的。" f2 G3 m1 P) d, T7 C
0 t4 N3 W. v" K
2.1 数据线
  a2 c# \; Z& p' i! R1 R8 t
USB使用差分传输模式,有两条数据线,分别是:
5 V8 k& o! `5 k* ]+ P' v# m2 v2 a: X6 |. B
USB数据正信号线,USB Data Positive,即USB-DP线,简写为D+! D6 Q1 n% y/ R( [5 E5 k
USB数据负信号线,USB Data Minus, 即USB-DM线,简写为D-7 }4 v! S* X+ a, s
9 R7 b0 Z5 i3 m0 c3 [0 b$ d4 r
剩下的就是电源线(5V-Vbus)和地线(GND)。
- O" c3 y% ~1 J
5 E8 T6 i6 C7 x- @, O+ A2.2 USB主机是如何识别设备是高速设备/全速设备/低速设备?0 ]7 r$ o# \! g. X, {
主机的D+和D-都接有15K下拉电阻。
- S* @7 ]# s! \7 l# E) C
5 Y9 K, c1 A7 S( B0 d4 `全速USB设备的数据线D+接有1.5K的上拉电阻,一旦接入主机,主机的D+被拉高
! \/ F& L! a) V7 ^0 x低速USB设备的数据线D-接有1.5K的上拉电阻,一旦接入主机,主机的D-会被拉高
- D; M! j* J3 q  {& @% Y! v
6 Q) I  Y3 _; q% @& C4 C+ _9 N% q& |7 A, U  t6 ^- \4 t1 u
因此,主机就可以根据检测到自己的D+为高还是D-为高,从而判断接入的设备是一个全速还是低速设备。& O2 B; Y; i: h( D* E: |' Y4 j7 x

/ G, d9 t+ N: t& ^; @- J  `  B所以你可以看到STM32板子上的USB口D+有一个上拉电阻,而且是必须有的:
2 }9 `7 @; N1 N2 z$ `* `
& |7 [1 c! X* L OG}IT_SY%QK)L68[KA_(9{0.png 3 A# h$ Q' R# r+ {

) l' m( R1 U8 d3、USB设备分类
/ s8 j% W- a1 ^% t6 H8 ]8 F
' b6 Z5 s. P4 z4 n' E$ x- Y BQKVM4710I9F5B8F(M7]A%U.png
* m& l3 p% p9 O% x
4 m0 f) Z7 x6 p5 r( F* u) |6 g# p看一个CDC虚拟串口的设备分类:
0 k6 q' e) }/ B! i$ K4 M
0 {0 ~- I- d$ R0 ? 9AFHVVM1``3(N}K}T$OV(X4.png 6 Y4 D$ {0 O' W0 R3 v
/ f; x0 G9 ?( ?
在看一个MSC的(模拟U盘):
' c/ g9 n8 l& x5 Z" l; N% d% e% k/ t+ X! ]+ s( p& K, ?3 K0 I
6`B)XRM]W5OTE5~LG$WPDN3.png ; m1 y/ g; B0 @3 J$ v& n  \

; b; x9 c. ^/ ~2 g0 U不同的类有不同的用途;不同的应用场合对应不同的产品形态;不同的产品形态可能会有自己特殊的描述符,比如: HID类有报告描述符、CDC类有ACM、Union描述符等。' P$ a" u  h4 n( n

0 ~+ o! B# J  S, Y4、描述符详解
" }# l1 Y# e1 t1 R% I
以STM32里的MSC设备为例,MSC类所需要的描述符有:设备描述符+配置描述符+接口描述符(数量由配置描述符里的bNumInterfaces字段决定)+端点描述符(数量由配置描述符里的bNumEndpoints决定),所以MSC类结构就是这样的:! N  R/ e) ~- X/ ]; A  l* [" n
' F% z# D; k/ S- h8 E; a" ?& F
  1. 配置描述符$ y5 O& H" l/ {$ N, j* [2 |
  2. {3 G( I+ c- {  D- i, R
  3.         接口描述符19 j$ B1 y  h2 Y
  4.         {: @* Y' J5 l/ G2 D+ A) L9 ~
  5.                 端点描述符1* O6 E0 P  R& a! R
  6.                 {
    7 W6 H3 v4 [- |
  7.                         
    ! L. m3 W2 P/ W9 F
  8.                 }4 n% T) U# S, h7 d$ |2 z- A2 c7 X- c5 ]/ y
  9.                 端点描述符2
    / F& h+ P0 N$ [" q5 L% k/ W# [, v
  10.                 {
    9 D1 [: h8 y* |& J/ `* T
  11.                         
    ( q4 Q9 Y8 [  D6 \/ Q, A* b
  12.                 }! E5 X. K  I* C; x5 d' x1 D
  13.         }& t. s! n# ?) ^" O% U: |8 h: B9 K
  14. }
复制代码

0 Z9 E+ K6 f# p# Z而CDC类是这样的:
; H2 ~. i: C- K* m# l* z
8 @8 ]2 y) {4 G: D% ?9 h0 z+ t3 Q' Z
  1. 配置描述符
    ; \+ N8 a, a% e4 e2 h% c- y
  2. {! X7 g. k7 ?! A6 a( P
  3.         接口描述符1(通信接口)4 d9 ~# ~# a$ ]: G5 U" p
  4.         {
    ; G- h7 G0 \& @
  5.                 其他描述符(特殊描述符)
    5 a$ |+ j8 ^+ n2 M
  6.                 {
    $ W- B4 q3 x2 _3 a
  7.                         /*Header Functional Descriptor*/( m+ R6 o0 X- |
  8.                         /*Call Management Functional Descriptor*/
    7 F) p0 [; V  A6 \9 }1 R
  9.                         /*ACM Functional Descriptor*/: c1 e+ K8 F6 ~9 i4 T; T0 u. @  S
  10.                         /*Union Functional Descriptor*/; R/ M7 [3 \% w1 y
  11.                 }
    ! ^3 s7 f# G- [+ ~( V( V
  12.                 端点描述符(命令端点)
    ) |# r3 g4 q3 Q% M' Z7 X3 E# e
  13.                 {
    + H( P. \: G2 Q
  14.                         ' ^1 v% g- A7 ~( d6 p* @& w1 `
  15.                 }. S9 B7 N2 Q1 u
  16.         }
    - N# h$ s1 K, j2 H) A2 {
  17.         接口描述符2(数据接口)
      w" ]1 d$ n: ~- f$ u" O& |" X
  18.         {6 U2 r7 E1 L' p6 z
  19.                 端点描述符1(输出端口)! Z" n8 c. P0 F- Z# q, D
  20.                 {6 z, `! k- \3 ]- s
  21.                         
    ' A! D1 S. [, ~' ?* u; g
  22.                 }
    " U7 K6 Y' C2 h; w
  23.                 端点描述符2(输入端口)
    0 m0 e9 r" q) e/ W' o/ j5 Z
  24.                 {0 y2 e9 h7 m4 ]7 ~1 ^$ S
  25.                         
    $ a3 @( d; {  x5 q; e
  26.                 }
    2 n, C/ o3 l8 a' [% _
  27.         }
    , F. a; s) H- O7 _# X  ?9 x  a8 t
  28. }
    ' ]( z! N% {9 X9 N' p+ u- r
复制代码
9 H0 E. L5 E6 u& w9 L
4.1 设备描述符
4 o# e! G/ Y: K  ]* g: L每个USB设备都必须且只有一个设备描述符,摘取一下STM32的MSC设备里的实例代码:
" U- J! H( |# Z
; B4 `) j- F5 ]# g Q4O0XI)3DGAR{L{CZVJO6%R.png
7 b8 e# A( a& W0 G9 W3 T, r; P8 a) k8 W# y# J
每个字段的含义:
  Q. B$ ~- p3 ~  y( M/ o0 X3 d3 J7 ibLength:描述符大小.固定为0x12;2 e# \# A0 P2 m* ^+ \
bDescriptorType:设备描述符类型.固定为0x01;! a) ^1 s- J6 D; l$ f3 E5 O
bcdUSB:USB 规范发布号.表示了本设备能适用于那种协议,如2.0=0200,1.1=0110;
3 T& P+ c3 t) R% K% t( E; p7 {bDeviceClass:类型代码(由USB指定)。当它的值是0时,表示所有接口在配置描述符里,并且所有接口是独立的。当它的值是1到FEH时,表示不同的接口关联的。当它的值是FFH时,它是厂商自己定义的;9 B# X2 r6 d- J3 e1 a# G# ~
bDeviceSubClass:子类型代码(由USB分配),如果 bDeviceClass值是0,一定要设置为0。其它情况就跟据USB-IF组织定义的编码;5 P  B' v$ [% J8 H+ L0 Q! S4 D
bDeviceProtocol:协议代码(由USB分配),如果使用USB-IF组织定义的协议,就需要设置这里的值,否则直接设置为0。如果厂商自己定义的可以设置为FFH;
5 Y  J( [& J; V1 Y% a' ObMaxPacketSize0:端点0最大分组大小(只有8,16,32,64有效);4 j! Z6 x* i- w2 b
idVendor:供应商ID(由USB分配);
$ q3 |9 U* \  OidProduct : 产品ID(由厂商分配),由供应商ID和产品ID,就可以让操作系统加载不同的驱动程序
& I4 {2 I/ f0 J; e% WbcdDevice :设备出产编码.由厂家自行设置;0 L6 O$ t4 l5 W+ b  C  \0 W
iManufacturer :厂商描述符字符串索引.索引到对应的字符串描述符. 为0则表示没有;, p" M1 r- ~) T: O
iProduct :产品描述符字符串索引.同上;
# T5 ~! a! C4 `6 _! siSerialNumber:设备序列号字符串索引.同上;
$ v5 f- p5 v7 k( @8 @  k. \. zbNumConfigurations :可能的配置数.指配置字符串的个数。
3 k% u+ R# d# Y) S# t
4 ^7 V( ~- v" L3 I: J$ U4.2 配置描述符, ]- C; U% z- X' }- l/ g; ?
配置描述符定义了设备的配置信息,一个设备可以有多个配置描述符。摘一个STM32的MSC设备的配置描述符:
4 g6 c$ D7 K2 u4 Y7 L% c
( F6 v' b! Q$ [' z4 y- k& B" m QV6[X)C[7`J@}LIASS9U7OW.png
& b' l" H! q) _- c3 c3 e+ F* W- v
1 [  F' {) O9 M用C语言组合就是这样的一个结构:& P0 Q/ [' l' Y( w& d1 M

: r% ]7 `  ~* C8 W/ a
  1. typedef struct _USB_CONFIGURATION_DESCRIPTOR_
    0 x0 a4 P& [4 x. R* N& v" x
  2. {
    9 R! }% ?! s8 ?
  3.     BYTE      bLength,
    6 e  O: X& T  o
  4.     BYTE      bDescriptorType,) o2 z" ]- K, y
  5.     uint16_t  wTotalLength,
    & U8 G5 j. Y3 q' Y7 F
  6.     BYTE      bNumInterfaces,) j* q( g- V& n( l1 R
  7.     BYTE      bConfigurationValue,. ]5 G+ r: I& v& y
  8.     BYTE      iConfiguration,
    + }/ w2 x/ t" q3 s
  9.     BYTE      bmAttributes,, L! V- j% Q- s
  10.     BYTE      MaxPower
    9 w* g, k; V) M( l
  11. }USB_CONFIGURATION_DESCRIPTOR;3 P6 w$ {4 d0 Z
复制代码

0 E4 \; _+ I5 {- @2 t: x: b每个字段含义如下:' y5 x. Z3 |2 }5 g8 @
bLength:描述符大小,固定为0x09.
0 B! a7 o5 u7 \9 ]! }3 G5 SbDescriptorType:配置描述符类型,固定为0x02.7 V* o& A8 K8 O* ~* f
wTotalLength:返回整个数据的长度,指此配置返回的配置描述符,接口描述符以及端点描述符的全部大小' a' ^% W- _; Q+ ^! S
bNumInterfaces:配置所支持的接口数。指该配置配备的接口数量也表示该配置下接口描述符数量( [/ {; p5 [- l8 v) ]! m" X3 i' ~
bConfigurationValue:作为Set Configuration的一个参数选择配置值4 S) z3 c5 r7 F4 i' b- V! n
iConfiguration:用于描述该配置字符串描述符的索引
, d6 H3 r( G! O: ?7 M- E3 {bmAttributes:供电模式选择,Bit4-0保留,D7:总线供电,D6:自供电,D5:远程唤醒& }2 p/ l3 [+ Y
MaxPower:总线供电的USB设备的最大消耗电流.以2mA为单位
3 A2 M$ X. `- H( S- i9 u: s( B3 r% u3 x& ?$ O
4.3 接口描述符
1 V( Y" l3 l- y$ E接口描述符说明了接口所提供的配置,一个配置所拥有的接口数量通过配置描述符的bNumInterfaces决定,摘取STM32的MSC设备类的接口描述符:& [2 x0 i. I6 E: O& H  e) r1 _

' U$ P) |) ?1 b. A N[Y@)]AS%`~62DO@AS8ST$X.png , b; e+ ^- b9 Q

( n& H8 B% I% W; d: G- u0 d# {2 Y4 s用C语言组合就是这样的一个结构:) D7 J( D" H) R3 K) J

1 k/ y0 N$ K: j- D/ D% k
  1. typedef struct _USB_INTERFACE_DESCRIPTOR_
    6 w; D' y5 s6 A* P# Y" L' I
  2. {1 u4 S6 J; q/ A- W0 E
  3.     BYTE      bLength,9 R2 t5 M2 B1 T: y) |
  4.     BYTE      bDescriptorType,. h( Y! w) ]- j7 r& [8 Z; c
  5.     BYTE      bInterfaceNumber,$ _9 C: T$ ]3 Z9 x3 T, ]$ ?
  6.     BYTE      bAlternateSetting,# A- g) c( _- b+ \3 A
  7.     BYTE      bNumEndpoint,  ]0 V: A. N! g2 Z" b5 f( h/ t
  8.     BYTE      bInterfaceClass,
    2 \# _( ?( T, f  q
  9.     BYTE      bInterfaceSubClass,& z1 d6 W' a3 l4 [9 _
  10.     BYTE      bInterfaceProtocol,  Y6 x2 L  @5 p7 |1 b" x2 d. e' o
  11.     BYTE      iInterface
    ) i8 b7 d" C9 B
  12. }USB_INTERFACE_DESCRIPTOR;2 P- p4 _. M% v" D
复制代码

& v% m! Y( y! j! l; {6 ~9 G& b每个字段含义如下:
! R1 ~/ p& W! F* H6 H3 |0 @
( d; X1 J0 |( g+ {2 S7 @% |9 M
bLength : 描述符大小.固定为0x09.2 K5 K' H) p* H" x! K/ L# G
bDescriptorType : 接口描述符类型.固定为0x04.( A, e3 ]9 R6 R: F/ m, ~  m# w
bInterfaceNumber: 该接口的编号.
  [3 I0 _  M$ O  G; Z0 Z, EbAlternateSetting : 用于为上一个字段选择可供替换的位置.即备用的接口描述符标号.
) s6 P" T, w8 k1 I0 I% x, qbNumEndpoint : 使用的端点数目.端点0除外.
% n1 o% L/ c* @, h& Z" ObInterfaceClass : 类型代码(由USB分配).6 i/ b9 e0 T$ E! W( E8 t6 a
bInterfaceSubClass : 子类型代码(由USB分配).2 J& a! r+ Z1 F! L$ S7 t3 w2 ?
bInterfaceProtocol : 协议代码(由USB分配).: v, _# I8 i6 c6 Z6 k
iInterface : 字符串描述符的索引
" i+ f0 F8 V, [4 Q6 U2 ]
& y, g1 L. @1 b+ A- ^4.4 端点描述符( l/ g2 }$ Q5 v1 |; w* c
USB设备中的每个端点都有自己的端点描述符,由接口描述符中的bNumEndpoint决定其数量。摘取STM32的MSC设备类的端口描述符:6 I3 d5 C! O$ g3 G9 o: W' |
  I) v% s. w7 G6 e& `* t: v5 Y
20210423144822577.png 0 e! N7 m; C4 X3 n" j8 j9 Q- Q
# h+ h2 v" G' |2 J) Y% s
用C语言组合就是这样的一个结构:: R6 }- t* ]) p. S5 h& }: i1 n- R9 n
6 ?: H8 U8 o1 L
  1. typedef struct _USB_ENDPOINT_DESCRIPTOR_
    / f3 K5 a1 @/ e6 ]: [2 B
  2. {2 p3 |. C( y7 u' B5 R( K
  3.     BYTE        bLength,  u: q2 O% w1 t' U0 D) C9 j
  4.     BYTE        bDescriptorType,
    # Q9 W0 I% b; u: [8 X
  5.     BYTE        bEndpointAddress,9 y- D; x1 b% V
  6.     BYTE        bmAttributes,0 f1 g: R5 j; v
  7.     uint16_t    wMaxPacketSize,
    6 X1 U1 q. A( v7 e7 ]) w
  8.     BYTE        bInterval
    5 {4 d/ Z0 x7 R- Q2 n: C
  9. }USB_ENDPOINT_DESCRIPTOR;
    : M1 _, e+ ^! c. d" {  }/ l
复制代码

  p6 ~9 Y; B* V; P每个字段含义如下:
  e+ D. X5 W5 ~) V! q/ q

4 @+ S; Q6 [$ |1 T7 \4 M# t3 gbLength : 描述符大小.固定为0x07
0 d. K$ m6 q9 v4 v! ^& J1 I2 pbDescriptorType : 接口描述符类型.固定为0x057 T" p* y2 M) F- `! R- X
bEndpointType : USB设备的端点地址.Bit7决定方向,1为IN端点,0为OUT端点,对于控制端点可以忽略;Bit6-4,保留;BIt3-0:端点号
  H) i" V# ^6 f# J3 b, T. t  t( GbmAttributes : 端点属性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中断8 b. c9 U) D  ^
wMaxPacketSize : 本端点接收或发送的最大信息包大小
/ y& z# h% T3 h; b6 B* xbInterval : 轮训数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255
8 X% ~$ k: }: \4 B4 I$ Q5 M
1 q! q6 }! e& u7 m% |9 S" A- J7 r; o3 z) O
4.5 字符串描述符
5 E0 J. T, [# i字符串描述符是可选的,如果不支持字符串描述符,其设备描述符、配置描述符、接口描述符内的所有字符串描述符索引都必须为0。  Q6 ^& v6 C' y0 f. A' ~
2 q! N  d4 q( [/ H) x
字符串描述符结构如下:/ @0 z' U- h8 V' A1 G

" Q2 P+ C5 S6 \) t: a+ q
  1. typedef struct _USB_STRING_DESCRIPTION_
    3 l6 X9 x$ t, X; u" r/ N
  2. {% W+ w) {3 L% H7 T: y/ C7 r5 G
  3.     BYTE      bLength,
    ( q) Q& ?6 y: @9 m3 ~8 I* U: Q
  4.     BYTE      bDescriptionType,( N! n) i3 `' I$ @9 Z
  5.     BYTE      bString[1];
    6 y8 w1 w! C3 X& m+ v9 @% ]
  6. }USB_STRING_DESCRIPTION;
复制代码
4 _) y& B9 s- a4 g" b' t) r3 j4 A
各个字段含义:
; F$ c) i2 i6 e+ U- T
/ P: R5 f8 K' b5 S4 \bLength : 描述符大小.由整个字符串的长度加上bLength和bDescriptorType的长度决定.( t% U" }$ P: i0 A! }1 n9 I4 E. W
bDescriptorType : 接口描述符类型,固定为0x03.4 s6 H% ~  D5 \3 ^
bString[1] : Unicode编码字符串$ n! w7 t1 n$ ?* [. w

3 Z3 e1 w1 O% O) i4.6 IAD描述符% v% H# A9 C6 G/ q
USB组合设备一般用Interface Association Descriptor(IAD)实现,就是在要合并的接口前加上IAD描述符。例如你想用一个硬件USB接口实现两个功能,又能到U盘又能当虚拟串口,那么在USB配置描述符中就需要加上IAD描述符来指明。
) A5 o! ~& l! s# C9 p
+ K5 p% v& l# k' J+ Q
  1. typedef struct _USBInterfaceAssociationDescriptor
    : w) L4 r! l% Q: K0 W$ ~
  2. {
    " t3 p3 y& Q* z8 V% }3 r7 b# Z0 p& c
  3.     BYTE  bLength:                  0x08        //描述符大小,固定5 i; J* l6 J1 ]) k; K
  4.     BYTE  bDescriptorType:          0x0B        //IAD描述符类型,固定
    , I# c9 ]: j$ u5 z6 ?
  5.     BYTE  bFirstInterface:          0x00        //起始接口编号) z* R4 G5 k5 t4 T/ Q& P+ m
  6.     BYTE  bInterfaceCount:          0x02        //本个IAD下设备类的接口数量1 q2 G: l1 p: x
  7.     BYTE  bFunctionClass:           0x0E        //类型代码,本个IAD指示的是什么类型的设备,例如CDC是0X02,MSC是0X089 ?4 A4 _$ P3 o- u2 r
  8.     BYTE  bFunctionSubClass:        0x03        //子类型代码; `0 e% h: F6 L
  9.     BYTE  bFunctionProtocol:        0x00        //协议代码$ }2 h7 ?% q: X1 t) ~8 k
  10.     BYTE  iFunction:                0x04        //描述字符串索引# o+ g# @. ~9 u8 ]1 q3 ], w
  11. }
复制代码
; Q! [2 M0 C; j" j4 t" _8 ~  _$ U
以MSC+CDC为例,他的配置描述符结构就是这样的:
& w# e. _' e# o: ?; A
1 @9 d8 u& R( ?" c( B( ?. E/ f8 }
  1. 配置描述符: }  [6 C1 c, h. W$ P) D* R3 J
  2. {; Z7 P* R+ k) n& h+ s
  3.         IAD描述符1(CDC)
    ! c* j: B1 i% u6 b+ k; X. O! L
  4.         {8 [: g( f. K: t" [) K6 e5 L& I
  5.                 接口描述符1(通信接口): G6 |: b/ U, ?* O, d4 }) [- ?9 Q: ~
  6.                 {
    ) a# Y: Z2 @5 v, c* g+ V" ?
  7.                         其他描述符(特殊描述符)5 d: P7 u# V: ]5 R
  8.                         {! N+ z* v. {. Y! T; M  p
  9.                                 /*Header Functional Descriptor*/6 [+ v+ y. w+ e; F
  10.                                 /*Call Management Functional Descriptor*/% |9 r# T+ G% k$ E8 h
  11.                                 /*ACM Functional Descriptor*/
    ( O  }/ L9 i6 F, b" u! W9 \* |6 h( I" z
  12.                                 /*Union Functional Descriptor*/' C2 Y) L4 C% [( y  ?5 H
  13.                         }
    8 _& f; B9 o; u1 U* h
  14.                         端点描述符(命令端点)
    5 D, r& r" k& z$ x1 H2 Z
  15.                         {
    2 }; e1 m3 w' @4 e7 ?( b3 Y( Z% b
  16.                                 
    ' k5 x0 z+ Q7 S5 ~3 x) k0 p8 C
  17.                         }' |: U" t# j" ~: c
  18.                 }/ R) n1 K! r$ [, ~" n  V& ]
  19.                 接口描述符2(数据接口)7 w( L3 i7 O- J/ S  M" I
  20.                 {" Y  Q9 B* b) u2 c( A7 V. e
  21.                         端点描述符1(输出端口)
    3 f' v( V$ R5 s) h" R( ]
  22.                         {9 |, M1 f: n$ q' g6 g' d- U) u9 d
  23.                                 - A# t- J) |; W2 @+ W7 ]  A
  24.                         }" v3 T3 o7 [$ G! r; n/ |
  25.                         端点描述符2(输入端口)
    , \$ `4 g$ G& O0 P
  26.                         {) D$ z9 B/ E7 q* I3 y
  27.                                 1 b9 W8 }, Q! m: t0 M2 Q; O' V
  28.                         }1 S( j) E2 q' {- D
  29.                 }3 {# y8 E% j/ U" ~
  30.         }4 v) G4 M3 b  [, d$ N! B
  31. . u# x% W' c# O. g
  32.         IAD描述符(MSC)
    3 h# w$ S6 H3 r5 G% X
  33.         {
    3 T$ G8 x' s" f9 I5 D: [2 ^
  34.                 接口描述符1' c5 n2 e. H/ T0 a) K9 H
  35.                 {
    7 Z- S$ N. t! N, }
  36.                         端点描述符1! H0 t  @) N6 o; f# q( Z' |! ?
  37.                         {
    1 x/ |4 u  p: c! x2 {) _" R
  38.                                 / W. }1 e5 f% Q  C
  39.                         }
    , p4 D. f( a: Z% }. Z
  40.                         端点描述符2: H) A7 v+ A' a
  41.                         {
    ) P7 f. J1 K7 o1 V4 R
  42.                                 & u7 p0 H! A9 o4 F4 X. W
  43.                         }
    % a" y* I4 T! b. ?: i
  44.                 }
    6 F. r# p1 B% n
  45.         }1 m% j0 o0 [$ T. K
  46. }
复制代码
3 L+ U% b/ d# K1 o$ {  X
5、STM32-USB详解
" B5 _3 I' {  w# \4 q+ }" s& e
$ o; J  O; P/ A
1C90HS%8{]RJ}G(XONA}WTR.png
' a( S+ C# Z+ W' m6 G2 x* C/ N# w! R. |+ z% O% x/ P* ?( A
这个512字节SRAM叫做Packet Buffer Memory Area(简称PMA),这个很重要,后面会详细讲解。# _8 a9 P! C& v: ^& d5 q
# v8 G7 X5 I1 `5 n+ G( c9 N& W
20210423152245989.png 8 ^! _; J! x* ^/ ?4 ^. I  j
- J, x" i5 ?! Y- s) b
根据描述可以,一共有8个端点,16个寄存器,一个端点关联两个寄存器,所以我们可以将他们规划为8个输入端点(0x80-0X87)+8个输出端点(0X00-0X07)。
2 [# [+ H) M% [- B0 n5 y# c, d3 L- y5 z; Q: R
6、STM32-PMA详解
% j% q/ F: A5 w+ V0 N- h先说一下USB的数据包大小,全速设备的最大包大小为64字节,高速最大为1024字节。6 N, w% C( u6 L( k

$ u( C0 T6 o% Y5 m$ SPacket Buffer Memory Area(简称PMA)# y, f; R8 S$ |+ t- G6 O7 ]" w3 ~
" E! ?2 L% f5 M! x% s/ `1 w
STM32F1/F3/L1系列都有且结构相同(其他系列暂未考证),译过来就是包数据缓存区,大小为512字节,按2字节进行寻址。* _! \2 }; M  {; ~0 v0 S8 ~

- x( b2 e3 Q6 H% G$ V这个PMA的作用就是USB设备模块用来实现MCU与主机进行数据通信的一个专门的数据缓冲区,我们称之为USB硬件缓冲区。
+ q! i6 R; P4 q" `% }% H0 P3 Y! T5 y
说得具体点就是USB模块把来自主机的数据接收进来后先放到PMA,然后再被拷贝到用户数据缓存区;或者MCU要发送到主机的数据,先从用户数据缓存区拷贝进PMA,再通过USB模块负责发送给主机。
; p: e0 q: ~2 {3 ~3 \% p( n6 N: [3 u0 Q5 Z
很多人利用ST官方的USB库修改自己的USB应用时候卡住,获取改完之后懵懵懂懂出现错误,估计大多数原因就在此处的修改!
2 y0 h5 M4 m8 Q5 J1 k0 o
7 C" ]2 y) p1 k: \摘取一下STM32F1参考手册里的PMA描述表:- l8 B0 b1 {7 m5 I+ M
9 o0 F6 d; m( T0 S7 A
PZS$S`SZZ_`Z`36[P3{G7LA.png 5 F  I6 i8 K5 A8 c4 ^* g( m: _* Y
  ?3 I- y* P3 a7 H; T$ N
名称含义:
; i' a' a9 A# D! w3 q: C: _# L7 `
# A& t5 H' _. \7 ~ADDR0_TX:输出端点0发送缓冲区地址) p2 y6 ~8 x* T1 v- ^& n# b5 |
COUNT0_TX:输出端点0发送缓冲区大小
5 `2 @3 W) p& D! J4 i9 O7 E. N  AADDR0_RX:输入端点0发送缓冲区地址6 b- O) ]" @# S/ Q; g# l+ u' e! p& B2 M
COUNT0_RX:输入端点0发送缓冲区大小1 _  x; v0 x8 S( p' H

* [  O  k1 g; R* s9 L$ l可以看到一个完整的端点描述包括:缓冲区地址+缓冲区大小。2 _8 f$ u8 ?5 D
* B- l7 b! m# t- g+ ?/ i
PMA的头部为端点的描述,每个端点占8个字节,实际使用了几个端点就有几个描述头,例如使用了0、1、2这三个连续端点(这里注意是连续端点),那么PMA头部的3x8=24(十六进制的0X18)字节就是描述,倘若你使用的是0、1、3这三个端点,其中编号为2的端点虽然没使用但是占用空间,那么PMA头部的端点描述就是4x8=32字节,编号为2的端点8字节的空间就浪费了(严格来说没浪费,就是不方便使用)。" ~+ e% c: x' n( W9 g7 |. M
% q& q5 k5 o& V+ o) i3 q1 b
头部的端点描述之后就是各个端点的缓冲区了,例如使用了0、1、2三个端点,占用了PMA头部3x8=24字节的空间,那么这三个端点的缓冲区地址就是从PMA偏移24字节开始的,当然只要是大于24就都可以,这里就是最关键的地方了,很多人修改ST官方库实现自己USB应用时候就是没改这里的地址,导致缓冲区的使用覆盖了PMA头部的端点描述从而出错!
! _0 b/ ~9 W0 B: j4 \" O% n1 R' D0 _: x
下面摘取一个STM32官方MSC设备的实例进行分析:3 M- y( [* L% T5 U
: x; u2 v- ]& H+ U/ Q$ ]
CEJODZ]EQIDCJYU4QBF@9M4.png # k8 e9 A0 k9 u' W( O; B6 I% d) x
8 ~4 J8 F0 ^1 H" b5 o  C
可以看到一共用到了4个端点,分别是输入端点0X80和0X81,输出端点0X00和0X01,其中0X00端点和0X80端点是供USB使用必须有的,0X81和0X01端点则是MSC设备输入输出端点。
: R' V, J: q1 v9 g0 o: e+ N% v& U4 H$ ^/ d7 K0 P
那么一共使用了4个端点,按理来说PMA头部的端点描述大小应该是4X8=32(十六进制的0X20)字节,0X20之后的才是各个端点缓冲区,但是ST这里的却是从0X18开始,也就是说使用了三个端点,这个地方我还没有搞明白为什么,欢迎各位补充!
8 T% F/ w) y  Y2 D( X, u
5 G( ?8 F9 Z  r" F至于为什么0X18之后是0X58,是因为USB全速设备的最大包是64字节(十进制的0X40),所以这里PMA的划分就是:/ x0 m0 Z8 w( v6 @
9 T. B- }( }5 N. |1 ~" i; F
头部0X18字节为各个端点的描述8 z# y, b/ h* I6 P8 _
0X18地址开始的64字节为输出端点0的缓冲区
8 R  g7 R: Q( d  Y0X58地址开始的64字节为输入端点0的缓冲区- ]3 S; R- M, k
0X98地址开始的64字节为输出端点1的缓冲区
, U7 G" a- m7 K8 q# p$ s( w- Q% H" b0XD8地址开始的64字节为输入端点1的缓冲区
: [: G5 l8 r8 L* q  R1 ?' ^0 i0 e2 j/ O
. ^4 ~$ W% y( W, c3 [, f: w2 h
这里注意一点,缓冲区分配好之后访问是不会溢出的,也就是说缓冲区之间完全隔离。/ q7 p- w9 m2 b# B

  C4 N. g5 l9 h: Y
收藏 1 评论0 发布时间:2022-4-9 23:28

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版