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

【经验分享】STM32 USB虚拟串口

[复制链接]
STMCU小助手 发布时间:2022-1-13 21:00
串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。

7 A) z$ v- d- K* h* f; e9 S# V: {5 \8 L+ o6 J
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下:
Projects\Virtual_COM_Port文件夹下,复制红线部分
图1

+ R" c; C- c& P- h  a; k- D
300035032772082.jpg

- r9 Y3 U8 D% z
图2
% k& ~+ _: I5 j8 J9 A: F4 ]
300035202932682.jpg
- u/ p) ~5 v  `. ]. n- `, m' A
我为了方便演示统放在usb/src文件夹下:
图3
7 E8 Z! _6 A0 T' X
300035313246246.jpg

# b1 E! O" T& F( g4 e
现在复制USB的库文件,这些文件不需要我们修改:
图4

5 t- C; I. u  J% p$ u0 r
300035404189967.jpg

5 l+ r$ r" ^* Z$ q
上图中的文件统一放在usb/lib文件夹下:
图5
3 m( r; m9 w# v" ^1 t* e
300036032935279.jpg
/ }! ]& Q2 m/ T. L3 a- g; I
1 n4 |8 s1 g7 L; C
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:

( f; h' P9 I; D4 \
300036178402768.png
* @/ o, M4 Q8 ]# D
图6
         由上图可知,PC通过虚拟串口发送数据到STM32 usb口,STM32再通过usart1发送数据到PC串口。我们做项目时,只用USB虚拟串口即可。所以我们现在需要把串口发送部分删除。把USB做为一个COM口来使用。我们要如何使用这个USB口呢?demo中是把USB发送数据做了一个缓存,先把要发送的数据存入缓存中,然后由USB自动发送出去。而接收部分是直接通过串口透传。我们在应用时就需要用到两个FIFO,1是发送,这个和demo方式是样;2是接收,接收也做一个缓存,我们通过查询来判断是否收到新数据。这下大家应该明白为什么使用两个FIFO了。 我这里有写好的FIFO库函数可直接使用Queue.c文件。
         现在开始修改:
1,stm32_it.c 更名为usb_it.c删除无用代码,只保留usb中断函数,和唤醒函数。代码如下:
代码1
  1. 1 /* Includes ------------------------------------------------------------------*/
    * r8 P; J* n! w0 i% V
  2. 2 #include "hw_config.h"& J$ D0 X& R, J( ?; q) E- W
  3. 3 #include "usb_lib.h": I! \5 x9 ?% i" T! d6 {
  4. 4 #include "usb_istr.h"
    9 i, Y9 t' @3 P7 U8 D+ Y  Q
  5. 5
    0 n  O. [: A6 I+ E$ S' G) b
  6. 6 % k& k# n/ {# j4 v; h
  7. 7 /*******************************************************************************1 x; P5 L4 ?# Z6 J
  8. 8 * Function Name  : USB_IRQHandler
    : i: |7 c, S- I2 ]
  9. 9 * Description    : This function handles USB Low Priority interrupts/ s. F9 J# ]1 M5 H
  10. 10 *                  requests.
    8 D, p5 Z' ?/ D! u& Q% M
  11. 11 * Input          : None
    4 {; Q& Q8 {/ {; i
  12. 12 * Output         : None
    ( I+ h  ^5 {/ K1 @- Z
  13. 13 * Return         : None5 M" m( I0 r" f, U, F$ H
  14. 14 *******************************************************************************/
    / B7 i+ g; }' O& `( X" d
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)& ~2 V# b8 _5 z% h) y& j5 W. O/ O
  16. 16 void USB_LP_IRQHandler(void)
    , a7 \" U( b0 z  D1 P; o! N
  17. 17 #else
    8 o0 ]' `& n& g' x9 D
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void)
    , H" o; b. g3 U0 L( a
  19. 19 #endif0 K! q* V; O, ^1 V9 y0 B
  20. 20 {- Q; j# ^1 ^; S9 |  ]2 v
  21. 21   USB_Istr();$ S7 V" M2 U/ M$ B, \( Q
  22. 22 }
    ' J3 C# `. `; |7 w
  23. 23
    / }. K' C; ~0 L$ z! L; O
  24. 24 /*******************************************************************************7 m. g' d# c5 l2 F6 l+ F
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler0 N5 Z9 o0 ]( l  V
  26. 26 * Description    : This function handles USB WakeUp interrupt request.; y; o9 t' C4 ?; L8 z% H8 D' `8 Z1 p
  27. 27 * Input          : None
      A8 h3 ^: D, {# X
  28. 28 * Output         : None$ Y  }7 @5 H- }/ r' Q
  29. 29 * Return         : None' l5 X. o8 N) a/ W
  30. 30 *******************************************************************************/3 f& K! }' N+ b
  31. 31 5 d3 P, z* i: Z  ^
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)1 J) ~; B: c& J8 Y/ u. e& d$ C; X
  33. 33 void USB_FS_WKUP_IRQHandler(void)* f& q% h2 X5 L2 d1 d+ K
  34. 34 #else: i$ e4 V% w5 q# L7 t5 _- C( y
  35. 35 void USBWakeUp_IRQHandler(void)
    4 o" S9 T2 Q1 q% D
  36. 36 #endif
    6 ]% Y! J0 f, q  x* X6 a
  37. 37 {
    ; M( A. e! ~" j( w$ j) [3 ?3 R
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);0 P4 l% G; ~- ~6 A/ Z
  39. 39 }
复制代码

