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

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

[复制链接]
STMCU小助手 发布时间:2022-4-9 23:28
1、基础知识
; ]0 j: {& `9 _. y: f0 m等级划分:3 k" t( u. H2 ]$ h1 c) |3 s% b
; v( `1 }! W/ |4 B3 H% R1 V% r
I@{P8IVPU$H)DO`BUMJPU6G.png ; w' f$ v7 H: \" t  M% ~# M
& l( v4 g# o4 a6 J
接口类型:
; W* c8 P( ^0 X& \7 b
8 }  z( [5 i) ]
PKUMM88ZPT]3(IMZKF{T72B.png
. H% x4 D% ]# }( N: I2 j
$ S/ t: y/ O* X. ~6 v) GSTM32基础型(F1系列)所带的USB是全速。
$ R, }' C/ [! q% \
9 w5 `& @& N7 b# O) c- W2、电气属性
  z9 t& v+ S: B! u7 U$ _( HUSB的通信都是由主机发起的,这一点与IIC协议是类似的。6 n# s: A; x4 ]5 }! V" f8 F
6 i& I% I1 I: u9 i* g9 R  R
2.1 数据线

8 B5 }( m4 t* |0 _1 h% TUSB使用差分传输模式,有两条数据线,分别是:6 G; |/ \! a) B7 }! m
( N) a$ h( l- u: t7 s
USB数据正信号线,USB Data Positive,即USB-DP线,简写为D+
$ Z! A- V# l+ t% d1 u, [" n2 D- ^8 c1 f0 BUSB数据负信号线,USB Data Minus, 即USB-DM线,简写为D-, t; f; o! b+ m
2 f0 a0 U# t6 m  e7 ?1 Q/ T
剩下的就是电源线(5V-Vbus)和地线(GND)。
+ Z: W7 v% j5 d: D8 a
- r8 |. V+ O' l2.2 USB主机是如何识别设备是高速设备/全速设备/低速设备?
) \4 R* C$ |" c; p. E主机的D+和D-都接有15K下拉电阻。
. q+ `' Y. l+ z! i# `' t! Z6 E
6 t  F0 ]1 J* t3 w7 }$ R7 e全速USB设备的数据线D+接有1.5K的上拉电阻,一旦接入主机,主机的D+被拉高% \' C3 j' }0 w" D. S# M
低速USB设备的数据线D-接有1.5K的上拉电阻,一旦接入主机,主机的D-会被拉高
9 @+ G) M# C0 i& n2 [. h2 D. q
' t6 }6 H6 D$ P5 b4 G: H* q, P; Y; |
因此,主机就可以根据检测到自己的D+为高还是D-为高,从而判断接入的设备是一个全速还是低速设备。
: U' y: N% V2 ?
5 K6 c; |: J; N5 P/ o+ ]4 j; O所以你可以看到STM32板子上的USB口D+有一个上拉电阻,而且是必须有的:0 b, q1 D% T! u* `2 }; _4 T& O

% F  Q; f/ E3 z/ ?2 [: G& ` OG}IT_SY%QK)L68[KA_(9{0.png
. p1 I! n' ^, ]+ t9 B
8 ]# H2 q% i7 U0 L3、USB设备分类
5 @4 J) [( X3 a/ F( ~0 I& B( k7 M$ i9 ^
BQKVM4710I9F5B8F(M7]A%U.png
  P' M4 B* A% \- M/ k- f2 D  |8 t$ n, G+ S% x3 J0 O
看一个CDC虚拟串口的设备分类:, j0 |4 `, D/ F* K
! ~+ U8 J/ Y% u( G- d4 M
9AFHVVM1``3(N}K}T$OV(X4.png
* \2 d1 y  x( N6 |# @- r# D7 T1 P: V% m
在看一个MSC的(模拟U盘):
, {: m/ \6 m* s5 }$ F8 B! B9 `' ]$ f0 D
6`B)XRM]W5OTE5~LG$WPDN3.png 2 Z+ y) X- j. ~9 j
, `, V( {7 N2 t1 R# m$ T* D
不同的类有不同的用途;不同的应用场合对应不同的产品形态;不同的产品形态可能会有自己特殊的描述符,比如: HID类有报告描述符、CDC类有ACM、Union描述符等。
4 F$ M2 X3 i( d9 ?4 c, S/ s$ L3 J+ Q4 s
4、描述符详解
& d+ a" \# l* L9 c$ e5 Z1 |' P
以STM32里的MSC设备为例,MSC类所需要的描述符有:设备描述符+配置描述符+接口描述符(数量由配置描述符里的bNumInterfaces字段决定)+端点描述符(数量由配置描述符里的bNumEndpoints决定),所以MSC类结构就是这样的:
1 E! u! n* v' M
* n1 c+ r% G7 p" H6 f
  1. 配置描述符
    9 _2 Z, g2 J9 Z8 c0 O1 I, x
  2. {2 D/ G8 w) u% g5 A/ ^2 ^" P
  3.         接口描述符1
    3 _& d! M0 r5 Z
  4.         {7 o; @& r1 Z5 ^) f" {2 Z
  5.                 端点描述符1
    , I3 ]0 q/ C3 z% q5 f9 j' U0 A/ ~$ Z
  6.                 {
    - h" V! j+ t+ J0 L
  7.                         
    4 H1 D# H7 j! H9 n2 `
  8.                 }
    & ]# v" m4 W/ p7 `: R2 i! T' ~4 @9 {/ w
  9.                 端点描述符2
    * }1 ?  n( u* b3 o7 }$ W2 d$ ^/ V
  10.                 {" p( j+ S. `+ x% T8 r3 [+ {
  11.                         6 Q7 `- t, q( b. X
  12.                 }
    ; M3 B- Z# \2 f1 K; C. K+ N6 H
  13.         }7 K% R: l$ S) S0 R
  14. }
复制代码

$ p4 D* }+ p: s6 R. T而CDC类是这样的:
8 {+ E3 S7 G+ v8 h7 N' a- h! D' G7 t$ _& y7 U1 K, v) c8 l1 b! n  T/ |9 d7 W
  1. 配置描述符' D, K8 ^8 K0 {9 \( _2 x
  2. {) C6 a8 }0 w- G& k7 b
  3.         接口描述符1(通信接口)
    0 l& Q. i) n3 w; w8 F4 k& Q' y
  4.         {
    9 f9 Z/ r6 h  f1 S
  5.                 其他描述符(特殊描述符)
    5 D0 q# ?: Z5 K
  6.                 {; E* \0 V7 R3 l+ S7 a. [
  7.                         /*Header Functional Descriptor*/
    # h, F- A, i! g8 o) p9 ~
  8.                         /*Call Management Functional Descriptor*/
    , M' k9 {$ l' N0 @' f3 E# F" P
  9.                         /*ACM Functional Descriptor*/
    7 D, F4 h5 _- |: M
  10.                         /*Union Functional Descriptor*/- w" [( n- Y# `5 ^
  11.                 }4 c( x# c- e; j# p) f
  12.                 端点描述符(命令端点)) o3 [+ h% \6 m* O: p, R$ p: Q
  13.                 {: a0 R- s' ]9 `
  14.                         * \3 M" E5 G' F; a  H( f
  15.                 }
    9 P6 g% F! i% U, J( J
  16.         }
    - }" C. _3 `! k9 P- {9 ~9 M6 W
  17.         接口描述符2(数据接口)0 \0 V8 N6 W2 [: f* ~
  18.         {  _2 C- I( J: n6 G5 F1 |2 t5 I+ }' X; F
  19.                 端点描述符1(输出端口)
    0 ?& O. J  a& ~: E8 v( c/ ?
  20.                 {
    ( X# Y- A: P3 i( m: ?6 P
  21.                         ; ]) }6 P  R( c, b" x$ x; p( G
  22.                 }
    . w5 \* e% m6 |2 [4 \8 Z
  23.                 端点描述符2(输入端口)
      Q3 u0 f  R3 q" H8 N6 @
  24.                 {
    % Z9 x% {3 Q4 C7 ]  P
  25.                         
    0 M2 T+ P# ]9 o+ {% ~
  26.                 }
    8 Z( \% r4 T' b. ?9 M) D1 D6 l$ G
  27.         }
    4 P5 `1 q- O9 d! k7 i6 c0 U* p
  28. }# [/ p% m) C/ w- ~- t
复制代码

9 {# `' @9 j! u! s( m/ V& l" L4.1 设备描述符
! c2 L0 j2 ^: e+ w; u0 X2 ]每个USB设备都必须且只有一个设备描述符,摘取一下STM32的MSC设备里的实例代码:
: I) M! f0 i8 c6 l! k& I0 a9 A' Y
5 H2 u# f% ?( N& B Q4O0XI)3DGAR{L{CZVJO6%R.png # o7 P5 l0 E& S( C+ @7 K. a
8 p2 W' Y5 F, g* V( {4 X2 y$ @  R
每个字段的含义:
- U2 h4 q+ N1 sbLength:描述符大小.固定为0x12;
. J% ], e% i  [6 gbDescriptorType:设备描述符类型.固定为0x01;, X3 Z( p6 G$ q$ X' H# [
bcdUSB:USB 规范发布号.表示了本设备能适用于那种协议,如2.0=0200,1.1=0110;1 ~) O( v0 _7 x# l3 H3 }, K
bDeviceClass:类型代码(由USB指定)。当它的值是0时,表示所有接口在配置描述符里,并且所有接口是独立的。当它的值是1到FEH时,表示不同的接口关联的。当它的值是FFH时,它是厂商自己定义的;8 W  c) `) v0 t# C3 k8 [) L
bDeviceSubClass:子类型代码(由USB分配),如果 bDeviceClass值是0,一定要设置为0。其它情况就跟据USB-IF组织定义的编码;
9 A% Z; s$ \% l* ^  E: F4 cbDeviceProtocol:协议代码(由USB分配),如果使用USB-IF组织定义的协议,就需要设置这里的值,否则直接设置为0。如果厂商自己定义的可以设置为FFH;: ?# _% y7 F, N4 @/ o
bMaxPacketSize0:端点0最大分组大小(只有8,16,32,64有效);
+ @3 x2 X6 ?+ J( KidVendor:供应商ID(由USB分配);
) K1 S: J6 n4 [/ _idProduct : 产品ID(由厂商分配),由供应商ID和产品ID,就可以让操作系统加载不同的驱动程序
' |% F6 @* R9 z. a/ v8 N6 `# i9 S; a# TbcdDevice :设备出产编码.由厂家自行设置;4 B3 k" b9 r* D1 T' k9 R7 Z* Q
iManufacturer :厂商描述符字符串索引.索引到对应的字符串描述符. 为0则表示没有;
$ @# V+ H" o8 KiProduct :产品描述符字符串索引.同上;
- R* i. @8 o+ m+ Y; HiSerialNumber:设备序列号字符串索引.同上;
4 V" Z% ?1 V* w& n" R2 I. f! I0 |bNumConfigurations :可能的配置数.指配置字符串的个数。
, d; y; v: ]$ n" I$ Y* R( C/ T, w! q* X: J/ d  Q  t% n! Q
4.2 配置描述符
) F" c  A/ b3 Y配置描述符定义了设备的配置信息,一个设备可以有多个配置描述符。摘一个STM32的MSC设备的配置描述符:
# l; H/ I/ @! c; W; {$ R( ?5 N+ C& z' g' u
QV6[X)C[7`J@}LIASS9U7OW.png 8 k. v5 S$ z& H: B; L

' C5 t$ C; z3 P6 L4 Z# m/ b用C语言组合就是这样的一个结构:
; y4 {7 M8 ?1 k% l5 Q" C
) O4 z* K6 k9 z6 K- O- X
  1. typedef struct _USB_CONFIGURATION_DESCRIPTOR_/ F5 z4 n9 r2 G1 C6 M, |
  2. {' F. t- s9 V, B; N8 [+ G
  3.     BYTE      bLength,
    8 {4 D/ o& z$ T
  4.     BYTE      bDescriptorType,5 X5 `, j! {8 |* I
  5.     uint16_t  wTotalLength,
    + H: t5 j/ n: }7 L2 k6 D
  6.     BYTE      bNumInterfaces,
    ( g/ _9 o2 X2 |3 I- t
  7.     BYTE      bConfigurationValue,
    ' C- B+ @4 X! f0 i1 W( ~) `/ g  k
  8.     BYTE      iConfiguration,
    * @; {; |/ \$ {
  9.     BYTE      bmAttributes,
    ) l* o0 u# u) u! r8 v. C
  10.     BYTE      MaxPower) Z) j1 j9 p( f
  11. }USB_CONFIGURATION_DESCRIPTOR;3 H* F( P$ H7 j
复制代码
- B0 W. L5 Q( @0 G) y( W
每个字段含义如下:
! `3 J  ~7 i# j2 v" QbLength:描述符大小,固定为0x09." u0 g- p- _+ N4 ~$ [. f* x3 E8 I  K
bDescriptorType:配置描述符类型,固定为0x02.
. P4 t5 J7 L) U% IwTotalLength:返回整个数据的长度,指此配置返回的配置描述符,接口描述符以及端点描述符的全部大小" j; _$ r1 R+ o$ U+ g
bNumInterfaces:配置所支持的接口数。指该配置配备的接口数量也表示该配置下接口描述符数量
: B- C6 _/ E/ _" ^! pbConfigurationValue:作为Set Configuration的一个参数选择配置值
5 G6 k+ I2 Z, NiConfiguration:用于描述该配置字符串描述符的索引4 j. p9 W) C% z9 q
bmAttributes:供电模式选择,Bit4-0保留,D7:总线供电,D6:自供电,D5:远程唤醒
3 p9 P8 t2 C) z! G/ FMaxPower:总线供电的USB设备的最大消耗电流.以2mA为单位/ r( m" Z# I) Z- e- {

! R! p3 S( t- o3 i6 q5 y; Q2 A4.3 接口描述符7 L! q/ E9 Z& T+ k
接口描述符说明了接口所提供的配置,一个配置所拥有的接口数量通过配置描述符的bNumInterfaces决定,摘取STM32的MSC设备类的接口描述符:4 m7 q# V6 H$ b4 f1 A0 L, v

6 e3 ~( K& t9 k' R; t: R. K N[Y@)]AS%`~62DO@AS8ST$X.png
3 F5 H" B& T; w, Y5 I8 a) ?3 ^( _  i. ~% d
用C语言组合就是这样的一个结构:
; h+ p) B* k9 H3 y$ h1 U0 M& x4 D4 `0 O! p
  1. typedef struct _USB_INTERFACE_DESCRIPTOR_
    3 `, ?* r5 j0 f! H+ G3 p/ _
  2. {
    * t+ x/ z. m1 r6 g4 E! a
  3.     BYTE      bLength,* b% Q! U% ?, k; x+ g2 a
  4.     BYTE      bDescriptorType,- I7 t, ]% t. q5 d$ J8 {
  5.     BYTE      bInterfaceNumber,4 C) y  l9 Q4 ^2 Z+ w# ], I- R; g- z
  6.     BYTE      bAlternateSetting,$ |: n0 w3 [" V2 B( T
  7.     BYTE      bNumEndpoint,
    2 \) Q" }6 r  L3 N' b' \8 ^( M
  8.     BYTE      bInterfaceClass,, o4 Y6 D% x7 m7 S5 v# z5 Q0 B
  9.     BYTE      bInterfaceSubClass,
    ' v/ T3 x) M8 q0 s5 c3 l
  10.     BYTE      bInterfaceProtocol,
    * G& O2 Z: G7 a1 p% p" N$ @# E9 o6 X1 m
  11.     BYTE      iInterface5 l2 e6 v: o5 |7 l' G/ A
  12. }USB_INTERFACE_DESCRIPTOR;9 j4 H2 h3 s% i. B1 a3 F
复制代码

+ ?) U2 T9 G7 V每个字段含义如下:
' @5 q5 k3 ]' t, f
  G5 N; |/ L6 Z* v4 R0 Z" H/ v$ D; t
bLength : 描述符大小.固定为0x09./ t3 F3 h( _8 t
bDescriptorType : 接口描述符类型.固定为0x04.
3 q! X! r1 t* ]$ s$ BbInterfaceNumber: 该接口的编号.
' \5 |3 P; L) [: [bAlternateSetting : 用于为上一个字段选择可供替换的位置.即备用的接口描述符标号.
% r9 O4 s4 y: x% D! TbNumEndpoint : 使用的端点数目.端点0除外.
. q8 }8 L* I; ?# p+ ~' i& ibInterfaceClass : 类型代码(由USB分配).
. p0 n. W* T: i( o0 J  ?bInterfaceSubClass : 子类型代码(由USB分配).. x- Z& C0 J5 Q
bInterfaceProtocol : 协议代码(由USB分配).8 M# q8 X6 j5 a+ v" y) _
iInterface : 字符串描述符的索引
  u6 V+ S) B! a7 i1 m/ `0 a  ~# |; O% e
4.4 端点描述符1 G, ?' Z/ G6 J& I
USB设备中的每个端点都有自己的端点描述符,由接口描述符中的bNumEndpoint决定其数量。摘取STM32的MSC设备类的端口描述符:
2 s1 c; x  t$ p; o1 ~/ {
, [3 A7 W$ }+ }4 D 20210423144822577.png 1 y0 v2 g7 X) |; d+ I! u
& K, N' H  K: ?: Q+ W
用C语言组合就是这样的一个结构:
' z2 s- J0 t$ P6 u! p* @
) w- E& u8 E; A4 q  ?
  1. typedef struct _USB_ENDPOINT_DESCRIPTOR_
    9 f, {4 J% g: P1 G
  2. {
    0 F* K" S* z+ Y; G& X
  3.     BYTE        bLength,) @$ U& N0 ?" r/ A* Q
  4.     BYTE        bDescriptorType,
    1 C  j; U4 A4 C
  5.     BYTE        bEndpointAddress,
    & V) r& I3 y7 u0 I4 |
  6.     BYTE        bmAttributes,
    5 c7 H/ p0 I7 O7 ?; n( l: t
  7.     uint16_t    wMaxPacketSize,  J$ y9 d2 g" C! K! x2 X! [! S+ w0 l5 }
  8.     BYTE        bInterval6 N0 G- S; n0 X
  9. }USB_ENDPOINT_DESCRIPTOR;! Z8 N2 l% ?* J; F- Y
复制代码
4 ]2 C& a2 X1 U% s# n; ?  x
每个字段含义如下:
/ _' {9 W$ ]6 [
" `5 s' k( Q9 k( {) e2 O) |; D+ y' l
bLength : 描述符大小.固定为0x07/ t+ Z5 k2 L/ l  P$ B
bDescriptorType : 接口描述符类型.固定为0x05
% U* `& R$ f) h" ]& _5 Q; U# @8 dbEndpointType : USB设备的端点地址.Bit7决定方向,1为IN端点,0为OUT端点,对于控制端点可以忽略;Bit6-4,保留;BIt3-0:端点号% W$ i. e; ?: F- h' o
bmAttributes : 端点属性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中断& v# I7 E* j" _7 N+ j( q
wMaxPacketSize : 本端点接收或发送的最大信息包大小
5 v3 y0 r6 T& ]9 ibInterval : 轮训数据传送端点的时间间隔.对于批量传送和控制传送的端点忽略.对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255# D- V0 y# d. H/ S/ v8 j: k
- }/ a6 i2 B5 S5 r
) V7 P: Q/ v3 _  ?
4.5 字符串描述符
9 W' q! a1 U4 y" y字符串描述符是可选的,如果不支持字符串描述符,其设备描述符、配置描述符、接口描述符内的所有字符串描述符索引都必须为0。
2 h% _" d( Z7 \" }$ {% ^% J4 G: X+ \! o6 e% Q. h# E
字符串描述符结构如下:
; P% F5 s4 G% Q! M* W
" O* ?7 m, U/ ~2 B( w+ H
  1. typedef struct _USB_STRING_DESCRIPTION_2 b% W& r6 [$ R1 ?4 u7 O( L2 G
  2. {
    6 {  g6 U1 f7 v( h
  3.     BYTE      bLength,! b/ |) x( W0 \/ p; |# p/ H( o
  4.     BYTE      bDescriptionType,& w* W0 @: G7 c! h  p) K
  5.     BYTE      bString[1];
    : h% o7 K) r! ?" F: ~
  6. }USB_STRING_DESCRIPTION;
复制代码
$ }; ~. d' ^) |1 X
各个字段含义:1 f; `; v( n/ O  B9 ~" P8 `

0 \4 v/ j  C# P! w0 ebLength : 描述符大小.由整个字符串的长度加上bLength和bDescriptorType的长度决定.
# [" |- D  L, }) B. JbDescriptorType : 接口描述符类型,固定为0x03.
6 h' u" f! ?0 b% c5 _, tbString[1] : Unicode编码字符串; @% ~9 E( v- v' l

6 J# `; Y+ j6 s% m& X0 N0 p4.6 IAD描述符
# P" h0 F5 H% g& \3 kUSB组合设备一般用Interface Association Descriptor(IAD)实现,就是在要合并的接口前加上IAD描述符。例如你想用一个硬件USB接口实现两个功能,又能到U盘又能当虚拟串口,那么在USB配置描述符中就需要加上IAD描述符来指明。
$ G" `1 Y4 H& [& l, e) K. N1 B' h# d1 q  m; t1 Q/ v+ b1 H8 `
  1. typedef struct _USBInterfaceAssociationDescriptor
    5 x1 j6 l( R* Q2 g4 x
  2. {2 s' ?4 r0 s" n9 y$ L$ @  L0 u
  3.     BYTE  bLength:                  0x08        //描述符大小,固定1 e* h9 T) e- p6 Y
  4.     BYTE  bDescriptorType:          0x0B        //IAD描述符类型,固定$ D( D5 H' b0 h' f) W( }8 J
  5.     BYTE  bFirstInterface:          0x00        //起始接口编号
    & N. {. R* j% J& Q/ C
  6.     BYTE  bInterfaceCount:          0x02        //本个IAD下设备类的接口数量7 t2 ]! E% t6 i# w9 E! A( `
  7.     BYTE  bFunctionClass:           0x0E        //类型代码,本个IAD指示的是什么类型的设备,例如CDC是0X02,MSC是0X08
    * `/ u2 M1 ^) q) j* K& z( Q
  8.     BYTE  bFunctionSubClass:        0x03        //子类型代码2 O, g8 M# e/ e5 m
  9.     BYTE  bFunctionProtocol:        0x00        //协议代码% h- N- |  x) t
  10.     BYTE  iFunction:                0x04        //描述字符串索引0 P) c, [+ D7 L7 k3 h9 a: H/ {  l+ n
  11. }
复制代码
1 a* k4 b2 f9 b$ X
以MSC+CDC为例,他的配置描述符结构就是这样的:$ M& ~( t0 f+ n* I
7 }; w+ B* X, S9 U  f
  1. 配置描述符
    9 ^8 P8 x8 D+ w+ t0 I6 J. k1 T, ~) C
  2. {
    1 O* }# @1 |8 k/ u" h+ I, O& W
  3.         IAD描述符1(CDC)
    2 B/ N$ W( }: ]& @" @
  4.         {
    % I  E( L3 }6 J" Q; _9 E
  5.                 接口描述符1(通信接口)2 }( `+ m5 h% U, r/ d
  6.                 {
    ' U6 C: s$ a5 f4 A; J* y4 f
  7.                         其他描述符(特殊描述符)) g8 W& s2 c4 s
  8.                         {
    ) n6 b# B5 U8 j& v! ?& M% [8 E+ F
  9.                                 /*Header Functional Descriptor*/
    - S/ z7 L* }' h6 F( q
  10.                                 /*Call Management Functional Descriptor*/
    * H! [5 `; U6 P% J, T
  11.                                 /*ACM Functional Descriptor*/2 E- H! Z' m: K. C5 ^) `
  12.                                 /*Union Functional Descriptor*/
    , K8 p/ n. O" L, u7 C! c
  13.                         }
    + B1 e' F$ ]* \1 m, s  }
  14.                         端点描述符(命令端点)7 L( B: e( ^, q3 d- Y* L
  15.                         {/ `/ B& v, u5 D- t3 K# |; T0 j
  16.                                 
    / B$ o* k' \0 U
  17.                         }1 R3 V7 h4 C! ]4 ^' N4 h
  18.                 }
    2 E7 j% t  C7 b6 G' B" L; E3 S
  19.                 接口描述符2(数据接口)
    , f. t/ v0 X1 e8 ?# F# H( c
  20.                 {
    ( L& X* s9 J9 l7 P
  21.                         端点描述符1(输出端口)
    3 v, b# ?& \. T0 m4 B5 \
  22.                         {
    ) ]. I, H3 w, ~
  23.                                 
    ( i4 v  E  U' C7 _$ M
  24.                         }8 u& ^6 }* D, x# x
  25.                         端点描述符2(输入端口)
    ' h0 }% |5 P5 [% |/ H9 \* |
  26.                         {
    - {4 B: M. d, s0 ?- q, |+ y7 p/ [7 g
  27.                                 ! |! Y" |, V  a5 M9 ^$ e
  28.                         }% H" g' J' T) ^1 g  @
  29.                 }
    / g% [% s8 Z) ?9 j7 K
  30.         }
    ) Z( D+ a9 y- S' n

  31. & \0 A7 R$ ^1 F) E1 {, v  a
  32.         IAD描述符(MSC)5 U# N* X% c7 l
  33.         {
    % R( n/ `& j/ g/ q8 P
  34.                 接口描述符1- b  |; @+ {  e+ V0 {
  35.                 {, _. W  N! P* Y$ D; K! Z
  36.                         端点描述符1% W: Z+ e& p7 Z$ E/ k- J$ L4 ^
  37.                         {5 O- n/ P0 M: }2 |5 D4 l8 r
  38.                                 ) `( o$ w' e& a9 D- ?/ {( R
  39.                         }
    4 Y5 ?; @9 B" t3 a. n
  40.                         端点描述符2# [$ A% j0 Z4 A8 N: j2 k
  41.                         {' U# z$ r2 p; {# m2 p5 W. |
  42.                                   \" z7 ^  T' J' L
  43.                         }- V, K8 l+ k, l8 ?" t7 V, \
  44.                 }/ T2 R  o' {6 b
  45.         }( q/ @6 S" m9 f7 @( R
  46. }
复制代码

" f  M! ]$ q7 T7 ^5、STM32-USB详解
* `+ ?3 A& A( t) ~8 I

1 c, u, l6 B- q% @ 1C90HS%8{]RJ}G(XONA}WTR.png
) Y! h* n6 b& k2 A& a/ Z
0 V, h4 p1 J# K6 J; B4 s这个512字节SRAM叫做Packet Buffer Memory Area(简称PMA),这个很重要,后面会详细讲解。  A6 u3 r$ D2 ~
+ x0 x) b# B9 W
20210423152245989.png $ b: a2 F, ?. p+ z4 c2 x( {

5 Y' z; P9 H3 g* Y, ?/ r+ ?( N根据描述可以,一共有8个端点,16个寄存器,一个端点关联两个寄存器,所以我们可以将他们规划为8个输入端点(0x80-0X87)+8个输出端点(0X00-0X07)。; \' ]! G* i) P5 r
9 ~! v. b2 q! b+ t3 E% {% U
6、STM32-PMA详解
' T. d/ @( N" Y% U% }6 d先说一下USB的数据包大小,全速设备的最大包大小为64字节,高速最大为1024字节。$ |8 f5 A3 n# v1 v: {

3 {( H2 W  t  d$ r6 KPacket Buffer Memory Area(简称PMA)7 \% z5 _/ [% @+ o/ z5 w; d5 k" P

+ \+ X: r& p5 U% A- X# y: KSTM32F1/F3/L1系列都有且结构相同(其他系列暂未考证),译过来就是包数据缓存区,大小为512字节,按2字节进行寻址。
: X- q: F1 O# {; P# r; U! h! t$ ~' h! C7 f/ O" i
这个PMA的作用就是USB设备模块用来实现MCU与主机进行数据通信的一个专门的数据缓冲区,我们称之为USB硬件缓冲区。9 Q% a0 Z  f( u# `7 y3 g2 c
& @0 d* m/ O. t# Y* P
说得具体点就是USB模块把来自主机的数据接收进来后先放到PMA,然后再被拷贝到用户数据缓存区;或者MCU要发送到主机的数据,先从用户数据缓存区拷贝进PMA,再通过USB模块负责发送给主机。+ }# s# E$ |/ P8 A3 |0 k
' ~; X+ P% @5 @/ Y
很多人利用ST官方的USB库修改自己的USB应用时候卡住,获取改完之后懵懵懂懂出现错误,估计大多数原因就在此处的修改!$ Y. I; c- U& ^: _) m. p4 x7 r5 b: X
+ V2 g0 ?0 N% u0 H& `5 t. S) I
摘取一下STM32F1参考手册里的PMA描述表:  O5 t7 T. P- Q7 V4 x! \

1 D1 I0 i5 Z/ L8 t! f- I6 {2 n* s PZS$S`SZZ_`Z`36[P3{G7LA.png 2 h4 Y6 i. T$ m0 l& h

* q: f0 Y& L0 ?# H4 d6 \, N名称含义:
) M5 A8 |$ i3 s% H
' |& `; M2 k# b- J0 KADDR0_TX:输出端点0发送缓冲区地址9 |$ f( H6 {6 b0 R/ Q. V( }( N
COUNT0_TX:输出端点0发送缓冲区大小
. W% k; a( A, m3 v. lADDR0_RX:输入端点0发送缓冲区地址
& Y, B( p, k3 Q, ACOUNT0_RX:输入端点0发送缓冲区大小
( Z0 o8 A4 |( Y( X6 w
. Z5 G5 U# y  m6 n4 q4 g  u' C可以看到一个完整的端点描述包括:缓冲区地址+缓冲区大小。
7 P9 M( k6 i2 \( i' E" E+ u$ ~8 n& y* P. ?, m8 K: o* f
PMA的头部为端点的描述,每个端点占8个字节,实际使用了几个端点就有几个描述头,例如使用了0、1、2这三个连续端点(这里注意是连续端点),那么PMA头部的3x8=24(十六进制的0X18)字节就是描述,倘若你使用的是0、1、3这三个端点,其中编号为2的端点虽然没使用但是占用空间,那么PMA头部的端点描述就是4x8=32字节,编号为2的端点8字节的空间就浪费了(严格来说没浪费,就是不方便使用)。
- Z5 W& C2 L. T" j; [% a2 Y6 i8 P" k( k5 f
头部的端点描述之后就是各个端点的缓冲区了,例如使用了0、1、2三个端点,占用了PMA头部3x8=24字节的空间,那么这三个端点的缓冲区地址就是从PMA偏移24字节开始的,当然只要是大于24就都可以,这里就是最关键的地方了,很多人修改ST官方库实现自己USB应用时候就是没改这里的地址,导致缓冲区的使用覆盖了PMA头部的端点描述从而出错!
" X5 x% _; U" d* {) O$ j. t. A! c7 d- b
下面摘取一个STM32官方MSC设备的实例进行分析:0 l4 S8 ^" j+ X! ~! Z3 A

6 Y% W3 e! ?1 |" u CEJODZ]EQIDCJYU4QBF@9M4.png
5 _: U7 s0 B/ p  K2 x; H! Y; f6 I! Y+ k( [- ]8 P+ W( Y
可以看到一共用到了4个端点,分别是输入端点0X80和0X81,输出端点0X00和0X01,其中0X00端点和0X80端点是供USB使用必须有的,0X81和0X01端点则是MSC设备输入输出端点。
3 P8 p0 {4 c9 Q: e2 R3 S4 R3 x" X8 a4 x
那么一共使用了4个端点,按理来说PMA头部的端点描述大小应该是4X8=32(十六进制的0X20)字节,0X20之后的才是各个端点缓冲区,但是ST这里的却是从0X18开始,也就是说使用了三个端点,这个地方我还没有搞明白为什么,欢迎各位补充!$ ^' U% W5 S" \) Q

) _; x, u- R5 s8 K. A至于为什么0X18之后是0X58,是因为USB全速设备的最大包是64字节(十进制的0X40),所以这里PMA的划分就是:/ Y- {6 p" V6 I0 M1 ?+ h
6 Z9 ~' H8 W8 \) Y& D* ?
头部0X18字节为各个端点的描述$ A6 s: T: E7 W% P' ~
0X18地址开始的64字节为输出端点0的缓冲区
. ?8 P& @+ {, c  U$ \0X58地址开始的64字节为输入端点0的缓冲区
. |" k! e. g: a$ s" Q0X98地址开始的64字节为输出端点1的缓冲区
) a& R" F9 \8 A9 o, j5 ?0XD8地址开始的64字节为输入端点1的缓冲区% S& B  j  k% p" |" d2 c1 t
3 e- e, D% B* C9 I; p6 g) q/ Q; k" f

, A+ F* i" n* I! @1 G( M& B这里注意一点,缓冲区分配好之后访问是不会溢出的,也就是说缓冲区之间完全隔离。6 s4 k2 q6 @0 ~; _
$ s- \9 o. d- i1 e( F+ F
收藏 1 评论0 发布时间:2022-4-9 23:28

举报

0个回答

所属标签

相似分享

官网相关资源

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