工程环境:
, g4 z. f" K. I P3 HSTM32F103RC
0 c. c/ o1 e& N2 RSTM32CubeIDE 1.5.1+ R |1 g8 X9 v0 L3 R1 k0 W
第一步:基础工程生成. _. ?( O2 W5 g8 Y
首先先用STM32CUBEMX生成CDC工程,并测试通过没有问题后,就可以着手开始下一步的修改,如果你还不了解CDC虚拟串口,那么可以参考文章开头链接第一条说明里的博客内容。
* y* q6 l9 \; ]( O/ {) P$ a
[% G1 ?( w: ~/ |第二步:USB设备描述符的修改; |: T0 `) q3 m$ l- T
这一步很简单的,就是修改usbd_desc.c中的设备描述符数组USBD_FS_DeviceDesc,将设备类型改为组合设备类型:
' n/ _' G ^' i+ \1 D J0 n1 W0 [; O
9 N% F3 \. @1 K- v9 s- s
7 x+ R) L% B% \6 H: y4 R第三步:修改PMA端点分布
: H0 i- k$ ~7 o
% ~5 A0 ?. Z2 F4 C
4 p+ n S7 _8 N# o9 V) U
+ K: k# E( J3 a) Q- @+ [& E' P
然后进入usbd_conf.c文件中,找到USBD_LL_Init函数,修改PMA端点初始化:; ]% X! A* {: M! h* X
5 J6 j. [! X& ]* x. @4 V
' ?7 S8 G. [# ]7 `2 I3 S
9 ~9 ?/ B; S H& ^3 y9 \; a说一下PMA为什么这么改,目前我们用到的端点有:
Z4 `; s1 ?+ W# \' X: k0X80、0X00为USB所必须的端点
8 ?2 W; r+ s# _/ U0X81、0X01为CDC1的输入输出端点, ~5 C* M/ e( H' x! l
0X82、0X02为CDC2的输入输出端点
6 P' ?* m! A- T/ R1 {4 R0X83为CDC1的命令控制端点(0X03端点未使用,但是占用空间)1 z; E1 T7 u( s8 w
0X84为CDC2的命令控制端点(0X04端点未使用,但是占用空间)0 F5 L# e7 b( I* p
; f5 R- _* P$ c5 w0 B; X
所以一共10个端点,10x8=80字节,十六进制为0X50,所以端点的缓存地址就是从PMA偏移0X50地址处开始。
7 H- F" n F$ l9 T7 \$ m y3 C; \1 l* z2 G5 R. L0 C' ^
输入输出端点最大可配置为64字节的缓存,所以是自增0X40。
' J& S; j2 C2 v/ j8 c' E4 S# |# P2 M- m
J) k5 z( b/ Y4 N命令端点的缓存数据大小是8字节缓存,自增16字节是因为光用到了输入端点,输出端点没用但是占用PMA空间,所以是自增0X10。0 e" l+ _4 Q& \- Q* u2 [2 V
" K2 h1 `0 f( z4 I如下图指示是8字节:
) z& P0 E' F: D; A# E* Z4 m* \! a4 p( h, T
1 w/ S& _& c# K: }3 n
* Y V+ ?# ^" D( n4 Q9 W接下修改端点初始化,找到USBD_CDC_Init函数,添加CDC2的端点初始化操作:4 P' q. N& W( N. Y3 \
7 K# T. |6 A; p- static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)0 M: [/ B2 c+ z3 y( z
- {
0 W. \; ]& m9 Q& }9 H. Y8 z - uint8_t ret = 0U;" h; ^( G9 `; V1 S4 o( M
- USBD_CDC_HandleTypeDef *hcdc;
0 H) R+ M4 y$ b" [0 R! f" x3 p - 2 p, _7 _+ ]2 P$ f
- if (pdev->dev_speed == USBD_SPEED_HIGH)! ?1 B; k+ O$ j8 J: o7 M
- {6 K+ K& h5 _3 J
- /* Open EP IN */
; z2 A' g9 l, i# S/ K6 E% i - USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,
5 ~6 P" ]1 m0 \4 C" q - CDC_DATA_HS_IN_PACKET_SIZE);
( _. v w: j7 f( I7 F
: F0 b8 Y; S& k& @) @: Z- pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;0 E; {, Z4 v6 a6 G ^( N
- s4 ` h: X& b
- /* Open EP OUT */: E$ X- T5 B0 s0 o; O% Y% H. I
- USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, ?$ Y" a1 t; u; |
- CDC_DATA_HS_OUT_PACKET_SIZE);5 U1 Q Y6 Z* p# t
- + t- G5 a+ g0 Q5 @6 a; i
- pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;3 o9 [" T' |* _
- 2 h; r! b# d7 l0 x4 x! o
- }9 Z. h9 {) k& r+ D" r/ m
- else8 I9 X1 f) \( ]3 ~* I" v
- {: G/ S& G% b5 M' z+ g
- /* Open EP IN */8 h. h, ?4 @+ v2 M. p
- USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,0 W8 x& X: k: p' l( H
- CDC_DATA_FS_IN_PACKET_SIZE);# `# g8 \8 ~- y7 D2 z$ ^$ m
9 k/ p4 k$ b+ s; W6 k$ x2 T- pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;, i: r6 k0 T: U6 n8 Q2 Z8 j8 L
( S" V& [8 E: h/ y' Q$ r- /* Open EP OUT */
# c! G( C* @' t - USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,7 c" ^: G# j0 ?3 x! i7 d
- CDC_DATA_FS_OUT_PACKET_SIZE);
( a0 c; X) |: T* u0 F0 Q
$ s" r3 W5 z, a. N3 k; Q( Q! ?/ D6 [- pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;
& e& d0 t5 y5 z# V3 l3 Q" m" l - ) E n) w% G. S! h
- / +++lakun
. d' m. ^2 m* @4 S" c: i - /* Open EP IN */2 U* a' s5 }$ _* ?# V' |( h
- USBD_LL_OpenEP(pdev, CDC2_IN_EP, USBD_EP_TYPE_BULK,
) ~+ w7 x+ ]# h* S$ L - CDC_DATA_FS_IN_PACKET_SIZE);
/ H# Z5 [. M- p2 `3 d% x - ' b7 G: }1 C8 r8 i
- pdev->ep_in[CDC2_IN_EP & 0xFU].is_used = 1U;: [9 u4 A9 P B$ m
- # Y* c0 m0 _1 L* h
- /* Open EP OUT */
2 _0 }7 E) p3 t3 C - USBD_LL_OpenEP(pdev, CDC2_OUT_EP, USBD_EP_TYPE_BULK,
& M" p& ]+ \7 e U/ F. F - CDC_DATA_FS_OUT_PACKET_SIZE);( _7 |9 o- K2 N
- 2 Q& m7 y3 C1 M; x% j' m
- pdev->ep_out[CDC2_OUT_EP & 0xFU].is_used = 1U;
. V5 F" [1 j+ d! y x( \ - }' v" V( U' B. @2 i& c7 Q
- /* Open Command IN EP */& I1 J% Q& f5 c e
- USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);) i* o7 o3 h# r ^' G4 L9 k
- pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;
& u* ?; r+ r% k# C* c$ l, A: z
0 f7 P+ x& D0 b+ D- / +++lakun - \/ L. T9 B6 L+ \" G6 s: Q8 \
- /* Open Command IN EP */
# p8 \# @, H8 g" } - USBD_LL_OpenEP(pdev, CDC2_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
: p8 l# R3 O) V; r3 Z - pdev->ep_in[CDC2_CMD_EP & 0xFU].is_used = 1U;
* w! L9 V) ~/ a5 S; L( R
6 M% f, a! L0 E8 t0 D. k1 Y8 W- pdev->pClassData = USBD_malloc(sizeof(USBD_CDC_HandleTypeDef)); V) a+ \1 F6 Z; P$ [
- $ g3 k! s! V6 N2 s1 ^
- if (pdev->pClassData == NULL)
$ ] _$ P. r8 `- j: P2 P3 @; w# p - {* i5 w6 }/ o# ^& n4 u& h) v( V
- ret = 1U;4 ^8 v4 v7 n% G6 k
- }& w+ i+ V# D6 A+ C; Y, @- P
- else2 e: ^ N% }# \' Y( z& m8 [- r
- {
: M( J; r9 @1 g% P, h" Y& F - hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;& v9 w R) L* y8 |1 t) f
- . J. f+ K: ?* R
- /* Init physical Interface components */
! y: [: ~+ `2 K6 ` - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();" i! G1 A" a$ i* o; ?& r
& @2 S K2 O& m! |# [ C8 ?- F) B- /* Init Xfer states */! A5 ]9 e9 V* w' f6 l% v2 t2 A2 P3 ^$ M
- hcdc->TxState = 0U;( K3 n: \3 ~ S6 K8 |6 Z1 U
- hcdc->RxState = 0U;/ l; W: `4 V0 P- b* M
2 y$ q9 l/ H+ M- x' p/ ~- if (pdev->dev_speed == USBD_SPEED_HIGH)
+ } V7 H, I. O1 W2 d0 A% r5 ` - {
* J3 M3 S" y$ L& z: I, d - /* Prepare Out endpoint to receive next packet */1 m5 }1 }" ]; d, a
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,/ G, ?; j' u8 B1 ?% d9 N
- CDC_DATA_HS_OUT_PACKET_SIZE);& f. b4 d, C" \2 p
- }
. Q2 |1 i O. o( u- d' r - else$ H; P. s. z' ~3 x
- {3 o+ ?/ v2 U9 p4 y1 @$ h$ H' Y
- /* Prepare Out endpoint to receive next packet */
5 R6 O* F. j/ m2 `. a' i) I9 C - USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
: ?0 f9 h$ h# i: h - CDC_DATA_FS_OUT_PACKET_SIZE);9 ]* E/ ^ \) f3 x+ R$ F
- ' w+ [! D! _) d) X/ X6 w+ A6 S
- / +++lakun
/ J. S# w3 ], x' w3 G9 ? - /* Prepare Out endpoint to receive next packet */
- A" }' Z' ]8 T' W - USBD_LL_PrepareReceive(pdev, CDC2_OUT_EP, hcdc->RxBuffer,
: c3 u- o/ A$ r - CDC_DATA_FS_OUT_PACKET_SIZE);* r) N; a2 P& Z# Z( v: r
- }
( ~. a* G4 o/ R - }
9 r6 u5 T: @$ m6 T7 y - return ret;
' J! r2 Y; G8 U% O - }
% A5 g5 |+ A* C2 c* L* e0 U
复制代码
4 M/ R/ ^, Q' `) W注:这一步骤的修改都非常挂关键,好多人组合设备修改出错的原因就是在修改PMA这里出的问题!在本文章起始处的蓝色链接《STM32 USB知识扫盲》文中有对PAM的详细讲解,一定要仔细看一下并理解!8 e6 h/ j6 \8 p
$ c) j" x7 h3 I2 |
第四步:修改配置描述符, `: |; N9 s7 ]9 R4 u8 S7 }: t
贴一下我修改好的两路CDC配置,修改过的地方都有+++lakun样的标记:
5 @9 F- A! ]) h7 M1 Z8 C `1 M2 A% ]! ?& e
- /* USB CDC device Configuration Descriptor */
4 a$ G) B5 U- n1 h- }. b# Z* e& T - __ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =9 d2 I$ B" w3 j+ f" W: P# \: g
- {/ Q; M2 W* Q6 x
- /*Configuration Descriptor*/# S$ M1 U0 w( ~, _1 P% b& W( C0 }
- 0x09, /* bLength: Configuration Descriptor size */
2 @5 ^, r' p+ R- t% N - USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
* }, F) }7 C: o% L# m( u - USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
; ~4 I0 N1 e( C9 _4 z$ f; o - 0x00,: L7 f8 v7 O) D4 |. w2 q- b
- 0x04, /* bNumInterfaces: 4 interface */ /* +++lakun:一个CDC用到了连个接口,两个CDC就是4个接口 */' x2 B) z3 n& J% Q7 y9 N
- 0x01, /* bConfigurationValue: Configuration value */
/ e4 C! Z* b A- Y; v - 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
% [: {1 E9 W$ K* n - 0xC0, /* bmAttributes: self powered */
: X' p% L1 [9 [! V! {; \ - 0x32, /* MaxPower 0 mA */& [. d s, A [
- ; y) l$ S4 O9 ^% A$ a
- /*---------------------------------------------------------------------------*/* H1 `- O/ B* |- w! M
- . T4 N. b, o5 K6 S1 u5 t8 f
- //
% W3 z* T) f% P; `' z0 e - // +++lakun: IAD(Interface Association Descriptor)
' I" [. E* L' Q: x& w - //
, q) |7 _% F& u" D5 c, T/ P - 0X08, // bLength: Interface Descriptor size,固定值% s) S% @! w, ~
- 0X0B, // bDescriptorType: IAD,固定值
" j& k9 E, a7 Q: D) i6 W1 o- c - 0X00, // bFirstInterface,第一个接口的起始序号
# f0 d( e4 f7 K9 k, l3 f - 0X02, // bInterfaceCount,本IAD下的接口数量
' A6 W1 }$ i9 U - 0X02, // bFunctionClass: CDC,表明该IAD是一个CDC类型的设备3 M$ T! A) z/ R9 S* T7 e
- 0X02, // bFunctionSubClass:子类型,默认即可
+ J! p) k3 |$ l0 [, M% h' H. u - 0X01, // bFunctionProtocol:控制协议,默认即可0 ]. Q! _& W+ t
- 0X02, // iFunction
% d8 O3 V( c! H$ w2 T
8 \% o" y* `' N+ g- /*Interface Descriptor */4 V, s' G6 m8 R
- 0x09, /* bLength: Interface Descriptor size */6 T& m$ [$ i! ~% r% g5 z
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
% W: x' o/ _" Y w$ y: x7 q \ - /* Interface descriptor type */: _, w& X1 i' }5 Q% M$ {& C3 x2 W1 C
- 0x00, /* bInterfaceNumber: Number of Interface */ /* +++lakun:接口编号,从0开始 */ {( D/ F' r# A1 p6 ?
- 0x00, /* bAlternateSetting: Alternate setting */8 Q( e& G* d" F+ a, ~/ e
- 0x01, /* bNumEndpoints: One endpoints used */% ~( a K! p! O" [4 }+ l O
- 0x02, /* bInterfaceClass: Communication Interface Class */
+ u/ M8 C% C! t4 g8 \ - 0x02, /* bInterfaceSubClass: Abstract Control Model */$ w8 C4 M* ~- H* w+ J
- 0x01, /* bInterfaceProtocol: Common AT commands */# f; A4 ]) q3 [* x3 N- J
- 0x00, /* iInterface: */9 z0 }* i9 e7 L2 W/ U
- & c3 e O; G' T% Z6 T8 \. B
- /*Header Functional Descriptor*/ \. S( T- v' h% o4 }9 H6 \
- 0x05, /* bLength: Endpoint Descriptor size */
4 e# F0 g, K' U9 p - 0x24, /* bDescriptorType: CS_INTERFACE */
% I, I V0 B! Y }! ~7 e - 0x00, /* bDescriptorSubtype: Header Func Desc */
% [6 }; J3 `7 H% g: S. j: g - 0x10, /* bcdCDC: spec release number */
7 r7 C# U8 f7 g - 0x01,
5 X, X4 n4 F. h. t( N% D
( i. A7 G, Z5 j f9 L7 X6 x& H! M- /*Call Management Functional Descriptor*// i3 [: P1 u# R6 M2 c3 M* R
- 0x05, /* bFunctionLength */ H5 o; F" L2 d7 L# |
- 0x24, /* bDescriptorType: CS_INTERFACE */
7 B) D4 u" G: q2 _ - 0x01, /* bDescriptorSubtype: Call Management Func Desc */9 B- S$ k6 n# x; h- }
- 0x00, /* bmCapabilities: D0+D1 */
3 X' S. h& K8 m) I - 0x01, /* bDataInterface: 1 */
; N7 }2 S8 x, O - ! Z( N& b. I# Q0 R' R
- /*ACM Functional Descriptor*/
' I# m- U: N: ~! q$ _% G1 T3 u - 0x04, /* bFunctionLength */5 n: r0 Z* _6 n
- 0x24, /* bDescriptorType: CS_INTERFACE */
$ d& x" d$ C6 Z) {2 l3 ~ - 0x02, /* bDescriptorSubtype: Abstract Control Management desc */% N1 y( C# {$ v7 w
- 0x02, /* bmCapabilities */7 `1 F. J6 Q0 F* x) j* f/ p3 ^ S- C
- : p2 h$ L, m: x" X
- /*Union Functional Descriptor*/
, y5 {+ m* T4 P - 0x05, /* bFunctionLength */
" _! Z/ U8 R6 d' p! u - 0x24, /* bDescriptorType: CS_INTERFACE */7 f# N! @4 ^. g. _
- 0x06, /* bDescriptorSubtype: Union func desc */
& M6 P2 l9 X: A - 0x00, /* bMasterInterface: Communication class interface */ /* +++lakun:这里指示的是本CDC的通信接口编号 */
' w6 {4 m% c& g1 b) j9 d- l! X - 0x01, /* bSlaveInterface0: Data Class Interface */ /* +++lakun:这里指示的是本CDC的数据接口编号 */% o* [9 h: D! F+ F/ m" E
- 3 J, P4 A6 @3 Q. V
- /*Endpoint 2 Descriptor*/; j, ^* t1 R4 C2 g/ Q' b) d
- 0x07, /* bLength: Endpoint Descriptor size */0 x9 Z3 y8 c4 M" U( z8 N; T
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */: }, @' A& Y3 c
- CDC_CMD_EP, /* bEndpointAddress */( \$ \* ^( j. K2 u4 x4 R
- 0x03, /* bmAttributes: Interrupt */
; y0 k8 ]" G4 w/ s" c" k - LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
6 @0 V, |0 O( s7 x6 r2 V - HIBYTE(CDC_CMD_PACKET_SIZE),' u4 J# ^* w+ @0 Y
- CDC_FS_BINTERVAL, /* bInterval: */
" x, t# ~! W" Y$ l2 i - /*---------------------------------------------------------------------------*/
3 T2 N+ e) S9 Z) Q0 ^+ N2 X
) S: f' ?: V7 V; f2 j- /*Data class interface descriptor*/
" b a8 @1 T/ j& z2 i4 a - 0x09, /* bLength: Endpoint Descriptor size */) X) q- x) Q/ T0 ~$ q
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
* o9 c4 G2 A# z3 M* V0 C1 Z - 0x01, /* bInterfaceNumber: Number of Interface */ /* +++lakun:CDC1的数据接口编号 */: w5 Z! }$ g. u5 j* J% B
- 0x00, /* bAlternateSetting: Alternate setting *// N3 {. q: v4 u$ T1 S
- 0x02, /* bNumEndpoints: Two endpoints used */( \) u4 j9 v* ~" \/ _1 k
- 0x0A, /* bInterfaceClass: CDC */
- c. h3 a& ?, G2 ? b! @$ p& A - 0x00, /* bInterfaceSubClass: */% G) n* k% P! t. w( U
- 0x00, /* bInterfaceProtocol: */
) L6 a& x% ?& k - 0x00, /* iInterface: */' e# ]) ~: z! a! C$ B4 z6 L, x; A
# c+ H, C4 p0 `$ r3 }2 H. m2 k1 F- /*Endpoint OUT Descriptor*/
4 k4 q+ h. c( u) n% A7 n - 0x07, /* bLength: Endpoint Descriptor size */
9 ^ \& E( m9 z/ r4 h - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ t$ H+ q- t T! f# T
- CDC_OUT_EP, /* bEndpointAddress */" C- i" l b- k1 B6 M
- 0x02, /* bmAttributes: Bulk */
! A& g( N! [% R8 W" {& Y5 \* k - LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
2 [. B9 g2 l, {+ t - HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),5 X! \; Q3 m+ `) d' \7 _& ^
- 0x00, /* bInterval: ignore for Bulk transfer */: n9 x5 Z' v. s/ U' H- v$ T1 e0 g
, e, q0 Q# |" W) n& G- h: M- /*Endpoint IN Descriptor*/6 c8 J F) T- _6 J' n! V
- 0x07, /* bLength: Endpoint Descriptor size */
2 f# y: z K7 Q- k - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
* l/ M' v. T7 a' v& f7 S9 f w - CDC_IN_EP, /* bEndpointAddress */
/ D0 c" J# V0 O, C - 0x02, /* bmAttributes: Bulk */) v8 R6 l o& d# r- B0 f, n& p- a
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
' |) C9 p9 j- V, B* m6 v9 d: L& P - HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),% I1 `+ `" x0 g
- 0x00, /* bInterval: ignore for Bulk transfer */' c: c3 e2 ~, ~6 v% {
- 4 Z3 m; l& v2 k$ x! ?, h
- 1 ^7 U) e0 z2 c8 U& c4 H
- // e8 ?' ?8 K1 ~: [8 D1 @
- // +++lakun: IAD(Interface Association Descriptor)* B- X) ^) V4 @( z
- //
; F! I; Y( w% ?% N - 0X08, // bLength: Interface Descriptor size,固定值( z$ p4 m8 m$ E4 {3 y7 S' ]. W
- 0X0B, // bDescriptorType: IAD,固定值; K g- \$ D0 y6 b' `5 k! z' B
- 0X02, // bFirstInterface,第一个接口的起始序号(第0、1编号的接口用于CDC1,现在是第二个CDC了,所以从2开始)& R) s. p4 p4 F& d
- 0X02, // bInterfaceCount,本IAD下的接口数量$ ^9 ^& m- @* q* d6 R" o! V1 `
- 0X02, // bFunctionClass: CDC,表明该IAD是一个CDC类型的设备
; J/ d& G" z2 r$ c* O - 0X02, // bFunctionSubClass:子类型,默认即可
4 z# Z, G1 e3 i; E* z* [ - 0X01, // bFunctionProtocol:控制协议,默认即可% a0 k) o2 D8 q
- 0X02, // iFunction
+ A! k* g4 W; z) L0 W" Y0 D6 K: I
/ ]* ^1 L: ^! m8 m! V' ^$ o ]& j/ j- /*Interface Descriptor */
" t3 s: n. d, Y% B% ?( V# u - 0x09, /* bLength: Interface Descriptor size */- X- H- s1 D" y# }& }
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
+ {: A1 q$ E; d - /* Interface descriptor type */, k4 W1 D9 ^3 G1 B7 T, |
- 0x02, /* bInterfaceNumber: Number of Interface */ /* +++lakun:这里就是第二个CDC了,第0、1编号的接口给CDC1使用了,所以是2开始的 */
. @. N' }' r9 { - 0x00, /* bAlternateSetting: Alternate setting */
& A' N! x5 I6 D. E - 0x01, /* bNumEndpoints: One endpoints used */9 v2 w5 _, I# v
- 0x02, /* bInterfaceClass: Communication Interface Class */+ j9 I' b2 P; O2 c9 D) e9 H
- 0x02, /* bInterfaceSubClass: Abstract Control Model */0 ~. I: w; j+ d( }6 \# b; @
- 0x01, /* bInterfaceProtocol: Common AT commands */' {' j L5 Q2 {: g8 d T; X
- 0x00, /* iInterface: */, c. j: n! P3 i# ~) M0 B8 f p
- - e$ K9 y$ m, N/ {! r
- /*Header Functional Descriptor*/
) G& y. G3 f2 @ - 0x05, /* bLength: Endpoint Descriptor size */0 Q3 F! f* e+ o. r; v
- 0x24, /* bDescriptorType: CS_INTERFACE */( Y& o& d! \+ a! a+ W8 N( ]
- 0x00, /* bDescriptorSubtype: Header Func Desc */9 P/ _% K- y8 t9 g! E' O' W8 E9 x6 h
- 0x10, /* bcdCDC: spec release number */9 F9 P6 P t; O; S/ ?. E8 F
- 0x01,
% `+ j# n3 S& T' k" b - ' x+ O" x2 N. a+ l7 ~) z
- /*Call Management Functional Descriptor*/
: x9 @6 W9 z+ z. V! J F - 0x05, /* bFunctionLength */- w/ g! r! w7 e1 k" P7 Y
- 0x24, /* bDescriptorType: CS_INTERFACE */
. F6 j1 J3 D5 S& m2 I0 u g - 0x01, /* bDescriptorSubtype: Call Management Func Desc */: P3 ~" u' t g$ i. Y
- 0x00, /* bmCapabilities: D0+D1 */
% O# l& S/ P( A# O K5 a - 0x01, /* bDataInterface: 1 */
- O/ Y" F- o. }# p8 n! L0 [! V
- z- k" o* L. s- /*ACM Functional Descriptor*/, r6 @% u6 t0 p# s* @
- 0x04, /* bFunctionLength */
* S9 V# n! S3 G# _% z# E3 t - 0x24, /* bDescriptorType: CS_INTERFACE */; `( _9 I! \5 l z4 U0 F
- 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
# ~, ^+ }4 x8 [* e8 _9 \ - 0x02, /* bmCapabilities */9 o3 S! [( d# C
9 B4 t% q3 a/ X- /*Union Functional Descriptor*/
& `3 k- z9 g" [; Y/ k* X+ i - 0x05, /* bFunctionLength */
7 A6 c* g1 Z/ E4 ?4 C - 0x24, /* bDescriptorType: CS_INTERFACE */
+ O' I2 g$ O+ I1 s) r6 K: y' b - 0x06, /* bDescriptorSubtype: Union func desc */
$ U1 X$ ^# V b9 h' ]- F" H - 0x02, /* bMasterInterface: Communication class interface */ /* +++lakun:这里指示的是本CDC的通信接口编号 */; r; r9 k" [ L9 ~; l1 V
- 0x03, /* bSlaveInterface0: Data Class Interface */ /* +++lakun:这里指示的是本CDC的数据接口编号 */
2 F9 l0 Y5 M; [1 a( V$ U2 g - 8 A/ X/ r4 S# Y! g5 S
- /*Endpoint 2 Descriptor*/& W$ q! }/ K9 A
- 0x07, /* bLength: Endpoint Descriptor size */( x& j3 B( G; |* N" ?4 a
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */) w' a7 t. Q" k. }
- CDC2_CMD_EP, /* bEndpointAddress */
' w% W9 _$ G/ ] - 0x03, /* bmAttributes: Interrupt */
8 ]- F5 D) `/ ^# h - LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
& i0 P4 N0 S) `& H/ \0 C3 L5 U - HIBYTE(CDC_CMD_PACKET_SIZE),
5 L5 I: l V; J - CDC_FS_BINTERVAL, /* bInterval: */" n; C$ k7 U6 C$ ~% z' i: c
- /*---------------------------------------------------------------------------*/
4 }, V ~4 b C, {: g+ e" _4 v
; f# r t3 J3 j! w- /*Data class interface descriptor*/7 A# ] t" G" A5 Z& P" B- N+ R
- 0x09, /* bLength: Endpoint Descriptor size */; n: o: h3 w) | d$ @
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */3 F0 I3 [% ]" V) c" U. W) ]& p
- 0x03, /* bInterfaceNumber: Number of Interface */ /* +++lakun:CDC2的数据接口编号 */ M1 R; I' ~8 F, U2 B
- 0x00, /* bAlternateSetting: Alternate setting */; q3 W0 j, _ f8 q8 X
- 0x02, /* bNumEndpoints: Two endpoints used */* n U( I( U% {3 ]
- 0x0A, /* bInterfaceClass: CDC */
8 I' c' u8 S7 Z( `- I! q' Q - 0x00, /* bInterfaceSubClass: */
m# b, w, }( j2 n2 e" Q5 v+ ^; ]6 [ - 0x00, /* bInterfaceProtocol: */% L& x7 w Z# ]0 b7 c% z {2 [
- 0x00, /* iInterface: */
9 I/ Y! _) Y4 D3 ]
$ Q" }$ B z1 Q, J8 n. m8 E- /*Endpoint OUT Descriptor*/
7 h W0 Z& N- A* H8 o: c) ^ - 0x07, /* bLength: Endpoint Descriptor size */
: ^9 V; d, U* e+ F( D$ O - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */- b1 Z4 E9 Z8 J- K/ u; j
- CDC2_OUT_EP, /* bEndpointAddress */
3 X6 {3 G6 ]* A9 W0 N - 0x02, /* bmAttributes: Bulk */1 c1 L/ o; n& ?1 x; @7 `6 ?
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */9 V, {- ~% F- X/ c- Y1 Y6 T
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),, V- ]; l" X4 |
- 0x00, /* bInterval: ignore for Bulk transfer */3 v- V5 C9 B r
- & h% a( w& K: T0 M
- /*Endpoint IN Descriptor*/% X( y6 V; K& |* [& I' \' T
- 0x07, /* bLength: Endpoint Descriptor size */
7 D- o& B# @! s! x' b8 s5 k9 ~ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */" B6 u5 j4 h* g c: s# ?- C0 I, g
- CDC2_IN_EP, /* bEndpointAddress */8 R5 R8 ]( L% B3 G
- 0x02, /* bmAttributes: Bulk */$ f7 t7 t j. \& k. U+ l
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
W9 ?! y3 ~4 d$ l# [% Z - HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),( ^6 D3 B% h. Z
- 0x00, /* bInterval: ignore for Bulk transfer */# C3 _8 n; f( ]6 g9 z3 ^
- Q* C# _5 d( _. y- m
- } ;& {" J/ x0 I& W; V; C6 a0 U4 l
复制代码 " i4 _1 C6 @- r- P4 [, F Y( f
第五步:修改函数接口
/ U8 V: R0 S7 p) @: N0 i, K; K X默认的HAL库函数是只针对一路CDC的情况,所以我们需要修改成多路CDC操作函数,将端口参数传递出来,一共有下面几个函数:" L: r% p a6 p6 W' R, @+ y
USBD_CDC_DataOut:USB接收函数回调,修改提供端口参数4 j9 ^3 S) o0 z
CDC_Receive_FS(uint8_t *Buf, uint32_t *Len):USB CDC接收函数
# q6 h2 x8 N9 q; W- lCDC_Transmit_FS(uint8_t* Buf, uint16_t Len):USB CDC发送函数
6 _) X3 l) y$ l. E7 gUSBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev)0 a; X% F" {7 ~7 W, e5 R
USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)! j- N4 s! h1 z2 ?! v; g
0 p5 Q# d8 f+ j; N6 `2 }
这几个函数的调用关系为:
0 @. w$ ^# [' F* i( U
3 `' H/ Q% h- q5 QCDC_Transmit_FS->
" y2 S6 F; B' H1 w# f3 z USBD_CDC_TransmitPacket->
" W9 J X3 t7 V8 L4 Y7 b+ I USBD_CDC_DataOut->- f( ], Y. `5 x3 i- p9 u) C
CDC_Receive_FS
" d4 \, w) D6 y0 g; b. ]2 l1 X q) }8 W! M: z
USBD_CDC_DataOut函数有一个参数是epnum,这个是端点号,我们就可以使用该参数来区分是哪一路CDC!8 _# Y+ `$ M, d6 k) _
6 O9 c8 ~' r0 Y+ F: E* ~9 d9 A
所以USBD_CDC_DataOut函数添加上epnum参数,改为:
' [+ F7 C/ L1 Y2 j4 `2 E! p* V% Q' W7 @4 f
7 L$ i0 y/ s& a$ q' h; a* O- h
( U1 z$ Q, q5 _" N# |; cUSBD_CDC_TransmitPacket函数改为:- B+ J8 g) N+ ~! m* s0 ^. K3 [
) ^. w. p$ F! P) H
: j* V$ I/ k- U. r/ p' b3 I
3 o$ i' m) F. s8 y3 m nUSBD_CDC_ReceivePacket函数改为:& Z: Q( z2 Y4 I5 X
6 f) ]+ S/ D) h! e9 O
4 O6 ~0 W) t/ p2 Y. d
& ^3 Z9 J9 b5 F. {# g
CDC_Transmit_FS函数改为:
5 o1 t7 H% _) Y" O9 D# |- K) r$ \- c, t8 a
. {. G" [ Z. ]( J; M; ~6 w" d. l+ N0 X, B. ~
CDC_Receive_FS函数改为:
! o5 c) F% F( |# A" P# Y- }/ j9 [ c- B7 }) |' f2 I7 C6 e
+ C0 z; G8 R2 K l' i$ Y* T5 g h. \* ~- j
到此就已经修改完毕了,将USB接入电脑后,设备管理区会多出现一个USB复合设备和两个端口,这样就是成功了:
' k [& H0 }+ h4 |
+ q! C# s1 A. y
2 L5 n6 }7 w: ]6 _" Y
/ d5 H/ T/ D3 b/ I下面进行测试,使用串口助手同时对两路CDC进行收发测试效果如下:
1 w% }" u) ^2 h' j6 w$ R+ L _- i; x/ J [) [* g! B
3 F. O5 ?! U; A, C9 Z3 E& P
6 B: S# h: a4 B4 l0 z
互不影响,USB两路CDC成功!2 d0 d9 g8 V+ s: G
, H8 y3 L( _6 N
注意事项及问题9 {" V) [( g: s+ S9 l
如果你移植的时候将USB接入电脑后,设备管理器里面只会出现1路串口,那么可能是因为你先用的CDC单独的工程测试的,电脑已经默认枚举为了VCP,那么此时有两种办法解决:3 W9 s4 h" s2 x% E4 T& k! r
. @4 X1 m t: z( t3 c- z( g
1.设备管理器卸载掉出现的虚拟串口,重新拔插USB
% L ]: ?+ e1 {. k2 q0 _2.修改程序里的USBD_VID,换个其他编号- U* ?' s( V& a. [! J* |9 Z
* ?( ~+ L& n0 l" V O7 m U9 M
* x7 w! o4 k/ d2 Y) y' c
. U+ {, b7 K, ~, m我是用第二个办法解决的!
}! S) C+ ]8 n- ]6 Y! A' c! v6 I9 W; u8 ] ?
补充:3路CDC实现
5 B6 M+ O7 R$ q3 t0 r1 i5 o+ G实现3路CDC修改步骤跟实现2路一样,首先是PMA端点的配置:+ z5 S1 V3 v% u9 g
+ Q6 p& i, u* {8 l! M
! ~: t! L% l& ]
5 _8 e. S- a+ p7 ?
, z. [2 m. |8 T/ K# p4 F4 L$ g# W/ N" L
+ F& z7 ?, O7 h8 W! y为什么要把CDC_DATA_FS_MAX_PACKET_SIZE改成32呢?% \! o# s* ]- R, \4 ?
7 l3 m- r' Q( P. M) S2 x现在我们算一下使用到的端点:6 F; B1 u/ W+ K6 }; W6 A/ h! R
0X80、0X00用于USB必须的
% Z1 i4 |! ? z0X81、0X01用于CDC1输入输出端点: M5 {. }8 W0 z2 n1 c; @! H9 e
0X82、0X02用于CDC2输入输出端点! B; ~1 ?" @. R1 h
0X83、0X03用于CDC3输入输出端点
& {4 d; L9 E. f: D. ~' V: L0X84用于CDC1命令控制端点、0X04未用但占用空间
% b1 P! B+ l U. o0X85用于CDC2命令控制端点、0X05未用但占用空间! W% X! O2 V% y
0X86用于CDC3命令控制端点、0X06未用但占用空间" w0 m4 f, Y5 u: @+ ^! _( s7 A$ Z
0 x4 X$ \* Z3 r: q" Q3 O" S/ z+ X% `- V
7 O# \/ a0 j1 D) p: B可以看到我们用到的端点很多,光算输入输出端点一共8个,每个端点最大占用64,那么就是512字节,而PMA一共才512字节,所以如果将端点缓冲设置为最大64字节的话,3路CDC串口缓存空间不够用!不改成32的话你会发现第1和2路串口收发没问题,而第三路串口返回的都是0!
1 p7 ~. r+ f9 y ^ u7 q. k; s6 a; h( `3 H9 O, Z
如下图:
! p, m# O4 {6 |% N& g, @1 ]6 o. n
; b) G& s/ j8 D$ H# ~
0 x7 Q; t# k; E
+ k& D8 K. r% P- f0 Y$ W) F6 F# O改成32后,效果如下:
- I0 _8 f" T( _& W+ F' t) }' \1 Q
0 j Z: k0 m3 o$ D) D% O; w% s
, _( n' Y7 r/ f9 A& @1 y0 l: X0 `: i, l! @5 @
6 q; u$ z- ?4 x7 G# C8 H# l1 c# J- E- L5 M5 C0 X y
$ r( P3 _: \2 A2 [, g. v* _4 i2 J三路虚拟串口收发数据互不影响,到这里就成功了!
; G$ J z" ~3 x3 f( I. F( a, b3 m& `% f9 b/ h" w# r9 Q& g
4 A( L$ H+ d- Y& J
0 P) V) ]' K7 U3 S. T6 |. R" L: j! M |