$ V" H, W" L( |/ ?
2,修改代码hw_config.c删除无用代码,新建立2组,读FIFO和写FIFO的函数。后面会用到。
代码如下:
代码2
  1.   1 /* Includes ------------------------------------------------------------------*/5 C! B0 H2 w/ r- x+ F! ]
  2.   2
    5 v, D4 `% l5 d$ G* r
  3.   3 #include "usb_lib.h"& h% Y/ x2 x9 {% j
  4.   4 #include "usb_prop.h"% b' r) I* U! J# [/ {
  5.   5 #include "usb_desc.h"
    6 _: K4 p1 u8 s2 i8 ]% F1 H
  6.   6 #include "hw_config.h"; ~; P0 C0 F; L+ C# d8 \
  7.   7 #include "usb_pwr.h"
    ' L. k8 Y% q( Q5 W
  8.   8 #include "Queue.h"
    + G- H1 T* S, ?7 A$ p; u5 o; N$ v
  9.   9
    6 z7 O1 }% H7 M/ Y, I( ?1 ~
  10. 10 7 R4 a- w& F% Q; h! b4 B
  11. 11 /* Private typedef -----------------------------------------------------------*// U' B7 K! _1 i
  12. 12 /* Private define ------------------------------------------------------------*/4 t, I/ o" i! [/ @* z/ J0 p" Y5 j5 ~
  13. 13 /* Private macro -------------------------------------------------------------*/. `. K/ e( x0 e9 Q8 e1 C; G
  14. 14 /* Private variables ---------------------------------------------------------*/
    - X0 Y" W6 M2 v, e( u* v3 f
  15. 15 ErrorStatus HSEStartUpStatus;
    ) R5 k2 A3 L& u  o$ f4 r2 `1 W' _
  16. 16 USART_InitTypeDef USART_InitStructure;
    " S; @  _: i+ }% {
  17. 17 EXTI_InitTypeDef EXTI_InitStructure;
    " c9 j( B" N: v" T% n
  18. 18
    : G2 u" z! {2 E& S2 k
  19. 19 + G2 ^6 n7 k# I# Q1 K0 F/ Y
  20. 20 #define USB_COM_RX_BUF_SIZE         (1024 + 256)
    ; D) U6 v0 u0 I8 t% w; n
  21. 21 #define USB_COM_TX_BUF_SIZE         (1024 + 256)
    2 g, o1 W! P3 z7 n  g
  22. 22
    $ A+ z$ u# q/ ~* ^: R4 B, p* ~* S
  23. 23 static QUEUE8_t m_QueueUsbComRx         = {0};( I2 M$ `/ ?3 i& T) f4 c7 O% x
  24. 24 static QUEUE8_t m_QueueUsbComTx         = {0};1 p% N+ ^+ ?7 K/ k  Q, J8 F- \
  25. 25 static uint8_t  m_UsbComRxBuf[USB_COM_RX_BUF_SIZE]      = {0};     - i/ G1 v* S# W( V
  26. 26 static uint8_t  m_UsbComTxBuf[USB_COM_TX_BUF_SIZE]      = {0};   
    1 w4 e! \, ?; O  o
  27. 27
    1 R7 J4 T6 `  |; |" w
  28. 28 static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len);
    7 B- o  F% m& F0 T( ~2 J& s. V+ b
  29. 29 /* Extern variables ----------------------------------------------------------*/
    ) Y/ ^0 g/ j2 H4 Q* b# `
  30. 30
    6 ~5 p! {& u. ]7 y9 q& t
  31. 31 extern LINE_CODING linecoding;6 E% W1 G1 u; A5 S0 ~9 z* u- l5 p
  32. 32
    # z, k4 o; L: q) R  |* F
  33. 33 /* Private function prototypes -----------------------------------------------*/- R( S2 X# M9 v. ?6 i# z0 T8 u4 {
  34. 34 /* Private functions ---------------------------------------------------------*/. x% y1 u7 V1 o4 v8 Q
  35. 35 /*******************************************************************************5 V: U7 X1 [/ K* k+ j  o9 r% ]
  36. 36 * Function Name  : Set_System
    + J* T4 {( @+ ]/ u& d% D7 Z; }1 k4 `
  37. 37 * Description    : Configures Main system clocks & power
    5 u! L+ u9 A6 Z
  38. 38 * Input          : None.. G# V) Y( u- q8 h) q, o2 M, C
  39. 39 * Return         : None.
    ; Y% T7 R2 ^6 Y9 r% _+ ^8 Z
  40. 40 *******************************************************************************/
    ! v# N$ z5 k' Z  X5 f2 V( z
  41. 41 void Set_System(void)0 [: q6 h4 _0 P0 k  A! G, W: u
  42. 42 {  H# j/ j3 [/ G
  43. 43   GPIO_InitTypeDef GPIO_InitStructure;
    0 T: W$ F  e" `( }9 A* i. x2 C
  44. 44 # x, r/ W6 T* _5 }, s% _
  45. 45   QUEUE_PacketCreate(&m_QueueUsbComRx, m_UsbComRxBuf, sizeof(m_UsbComRxBuf));
    % v- [" R6 j4 r2 c
  46. 46   QUEUE_PacketCreate(&m_QueueUsbComTx, m_UsbComTxBuf, sizeof(m_UsbComTxBuf));
    " E! W9 v+ Z3 |! Y( y9 p+ _
  47. 47   
      g& ~; L& _9 D
  48. 48   /* Enable USB_DISCONNECT GPIO clock */* p% ^: ?+ h5 N+ ]
  49. 49   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);
    6 F6 V9 W2 V% M8 S% S
  50. 50
    % q( w6 {7 C2 S
  51. 51   /* Configure USB pull-up pin */1 H" }( _8 @& f( T
  52. 52   GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;
    & y+ _/ C& K# u+ U; |6 }
  53. 53   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ ?6 f" f! Q5 B, s' C3 e
  54. 54   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    - S1 Y' p! Z+ x' ?" o0 k! U
  55. 55   GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
    7 [' @8 i; b7 l& D( m( j
  56. 56   
    . n% k& L  D( c0 l/ Y6 k" ^
  57. 57   /* Configure the EXTI line 18 connected internally to the USB IP */! _4 @/ j  Y( n7 z
  58. 58   EXTI_ClearITPendingBit(EXTI_Line18);
    $ E6 K. ]& `' L& y" k7 ~
  59. 59   EXTI_InitStructure.EXTI_Line = EXTI_Line18;
    , @. V, n! U2 t
  60. 60   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    6 J+ ^, D5 _# F+ z' j
  61. 61   EXTI_InitStructure.EXTI_LineCmd = ENABLE;0 K. I4 V+ ~( t/ {
  62. 62   EXTI_Init(&EXTI_InitStructure);5 E4 {5 Z2 s& X3 h3 y" q& f5 i- f4 V
  63. 63 0 ~. ]% N8 ?$ q2 B; A
  64. 64   
    ; k5 L  m8 G9 t
  65. 65 }1 N8 m' a, l# N
  66. 66 0 c0 w  X/ Q% }
  67. 67 /*******************************************************************************
    $ C4 B5 i3 o8 _
  68. 68 * Function Name  : Set_USBClock
      J# q! {9 z' R( ?/ J0 q& Y
  69. 69 * Description    : Configures USB Clock input (48MHz)
    6 Z4 b: S+ K3 W
  70. 70 * Input          : None.6 R8 G9 C8 F6 A2 t
  71. 71 * Return         : None.
    ( L" x" M; T* H
  72. 72 *******************************************************************************/
    : ?8 x! x6 h, |9 `
  73. 73 void Set_USBClock(void)- b& u6 z  r  C2 N; p. S) z, g) M
  74. 74 {
    * z* }8 Q& _* y# {& X
  75. 75   /* Select USBCLK source */
    2 O: I4 K7 w- G5 n1 c: `
  76. 76   RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);; c2 Z: n: i* T7 Y
  77. 77   
    ! s, m1 P! W  o+ Y3 u; d2 Q: U1 |" g: [
  78. 78   /* Enable the USB clock */5 G/ w4 ?$ g( b0 J1 i
  79. 79   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);3 \: ~/ x: U* B0 d* ?
  80. 80 }  H& h8 g( R6 ?
  81. 81
    ( ~- ^- v. \+ m  T1 M- S
  82. 82 /*******************************************************************************
      W8 r+ A0 y1 M3 i
  83. 83 * Function Name  : Enter_LowPowerMode# D, b& k! \: \1 ?0 a/ _3 t8 f
  84. 84 * Description    : Power-off system clocks and power while entering suspend mode
    ) X2 {8 p  ]0 q% w
  85. 85 * Input          : None.
    8 G* {  `! L* ^; Q
  86. 86 * Return         : None.
    , i& Z' b. J1 S: M3 V
  87. 87 *******************************************************************************/7 G  k9 b( q/ R( q$ [
  88. 88 void Enter_LowPowerMode(void)+ r4 P/ @) M4 b+ i8 u( o. ?+ g
  89. 89 {; a' p7 i) }- j% P; |  f
  90. 90   /* Set the device state to suspend */8 k8 K9 g+ ?6 s! X4 U6 G1 ?8 Z
  91. 91   bDeviceState = SUSPENDED;' l1 P# I& Y: I. F4 ?
  92. 92 }
    ' _; O' H' Z( U
  93. 93 0 V0 ]& g& U* ~. J
  94. 94 /*******************************************************************************
    / }% M$ U! q0 @9 Z
  95. 95 * Function Name  : Leave_LowPowerMode
    - E# l7 [1 [' a* m" E; Z* Z* w! g
  96. 96 * Description    : Restores system clocks and power while exiting suspend mode
      P' N2 ^9 U  c4 ~5 Z
  97. 97 * Input          : None.
    9 T) P; X; x5 ?0 C. |6 S
  98. 98 * Return         : None.
    $ }* T) n$ e# d- B% h
  99. 99 *******************************************************************************/* j: Y7 r# [" n3 z
  100. 100 void Leave_LowPowerMode(void)
    ' ]" e- N$ G& }; Q5 ^- d
  101. 101 {
    2 N7 H$ Y: s! ]6 u
  102. 102   DEVICE_INFO *pInfo = &Device_Info;
    , r/ g$ f/ P( K4 `  I& n& c- x
  103. 103
    . ]% p: d6 f" q5 R1 J: p) c7 m
  104. 104   /* Set the device state to the correct state */
    5 i( ?1 P- {: W+ I) c
  105. 105   if (pInfo->Current_Configuration != 0)/ Y! H& `9 v) w  {
  106. 106   {2 g4 R5 R! ^3 B, \4 A1 f4 \
  107. 107     /* Device configured */& H/ a: ?7 W( e0 G% t
  108. 108     bDeviceState = CONFIGURED;
    - n1 p' q  A  x; Y. S  y
  109. 109   }' Z, a3 [* g+ c; {
  110. 110   else- V; E- Z. x5 s5 x5 Y3 p: h
  111. 111   {& g8 D: j; M8 z' |
  112. 112     bDeviceState = ATTACHED;
    5 Q4 l  l1 `% d4 j8 b4 R
  113. 113   }
    . G* ?9 U* b& H/ }8 Z( r
  114. 114   /*Enable SystemCoreClock*/' E7 H- p& D) c+ p- p
  115. 115 //  SystemInit();
    ; D' e8 ^4 e: A0 Q
  116. 116 }; q1 w% o7 m/ o, p7 x: S- g
  117. 117
    % R' k3 i6 O( r& k- \7 ~: s) [" g
  118. 118 /*******************************************************************************, Y% u* Q$ Q  T& ^% l1 A
  119. 119 * Function Name  : USB_Interrupts_Config
    5 z/ g) V" I; }: x$ w9 K& K
  120. 120 * Description    : Configures the USB interrupts
    : R7 M+ v9 b" b/ j" D
  121. 121 * Input          : None.
      h/ L2 Z/ j, k6 ]
  122. 122 * Return         : None.* \6 ]3 B5 |( s# v" u5 D# b1 u
  123. 123 *******************************************************************************/
    - z& u7 D) y- Z
  124. 124 void USB_Interrupts_Config(void)5 V6 V+ f# I( z
  125. 125 {
    ; W# d6 F4 o  m7 }
  126. 126   NVIC_InitTypeDef NVIC_InitStructure; ! m6 c8 m9 a( `0 S  `% C
  127. 127 5 E' p9 j) C$ [% k. z( I7 R; f
  128. 128   NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;( l* U/ D: e9 n0 ^/ T6 @5 Q" w4 U
  129. 129   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    , N0 Q: |  L, W# k
  130. 130   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    0 O+ w3 W: d  z2 q: c. r/ ?, L- E4 ~
  131. 131   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;! V) @# m3 r1 ~' l
  132. 132   NVIC_Init(&NVIC_InitStructure);
    6 [0 n6 t/ v: G" j, [7 d5 V
  133. 133   9 }$ u$ [9 ^+ M+ a: ]1 Y8 J
  134. 134     /* Enable the USB Wake-up interrupt */
    ) d- {& P: A8 Z8 Q# E+ j- m& h
  135. 135   NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;" K3 [2 F- s+ ?6 @  {. N/ U
  136. 136   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;) s: t0 L9 t; [9 p! K" M
  137. 137   NVIC_Init(&NVIC_InitStructure);
    4 d* [5 N6 I0 a( j  o0 [+ T4 F; J- m
  138. 138 }
    - z- g3 o* M+ _8 z3 o
  139. 139
    ; c$ m% V! L. s% A
  140. 140 /*******************************************************************************, h) U! t. \) x/ F) L9 I
  141. 141 * Function Name  : USB_Cable_Config
    ( m+ n2 Y; D' `$ r1 o: E
  142. 142 * Description    : Software Connection/Disconnection of USB Cable
    " \2 o2 F3 ^' W! w. x( }5 c
  143. 143 * Input          : None.3 k% [$ v9 h! ^2 ~4 Q
  144. 144 * Return         : Status
    ! B* j  t" `3 E# b7 w: n4 G- g, [/ z& G
  145. 145 *******************************************************************************/. a: b3 f0 N9 O
  146. 146 void USB_Cable_Config (FunctionalState NewState)1 z" M& D; }/ r  `! q0 e' {2 A/ x
  147. 147 {
    ! c1 q8 f7 o; h9 w$ Y( `5 t
  148. 148   if (NewState == DISABLE)2 w0 r+ H3 R! D# S
  149. 149   {
    9 l" L. @5 s' T' T' P, G
  150. 150     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);3 ?9 [- L' ~, n+ q+ n& U4 z
  151. 151   }
    / m1 Z) M. G) o4 _* {" T% Z" Z2 N
  152. 152   else
    ) o9 N; t( W! W; G4 I
  153. 153   {" e" `  d( u* h- G8 Q+ m
  154. 154     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    * _5 c$ v4 @4 s; `- A2 @: r' `
  155. 155   }& @7 c/ J9 d' A3 Y6 U0 Z
  156. 156 }  ?- z9 `4 I( R+ t
  157. 157   y- ?. r% T+ F* j* S
  158. 158 /*******************************************************************************
    $ j! ^* C- y3 h2 }' Z, y
  159. 159 * Function Name : void USB_Config(void)! F2 n6 O5 N, U' I
  160. 160 * Description   : USB系统初始化
    " K( I' F( u6 [) S# a8 Z! e
  161. 161 * Input         : 2 H* v" y8 z$ n  Q! ?" y; O
  162. 162 * Output        : ; u- S- w0 h2 N" R' `0 r, {& y
  163. 163 * Other         :
    ; ^4 q" \: `' P7 S& m$ l0 x
  164. 164 * Date          : 2014.11.285 `4 P& V) y5 V. k# Y  K
  165. 165 *******************************************************************************/" n. {7 Z' Q# T6 g; z  V
  166. 166 void USB_Config(void)7 H/ X3 d: f9 H6 l9 B+ r6 g
  167. 167 {
    + U5 u1 R3 a3 \
  168. 168     Set_System();
    8 j4 R+ a2 u! k, B0 F& w9 q6 w
  169. 169 5 Z; [- i7 R, V: m$ z# w; T( V  m
  170. 170     Set_USBClock();$ Y* ~5 F& |% h( D  Z' c1 l
  171. 171 ) O5 |* t, S! K+ @# H& ~
  172. 172     USB_Interrupts_Config();9 f- x& d0 c3 \' V9 g8 J
  173. 173 - }4 y* S! _  m# f- P
  174. 174     USB_Init();# w* L2 H- V  x* P, l# D; J& o3 S
  175. 175 }
    : @0 S0 o. X2 E; F
  176. 176 . G1 B( ?* ]; c4 k& |9 O5 o
  177. 177 /*******************************************************************************
    & {) T. C0 y6 S, h. ]  H" |
  178. 178 * Function Name : uint32_t USB_RxRead(uint8_t *buffter, uint32_t buffterSize)+ n5 e+ Z& [; u6 ?
  179. 179 * Description   : 从USB接收缓存中读数据, [0 b1 @0 C; _# W8 N4 y0 {# n
  180. 180 * Input         :
    0 r, Q$ q1 A2 i
  181. 181 * Output        : . J0 w7 K' Y  f) ^* e4 v3 H
  182. 182 * Other         : . ]8 C# d, [% Q
  183. 183 * Date          : 2014.11.28
    6 l/ @$ U2 C; o4 h/ j5 {% a/ }
  184. 184 *******************************************************************************/: c( w" ]6 D3 w1 V6 o7 E
  185. 185 uint32_t USB_RxRead(uint8_t *buffter, uint32_t buffterSize)
    ( A+ e: x6 r7 W# i+ A! x( E
  186. 186 {
    / e0 k# ^: Y$ g% X$ g: s
  187. 187     return QUEUE_PacketOut(&m_QueueUsbComRx, buffter, buffterSize);
    + H/ V# Z8 ]! |2 P
  188. 188 }
    2 q0 Q7 ^& O8 L8 [$ h' L
  189. 189 /*******************************************************************************3 S( G6 e7 s; A( r
  190. 190 * Function Name : uint32_t USB_RxWrite(uint8_t *buffter, uint32_t writeLen)  c/ k2 Q8 i7 A
  191. 191 * Description   : 写数据到USB接收缓存中0 {4 G# e5 \+ h: z: t  f# w. }
  192. 192 * Input         :
    . o  Y4 K! w6 |. X
  193. 193 * Output        :
    5 y9 g. v: z; i3 Y
  194. 194 * Other         : 1 e* D8 ?; U0 l3 n" S5 J9 u  ?
  195. 195 * Date          : 2014.11.28
    ) u2 ]0 L! S! a4 R2 c8 `! I3 s
  196. 196 *******************************************************************************/
    7 r  H: a+ z: V
  197. 197 uint32_t USB_RxWrite(uint8_t *buffter, uint32_t writeLen)2 j& p6 V/ }+ N8 R' j$ V: W
  198. 198 {
    5 |* ^7 h0 H; u* |! ]
  199. 199     return QUEUE_PacketIn(&m_QueueUsbComRx, buffter, writeLen);$ ]9 Y4 I+ s. L: L
  200. 200 }
    : n& [6 ]- A- H! H% [
  201. 201 /*******************************************************************************
    1 h# {1 [) h2 V9 |5 b$ L
  202. 202 * Function Name : uint32_t USB_TxRead(uint8_t *buffter, uint32_t buffterSize)/ V5 o/ X% E8 n- s
  203. 203 * Description   : 从USB发送缓存中读数据
    0 q$ c  J: |# j5 D+ ?" n& \
  204. 204 * Input         : + y# i( ~7 N& S& e' U) l
  205. 205 * Output        :
    " w' t4 u. B* t3 F; t2 F
  206. 206 * Other         :
    / ?3 L! w  Y, D$ T: o1 d
  207. 207 * Date          : 2014.11.28
    & E  A. ?2 i5 x; V1 \- W+ h0 |" V" H% y
  208. 208 *******************************************************************************/
      U5 o. t6 |) `! R0 R4 D# T
  209. 209 uint32_t USB_TxRead(uint8_t *buffter, uint32_t buffterSize)
    , [2 H0 [* r: M: _4 p+ F
  210. 210 {2 C6 Y2 x( S$ t6 r- N: _
  211. 211     return QUEUE_PacketOut(&m_QueueUsbComTx, buffter, buffterSize);;/ U$ J: y- T4 B* t# {; b/ H) V
  212. 212 }' ~) p; D  |0 c; E
  213. 213 /*******************************************************************************& o: m% a3 k$ D# Q) R' z
  214. 214 * Function Name : uint32_t USB_TxWrite(uint8_t *buffter, uint32_t writeLen)( f: w( c8 {- s: H: p
  215. 215 * Description   : 写数据到USB发送缓存中  }. t  x* r( H3 S
  216. 216 * Input         :
    5 t. D+ X9 t1 [; w6 d0 R# T
  217. 217 * Output        :
    - m3 A3 D2 {3 _2 i/ z
  218. 218 * Other         :
    . M1 e# z6 o5 A) |; d7 s
  219. 219 * Date          : 2014.11.28
    / Y( a3 M, ^$ q) p2 G
  220. 220 *******************************************************************************/2 ]# J) W2 \0 X
  221. 221 uint32_t USB_TxWrite(uint8_t *buffter, uint32_t writeLen): `7 i% p! p$ b  N
  222. 222 {, {' u7 K7 z* s+ A1 m5 K: r0 Q8 a
  223. 223     return QUEUE_PacketIn(&m_QueueUsbComTx, buffter, writeLen);
    ( P' p0 L* c$ p7 F2 ?4 x: Z2 U$ a
  224. 224 }
    ) J# F* D" i3 ~" N
  225. 225 5 I: E' w6 R2 S3 k" z/ K& S
  226. 226 2 b+ v( n+ Z7 n4 Q$ u/ u) ]  E
  227. 227
    0 D. s5 \1 ~) x8 M
  228. 228 /*******************************************************************************/ x* V' w9 t5 J; H6 l
  229. 229 * Function Name  : Get_SerialNum." W8 n! n2 Z7 ~( Y( x6 Z7 R
  230. 230 * Description    : Create the serial number string descriptor.& i3 {/ z: ?6 X/ G
  231. 231 * Input          : None.
    + n+ p; b: [2 ?% y
  232. 232 * Output         : None.# _) r1 [5 E0 b. Y5 x( f& f
  233. 233 * Return         : None.# d; T' P; [- {
  234. 234 *******************************************************************************/
    - x0 A1 a( P0 T. R" b/ D
  235. 235 void Get_SerialNum(void)
    ) K' g( F$ q. K1 B% u0 F
  236. 236 {
    3 s' X+ m* V! G9 H; d
  237. 237   uint32_t Device_Serial0, Device_Serial1, Device_Serial2;! e; ~- J" X0 ?( `; m
  238. 238
    " l- f# o* b3 z
  239. 239   Device_Serial0 = *(uint32_t*)ID1;, B( a2 b! v9 u8 t
  240. 240   Device_Serial1 = *(uint32_t*)ID2;
    7 r' Q8 K5 T0 y% B4 N
  241. 241   Device_Serial2 = *(uint32_t*)ID3;  
    3 `/ H; b% K1 t+ {4 @! Q# ]
  242. 242 9 j) S; K# u& M) Z
  243. 243   Device_Serial0 += Device_Serial2;" v8 A2 C6 l0 i0 a, F* H! g8 g
  244. 244 ( K' d5 @5 \9 T2 }! L3 i( w" U0 n
  245. 245   if (Device_Serial0 != 0)
    , o! k! z& }: W, L; P9 ]- K1 x; K* h
  246. 246   {
    : A# G: h% x% ]9 e+ X$ q  P
  247. 247     IntToUnicode (Device_Serial0, &Virtual_Com_Port_StringSerial[2] , 8);0 u5 E7 G! |9 J" b6 I; |% d
  248. 248     IntToUnicode (Device_Serial1, &Virtual_Com_Port_StringSerial[18], 4);
    6 ^% w0 g3 \" B4 L4 s, ~
  249. 249   }
    6 ?0 w! G& e) G/ U& o$ G4 R
  250. 250 }9 a2 M6 [& e5 R4 C  i; \2 _
  251. 251 ! d7 R) A& w! s% I6 q( H+ q  k
  252. 252 /*******************************************************************************8 ^1 M" J& }4 ^* q
  253. 253 * Function Name  : HexToChar.
    / z$ A( T: S7 K4 M  P
  254. 254 * Description    : Convert Hex 32Bits value into char.
    % W+ z+ _$ t8 E% M$ r7 A8 q: v' I9 c
  255. 255 * Input          : None.% W; m/ \: F/ W* C2 K* o
  256. 256 * Output         : None.
    ) n) ~! X" O' r: a* A9 [
  257. 257 * Return         : None.! B5 z7 S6 m2 I
  258. 258 *******************************************************************************/; b7 b# L% ]& v
  259. 259 static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len): ~: j( A& ~5 f
  260. 260 {6 ^) @: n: G1 D" s! m7 k
  261. 261   uint8_t idx = 0;6 P' J+ d& c0 G; J
  262. 262   2 Q2 X- ?* n1 P. Q& w: N. G
  263. 263   for( idx = 0 ; idx < len ; idx ++)
    7 T3 [, d) B/ G8 e1 {
  264. 264   {( e2 }% c& d( M
  265. 265     if( ((value >> 28)) < 0xA )7 O) P1 J: z% A* z: m3 [9 q8 u
  266. 266     {" @% F5 X5 ]7 I0 x: p6 D' W3 G
  267. 267       pbuf[ 2* idx] = (value >> 28) + '0';; g6 B  T8 @+ A& g! q/ z
  268. 268     }
    ! b# B4 w7 Y- H, X2 ^0 E
  269. 269     else
    6 S- F+ n& F; u: ?9 C
  270. 270     {5 [; _) O6 n# g" y
  271. 271       pbuf[2* idx] = (value >> 28) + 'A' - 10;
    8 x- f& L1 ^& \5 G2 E
  272. 272     }
    $ l/ }+ \5 M/ w# k) s# [0 r
  273. 273     ' w3 S6 R/ V2 |( a8 [9 N
  274. 274     value = value << 4;7 ~5 Y2 P4 t, R: o. w
  275. 275     
    ; u; M) ~& O, o: b; ]1 B
  276. 276     pbuf[ 2* idx + 1] = 0;
    ( G. F5 _+ J; |' e! R
  277. 277   }: Z# Z/ s2 v+ }8 Z$ p+ S5 {/ F5 F
  278. 278 }" v  u6 X9 X" f; D- f2 [5 R5 K  y
  279. 279 . l& r6 v+ f# m0 @, |* L
  280. 280 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
) q6 N( Q" N! Q. ?6 Z3 c1 L* r, v

- L+ w- O3 G! J; Y6 v0 d1 h! ]+ o5 Y
这里要讲一下为什么要屏蔽SystemInit(),因为demo只运行虚拟串口功能,在USB未插入的情况下,是进入低功耗状态,插入时从低功耗状态退出后会调用此函数。当然我们在项目中一般不会这样,系统是否运行和插USB接口没有联系。所以我在下文中把进入低功耗代码屏蔽了,自然也就不用唤醒代码了。
图7
5 R: {: b% |8 e0 H
300038465597900.jpg
& c% c! @& n) `) b0 L
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************
    ( z3 U' R  b( c  X! ~* `; Y
  2. 2 * Function Name  : USB_Cable_Config& @$ j6 V/ c8 m2 V
  3. 3 * Description    : Software Connection/Disconnection of USB Cable
    # q7 U& ]* `6 S( R: B
  4. 4 * Input          : None.
    ; b% f4 Z( H1 c! Q7 d" i0 j& o
  5. 5 * Return         : Status# _0 L& v% @' @8 s7 P2 f% h5 w
  6. 6 *******************************************************************************/5 X4 h9 z: k2 P+ \. z
  7. 7 void USB_Cable_Config (FunctionalState NewState)2 \5 V: E. Y; [2 L  R8 ]: J
  8. 8 {8 n; q: r% U4 L$ ^3 Q
  9. 9   if (NewState == DISABLE)$ B0 ]+ p- n) X8 j5 T& M% e
  10. 10   {
    2 l0 |$ r! e8 U  t' l# e" w) ~" u4 U
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);8 X. F* C# s+ `' G1 k7 l& g
  12. 12   }
    9 \/ z0 p  V5 _. Z: }
  13. 13   else, {& i% Z$ t  ?9 j- |* y
  14. 14   {5 s3 j4 R8 h. Y: `3 o0 N
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    # Z% e; K6 ~+ b& y: A3 u
  16. 16   }6 S% d; ]4 x+ n5 u
  17. 17 }
复制代码

' e% J5 A: N9 K! @0 N8 w- G
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/
    % F. w. l2 ?4 \% j
  2.   2 #include "usb_lib.h"( v* `, B$ m- J  e. U" C% B
  3.   3 #include "usb_desc.h"4 A$ B$ Z% ^) Q2 j2 A
  4.   4 #include "usb_mem.h"" X3 {( ~( j+ x4 Q/ \
  5.   5 #include "hw_config.h"  |1 J& V: _& I5 l, F% [
  6.   6 #include "usb_istr.h"9 Z7 m4 j' J3 K# Z/ D. K
  7.   7 #include "usb_pwr.h"" y) ^' a$ q$ i! N" k
  8.   8
    5 {6 C' G0 z% E* R& ~; b
  9.   9 /* Private typedef -----------------------------------------------------------*/
    5 y0 Q1 a" }& u' P
  10. 10 /* Private define ------------------------------------------------------------*/8 O+ f2 s! y) C+ C5 A( U6 I
  11. 11 0 d/ h$ A0 N+ q$ c- k. j- ?6 x
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
    6 g; p0 W2 y7 s. N4 h$ x8 X, h5 J- P
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             5
    ) `; a, U/ |7 N* `% S+ G
  14. 14
    . E# g8 J- T/ W  T5 q2 s
  15. 15 /* Private macro -------------------------------------------------------------*/
    8 o2 Y, |* }7 N0 j5 ^
  16. 16 /* Private variables ---------------------------------------------------------*/
    * }& M$ \* j8 K$ a$ j
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    + m: B# n* u" ~3 z
  18. 18 static volatile uint8_t txFlg = 0;  A; |' n7 b  s/ e8 s
  19. 19 static volatile uint32_t FrameCount = 0;
    ! V9 B/ ^' D4 T( J( v
  20. 20
    7 R6 T6 d  u8 r2 I7 r" E% }: S. E, l
  21. 21 9 ~( L( f# q2 z( X" `5 R' ~
  22. 22 /* Private function prototypes -----------------------------------------------*/* p  s6 k" P" j+ l) o4 k
  23. 23 /* Private functions ---------------------------------------------------------*/3 M8 X7 I. k$ C& \; q6 u
  24. 24
    2 t# s3 \7 F$ n) P2 b5 y' Y
  25. 25 /*******************************************************************************
    % s! U/ b( c8 ~5 I# N9 g; w  _% k
  26. 26 * Function Name  : EP1_IN_Callback, [6 Q, b, @  x! }/ y+ h" a
  27. 27 * Description    :9 H) s9 b4 f& A# Z9 @. }
  28. 28 * Input          : None.
    & ~' y5 e  N( l# @4 G
  29. 29 * Output         : None.
    : q4 ]& B- d' q8 W# Z. [
  30. 30 * Return         : None.
    " }' @/ y- @- T8 Z$ n/ c; x/ x8 D# s
  31. 31 *******************************************************************************/, u! C  Y. A- _! r
  32. 32 void EP1_IN_Callback (void)
    * m3 m" @5 l* _  Q7 R
  33. 33 {
    % a8 Z: H: U3 H
  34. 34     uint16_t len = 0;
    0 j7 I* b2 T- ~3 s: U
  35. 35     0 W5 V  T7 K$ J& @. k1 Y1 M* H4 T2 K" L
  36. 36     if (1 == txFlg)5 A7 x' O2 n, |- D5 P
  37. 37     {9 s4 w% c& C9 x$ H$ ~6 z
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));4 }! T9 ~6 j8 H0 I+ T/ A
  39. 39
    8 _7 h4 e/ W" B3 g
  40. 40         if (len > 0)5 u. n+ r, X, t( F8 g" F
  41. 41         {6 r% z2 A: F0 q* t& L( L
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);/ e7 h1 \9 f9 `; X+ u0 `/ P1 q: T
  43. 43             SetEPTxCount(ENDP1, len);
    ' N& z7 _+ h9 B7 B+ a- o
  44. 44             SetEPTxValid(ENDP1); ! ^9 j& i6 a3 r, @$ x7 c
  45. 45             FrameCount = 0;
    % p- P: _; ^/ t& O& c
  46. 46         }, O5 u& l' J0 X; S! q( U8 k
  47. 47         else2 k- |7 h+ \1 {4 q7 I" z8 H5 @
  48. 48         {
    % W! q% z+ e+ m* U
  49. 49             txFlg = 0;7 M. S: p& x/ Q1 _5 r
  50. 50         }! P: h9 R2 x2 V2 W; v* \
  51. 51     }
    ! L) e3 w' B& M3 I/ g+ Z
  52. 52 }
      F, B* @% N8 e' u  a0 N
  53. 53
    * x) H$ S0 l+ i* u* g
  54. 54 /*******************************************************************************
      g$ x' G. N  g/ ~+ P3 T
  55. 55 * Function Name  : EP3_OUT_Callback/ J/ U. ]8 B3 P
  56. 56 * Description    :
    1 u$ U1 k( o: {' |
  57. 57 * Input          : None.  b8 [! `$ A- `$ m
  58. 58 * Output         : None.
    - d9 M- j; I( v6 ~$ `4 K" P
  59. 59 * Return         : None.; y3 {1 L. e( n( g% \( b1 W
  60. 60 *******************************************************************************/
    / j( b; U9 s) C- w' C
  61. 61 void EP3_OUT_Callback(void)9 E+ F  [: K0 s4 \) b4 J5 Y! Y) F
  62. 62 {
    / o9 Q4 ?- x! @+ R8 S) t* w
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    ' B" s6 k# W- `; u; ?" S8 h
  64. 64 & o3 M! Z' ?4 S3 o1 H
  65. 65   uint16_t USB_Rx_Cnt;
    ' R) d8 b$ L3 X, v) c+ ^
  66. 66   
    ( U: n" u3 D( n; Y& L- V
  67. 67   /* Get the received data buffer and update the counter */
    * d" p# ]! \' W- T! b- p
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);- d; J% s+ W4 l& {
  69. 69   
    1 o% u* L' N+ m
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being
    5 }$ \& G; o$ }8 _0 y0 i
  71. 71   NAKed till the end of the USART Xfer */
    # x, m$ ?. f- d  x+ \$ s7 `
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);
    & \( r- q2 v* B& K0 B
  73. 73 , T# ^7 p8 p  S* E
  74. 74   /* Enable the receive of data on EP3 */5 V' \0 p% Y4 Z9 ]* {1 B+ K% e% D
  75. 75   SetEPRxValid(ENDP3);% ^. ~% p' t3 O/ j/ ?" ~2 e
  76. 76
    ' _/ `/ C  s1 [3 x& Y
  77. 77 }
    9 N) i. v1 o9 [
  78. 78
    - d3 T/ [! o* B
  79. 79 3 Q. w1 Y; _% t! b- y5 s
  80. 80 /*******************************************************************************
    % G2 m3 H; T' X$ c  r3 O" @5 ?
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback) D! @8 \0 \" e9 ?' ^; @- r( c% o6 |
  82. 82 * Description    :
    5 u- \3 ~* `. ~# J5 v6 I
  83. 83 * Input          : None.9 P& a2 c$ \0 O: j& A& J
  84. 84 * Output         : None.
    & L2 w; d% E  Z3 F
  85. 85 * Return         : None.$ U8 t* X$ v7 i7 B; U
  86. 86 *******************************************************************************/. v% [$ y8 o; U5 l  e
  87. 87 void SOF_Callback(void)$ g# {$ p8 P4 z5 P2 H1 Z
  88. 88 {
    4 B, b$ r% b5 J: j# B
  89. 89     uint16_t len = 0;
    + W0 q0 S* H5 \: n% }3 w5 O
  90. 90 1 @. N2 N7 y+ E2 Q  \  J) c
  91. 91     if(bDeviceState == CONFIGURED)8 A7 t+ a/ p; d! Z7 d7 O2 R
  92. 92     {0 }6 g9 Z* R1 v, N9 Q
  93. 93         if (0 == txFlg)8 I+ u; ]! ?5 ?- S
  94. 94         {
    . p7 N: K( [! k3 o, Q2 k
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)9 E- |. c* D: L* u2 t
  96. 96             {
    3 M. O- ?' |4 ?; ]8 R  q
  97. 97                 /* Reset the frame counter */
    * K2 e" j/ K: E+ h
  98. 98                 FrameCount = 0;
      i1 e6 r$ i( o
  99. 99
    - p# u$ p  ?' p
  100. 100                 /* Check the data to be sent through IN pipe */, C% A) [/ @2 y( G0 ?, Y4 B
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));
    ! F8 B7 i. f1 e; I
  102. 102
    - G3 B6 o. M' w- V+ C
  103. 103                 if (len > 0), h4 }) L4 m% V# m
  104. 104                 {
    ' p: @+ P7 w$ U' r/ f3 O4 [5 f' G* \
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
    / [1 b6 D0 A3 Y+ A
  106. 106                     SetEPTxCount(ENDP1, len);1 ]+ s# Q2 o* o# v
  107. 107                     SetEPTxValid(ENDP1);
    8 c2 B% j9 T+ L& z' {
  108. 108
    3 A" V! H/ n* I2 |; @
  109. 109                     txFlg = 1;
    ! J! A0 Q  \' i
  110. 110                 }
    - \' d. @$ t1 |1 y
  111. 111             }
    4 p$ K# p& D  b* u2 `
  112. 112         }; m# q, j& J* Q" Y$ u& }* `
  113. 113     }  9 B% x# ?, u# g7 e
  114. 114 }' }/ u/ Q; [4 v# z* [. N$ s
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码

- ?5 d9 {$ h6 q! K( }% G9 t. \% J; b2 R3 Z5 u% i
这里讲下大概意思,函数EP3_OUT_Callback是在USB口收到数据后,将数据存入FIFO中。
函数SOF_Callback定时查询用户是否有要发送的数据,如果有则进行发送,在发送完成后会触发发送中断EP1_IN_Callback函数,如果发送完毕就不调用SetEPTxValid(ENDP1)函数,发送完成后就不会再触发EP1_IN_Callback函数。
4,修改usb_pwr.c在前文中说到:不让系统进入休眠状态,这里屏蔽185行 __WFI();
5,修改usb_prop.c屏蔽COM初始化代码。137行USART_Config_Default(); 237行USART_Config();
6,修改usb_desc.c 这里修改需要参考一些USB专业的书籍,推荐全圈圈的书,讲的通俗易懂。关于本程序的驱动,笔者在win7下测试可以自动安装,如果无法自动安装可使用文章开始的链接中的驱动程序。本文件如果修改需谨慎,其中pid,vid是制造商ID和产品编号,如果修改了那驱动也要对应修改,官方驱动就无法自动进行安装了。
到这里移植就差不多完成了,下面进行测试。由于USB虚拟串口不受波特率限制,所以笔者进行过50k/s的压力测试,运行半小时未丢1个字节。
移植好的工程STM32_UsbVirtualCom.rar也一起存放在上文章开始的链接中。
7 u" p! u# ^7 A% L
赞 收藏 评论0 发布时间:2022-1-13 21:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版