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

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

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

8 v# B  d$ F  c1 u* X
* E( e! a8 n; S/ N" L3 |7 |
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下:
Projects\Virtual_COM_Port文件夹下,复制红线部分
图1
4 k0 P7 D; C$ n9 e1 O0 p8 x
300035032772082.jpg
% h* O  B: g4 _3 X( Y1 H8 I. o7 M
图2

% O" i' [2 m' F& `
300035202932682.jpg

( r$ Q! n) F* m7 c/ J/ {7 i
我为了方便演示统放在usb/src文件夹下:
图3
. w$ E+ }- U: W6 q0 i9 H5 z
300035313246246.jpg

* ^6 d+ k  D6 M2 i: y9 c( j
现在复制USB的库文件,这些文件不需要我们修改:
图4
0 ^  e4 j: M6 E5 H  c, M5 O) D0 G3 @
300035404189967.jpg

* D, u+ V; b8 T0 `- r# I
上图中的文件统一放在usb/lib文件夹下:
图5
& N" C% Z  M; r+ D
300036032935279.jpg

( D% ]& K# j( ^  N* a
+ `4 \  c3 R1 m/ [
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:

4 `: D! j) K: _
300036178402768.png
; N* K9 F3 }4 [& F! H5 _+ K
图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 ------------------------------------------------------------------*/
    ; a3 g0 S& N- l/ c5 }# w- Q- {$ `
  2. 2 #include "hw_config.h"0 p  K, u7 M$ H! A+ t3 c: b/ i
  3. 3 #include "usb_lib.h"$ ~% p0 n$ L2 M5 Q2 T
  4. 4 #include "usb_istr.h"/ Y; w2 O7 t5 }6 [
  5. 5 # H$ c( S: H1 i$ \
  6. 6 3 [, o% M; g' D" n% H2 l1 @
  7. 7 /*******************************************************************************
      [$ o  z- w% Q4 ]4 G4 L# l+ D
  8. 8 * Function Name  : USB_IRQHandler& K  O/ b/ E- D
  9. 9 * Description    : This function handles USB Low Priority interrupts
    4 I2 e; R9 _8 p: w: [; Y
  10. 10 *                  requests.
    $ G/ J2 S- J4 S6 k7 Y0 ~
  11. 11 * Input          : None2 }) [* O) V. A. n; o& u
  12. 12 * Output         : None
    9 A9 y; a7 l% {
  13. 13 * Return         : None
    3 X! P1 K2 |9 r7 T, p4 |
  14. 14 *******************************************************************************/
    9 d* u7 w% w* K
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)
    5 V8 S+ g4 H8 M# ^8 A% w
  16. 16 void USB_LP_IRQHandler(void)$ p& x: }0 Z6 R6 }3 `/ M1 I
  17. 17 #else
    : k4 H8 ]- o; W3 Z7 ~: ^) `
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void)
    ; l0 k1 r1 j( T3 R  d  P2 S
  19. 19 #endif
    ( }- ^: B! J! o# o4 n
  20. 20 {
    9 h5 c: M# X3 p
  21. 21   USB_Istr();6 c# i- ?. Q: D
  22. 22 }# h) v4 I; I2 Z! l3 O1 {! T5 v
  23. 23
    - D& O# v1 U; N' ?+ B% O) N
  24. 24 /*******************************************************************************
    " c  D! |3 j4 ~- `
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler( A" V5 z3 S# {2 W5 x" U# A* {
  26. 26 * Description    : This function handles USB WakeUp interrupt request.- i- ?5 p5 E" U7 `
  27. 27 * Input          : None
    * S  W2 X. v0 w
  28. 28 * Output         : None
    7 E, l; n8 I1 k% A) p/ O7 V9 v$ R
  29. 29 * Return         : None
    - z; P: P0 V/ n' R9 C
  30. 30 *******************************************************************************/
    3 c. R5 W& H! X% W
  31. 31 . R% X! h6 W5 e3 M
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
    4 h  I' _- T! c! y, y7 @
  33. 33 void USB_FS_WKUP_IRQHandler(void)" Y9 q8 y& Y# ]
  34. 34 #else
    ( {' C0 F9 i  L0 j* F9 S4 ^
  35. 35 void USBWakeUp_IRQHandler(void)- y( z. h( v" J- P6 S+ `7 I" ]
  36. 36 #endif
    + I8 H1 W$ f3 L* U- O
  37. 37 {, z4 s) M. m* P. b
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);
    0 u' ~% X& V: k7 U( I  ?7 F
  39. 39 }
复制代码

5 }! `& b% e! O* w
2,修改代码hw_config.c删除无用代码,新建立2组,读FIFO和写FIFO的函数。后面会用到。
代码如下:
代码2
  1.   1 /* Includes ------------------------------------------------------------------*/$ G% a6 S# ^. q+ S; B  T
  2.   2 3 w: p. S8 b4 B  c4 w3 p' o
  3.   3 #include "usb_lib.h", F6 u& ?, A. d* K- J6 B
  4.   4 #include "usb_prop.h"
    # `4 x2 V3 ]0 {6 k, F! D
  5.   5 #include "usb_desc.h"3 V3 J) q8 x' _" I' v6 x% c
  6.   6 #include "hw_config.h"! |1 g( q4 z# ~( k+ W9 N
  7.   7 #include "usb_pwr.h"5 j$ {* [' h4 e- O! ?
  8.   8 #include "Queue.h"
    7 Z3 b6 A0 s# E# V+ L+ R( V% r
  9.   9
    5 |4 v8 {" R1 Z, k
  10. 10 8 S2 L7 i, v) F' i  ]' B; N1 D
  11. 11 /* Private typedef -----------------------------------------------------------*/
    5 N" W) L$ d2 l; z. ?3 a9 z! M# o
  12. 12 /* Private define ------------------------------------------------------------*/: }5 y4 k* R# S; \/ C' Y+ N: R- D
  13. 13 /* Private macro -------------------------------------------------------------*/
    " G# J: ~4 s3 m3 V( s
  14. 14 /* Private variables ---------------------------------------------------------*/
    3 Z) D4 |6 u# E
  15. 15 ErrorStatus HSEStartUpStatus;3 P! m5 p& t! O8 |+ `- D
  16. 16 USART_InitTypeDef USART_InitStructure;
    ( p% z$ ^5 Y5 H* g, _& L" H# ^/ O
  17. 17 EXTI_InitTypeDef EXTI_InitStructure;
    & t8 s3 n- G# {3 v9 z7 r
  18. 18
    % P- x$ U- k, v# \" n3 k- g
  19. 19
    ! z' i& m& }5 \+ }$ J
  20. 20 #define USB_COM_RX_BUF_SIZE         (1024 + 256)
    9 _$ l$ B7 l- Y+ S7 j- D0 v! ]( n
  21. 21 #define USB_COM_TX_BUF_SIZE         (1024 + 256)9 y( q% \! w0 O+ W/ R% R
  22. 22 ! A7 ?1 i4 M9 X0 B7 d1 O
  23. 23 static QUEUE8_t m_QueueUsbComRx         = {0};
    ' Q2 R" x: ~) _0 n( h6 l- G
  24. 24 static QUEUE8_t m_QueueUsbComTx         = {0};$ [; h: ~! I( B) G2 B
  25. 25 static uint8_t  m_UsbComRxBuf[USB_COM_RX_BUF_SIZE]      = {0};     
    2 [6 C& ^* E$ ^: f  A' ^2 I
  26. 26 static uint8_t  m_UsbComTxBuf[USB_COM_TX_BUF_SIZE]      = {0};   
    , {) u; Z  V5 i  |4 h% r
  27. 27
    ; U2 o8 V, W, \/ w, j
  28. 28 static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len);
    / U; @5 y( _/ {2 B7 ]* k
  29. 29 /* Extern variables ----------------------------------------------------------*/
    $ i  U8 g4 q, Z' G) E% ~
  30. 30
    . X# H" }# r2 u! }
  31. 31 extern LINE_CODING linecoding;
    9 _  L( M5 s$ ~9 F* }1 H
  32. 32 ( I! W# p4 T3 d0 I# A, g( s
  33. 33 /* Private function prototypes -----------------------------------------------*/8 O7 _1 A4 \0 q" d9 i
  34. 34 /* Private functions ---------------------------------------------------------*/
    4 ~9 A1 r( `' r  G1 z0 o, v  B
  35. 35 /*******************************************************************************
    $ m* W" {- J/ `/ z/ w
  36. 36 * Function Name  : Set_System5 M( C. R' E+ F0 n! t
  37. 37 * Description    : Configures Main system clocks & power1 Z" b2 H3 r( m: A6 ]7 ]
  38. 38 * Input          : None.# X7 i0 p7 @; _7 f( r
  39. 39 * Return         : None.. S/ v2 P; z3 r2 w( X
  40. 40 *******************************************************************************/9 r! A% ]: J9 r* g! b
  41. 41 void Set_System(void)3 J6 U. b8 q2 d6 {! G; ~, s4 B
  42. 42 {
    3 g9 ~6 g8 o9 g6 W* J& C# F
  43. 43   GPIO_InitTypeDef GPIO_InitStructure;; _+ C* e& u  {, x: J
  44. 44
    - h2 J% Y+ y2 Q: `
  45. 45   QUEUE_PacketCreate(&m_QueueUsbComRx, m_UsbComRxBuf, sizeof(m_UsbComRxBuf));% F  A: Z7 d6 D1 h" o* _7 _
  46. 46   QUEUE_PacketCreate(&m_QueueUsbComTx, m_UsbComTxBuf, sizeof(m_UsbComTxBuf));
    ! N+ F' _" g/ O' j+ T% M4 |" t
  47. 47   
    ( B/ p' F$ J9 u4 V6 r4 T& H
  48. 48   /* Enable USB_DISCONNECT GPIO clock */
    ' n. E# t, ]  A, X
  49. 49   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);3 V& P; }1 S: R; D# B; q; n2 l
  50. 50 - U2 ~8 B. M( b: @
  51. 51   /* Configure USB pull-up pin */
    ! R! q6 {8 b1 O$ `9 E) }
  52. 52   GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;6 Q1 v4 v( U! a' C4 Y( I
  53. 53   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;1 U8 A' e% @. o& C4 R
  54. 54   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    / @7 ]9 c- r# R2 _$ K% j4 L2 ]
  55. 55   GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
    * t! ?, t$ Z( ^  a3 E
  56. 56   
    $ \9 |% B  C# n+ K7 |
  57. 57   /* Configure the EXTI line 18 connected internally to the USB IP */
    7 h* p$ s8 Z7 S& F+ p* d
  58. 58   EXTI_ClearITPendingBit(EXTI_Line18);
    ( }* L  x9 t$ d* H/ M8 ]
  59. 59   EXTI_InitStructure.EXTI_Line = EXTI_Line18;
    5 C' I7 s8 L( J; z6 Z; C9 o0 Y! i5 z
  60. 60   EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    * D# L. a# e/ V6 A6 y) d
  61. 61   EXTI_InitStructure.EXTI_LineCmd = ENABLE;$ z3 @8 b# l9 ~( R- q# T) u
  62. 62   EXTI_Init(&EXTI_InitStructure);
    ' I9 K( \, M0 a" I% q
  63. 63 - ]8 S8 D1 c: c5 j
  64. 64   ; f, R. B- V# s2 |3 U! X6 r0 e
  65. 65 }9 l! ^2 n7 d5 u
  66. 66
    - S. t5 L' A2 F2 W7 Z+ K# ~
  67. 67 /*******************************************************************************
    2 v7 K  v6 J/ D, U
  68. 68 * Function Name  : Set_USBClock/ t" G1 w$ Q' e4 R4 E. H6 K4 o
  69. 69 * Description    : Configures USB Clock input (48MHz)
    : [7 F7 G5 ^9 t+ X$ j
  70. 70 * Input          : None.2 K5 k+ C( l$ p1 G: l' s
  71. 71 * Return         : None.0 g4 U0 y3 U8 w2 ]3 _% _
  72. 72 *******************************************************************************/
    5 Y3 b6 b  Q  a
  73. 73 void Set_USBClock(void), T5 T8 E# B! A# h3 a
  74. 74 {' C& m( c3 Q3 l! d4 N( g
  75. 75   /* Select USBCLK source */9 l3 c0 S3 D$ j( |
  76. 76   RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
    * I8 p1 T: Q2 W& s& y3 @+ P5 B' j
  77. 77   8 Y  O8 d+ G# J( e
  78. 78   /* Enable the USB clock */; z' J  |4 y' \( J5 N9 H
  79. 79   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);+ _( }  m" S1 V9 h# I( N2 J9 W
  80. 80 }. q/ l4 E( w& D( k
  81. 81 $ ~/ T! S, w7 l- ]2 ^
  82. 82 /*******************************************************************************6 ]2 W7 l5 J/ H# Z/ G  o
  83. 83 * Function Name  : Enter_LowPowerMode
    1 Q& x) U. Q8 G5 M# B
  84. 84 * Description    : Power-off system clocks and power while entering suspend mode
    * a3 ^1 e' S& N/ Q) y$ P
  85. 85 * Input          : None.5 f' K1 N& l9 }, t& j3 I
  86. 86 * Return         : None./ t8 S# P/ v( z7 H# u
  87. 87 *******************************************************************************/
      g$ V, d4 T3 n; T
  88. 88 void Enter_LowPowerMode(void)+ P6 o$ Y5 Z0 ?3 V8 ~7 Y2 f
  89. 89 {* B, R$ `, f; R/ n3 x( Q
  90. 90   /* Set the device state to suspend */; l' [4 a; G( y8 }/ t5 ?
  91. 91   bDeviceState = SUSPENDED;
    * t; V* x  Y' n  G( C
  92. 92 }. J, d8 W8 H% c8 I8 p- I4 |
  93. 93 ( _9 l+ }( I# z1 e. Y* S* H/ k
  94. 94 /*******************************************************************************8 e' o$ x$ Z6 G! K5 s
  95. 95 * Function Name  : Leave_LowPowerMode0 X# t. W! k: \3 S0 @0 o2 A
  96. 96 * Description    : Restores system clocks and power while exiting suspend mode
    9 _& M( z- t% n* J
  97. 97 * Input          : None.
    # T9 h& o& B/ s1 a0 w! V
  98. 98 * Return         : None.
    - }2 t. E7 [9 o3 b: D
  99. 99 *******************************************************************************/
    6 Z9 N; f; {* D9 Z
  100. 100 void Leave_LowPowerMode(void)4 _: C- n3 P4 m0 ?( L
  101. 101 {
    ! m/ _* ^! h, E7 ^( S0 l2 C: \
  102. 102   DEVICE_INFO *pInfo = &Device_Info;
    5 N, c( @6 f; Y+ A- e9 |+ l
  103. 103
    5 w  W" Q& Q8 ]5 N1 r( m
  104. 104   /* Set the device state to the correct state */4 _/ a8 ^3 q/ G' ]7 Y
  105. 105   if (pInfo->Current_Configuration != 0)
    ! Y9 @) D2 O! u. y: [
  106. 106   {' p3 @: f3 K$ c# G- @3 Q
  107. 107     /* Device configured */7 c2 u2 l8 c, \+ L
  108. 108     bDeviceState = CONFIGURED;' U. X0 x3 U0 Z1 ]
  109. 109   }
    + g! {- V' F# I% h4 _- z& s* ^
  110. 110   else
    + `" e& n) R9 T9 M( B7 c+ Z1 [3 |
  111. 111   {
    5 S* T+ M" d7 C, F% i& ?
  112. 112     bDeviceState = ATTACHED;9 \" F# {5 `- p4 a6 F  N5 I, @8 u
  113. 113   }
    5 C" {1 H: @7 A" ~) t( k& J
  114. 114   /*Enable SystemCoreClock*/8 m, [5 P7 D! A: V* C: K: e
  115. 115 //  SystemInit();
    ) N4 s. D1 J+ I- B  e* F, X
  116. 116 }2 X) l# U" e/ k& Q
  117. 117   d, C5 r* g  y: V" e& o. @
  118. 118 /*******************************************************************************4 J& a3 Q. ?$ f* H
  119. 119 * Function Name  : USB_Interrupts_Config
    9 S7 r+ g; W5 p* c$ m
  120. 120 * Description    : Configures the USB interrupts$ h$ e+ @( p  g
  121. 121 * Input          : None.
    - @, G! B5 s  z5 ~2 |
  122. 122 * Return         : None.: s* f6 w  K* l* X
  123. 123 *******************************************************************************/
    ! j' P. K/ D( d& q" p
  124. 124 void USB_Interrupts_Config(void)
    & s7 @% @# r* x2 N8 p' A* P% Y2 ~
  125. 125 {( k$ D3 q/ F* G# K9 s
  126. 126   NVIC_InitTypeDef NVIC_InitStructure; 1 s  _  w( o! C5 a) q1 X
  127. 127
    ' a3 T/ O  u  `  P: z" U
  128. 128   NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;% X+ B. S5 m  G3 O0 w6 T8 w+ N
  129. 129   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    3 n3 u) Z9 D4 @" Q% {7 ~8 G
  130. 130   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;) C: R7 l1 T+ U! l2 ~$ a
  131. 131   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;1 n4 m5 a) j+ M( C* k0 y
  132. 132   NVIC_Init(&NVIC_InitStructure);
    8 M2 W4 L# y1 }% y- j! g
  133. 133   2 E* x( p" F& C- _
  134. 134     /* Enable the USB Wake-up interrupt */
    ; Y( M1 V* v$ S
  135. 135   NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;5 |( g$ g: ~# d, u" R! U5 m
  136. 136   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;. `7 V: r- K6 P& q. F
  137. 137   NVIC_Init(&NVIC_InitStructure);6 z: ~  n% H  F  [
  138. 138 }
    ; |# q9 M- M& f& H* ~/ q
  139. 139 1 i+ }/ S( \9 D! u: E0 K1 r
  140. 140 /*******************************************************************************3 ~8 U9 V& Y5 C* T1 O
  141. 141 * Function Name  : USB_Cable_Config/ w7 T4 v6 g" O9 m
  142. 142 * Description    : Software Connection/Disconnection of USB Cable
    - }. e+ L' O$ B0 F
  143. 143 * Input          : None.* T: N+ a' }2 T/ R. T% ?
  144. 144 * Return         : Status+ ^$ U0 q/ K" x
  145. 145 *******************************************************************************/6 I2 Y  P) b3 r
  146. 146 void USB_Cable_Config (FunctionalState NewState)/ H3 R- O& z* j/ J( {8 a: a' \" N
  147. 147 {: I1 u; N5 x* n3 s7 Q
  148. 148   if (NewState == DISABLE)
    0 U$ Z) w; B% D# f! N- y) A
  149. 149   {3 L% P" ^2 y, n1 s  Q
  150. 150     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    ! ?5 ?  T" r2 y1 i* W( b
  151. 151   }
    , W+ n0 F$ B7 c- \2 E+ f8 t' p+ T: a
  152. 152   else+ R- H; c/ J1 G7 t, _7 Z
  153. 153   {: w& q/ a; M- ?8 C
  154. 154     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);5 r7 J$ [3 P) n9 _3 F6 d! Z
  155. 155   }" B& g) a5 C& {$ ?. Q8 ?3 t
  156. 156 }; y& w, W( v2 q
  157. 157   y  x- \3 {& V2 p2 o
  158. 158 /*******************************************************************************
    ( n/ i# r8 f' @) e
  159. 159 * Function Name : void USB_Config(void)3 w. t! U4 f8 {/ T% {; o2 V5 i( E
  160. 160 * Description   : USB系统初始化
    + k1 V) L& y& J" q" o* q/ P
  161. 161 * Input         :
    ; i* P% T% }! y+ I
  162. 162 * Output        : 7 b7 H; i7 U3 X+ a
  163. 163 * Other         :
    9 w0 Q( [% I; t) u' M2 K0 |
  164. 164 * Date          : 2014.11.28' I, W* @* z$ n: J
  165. 165 *******************************************************************************/
      e" C) X- l' b; I# h5 n$ n5 P. c
  166. 166 void USB_Config(void)  c& c( R& Y1 f3 O# ^. ?; H' \* J/ P
  167. 167 {
    9 p4 c; d5 O8 e/ b: w5 ]* K
  168. 168     Set_System();/ q# }6 ^. E/ p
  169. 169
    8 O% I0 r) `) _/ S
  170. 170     Set_USBClock();3 y1 f9 v& Y, W: m+ H. L6 y6 k
  171. 171 " t; I$ X. }6 z
  172. 172     USB_Interrupts_Config();
    + h9 I8 E) n2 ?3 B
  173. 173 9 [- }; f  Z/ X- v0 B
  174. 174     USB_Init();
    3 I. i9 }8 n4 C( V; x$ {
  175. 175 }6 t7 @$ N! p1 O( a* _! o
  176. 176 8 t1 L' c7 G, R" T/ x) N
  177. 177 /*******************************************************************************$ E7 q) Y. O+ Y2 f- j, h
  178. 178 * Function Name : uint32_t USB_RxRead(uint8_t *buffter, uint32_t buffterSize)! H% O$ }. |6 v; e
  179. 179 * Description   : 从USB接收缓存中读数据2 K- p7 E) u1 L, Q6 ]1 c6 f
  180. 180 * Input         :
      m- B! Z" {( P
  181. 181 * Output        : : Q- Y4 H6 ^% K  ~0 m. x8 ^! |' j
  182. 182 * Other         :
    / j( T/ D0 m6 q
  183. 183 * Date          : 2014.11.28
    8 G) P; d& y' V
  184. 184 *******************************************************************************/
    3 D+ p1 ^5 r& Z& r' \! X5 ^4 r
  185. 185 uint32_t USB_RxRead(uint8_t *buffter, uint32_t buffterSize)
    ; v; Z+ U4 V: \8 q
  186. 186 {
    1 Q/ M4 y: s7 P0 c5 |
  187. 187     return QUEUE_PacketOut(&m_QueueUsbComRx, buffter, buffterSize);2 G3 _& i/ [0 ~4 l4 G4 C0 W
  188. 188 }
    ( X% _+ {! P5 Q! i4 @4 b
  189. 189 /*******************************************************************************8 E/ t+ }; o! S, U" ^7 ]8 M
  190. 190 * Function Name : uint32_t USB_RxWrite(uint8_t *buffter, uint32_t writeLen)
    # T8 |; l% R! S$ d: Q1 K, y2 H
  191. 191 * Description   : 写数据到USB接收缓存中3 D, q" R% D. Y6 \$ d
  192. 192 * Input         :
    3 i( ^6 X" C& S- ?
  193. 193 * Output        :
    ! G! k' Y8 Q& Q1 j
  194. 194 * Other         :
    " |% m# a1 A1 P+ b, z5 d
  195. 195 * Date          : 2014.11.28
    0 [2 y0 y2 s- W6 R! ~
  196. 196 *******************************************************************************/7 O' G; W, z* Y1 J
  197. 197 uint32_t USB_RxWrite(uint8_t *buffter, uint32_t writeLen)* O# j: X4 L/ @9 c
  198. 198 {$ _+ r/ q" M. A4 [, w
  199. 199     return QUEUE_PacketIn(&m_QueueUsbComRx, buffter, writeLen);
    ) A" A. J1 M, P, _2 _- m: K
  200. 200 }2 H' Z5 r6 \, [. [; j  H6 X8 G4 `" [
  201. 201 /*******************************************************************************+ L, |6 h! ?8 `$ W3 }' o
  202. 202 * Function Name : uint32_t USB_TxRead(uint8_t *buffter, uint32_t buffterSize)
    : D# u6 Y4 R" H  s% \
  203. 203 * Description   : 从USB发送缓存中读数据
    + X% l6 ]# X2 C6 a' w
  204. 204 * Input         : - a. X3 ?" q+ t# V9 R3 k& g# {
  205. 205 * Output        : , }" }3 H% V1 e7 D
  206. 206 * Other         :
    & I+ g  Q3 n" n; F7 X
  207. 207 * Date          : 2014.11.28
    7 q& ^4 W. s: o2 n
  208. 208 *******************************************************************************/) z$ [+ t6 I- f6 x$ Q9 V4 Z2 F6 B" ]
  209. 209 uint32_t USB_TxRead(uint8_t *buffter, uint32_t buffterSize)
    , K+ R  `. K6 b* U) v/ e
  210. 210 {9 F" @' r) t0 z! M; R
  211. 211     return QUEUE_PacketOut(&m_QueueUsbComTx, buffter, buffterSize);;; ?8 v+ V  o- y$ x1 c" A/ I$ c
  212. 212 }
    . @! [" u1 H) @/ w- }' `/ W& }! W
  213. 213 /*******************************************************************************" k3 p) M/ h. x( J) Y
  214. 214 * Function Name : uint32_t USB_TxWrite(uint8_t *buffter, uint32_t writeLen)
    ) Z! ~9 y! K& w) v
  215. 215 * Description   : 写数据到USB发送缓存中
    ) D, {2 G5 A  N% Y, L
  216. 216 * Input         : 2 i: q9 {; C0 X( _& M* U  ~1 [* Y# w
  217. 217 * Output        : 2 i5 @8 d; E' y) `- ~$ p' X' t
  218. 218 * Other         : ) A3 f' b8 z0 A7 d
  219. 219 * Date          : 2014.11.28
    ! W# N& b. L. E% w. R
  220. 220 *******************************************************************************/
    8 N% H  Z' U7 P% d5 }; k: `1 Y- ^, G- n
  221. 221 uint32_t USB_TxWrite(uint8_t *buffter, uint32_t writeLen)% w9 }; f/ T' t- ^+ }
  222. 222 {; J# _8 u9 d. j# Y( ~# U# D6 b
  223. 223     return QUEUE_PacketIn(&m_QueueUsbComTx, buffter, writeLen);0 W5 c4 y8 S! H9 L! O8 [  w/ Y* Z
  224. 224 }
    " s, l( O5 y) j1 b: @
  225. 225 5 m: M3 K2 g  }% }
  226. 226
    0 X% Z9 Q! o: R$ m+ n
  227. 227
      u, o* G: y5 J6 `! B3 x
  228. 228 /*******************************************************************************
    7 L& e7 k5 t0 e# j( K
  229. 229 * Function Name  : Get_SerialNum.* U* V( L2 Y) e/ s
  230. 230 * Description    : Create the serial number string descriptor.
    7 R7 Y* L) t) w4 d* s
  231. 231 * Input          : None.
    / U" c& A  r/ W' I/ j
  232. 232 * Output         : None.) g8 @& ~6 A! C, p7 {! b0 S- y- D  t
  233. 233 * Return         : None.* U  T. G7 O6 B9 ]" A- k  g
  234. 234 *******************************************************************************/
    ' Z2 |* Q! f9 _. L8 A, C
  235. 235 void Get_SerialNum(void)
    # d  n  M3 w6 |
  236. 236 {4 S- g+ h  j. \1 L: ?/ |% ^
  237. 237   uint32_t Device_Serial0, Device_Serial1, Device_Serial2;
    # z: Y- F( z* [, e0 B, k$ Q& ]
  238. 238 1 z3 _1 @$ X3 E7 D7 C; J) y; }* i
  239. 239   Device_Serial0 = *(uint32_t*)ID1;
    ; b/ U+ E% m6 i3 o
  240. 240   Device_Serial1 = *(uint32_t*)ID2;' M% R" D) H6 G! m* y* e
  241. 241   Device_Serial2 = *(uint32_t*)ID3;  
    7 j* Y9 N# \$ ?8 I! u
  242. 242 ) G6 `" B& o+ Q  x' i
  243. 243   Device_Serial0 += Device_Serial2;
    + X3 c; w: J; }3 A1 \" K0 `
  244. 244
    3 z- U3 ^+ p4 G5 p( L, K- B
  245. 245   if (Device_Serial0 != 0)
    5 W/ }' l. t* b+ U
  246. 246   {9 y6 O- {) r. _4 Y
  247. 247     IntToUnicode (Device_Serial0, &Virtual_Com_Port_StringSerial[2] , 8);
    ! I5 @" ~1 P5 H$ B4 L# |, ~7 k
  248. 248     IntToUnicode (Device_Serial1, &Virtual_Com_Port_StringSerial[18], 4);
    , h$ d* \' \2 V  h  [
  249. 249   }
    - p7 U6 n5 N% X* Q9 M5 v! Q
  250. 250 }+ x& n2 [6 B6 P& a9 _
  251. 251
    9 q, ?4 M0 F8 J# b" z5 o
  252. 252 /*******************************************************************************
    ; |6 G" J/ q( }+ ~
  253. 253 * Function Name  : HexToChar.
    % N( ~) e. e; t2 _; W0 G; E) P
  254. 254 * Description    : Convert Hex 32Bits value into char.
    3 g5 q( b4 R7 s* E, S  |7 U
  255. 255 * Input          : None.+ Y# A6 `+ e* v% w
  256. 256 * Output         : None.
    ( I0 A! n$ o4 ^# M
  257. 257 * Return         : None.
    ! y; k( @7 d5 B1 D: k/ a) b# A
  258. 258 *******************************************************************************/5 n3 w* u6 \- [* t) F5 ]( _8 _1 c
  259. 259 static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len)# s9 B2 L( `7 Z! r8 x# E
  260. 260 {
    ( {* w/ ?0 D. m# }/ m
  261. 261   uint8_t idx = 0;( ~& U, ^1 C& l) H* j' I% C
  262. 262   8 E8 I9 w7 I' H
  263. 263   for( idx = 0 ; idx < len ; idx ++)
    5 c4 _9 d4 B" I7 ?+ h
  264. 264   {
    - g3 w4 m8 z$ ?) b& n; @9 T
  265. 265     if( ((value >> 28)) < 0xA )
    + B7 Y3 M/ H9 [
  266. 266     {# d( K" h0 J6 O0 S
  267. 267       pbuf[ 2* idx] = (value >> 28) + '0';
    ( `. z. y! j# {0 m6 D- i
  268. 268     }
    & [6 O0 k$ n8 E/ H( I& R" w+ `
  269. 269     else8 I: [- L  M6 f; _' X$ n9 L
  270. 270     {( F1 G; _; f' N9 m$ e. B8 S
  271. 271       pbuf[2* idx] = (value >> 28) + 'A' - 10;
    ! j; J" m9 l  i( `2 @/ k/ i
  272. 272     }% V: U; D) C9 M( J/ U; G; Y
  273. 273     
    , J* U  Y2 y% N& S& }3 b+ x
  274. 274     value = value << 4;
    , ^+ }, o, Q5 {9 j( g4 J
  275. 275     
    ; n# M- F: x: C7 d/ M; u3 m
  276. 276     pbuf[ 2* idx + 1] = 0;7 Q% `5 F; e) z( S% ]- O
  277. 277   }
    8 r) U0 H$ j8 `
  278. 278 }
    4 @8 J* ~5 p8 d, J
  279. 279
    ( d/ o' e% q# e9 r, s! @
  280. 280 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码

4 d! G) p* h1 U" {8 d! |9 ]2 D: c4 b; Y1 J5 P
这里要讲一下为什么要屏蔽SystemInit(),因为demo只运行虚拟串口功能,在USB未插入的情况下,是进入低功耗状态,插入时从低功耗状态退出后会调用此函数。当然我们在项目中一般不会这样,系统是否运行和插USB接口没有联系。所以我在下文中把进入低功耗代码屏蔽了,自然也就不用唤醒代码了。
图7

. t; _+ l  r( P
300038465597900.jpg

9 `3 `2 |+ e% A* p& q; X5 i
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************" o& G" @0 _0 G& w& s0 u" ~
  2. 2 * Function Name  : USB_Cable_Config
    5 e* y+ x8 _8 Q6 R3 Z; i
  3. 3 * Description    : Software Connection/Disconnection of USB Cable) v4 ]( S# r( H. [' d- k9 |5 G
  4. 4 * Input          : None.
    7 N. S& H! `1 h8 G
  5. 5 * Return         : Status
    - `0 S7 U& E2 E8 Y7 k, r0 i; X
  6. 6 *******************************************************************************/0 G" \/ b% ?6 U1 O0 H
  7. 7 void USB_Cable_Config (FunctionalState NewState)* s; C; v1 r: S. }4 J* d
  8. 8 {7 I( q% V+ \* N& E
  9. 9   if (NewState == DISABLE)
    . e2 [# C1 m: Y, A* P
  10. 10   {  E& k; X& S' z( T% [0 L
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    " U2 F6 B$ N0 b3 t( R( x
  12. 12   }
    . ?# ~( O. r" n* y) E
  13. 13   else
    $ ?/ L. U- e' L% Q5 k3 J0 D# K1 r" C: r$ s
  14. 14   {
    ' Q0 _# u* c& f$ l0 a% X; K
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);% C7 G/ L$ L) s& m/ X! C
  16. 16   }
    2 O3 G9 R! M; ~, H
  17. 17 }
复制代码
) B, Z$ x3 ~/ }) ]3 n9 B  P
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/; g. A  ~. ^' ?( q) I8 g: @( T) z
  2.   2 #include "usb_lib.h"/ Z+ G$ ~+ m+ @( L! n4 @
  3.   3 #include "usb_desc.h"
    5 y& w, R: _* P. q; S! i
  4.   4 #include "usb_mem.h"* [% r( S/ x0 a$ B! T. N0 I
  5.   5 #include "hw_config.h"7 c$ c) G/ z" ?: p) L
  6.   6 #include "usb_istr.h"8 Q6 M/ B6 U: R) T1 ~" F- E& e; t
  7.   7 #include "usb_pwr.h"$ [5 a; N6 X! E8 ~$ T% E
  8.   8
    3 d4 M9 Z* U- @' T" E" P
  9.   9 /* Private typedef -----------------------------------------------------------*/
    3 e: n2 n5 N2 l$ M; j+ ^
  10. 10 /* Private define ------------------------------------------------------------*/
    ) ?0 H' E4 g2 P. s
  11. 11
    4 [" b( s6 L, }2 G% H/ y, F
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
    ( ~6 @. n) f* J) n
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             5( w4 ~' K* T) q, V+ c( E3 k- h0 R' z5 g
  14. 14
      A+ {& [; x/ B* b9 c+ g6 J9 M
  15. 15 /* Private macro -------------------------------------------------------------*/
    & O9 \; X, M; N
  16. 16 /* Private variables ---------------------------------------------------------*/4 J, s1 R8 v* I7 F$ Y. F
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};5 i0 G8 S" o4 t
  18. 18 static volatile uint8_t txFlg = 0;
    % y( Z8 W7 S' U3 }
  19. 19 static volatile uint32_t FrameCount = 0;
    / U1 M8 B) P9 [3 V2 B
  20. 20 1 e9 B( \! \# o& e$ m
  21. 21
    1 s5 a" G# w; E; ?0 O
  22. 22 /* Private function prototypes -----------------------------------------------*/
    8 k& q$ I. r, K' R
  23. 23 /* Private functions ---------------------------------------------------------*/
    6 e; }) a; ~/ J( x3 \2 o
  24. 24
    - a; {5 s' F6 J) M2 @
  25. 25 /*******************************************************************************& P! t0 z4 ^% N9 n) T
  26. 26 * Function Name  : EP1_IN_Callback% ^9 C# c# u1 Y* O! h3 @- r
  27. 27 * Description    :
    9 X) k8 Q9 n; a& D
  28. 28 * Input          : None.! Y" \- r/ G+ \' f# a! D
  29. 29 * Output         : None.
    % u, P8 |9 K. e4 s
  30. 30 * Return         : None., H4 Q/ K- c2 d0 c
  31. 31 *******************************************************************************/' ]$ D5 c* B, g2 R
  32. 32 void EP1_IN_Callback (void)
    ; U1 N& b9 |6 D4 F2 a
  33. 33 {0 W. H6 m# G+ X' @% @* A% \# Q
  34. 34     uint16_t len = 0;2 v$ q6 D$ ]7 ?% w- N) l% D
  35. 35     
    9 e$ q9 ?* W5 F7 a0 Z! k7 T
  36. 36     if (1 == txFlg)! V2 s+ l2 q8 {, a( I. W
  37. 37     {1 S, W' Z5 f, ~" L9 Q
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));
    7 {0 K# P) Q- Q3 ^, g
  39. 39 9 t# w5 d. Z8 ^# W
  40. 40         if (len > 0)$ x+ O7 M2 v7 P
  41. 41         {
    - v- F& v) a# U7 w4 s) l
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
    8 K6 p+ Z, S. x7 |
  43. 43             SetEPTxCount(ENDP1, len);: G* l- w8 L* L$ |. e
  44. 44             SetEPTxValid(ENDP1);
    ( e' g) ^5 J( N- X
  45. 45             FrameCount = 0;+ C/ A" ]; p1 U  Z  V
  46. 46         }. o; v+ w4 X3 k' @7 @
  47. 47         else
    9 P5 Z' @6 K/ s6 v" T3 |- [
  48. 48         {2 ]5 M/ \7 M9 D( `6 h9 G" M
  49. 49             txFlg = 0;; A) w7 o6 p2 T/ @, G- O8 Q
  50. 50         }" v/ b5 T; o: l; ~
  51. 51     }
    ) M. a' `4 j% }) J
  52. 52 }
    8 O  @1 F+ p1 `: u$ f: O' q
  53. 53
    . h) Q) v& k0 S) r4 i8 o
  54. 54 /*******************************************************************************) _3 ~3 X. a$ N: I) _
  55. 55 * Function Name  : EP3_OUT_Callback
    5 x  Q5 a; b6 ]: [, ~5 f$ F( M
  56. 56 * Description    :6 M% Y+ m! I- w; H9 X
  57. 57 * Input          : None.7 c+ ^" I7 b! S9 J- V/ I, C% |" l
  58. 58 * Output         : None.
    " {& a8 v; W& M5 S
  59. 59 * Return         : None.
    8 c) c& k/ [: w+ P  y# a4 ]' F
  60. 60 *******************************************************************************/  B: q& S2 E; j: Q( L) d. ]' V4 f
  61. 61 void EP3_OUT_Callback(void)
    ' W" J9 h7 c5 e9 M
  62. 62 {% F7 S/ H* m. L! O) ^! h( n; m/ |
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    5 D. C4 k  [9 `8 M. ]' Z3 ?5 G4 m
  64. 64 , q6 L* A( e9 g4 [8 S1 j0 J
  65. 65   uint16_t USB_Rx_Cnt;6 u, I0 [& j' `; V+ k7 j2 L6 {
  66. 66   
    6 h) ?- u+ l4 t# d
  67. 67   /* Get the received data buffer and update the counter */
      H; u1 V" Q, C
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);- j) o7 L& w& v
  69. 69   
    8 n4 Z' Z% l- v) K( T" T  B+ X- P
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being 1 i! W: v" {' H0 m7 b, `5 N/ P
  71. 71   NAKed till the end of the USART Xfer *// O+ r# J; S8 i+ v+ T# G
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);
    3 M' ~( k, z9 @  Y
  73. 73 * T* w7 |! |  u" Q4 c# \5 E
  74. 74   /* Enable the receive of data on EP3 */9 E5 z% I* D0 k" ?6 c  q, w6 B2 O: E' {+ ^
  75. 75   SetEPRxValid(ENDP3);8 t4 I2 H9 |* V# _
  76. 76
      T: i' F" q: R0 _
  77. 77 }
    + ^# _! G6 e; l, k/ p
  78. 78
      p$ C/ e+ Z, [0 [/ P
  79. 79 0 c3 d0 E! E+ h' N, ~3 w, T
  80. 80 /*******************************************************************************
    + W8 `, I$ N: a1 t6 |
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback1 \; r* H6 @& D9 }- j
  82. 82 * Description    :3 O* Q0 N) `- [9 y6 ]; P
  83. 83 * Input          : None.
    ! s0 L4 H. P/ D8 D( D& d' F; M
  84. 84 * Output         : None.
    9 v8 u) g/ C' Q# s5 d4 G. D. n
  85. 85 * Return         : None.2 V2 j) U; i# K. C0 W6 N" g
  86. 86 *******************************************************************************/
    / d0 z0 t) S) y1 M; r- I+ H
  87. 87 void SOF_Callback(void)
    - d8 ~9 D2 b; w& g' J% M
  88. 88 {% C1 I7 p/ T3 a# m# S, X- }" W
  89. 89     uint16_t len = 0;
    ! U- b3 ~. y% x/ p
  90. 90
    # Y/ k$ n6 M. ^$ Z* K( x
  91. 91     if(bDeviceState == CONFIGURED)* P1 Q1 N4 T+ O+ Q7 S/ R" m/ i: v
  92. 92     {
    - J! x) d9 E$ L  c* t1 h
  93. 93         if (0 == txFlg)
    2 o! s% P3 p4 i; x+ B
  94. 94         {5 f- c$ O8 y! @$ R4 l
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)2 G9 L! O6 P0 T& e  U$ ~* I
  96. 96             {$ S4 I0 y" g0 J- h2 v, w* C
  97. 97                 /* Reset the frame counter */7 I2 z+ f) e8 q" v+ Z# ^1 y
  98. 98                 FrameCount = 0;
    3 q2 N- Y& B. p
  99. 99 5 s/ Y1 j/ f+ Z' o, K; p9 w
  100. 100                 /* Check the data to be sent through IN pipe */
    " M+ W* z6 z5 j9 r
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));. g5 l# p0 O6 k9 ~0 A, g" j8 I
  102. 102 : P+ _" _$ {" F+ F- s6 X4 D
  103. 103                 if (len > 0)1 @# ]9 p4 Q+ \& f6 S4 L
  104. 104                 {$ T* {+ y; B- Z9 |: y( j
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);0 _# K1 h, a) S5 n
  106. 106                     SetEPTxCount(ENDP1, len);/ q$ J" M1 O5 o2 [
  107. 107                     SetEPTxValid(ENDP1);
    8 |4 o$ y) n! }
  108. 108
    + i- k% u* q$ W8 I8 W  _- _
  109. 109                     txFlg = 1;- ^7 R4 b' D6 Q
  110. 110                 }8 _3 ~* L2 `7 Y  V  J
  111. 111             }
      c# X6 }/ o. p+ W; h
  112. 112         }) R* I' O) F+ }5 U# N+ T& g( p
  113. 113     }  4 R* R8 b! I6 [: j2 g
  114. 114 }/ U9 b- v4 M' l; ~7 `+ a2 ^9 }6 E
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
6 i; {8 O+ s  X  Z
; a4 K: g& g: J) i3 \( s
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。

1 a5 ?, i- t% d
收藏 评论0 发布时间:2022-1-13 21:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版