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