工程试验环境 C& u7 n9 ?2 F& w+ y
STM32F103RC8 t& H6 M% w! }2 u
STM32CUBEIDE1.5.18 f& c/ t a6 W( |" A
1、本教程默认你已经会使用STM32CUBEMX生成CDC代码和MSC代码,这两个工程的生成很简单,网络上的教程一搜遍地是。. A: C# K& t [3 @ [9 m. b% t
( a0 ^: B; ]% Z3 R: C& e% _2、USB组合设备的移植修改需要具备一定的USB知识储备,如果没有强烈建议看一下我的另一篇博客:STM32 USB相关知识扫盲
! M3 w! f; K7 }, x0 ~5 w
$ b# H' i) _2 x/ Y( k: L0 }* ~首先说一下STM32的USB库的初始化操作,MX_USB_DEVICE_Init函数中使用USBD_RegisterClass函数注册绑定了实际的端口初始化控制等操作,如果是CDC那么注册的就是USBD_CDC这个结构,如果是MSC那么就是注册的USB_MSC这个结构,所以我们的组合设备思路就是用哪个的时候,就将这个结构切换成对应的操作结构。
0 n* Y5 y7 _1 {+ F; S7 C; C5 b6 x
4 N1 X# g3 F$ i2 d& _# C# o9 @5 S第一步:基础工程生成# w! `/ d% O3 M6 w! G# P8 i
首先先用STM32CUBEMX生成CDC的工程和MSC的工程,并测试通过没有问题后,我这里使用CDC工程为基础工程进行修改成USB组合设备。( }& j, `8 ^) p0 K$ o8 j
) y5 }0 E" e$ @8 { Q7 {9 n" Y基本步骤如下:/ p# J. o5 N7 U( t
# @9 u' P9 i) N6 R( Q
USB设备描述符修改成组合设备类型' v8 a4 r$ P& M* E7 `
修改PMA端点分布7 C5 |7 k# B$ ^5 s. ~# I7 P/ s; b
修改USB配置描述符并添加MSC的配置描述
: E9 J9 I& h: X3 E) A2 h修改初始化函数接口,改写成我们自己的组合设备初始化操作函数
6 N) N0 Q+ [8 h# D/ h9 a. v修改MX_USB_DEVICE_Init函数,注册成我们自己的组合设备. q) p. B, Z6 v0 J ?8 _
; m# O" d; `9 b: a/ y
, D* o) F8 T" W4 z
下面进行分布修改。! }" |; s! d0 D9 X- S
$ @- D" j2 @6 E |7 N# c/ o
第二步:USB设备描述符的修改* C2 r! Z3 T0 n* C) n
这一步很简单的,就是修改usbd_desc.c中的设备描述符数组USBD_FS_DeviceDesc,将设备类型改为组合设备类型:) e7 X" l; v! K4 K" o
( e7 ]- n2 R% z" \- H, e) e
+ N- b, d) F3 e K! W3 M2 t, `
* \- ~! a' h: B0 G4 J' P第三步:修改PMA端点分布3 c# I. v- F" A) \6 Z4 W$ x7 A" D
首先修改一下CDC所用到的端点地址,CDC的输入输出端点不动,将命令端点成0X83: M8 R$ i) E3 J5 k6 ~2 G6 G% B$ r
% S- \6 F* ~, F6 ]0 x2 M& }. P6 M' Y# a) E3 k+ d, ^ ~% n
5 `# d' Y7 l2 N" m* C; Z8 M, G8 f
然后进入usbd_conf.c文件中,找到USBD_LL_Init函数,修改PMA端点初始化:
: A; j' }. z- H; S" v9 d( p. M7 ~! N: f( X. s4 H+ X
9 |6 x. B, O3 x' g1 N0 Q6 |
: K- \ Y% X, X1 ]这里的修改非常关键,为什么addr要从0X38开始呢?
& k& t5 e; g9 K) V# b, {5 c: C% v9 z: E
! k% Q9 B. ~" ?! P2 o因为我们目前用到了:* ?$ Q' E8 M9 q( L3 p: [8 ]
; z8 X, a' s& n8 F7 H" ?% d6 Y7 Q默认的两个端点(0X80、0X00)
& h9 B! b# ]) n& WCDC的三个端点(0X81、0X01、0X83)! F6 {6 S0 S: [+ G, ^' ]1 v9 W
MSC的两个端点(0X82、0X02)* n8 x% J; m6 x+ n( d5 j
9 ?$ H/ f" H- g; }/ G
/ o4 u" Z9 T# k, h( i
共7个端点,每个端点占用8字节,总共56字节,十六进制就是0X38!: l" _& I' I8 h
$ j9 Z# w; g* o5 F+ j
好多人修改自己的USB应用时,就是卡在了这里!如果起始地址修改不对,那么USB端点缓冲就会覆盖PMA头部的端点描述!
) B2 ]! H! V! S( z) ]
9 \2 D1 j$ k* S- k$ g9 W有关PMA的详细描述及使用,请看文档头部的《STM32 USB相关知识扫盲》链接文章!
) e1 q9 Y( U6 _2 V% \3 g9 {3 \% I+ M0 c" {8 f5 X. ?" M
第四步:编写我们自己的组合设备配置6 x6 n# ]6 P _2 I
这里我为了方便修改,新建了一个usbd_composite.c文件,用于编写组合设备驱动,文件内容如下:
: @+ \8 J7 [, Z) c4 d
9 |$ c1 m$ g' ?# p- h W- #include "usbd_def.h"
* p: q; U( n3 ~% ~+ G2 S, w. N6 K1 \" K - #include "usbd_msc.h"
) Q; F" Y3 p: e, E9 g* ^+ s4 z - #include "usbd_cdc.h"; h; h* @, J4 V+ W
- #include "usbd_storage_if.h"
2 ?+ E, b* I- t/ F7 w7 R A - #include "usbd_cdc_if.h"
4 n2 O+ e' R6 P' S2 x+ h; A
) B( x, V, Q# ]
* i/ k( Q4 _+ g. V( K; ~- #define USB_MC_CONFIG_DESC_SIZ 106- \) ~: O o5 r1 g0 S; r' _8 l7 J0 x
- O$ o t/ q7 d
, x& g7 z6 n4 `# ~2 r$ s, f) }- extern USBD_HandleTypeDef hUsbDeviceFS;
0 \6 R1 _0 `4 g/ c2 A" \# o - & f7 a7 C9 x& a1 A& L: j4 n
$ F, b0 {$ e$ [/ t- extern uint8_t USBD_MSC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);% Y5 @' d9 ~3 f# x9 [. U
- extern uint8_t USBD_MSC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
: P3 B5 b. f% f2 { - extern uint8_t USBD_MSC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);" ~7 h: R* O5 ^ c, j2 T) @( J \. i: M
- extern uint8_t USBD_MSC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);4 N* T2 t$ ~" ?$ E% F* E
- extern uint8_t USBD_MSC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);% R% p+ @ D& `; T3 @& i5 M
- & A" _$ V/ U! V ~. s
- 7 a' B6 x# g) x7 V
- extern uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);- _6 Y& Z6 l4 u8 M8 s6 o0 ~
- extern uint8_t USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);1 O. {) Y# O# D* a$ |5 T
- extern uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
1 c3 @4 j; Q3 t8 f; U - extern uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);$ }0 s2 [& \3 W' k
- extern uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);# x* C! k6 i/ F5 e" z; `
- extern uint8_t USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev);* [5 g4 [7 o j8 V! `
5 e" a! A* ]- ?' [5 \% g+ n- ) n: B J5 }4 t7 U, b2 S! E
- static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx);' u# [* u4 p6 m6 |2 ]
- static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx);
2 T. n* X0 f- }6 e - static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
% q* y5 I9 b9 a" m1 C) r; G- I - static uint8_t USBD_Composite_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum);
# ?& _% a4 A3 k$ L- Q! j4 J( [ - static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum);3 t9 X9 }* y' g/ I
- static uint8_t *USBD_Composite_GetHSCfgDesc (uint16_t *length);# A( ^+ g0 b0 [) A( p0 g0 e/ `
- static uint8_t *USBD_Composite_GetFSCfgDesc (uint16_t *length);
; s! ~9 R) y/ E7 m - static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);3 N1 S2 j; ]6 m2 H! [% u
- static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);& f J3 g% W- Y C
- static uint8_t USBD_Composite_RxReady (USBD_HandleTypeDef *pdev);# D* F, \! w) x/ g" y. |
- static void USBD_Composite_Switch_MSC(USBD_HandleTypeDef *pdev);
7 a8 B. k3 z { - static void USBD_Composite_Switch_CDC(USBD_HandleTypeDef *pdev);) {" b# |* ?6 ?: @( m+ b
- ) W/ A# \+ B# i& @
- 1 O! m9 H2 n, G2 Z" A
- USBD_ClassTypeDef USBD_Composite_CDC_MSC =* f9 G- l/ ^6 {$ z
- {9 B: ?# r0 M/ w# H+ m
- USBD_Composite_Init,
; a4 {$ }3 Q; S$ q' L( M - USBD_Composite_DeInit,
2 a0 o& Z G+ t+ {, I+ ` - USBD_Composite_Setup,7 j6 P# o3 P5 h* A1 L6 g" ^
- NULL, /*EP0_TxSent*/, I, h$ K3 M/ G& i7 C; W/ X* c
- USBD_Composite_RxReady, /*EP0_RxReady*/1 n D/ p: W' L- w! b
- USBD_Composite_DataIn,
9 ]1 z) S1 }; V! f. y - USBD_Composite_DataOut,
9 P& e5 h* t7 U( V [- B$ e4 h - NULL, /*SOF */! r6 `0 U1 X p8 A$ D- e: h |6 U
- NULL,4 t* A+ O& p: E
- NULL,
" [' p* J. n; ^ - USBD_Composite_GetHSCfgDesc,
& p H% r7 l, s' K K" E V - USBD_Composite_GetFSCfgDesc,
( B0 l% ]% _8 S. M7 W0 G8 d2 J% q - USBD_Composite_GetOtherSpeedCfgDesc,
5 P! Q, k1 L4 z% j- ? - USBD_Composite_GetDeviceQualifierDescriptor,: H% J* d' X% S; A/ o0 c' z `
- };
; H7 J. B: N% m, C: A6 v9 g
. o& f- m, o+ k, f+ |: ]. n3 o! z( [( S: j
0 v6 v( K/ j" y7 b- i9 \) j- /* All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
/ k# Y) y/ r* K8 ]9 |( \& k - __ALIGN_BEGIN uint8_t USBD_Composite_CfgDesc[USB_MC_CONFIG_DESC_SIZ] __ALIGN_END =
/ B- j* B' o$ s0 w- X - {
( c2 P P- [& h - /*Configuration Descriptor*/
5 Q: ~8 a( Y& l+ j: a - 0x09, /* bLength: Configuration Descriptor size *// ^/ Y! B! N2 c5 O4 |
- USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */4 B0 { E) P" {
- USB_MC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
& N% n' M5 X8 E, P - 0x00,
. P- \0 P5 C- w, d) B+ f1 O H' ^5 t - 0x03, /* bNumInterfaces: 3 interface */ /* +++lakun:CDC用了两个接口,MSC用了一个接口,所以是3 */2 ]( W- }% M" I" L
- 0x01, /* bConfigurationValue: Configuration value */3 }2 x, R2 o. y1 @/ c* s8 n
- 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
/ Y# C! H+ y" F- V - 0xC0, /* bmAttributes: self powered */
9 h' L9 B4 M- W8 f2 e3 y+ S& D - 0x32, /* MaxPower 0 mA */
9 t6 ^0 c; w3 H* z
7 y$ f: l% x9 X3 i/ n/ F- /*---------------------------------------------------------------------------*/8 P7 v7 v9 s! M, b6 u
8 i$ r! s+ M4 ?& p- H- //8 ^- b# P. D3 H/ l4 F
- // +++lakun: IAD(Interface Association Descriptor),用于指示CDC
$ a" P3 ^9 D: k4 G% ? - //
5 M7 g! X) f U! V" l9 |* \1 J - 0X08, // bLength: Interface Descriptor size,固定值# n" s7 j- H( D
- 0X0B, // bDescriptorType: IAD,固定值& q3 F3 H+ P6 Z6 S2 K" F, ]
- 0X00, // bFirstInterface,第一个接口的起始序号,从0开始
1 F2 o8 G* b5 l: {5 l1 a5 Q - 0X02, // bInterfaceCount,本IAD下的接口数量7 i( L; O- [, w; a7 Y% l ^& O
- 0X02, // bFunctionClass: CDC,表明该IAD是一个CDC类型的设备
& O6 C! k: x. T1 L* n9 u - 0X02, // bFunctionSubClass:子类型,默认即可2 I2 ]: {$ y8 P; H# }/ u
- 0X01, // bFunctionProtocol:控制协议,默认即可
# O% j1 p7 G! n/ i6 T - 0X00, // iFunction2 p# R; d( ?2 @9 `+ |
- " _% d H. c7 i" Q( Y
- /*Interface Descriptor */% w8 L/ a$ T. r* u$ n7 R1 m4 i
- 0x09, /* bLength: Interface Descriptor size */
7 u8 ?; H f/ B, x6 c - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
6 A( e+ ?0 U4 i4 i' S - /* Interface descriptor type */' n# Y! n' U5 t5 j* D- N4 h
- 0x00, /* bInterfaceNumber: Number of Interface */ /* +++lakun:接口编号 */5 L) }" C7 l4 I' J. N4 |
- 0x00, /* bAlternateSetting: Alternate setting */6 }7 p# J3 ~9 z! }$ [: i8 q
- 0x01, /* bNumEndpoints: One endpoints used */
/ H' i, P4 I* G3 [- R - 0x02, /* bInterfaceClass: Communication Interface Class */ /* +++lakun:表明这是一个通信接口 */
! j* n; p! A- o e$ ]) } - 0x02, /* bInterfaceSubClass: Abstract Control Model */
6 w; H8 h- g- V5 w - 0x01, /* bInterfaceProtocol: Common AT commands */
$ }3 s; W0 S2 G1 F# u - 0x00, /* iInterface: */
# t) z7 M, |/ d8 D
5 q* W) t4 \5 ^0 A/ w3 H- /*Header Functional Descriptor*/
Q, ?1 j0 @+ J% v* z4 ^+ z% ~ - 0x05, /* bLength: Endpoint Descriptor size */
+ ]' ?1 R9 P5 w - 0x24, /* bDescriptorType: CS_INTERFACE */# S6 n. c' ]/ b8 H
- 0x00, /* bDescriptorSubtype: Header Func Desc */
' \0 K7 K6 Z% `' R - 0x10, /* bcdCDC: spec release number */9 V6 s1 K1 j* V2 E4 U
- 0x01,
. [# d% @* B, I: y6 A. t! a; D
# t" y6 f, N" ?+ O8 T, I; z9 h1 u- /*Call Management Functional Descriptor*/
$ \6 _' i8 K# k! |$ R( p6 }$ k$ ]' N - 0x05, /* bFunctionLength */
* X2 f8 P$ w' I' E6 z0 S2 O" I! Z - 0x24, /* bDescriptorType: CS_INTERFACE */
; M Y; q& h/ N' r( w- R) X - 0x01, /* bDescriptorSubtype: Call Management Func Desc */6 f6 M: j* T7 d) f/ ~& V" L4 Q
- 0x00, /* bmCapabilities: D0+D1 */ T* D5 z8 A' O; X
- 0x01, /* bDataInterface: 1 */4 A# x0 y/ b' R! ~- Z9 y
9 o' f3 N% `2 y. ^) E" n- /*ACM Functional Descriptor*/
; {7 n% ]( p( `( `7 L - 0x04, /* bFunctionLength */- A: v5 x) Z8 t
- 0x24, /* bDescriptorType: CS_INTERFACE */
4 L! Z' D2 y% C- `) ?, a3 g - 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
) X5 G- x8 Y* }/ u% W( h1 ?4 o/ U - 0x02, /* bmCapabilities */* `$ {4 Y" ]& o8 S- Y6 e$ [7 Y5 }
- $ H1 F& k8 J3 [4 V( m! @# ^
- /*Union Functional Descriptor*/
- u6 k! L, \+ ?1 Y - 0x05, /* bFunctionLength */
' G! p0 }" R* P) u" g - 0x24, /* bDescriptorType: CS_INTERFACE */
* Q$ r8 w4 h0 c! C- z - 0x06, /* bDescriptorSubtype: Union func desc */" @2 _5 w3 y* `! f2 V
- 0x00, /* bMasterInterface: Communication class interface */ /* +++lakun:这里是用来指示CDC通信接口的编号的 */
) T/ P" z7 c- n& B2 ^6 } - 0x01, /* bSlaveInterface0: Data Class Interface */ /* +++lakun:这里是用来指示CDC数据接口的编号的 */
4 @ e) y( Q/ d) B/ j3 x
% ^. U0 x, F1 y9 O: [5 ?; n- /*Endpoint 2 Descriptor*/
5 G, R2 e/ c; m6 Z" L' ? - 0x07, /* bLength: Endpoint Descriptor size */
F4 Z6 {' D- p8 [ q - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
4 t6 Z z! B" K2 A - CDC_CMD_EP, /* bEndpointAddress */
3 M6 V" ^+ t" \ - 0x03, /* bmAttributes: Interrupt */
! v) n; o2 j* e; H' T. K - LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
* Z3 l7 G5 ?- N0 X - HIBYTE(CDC_CMD_PACKET_SIZE),+ G8 g/ N& c3 c0 [+ S) n# m- a- e4 \
- CDC_FS_BINTERVAL, /* bInterval: */; @! T4 y# w {! m/ V) |, k
- /*---------------------------------------------------------------------------*/
) M t3 F" b" Y q7 h
( |6 ~6 F) q8 a- J- /*Data class interface descriptor*/
! S6 W1 X& j, d% {/ g: Y4 S2 e - 0x09, /* bLength: Endpoint Descriptor size */
) ~3 ]& v# \+ h# H3 }1 M$ W - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ [& _" ~8 A( o
- 0x01, /* bInterfaceNumber: Number of Interface */ /* +++lakun:CDC数据接口的编号为1 */2 R# M# r" W" a* {! Q" Y
- 0x00, /* bAlternateSetting: Alternate setting */2 `; w4 T" H+ w' x7 c7 a
- 0x02, /* bNumEndpoints: Two endpoints used */
* {1 G+ t+ L1 Y8 B" e5 b0 p - 0x0A, /* bInterfaceClass: CDC */5 P. X G- b+ _ B' o) P" l) @ S
- 0x00, /* bInterfaceSubClass: */
2 n! k9 q2 d: ^% L1 k7 v! G# l - 0x00, /* bInterfaceProtocol: */
L0 E! L* V5 x% @! L) } - 0x00, /* iInterface: */% ^% H/ ~! Q+ J9 W5 P( i- p( t( ~
- + B6 I8 p& H4 R. z- Q
- /*Endpoint OUT Descriptor*/
9 T* k" R% Z) K - 0x07, /* bLength: Endpoint Descriptor size */0 ^" [" D! ?, {6 g( ]0 B' t
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
J) O- X3 `$ N: M* p; ]7 q - CDC_OUT_EP, /* bEndpointAddress */" p, p$ D5 T* x2 e6 g# H
- 0x02, /* bmAttributes: Bulk */
9 h3 F/ k/ `2 @# J2 w - LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */! p& P# \9 e: z+ |& I$ B
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
( b/ t* E( T( y3 y+ |/ h - 0x00, /* bInterval: ignore for Bulk transfer */0 \; m( c5 j0 y
- 2 O$ y S# m D6 g
- /*Endpoint IN Descriptor*// j9 A: P' W) o; |9 D# U0 S
- 0x07, /* bLength: Endpoint Descriptor size */2 x( ?, V8 {$ X, G2 N' p& O( o1 {
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
" n/ I7 T' t+ r* j - CDC_IN_EP, /* bEndpointAddress */
5 c. x9 r( o! q# j5 u4 H - 0x02, /* bmAttributes: Bulk */6 F8 |( T' |! T0 E4 Z" ~) G$ J
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */: F2 s! a4 c$ N, m! K
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),- h3 W$ s# l5 g, b$ H8 s W
- 0x00, /* bInterval: ignore for Bulk transfer */
9 y# W! n" g4 w8 i2 |8 J - 1 o2 A7 q) s- d% B0 ?) [
- //
( P6 D: V# T* m, n+ {; ` - // +++lakun: IAD(Interface Association Descriptor)
0 h4 S% z+ J* X8 a+ V" P - //( o y: | O( ]: X. m0 v' C
- 0X08, // bLength: Interface Descriptor size,固定值5 X9 ~% n& U/ q( b0 T& s
- 0X0B, // bDescriptorType: IAD,固定值
9 d- }/ f+ o& s - 0X02, // bFirstInterface,接口的起始序号(第0、1编号的接口用于CDC1,所以从2开始)1 ^% n$ N0 r ]" G) z, @; y' t% d* S
- 0X01, // bInterfaceCount,本IAD下的接口数量
! R" q( c0 Y$ f5 Y q# D - 0X08, // bFunctionClass: MSC,表明该IAD是一个MSC类型的设备3 ^! T# B) | r$ O3 U
- 0X06, // bFunctionSubClass:子类型,默认即可
# D! {0 q, X! a0 b - 0X50, // bFunctionProtocol:控制协议,默认即可
/ \1 ? w7 a: t; V" f; |, U - 0X05, // iFunction
5 W" o4 z4 }' X* S8 H
, y6 c3 N9 e6 o- /******************** Mass Storage interface ********************/! }) {# u( M/ B, h
- 0x09, /* bLength: Interface Descriptor size */
' x: d* L5 x+ e5 R: d - 0x04, /* bDescriptorType: */* I. b. Z6 }, y/ e
- 0x02, /* bInterfaceNumber: Number of Interface */ /* +++lakun:第0和1编号的用给了CDC,所以MSC接口的编号从2开始 */
; k K: a8 C4 V! }9 g! u$ e: z - 0x00, /* bAlternateSetting: Alternate setting */8 M4 S* r0 c+ q* B/ T) j( f/ @
- 0x02, /* bNumEndpoints*/, S: ^9 _( z5 c0 l
- 0x08, /* bInterfaceClass: MSC Class */
7 m6 Y) q( A+ P4 U - 0x06, /* bInterfaceSubClass : SCSI transparent*/
+ X: {+ E( e; P+ K4 @% ]7 D2 C - 0x50, /* nInterfaceProtocol */
" u/ M+ S1 D* [ - 0x05, /* iInterface: */
6 C* z5 z+ l: h4 c1 B - /******************** Mass Storage Endpoints ********************/
! i) S- D! F: i. F - 0x07, /*Endpoint descriptor length = 7*/: t- t* j6 [ Y% ~% N
- 0x05, /*Endpoint descriptor type */$ O$ z: d I! ^8 v1 ~
- MSC_EPIN_ADDR, /*Endpoint address (IN, address 1) */
$ z Q3 }& R3 @/ K) W - 0x02, /*Bulk endpoint type */; n, V1 |; f, T* z! N
- LOBYTE(MSC_MAX_FS_PACKET),; A H4 t5 `% D/ s2 c# z* E( e
- HIBYTE(MSC_MAX_FS_PACKET),
8 e( e; g- |$ Y* [# F* Y - 0x00, /*Polling interval in milliseconds */- x1 M2 H' T" |2 `" ]: J
7 G* @, g4 F4 z5 y1 T4 v- 0x07, /*Endpoint descriptor length = 7 */
( w& z! l1 k; p# y) z$ o - 0x05, /*Endpoint descriptor type */
: s: _2 A1 v; l& j/ z/ f! f - MSC_EPOUT_ADDR, /*Endpoint address (OUT, address 1) */' r' S3 T! x6 g1 {* `) ^/ {
- 0x02, /*Bulk endpoint type */
4 t# a8 ~, D7 e. ?, S* h - LOBYTE(MSC_MAX_FS_PACKET),
5 G9 a8 V% P3 e7 ^3 l - HIBYTE(MSC_MAX_FS_PACKET),- G% e% N: [! h/ S( B
- 0x00 /*Polling interval in milliseconds*/& {, H4 h' P0 y
- } ;" t# p1 ]. m: l2 L4 Q3 M: l
- 3 @0 _; H: G6 }' Q) t3 a
- 8 ~. ]! X' h, B3 Q
- /* USB Standard Device Descriptor */
5 M" v% E. I& V6 @6 h3 L$ A - uint8_t USBD_Composite_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] =
a P; V3 u2 Y( s0 g7 _ - {
9 |! M; P; R+ }) p - USB_LEN_DEV_QUALIFIER_DESC,
) O6 a% b3 Y9 g6 f* ]! o - USB_DESC_TYPE_DEVICE_QUALIFIER,5 K1 c7 x8 }' q1 Y2 ~# ~" S
- 0x00,/ T5 ~/ e% |/ s! ~% q6 Q( d) G3 h
- 0x02,
9 _/ C4 x I' C& s0 O8 G - 0x00,) w8 d2 J4 \1 ?, ]" P5 V5 X
- 0x00,
& r o- k& `7 d6 P: C) a. y0 ^# n5 d& N - 0x00,
* J" P9 Y% s- ~. O+ Y& |7 G- O9 \ - 0X40,. V- q: g ?! K F7 s
- 0x01,
5 p& D0 x, Y4 U, D+ L - 0x00,
, F1 [! @" k. X - };
) t* o6 r' q1 n; D0 k
7 R& q! K) {7 a e" {; e z
- E$ u& ^* _* c! Y2 U& @. E8 @- // 这个函数是修改的USBD_MSC_Init% J. {/ ~1 ?4 f% Q/ m+ F
- static void USBD_Composite_MSC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
9 A; M# C# J3 p6 h+ S - {6 ]" U; J8 C+ u- W: P$ F/ h: W1 W
- USBD_Composite_Switch_MSC(pdev);
: W3 g! H J! _, }7 f2 e* q# [
! L6 x* v& w3 A7 F, y/ Q" B4 B- /* Open EP OUT */7 X/ l4 Q# w, y6 [2 f0 h
- USBD_LL_OpenEP(pdev, MSC_EPOUT_ADDR, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);
) a# a$ \ Y1 T% U; A. D6 R - pdev->ep_out[MSC_EPOUT_ADDR & 0xFU].is_used = 1U;& |' ^7 [! \4 g* H8 B
- " |1 u1 E8 W) |
- /* Open EP IN */* c9 _0 D3 @' t, [. F- Z$ Z3 F" [
- USBD_LL_OpenEP(pdev, MSC_EPIN_ADDR, USBD_EP_TYPE_BULK, MSC_MAX_FS_PACKET);
/ a+ q( T. M1 \+ ~; E# b& ?! e9 v - pdev->ep_in[MSC_EPIN_ADDR & 0xFU].is_used = 1U;
: c6 U8 Z8 V6 ?, H5 G) R. `- T# i
6 z% m' s7 e0 E- w- /* Init the BOT layer */
! @, o$ D$ \; I - MSC_BOT_Init(pdev);
' a+ ?3 |( c& r+ X - }8 |/ [4 ~- ~" v# e6 Q
- 6 t8 T" a! u) m9 x
- // 这个函数是修改的USBD_CDC_Init
( l/ v9 }% f6 p' G% }& t1 W) r - static void USBD_Composite_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
& N8 G0 W$ j# w( P6 _2 w0 O - {
, C5 s. Q8 J) M) \ - USBD_CDC_HandleTypeDef *hcdc = NULL;
! p# T$ r% S/ Q m8 `' d
5 t, E$ j& `5 P- USBD_Composite_Switch_CDC(pdev);
7 Z) x, t3 l6 Y; N+ A' S
0 u/ c6 F: J: H* [; a1 H8 [3 t- /* Open EP IN */' g0 c3 Y F# Y1 Y% I K
- USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, CDC_DATA_FS_IN_PACKET_SIZE);9 V* Y4 y' w" `1 |
- pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;
3 f1 j# w8 n! Z0 L+ |8 Q - * C" T8 P6 s% H3 X: H
- /* Open EP OUT */
2 s b2 ] V% ~5 n6 M: V9 d - USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,CDC_DATA_FS_OUT_PACKET_SIZE);8 b( X% f0 u7 i$ `7 |- Q" Y! \
- pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;; `' f( k4 f% y
" l& y/ G0 a# h& m/ U. {' t- /* Open Command IN EP */6 f, q9 l( @) f* k, V+ J' {* c
- USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
6 O! T$ u. { e& {7 a - pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;
& n, ~1 r: W8 R6 i* i( \ - i2 w- ]: y* c) I1 F
- hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
/ H: r8 \7 v E( @" V
4 }% |" q* J. i e5 |- /* Init physical Interface components */
; L+ J7 k* k% V) K - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();
8 R& Z) T X3 k( w! S& j# a5 O
. D* d s, b5 p: o" P- /* Init Xfer states */
9 p: A1 k' u8 x - hcdc->TxState = 0U;
+ k) _6 S% ]* |0 j - hcdc->RxState = 0U;( o/ S5 E7 E5 P1 N
' c* @7 ~! |, _' G3 v8 D& F$ \- /* Prepare Out endpoint to receive next packet */
1 F/ Z* B* W9 \4 _0 Y; y - USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, CDC_DATA_FS_OUT_PACKET_SIZE);& h9 s6 b( |& P* h
- }( G3 H! |3 K. V9 h+ {, c
3 |$ L" H; Q+ V- uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
) r# Q5 K: ~2 q+ X. [; u - {
. s0 T5 @! {3 o - USBD_Composite_MSC_Init(pdev, cfgidx); // 初始化MSC
- {1 e, C k0 s* q9 W - USBD_Composite_CDC_Init(pdev, cfgidx); // 初始化CDC' j0 L i6 N _2 T: r& O
- return USBD_OK;/ l* p$ c$ ~6 d- u2 E" q$ ]9 E
- }8 c' C9 I) K% P+ }+ x0 }
5 I) G1 {% e/ @3 X3 j/ u- uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
7 J }; t* o# S# b& N& u - {3 B5 W6 [+ a9 p: D% }
- return USBD_OK;3 c% q3 m+ P* g" M* u
- }6 C2 i& l( A" R Z E
! A( o, y, h5 j5 u- uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
. \& Q$ |8 K( x. X& r - {% |7 y, ~4 s7 L. H6 Z4 k
- switch(req->wIndex) // wIndex是接口编号,在配置描述符里面我们第一个IAD是CDC使用的是编号0和1两个接口,第二个IAD是MSC使用的是编号2的接口
: e! i/ p$ t2 n( ?7 p9 B - {
0 f- J# X0 g) W) h& e6 e - $ q' t( Y5 a1 D/ N. X$ a2 ?
- //
0 Z) R* h5 z O# K - // 第一个IAD+ ]: A- v8 ]( p
- //+ }: G& W5 t7 x* `( G3 k( X
- case 0: // CDC的命令接口编号为0! j1 {1 L* P4 u& t. }9 ?
- case 1: // CDC的数据接口编号为11 ?% v/ x$ K6 S; p: m
- USBD_Composite_Switch_CDC(pdev);
- ]/ S3 v$ y' X2 m# P: C. @$ l: j - USBD_CDC_Setup(pdev, req);( \; t* e1 p o$ v* J3 b
- break;
& ?2 Z/ }; b E, a# Q6 Z; Q) \: w - 9 X( m6 V8 z7 J: @& d0 M
- //3 q* C" B% I* d& q" m
- // 第二个IAD
) b; j/ C) R3 r. f0 Y$ d - //
: ]+ X0 l2 [0 B1 o4 h- U2 ]# X" _ - case 2: // MSC只有一个接口,编号为2
, K e$ O8 d/ t2 X; c+ [4 C - USBD_Composite_Switch_MSC(pdev);
$ b" I: ~+ ]( O$ d4 O - USBD_MSC_Setup(pdev, req);
7 p+ `7 h1 {; D p9 M8 u3 ]/ h$ k. K - break;
- J) O& Z2 k1 o" g P( X" p
& h4 S; O9 y U" a- //
1 M( X8 h J" {" B/ L* R - // 第三个IAD(如果有,在这里初始化)
/ d$ f+ ~" A3 W. C& n4 @* O - //
3 U4 l; ~. ?$ Y1 E" }7 l4 ^: |# L - case 3:
* Q7 r4 E9 ?9 m - break;0 `3 E6 e4 V J: P* @* s: q7 V
- default:break;2 B. Y3 y0 \4 N+ A
- }' c& ~) E9 m# X( @
- return USBD_OK;9 ]( `+ _, v% C
- }
! ]) S. w6 _# ]% Y' @7 d& K
& B) F; c9 K* f. | s( e- uint8_t USBD_Composite_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
: m/ w# s! }* V' U0 r! r - {
; O0 d8 u6 O8 P' F - switch(epnum | 0X80)
, r" ~! }8 Q! j! Q( O - {
, A# c! G% P) u - case MSC_EPIN_ADDR:
" v4 J. ^) Z+ `0 K) [ - USBD_Composite_Switch_MSC(pdev);& G3 |1 u' F4 w, j
- USBD_MSC_DataIn(pdev, epnum);9 O' B" X4 |/ z5 t9 \( E
- break;
$ W5 C$ M$ A8 `% w$ k8 r - case CDC_IN_EP:
% V, A6 E$ B9 L+ h - USBD_Composite_Switch_CDC(pdev);
" Z' B% X6 A# D- j/ @0 A - USBD_CDC_DataIn(pdev, epnum);. |6 }, Q( y2 F. P1 U
- break;
5 J) d4 a) q) p( _8 U$ j - default:break;
8 k9 H+ A2 T9 e7 i- D$ [' } - }
: e5 p) E2 v% o8 h C - ( v# |3 a) f3 g2 q- }
- return USBD_OK;! n8 i+ X6 t$ }. P7 s
- }, o+ o2 V) ]8 H8 T4 R7 m4 a
- , U: F+ ~( v+ y/ o& R" E% o
- uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum): w3 O) E2 X) I$ s8 H) u
- {
4 F i3 g0 N* S9 f+ } - switch(epnum)7 g( N2 e3 d' x' R" q3 X
- {
* X% R' `4 Q8 m6 C& U - case MSC_EPOUT_ADDR:
, j0 l$ O8 `% J/ D$ m5 k - USBD_Composite_Switch_MSC(pdev);
5 J5 n8 s% p3 w/ Y - USBD_MSC_DataOut(pdev, epnum);
1 [- Z" e- L* Q+ S) ] - break;
8 x0 A5 ?8 h- n' m1 e3 Z v - case CDC_OUT_EP:
" g7 W6 \0 y0 u6 G2 X5 {4 a, E1 |/ l! G# P( ? - USBD_Composite_Switch_CDC(pdev);/ a t m5 H& L
- USBD_CDC_DataOut(pdev, epnum);
5 M( p% Y6 D" t5 l+ [2 s+ N$ j7 C - break;, ]1 U' n4 [0 Y0 K( |
- default:break;- D% N; j- \ {! @/ m
- }
" T$ y7 H1 k5 [ - return USBD_OK;
: d0 [! q* G( k; Y R* ~ - }
# b3 X/ r) O* J( ~# A7 K8 {
4 I! V, p% a5 _9 o$ ?1 v V- t8 y& C7 Z- uint8_t *USBD_Composite_GetHSCfgDesc (uint16_t *length)
' V. z$ r; d/ ` - {* g( p" z/ E* ?) c1 k$ y* L/ j. e
- *length = sizeof (USBD_Composite_CfgDesc);3 H+ _2 g; l) z, v |
- return USBD_Composite_CfgDesc;
" s% T; X5 E$ E8 x4 p# W - }
4 `% D; \2 h. |/ ], Z, ` - * R1 s5 h' |/ X
- uint8_t *USBD_Composite_GetFSCfgDesc (uint16_t *length)
' U# _9 r0 \3 R& s! Y - {
/ R( ^$ ]: H4 B& f' \ - *length = sizeof (USBD_Composite_CfgDesc);
/ `4 J8 D/ H0 {% j# ~ - return USBD_Composite_CfgDesc;
7 ^: i7 L1 `) b$ q# l# u8 o: q2 c - }$ w2 ^( \# D/ l; R
- 7 r, O ]) W5 P/ [0 k3 V
- uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length)
- W% Z1 S3 |0 h8 V0 h- \ - {# u' s, g% n8 ~% @
- *length = sizeof (USBD_Composite_CfgDesc);
1 [3 [. o+ M# A - return USBD_Composite_CfgDesc;
M6 G( O& v7 H/ { - }
e5 T2 s; i" `3 a+ r - 7 h# I0 k) N; c- I* l( o8 L! j: Y
- uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length)0 L8 n8 x$ W5 e' l
- {
9 [5 d! D" c% P - *length = sizeof (USBD_Composite_DeviceQualifierDesc);
5 y* L- }5 C3 ?1 b! F - return USBD_Composite_DeviceQualifierDesc;
2 ? |" v m4 d5 } - }: ~! P- r$ \, f, k1 j2 I
( @2 T$ k9 D6 Y+ s# q1 u+ q- static uint8_t USBD_Composite_RxReady (USBD_HandleTypeDef *pdev)
" ]6 D! }8 d! P5 p" [: Y9 z - {/ S g Y( q* B, i+ E% z5 A
- uint8_t ret = 0;% v' V; ]% f: Y* i* X- G
- switch(pdev->request.wIndex) // wIndex是接口编号,这里我们通过接口编号确定CDC还是其他设备的EP0接收9 e5 ~. L6 A2 p, t9 v& O
- {2 L. r, C% T0 F+ r- K
- case 0: // CDC的命令接口编号是0- |0 T/ D! e% N _
- case 1: // CDC的数据接口编号是1
. t$ p& u2 d0 T3 g! |; ? - USBD_Composite_Switch_CDC(pdev);- k9 d( u- F; s. ?$ i) v. a0 g6 d
- ret = USBD_CDC_EP0_RxReady(pdev);
d& U% q2 P- e R7 @ - break;
( f% |# s8 D$ [" K% O0 Y% H5 U4 t8 u - // 如果有其他设备还用到了EP0接收,在这里加入
# ]6 _; G7 `5 Y$ _7 s/ q) h7 P - case 2:9 u) b9 k4 s& |# [: c4 h: B' ?) t! [* H
- break;2 E7 E7 b+ e9 F0 ^2 ~! C/ w
- default:break;" c7 L9 W# E* _! O' M
- }
9 _3 X7 b/ ~9 O, u& e - return ret;
+ A% X- B8 O! P- L6 _: n1 v - }; I J; C, @, L3 L; E0 {+ f
- & `7 V4 l4 L/ l5 `" O3 a# q
- static void USBD_Composite_Switch_MSC(USBD_HandleTypeDef *pdev)& |. o2 ]6 A) N! Q
- {2 Y1 M, X' B% D. D/ D
- static USBD_MSC_BOT_HandleTypeDef USBD_MSC_Handle;. z& n" M2 S) W2 C" f
- USBD_MSC_RegisterStorage(pdev, &USBD_Storage_Interface_fops_FS);
* N5 A+ X0 B; P' I - pdev->pClassData = (void *)&USBD_MSC_Handle;# y& `' @4 n. s; t, ?; O
- }
F& Z1 x' |3 l8 H: f- U
: \& z$ R) R" D+ j9 i- static void USBD_Composite_Switch_CDC(USBD_HandleTypeDef *pdev)
! V6 | \3 k* c) y' l4 ~3 k1 i" J - {
# V% d- b: k' h' h# F - static USBD_CDC_HandleTypeDef USBD_CDC_Handle;
* P+ J. d& q+ [3 E) ] - USBD_CDC_RegisterInterface(pdev, &USBD_Interface_fops_FS);. \/ t2 `: A- u1 { ^5 s
- pdev->pClassData = (void *)&USBD_CDC_Handle;
+ s5 k$ z: d0 h V# r% Q& P - }
+ H+ l8 u0 D# a7 z5 V
复制代码 3 L0 Z' G6 F* ?' _. v) v3 V
其中有关设备描述符的修改,我都用+++lakun进行了标注,并写上注释,方便查看。
* J- k4 }! M5 c3 q- Z8 Q* T# n0 k$ X0 p- v; [# o, A
第五步:MX_DEVICE_USB_Init修改6 o3 y* k& N) P* ?* O
上面我们已经完成了统一接口的编写,现在就可以修改MX_DEVICE_USB_Init函数了,屏蔽掉接口注册函数USBD_CDC_RegisterInterface,然后将类注册换成我们自己的组合设备,如下图:! s& X9 B2 Z" M) l$ p
3 B$ C! ~9 l2 `3 k" A: V
6 x' v; \$ s& x# M
4 n8 ~7 ~# o2 j到这里就已经改完了,运行程序之后设备管理器会出现:一个组合设备、一个虚拟的串口、一个USB大容量存储设备,如下图:0 k! d H2 U6 G7 P0 ^
# K+ n" P1 u7 N) ] c
, W4 y, F. P5 g. ~! ]* z' Y& R( h5 \" Q0 R# j- [+ |
这样就是成功了!
) ~& |4 ?& }. K ?7 A1 k4 J4 B+ \& j/ q0 q1 |2 d" @. H" ~: M8 M4 u7 Y2 `& Y* u* c
!!如果修改过后发现设备管理器的MSC或者CDC显示感叹号,那么在回头检查PMA的配置以及组合设备描述符!!3 F7 M. p3 r l( Q. Q7 |. z: c
' y& w. t" q/ I* ]6 `2 }遇到的问题
$ o- Q4 \# M# N. n) W2 ^! C我修改完毕之后,将板子接入电脑,虚拟出来的串口没问题,但是就是死活认不出U盘,后来找到问题的原因是因为我先使用的CDC串口这个单独的工程,我的win10已经默认枚举成了CDC,所以认不出U盘。' L4 @! n! I) X* B5 G
4 L) n/ J' w0 E% @: }4 v6 i% q解决办法:' J6 G, J- D6 X6 c0 C* h2 e" i. ]' O
1.卸载识别出来的串口,重新拔插
4 |( Z0 B, \$ m2 G2.在程序里修改一下PID" Z6 e7 F& y& q' o1 m4 h/ K
4 y3 E5 e8 l+ R& L* s% X5 T: H( V1 |4 x: v1 m( m% J1 L
$ \4 v4 v1 o/ {( C2 R/ c, [. j4 x4 Q0 ^0 H( E
我是用的第二种办法解决的!4 l; p H3 H. d% t% V8 g
+ `0 W" t1 ]2 q4 v' t: ^. I2 A% ~0 O; ~; O8 L
" Q. b$ ~ A* t |
不需要composite.h吗?编译出错,有几个没定义的。能给个源代码吗?不胜感谢
是拼写错误导致的,程序可以运行。谢谢