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