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

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

[复制链接]
STMCU小助手 发布时间:2022-1-7 19:00
串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。
4 G/ ]) J# e  K  S) \& R6 f
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下:
Projects\Virtual_COM_Port文件夹下,复制红线部分
图1

* a# Y( R- Q4 Z) Q
300035032772082.jpg

. A  A6 ^2 R" a0 J
图2
5 ^2 G  @" e" C
300035202932682.jpg

! r. M; y" C! [1 C( B/ L
我为了方便演示统放在usb/src文件夹下:
图3
: h' g" Z3 h5 p, @
300035313246246.jpg

* C9 @3 p3 ~  A3 A. w: R! f% C0 g
现在复制USB的库文件,这些文件不需要我们修改:
图4

- l9 y' k, [* A4 ~* Q3 v! s/ ~+ M* I/ J
300035404189967.jpg

. c' ~( \! O3 Q+ }
上图中的文件统一放在usb/lib文件夹下:
图5

, M+ }4 {  b4 [% h$ _& M
300036032935279.jpg
& w3 M9 [4 P0 G/ D1 C- @
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:
图6
# M3 L2 x, H' a8 R
300036178402768.png
         由上图可知,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 ------------------------------------------------------------------*/" m$ `' g" ^9 B
  2. 2 #include "hw_config.h"
    0 M" j& f1 |" S9 D
  3. 3 #include "usb_lib.h"' w  x* U* N3 }, ]0 q/ S, {4 a
  4. 4 #include "usb_istr.h"
    8 ~8 Q) M) T5 M! q0 p
  5. 5 ' x/ u0 v& r# `( n& d8 o
  6. 6
    ! b  ]1 K' ^7 V# G
  7. 7 /*******************************************************************************: I% u) ]# W7 w* v3 g; m
  8. 8 * Function Name  : USB_IRQHandler
      C; _4 ^/ U' g' {
  9. 9 * Description    : This function handles USB Low Priority interrupts2 s& i; ?* E0 m( d; ~
  10. 10 *                  requests.; O6 Z1 q3 ~6 m( p9 X9 w* A
  11. 11 * Input          : None; g8 w5 G  \/ G- d1 v6 J& e+ D
  12. 12 * Output         : None0 t5 Y( t. K* u
  13. 13 * Return         : None
    : T& O% M  Y: p# y+ k
  14. 14 *******************************************************************************/
    8 h* |+ C* ]9 `4 f4 O( K
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)
    1 B. f/ |+ \. ^' p
  16. 16 void USB_LP_IRQHandler(void)
    . `  N) k: ^8 u* @
  17. 17 #else
    5 q5 R* Q. p( V' j7 j7 v7 y9 d* f+ _
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void)
    / u7 T) A& L, w
  19. 19 #endif$ g- L% R) U: P
  20. 20 {
    ' @8 ^/ J: T. G! O+ p
  21. 21   USB_Istr();
    5 `$ S' V% q' I" x4 \+ K9 f
  22. 22 }7 t/ E. `0 n# O9 m1 a2 S3 k( O- N
  23. 23
    : ^5 a% x- f& V" K7 n
  24. 24 /*******************************************************************************$ X& {8 M  f7 \2 `2 y
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler! q) k" w+ L8 r8 A, m  B% e& s8 P
  26. 26 * Description    : This function handles USB WakeUp interrupt request.* Y7 d  I+ g/ e' F/ {  B# Y
  27. 27 * Input          : None; c% g, [% z% I* a
  28. 28 * Output         : None
    3 Y) E7 B9 o4 a' C" ]
  29. 29 * Return         : None
    ( ]3 O, L! C% M. y) t
  30. 30 *******************************************************************************/
    , \: x% W$ U8 ~8 I0 o
  31. 31 : K" j/ l7 o$ n0 N- _+ Y9 Z* P
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)' e' w% i9 T3 O0 n: `* |4 @% O
  33. 33 void USB_FS_WKUP_IRQHandler(void)
    " x2 [7 Y0 w8 X1 X" |
  34. 34 #else: y% t0 w' n4 a' C
  35. 35 void USBWakeUp_IRQHandler(void)8 l# I6 ~1 ]* Q; S
  36. 36 #endif  \% H" G& C+ }: i3 ?  ^
  37. 37 {: t. }: u3 p) N( g# J( H. D
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);2 D; |$ b& T. e, \
  39. 39 }
复制代码

! w. I' G2 E4 C+ a
2,修改代码hw_config.c删除无用代码,新建立2组,读FIFO和写FIFO的函数。后面会用到。
代码如下:
代码2
  1. <img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" border="0" alt=""> View Code
复制代码
这里要讲一下为什么要屏蔽SystemInit(),因为demo只运行虚拟串口功能,在USB未插入的情况下,是进入低功耗状态,插入时从低功耗状态退出后会调用此函数。当然我们在项目中一般不会这样,系统是否运行和插USB接口没有联系。所以我在下文中把进入低功耗代码屏蔽了,自然也就不用唤醒代码了。
图7
9 I4 s- g& U+ e0 H( }6 `9 J
300038465597900.jpg
: K- ^, ]3 I% M4 J, w2 F
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************
    $ V% O0 C& X1 M0 R
  2. 2 * Function Name  : USB_Cable_Config
    ) r1 k- f3 p1 h1 ?( Q2 M1 S
  3. 3 * Description    : Software Connection/Disconnection of USB Cable: ^" r. {# D, w7 p
  4. 4 * Input          : None.1 B" B% A3 v& W  `+ e. }4 R! j1 d
  5. 5 * Return         : Status9 }' \3 U* F4 V( F' j
  6. 6 *******************************************************************************/
    ( V' c  H& n) e8 I
  7. 7 void USB_Cable_Config (FunctionalState NewState), I, V0 L) I$ b
  8. 8 {' r8 z% \7 L. }: W! R8 ?) `
  9. 9   if (NewState == DISABLE)6 F) c6 X+ q3 a4 B6 m  o2 W" h
  10. 10   {
    0 z* A) L0 N# r
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    0 N+ ^1 K. _0 l" H' k1 p
  12. 12   }. f* F) t  I" m% g2 I3 N& q
  13. 13   else
    + u9 m. [+ A* X$ u/ q% u: T
  14. 14   {, n8 U" o; ]7 G: M. X2 l
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    , @0 X$ U8 r$ ]/ h
  16. 16   }6 p5 T5 M% a; \" E" q( j& V
  17. 17 }
复制代码
. b$ t9 i- Z) c5 N  i" u
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/: v+ a/ R$ Y$ x' `: m3 Q* Z
  2.   2 #include "usb_lib.h"/ O9 n+ ], F+ ]* U8 N
  3.   3 #include "usb_desc.h"; ]) H5 W' p1 G3 ]! I( ^  J( C
  4.   4 #include "usb_mem.h"- \: d# i2 B0 b- H& R1 b# x
  5.   5 #include "hw_config.h"
    . J5 `) l% o5 |( @& n$ I
  6.   6 #include "usb_istr.h"
    7 s: j# [2 r# w% k+ T, Q
  7.   7 #include "usb_pwr.h") A/ G3 w: h* m( [/ ~
  8.   8
    & j1 z1 B! p2 k' `+ }  N
  9.   9 /* Private typedef -----------------------------------------------------------*/
    ) B3 {9 U/ @( P2 H$ y5 o, i# e
  10. 10 /* Private define ------------------------------------------------------------*/
    , O6 ^$ U' X4 c
  11. 11
    9 m. x- H9 X- e, V1 u
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
    8 x. x5 j  `6 o2 c* m4 y( {" Q
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             5
    4 k0 f0 W. \( r+ f' M
  14. 14 4 A! g2 Z" t- l6 D! A
  15. 15 /* Private macro -------------------------------------------------------------*/
    ' x7 t$ Y/ {7 T! w5 }
  16. 16 /* Private variables ---------------------------------------------------------*/9 I1 o! Y, D' K6 {1 r
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};/ Q& ~: V0 h- g! f
  18. 18 static volatile uint8_t txFlg = 0;
    ; Y0 R. f9 \9 u
  19. 19 static volatile uint32_t FrameCount = 0;
    ! z0 y# G. I8 g( q
  20. 20 9 d7 e7 C9 M& e" f* w
  21. 21 + C0 ]5 d- U. Q0 _9 E
  22. 22 /* Private function prototypes -----------------------------------------------*/
    1 H/ C; G3 N0 ^/ l. k5 C
  23. 23 /* Private functions ---------------------------------------------------------*/. l0 }3 A$ |& U' I6 ?5 f
  24. 24 " c, x$ p! V0 s, s+ j" P2 O
  25. 25 /*******************************************************************************
    & l, q: \7 F  }4 p; z* c6 U
  26. 26 * Function Name  : EP1_IN_Callback  |! {* L" j+ \+ ]+ b( Z, ~4 r2 k& ]
  27. 27 * Description    :# k1 O) r& l# N: r# v
  28. 28 * Input          : None.
    4 C2 {+ H' c5 ^9 w% ^& m' v. G3 i
  29. 29 * Output         : None.) E' K3 o7 f7 }; j, k. t
  30. 30 * Return         : None.; Q3 \  _( j; F2 b* J
  31. 31 *******************************************************************************/
      N* p, R" N: n) R
  32. 32 void EP1_IN_Callback (void)
    8 \+ a" g8 ?" k( j% E5 x
  33. 33 {" i2 X# l0 ~) {- U) k5 W
  34. 34     uint16_t len = 0;
      ]# P  g& H! j! ~' p
  35. 35     ' y7 A+ r: o- A/ C8 i& \
  36. 36     if (1 == txFlg)
    ; M# g' d, D- K; t2 m
  37. 37     {
    3 @  C' q. g2 Q, K5 k  R; l  K3 V  ^+ p
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));" {. G- p6 l4 a4 B5 [1 t& S; R
  39. 39
    8 k- R- s0 Z' v" n9 {
  40. 40         if (len > 0)
    : a+ K% |) T, K- y8 X8 h+ k
  41. 41         {
    5 z: ^/ Y/ L: J% G" n
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
    5 x6 r7 e4 m8 P' G' ?8 o9 F
  43. 43             SetEPTxCount(ENDP1, len);
    % E1 @3 }3 K$ A6 Z' o
  44. 44             SetEPTxValid(ENDP1);
    7 q6 X- [' [$ G" X* q/ H
  45. 45             FrameCount = 0;
    * M1 \1 E4 P' t) c2 s+ C
  46. 46         }
    1 T, P8 y8 A+ ]! {% e8 u+ \
  47. 47         else
    7 x. K- Z5 a; O  O
  48. 48         {9 K  X' j/ B2 x5 E/ v4 m8 z- z& T2 y
  49. 49             txFlg = 0;
    & G: n, _' F% X" u6 Q, M3 \5 A
  50. 50         }$ E) p1 f  i. S3 R
  51. 51     }
    . X, i* ~/ L0 X6 D5 ^
  52. 52 }& E* u. r9 I7 p& w% z
  53. 53
    . C4 x% D7 V% H  a& h+ W; q
  54. 54 /*******************************************************************************% ~9 }( K" T- A0 y& O" o6 o
  55. 55 * Function Name  : EP3_OUT_Callback4 I$ C# X/ H6 S/ q0 C9 x
  56. 56 * Description    :$ B  }4 {6 {% \
  57. 57 * Input          : None.
    # }" r' O6 Z6 K) h
  58. 58 * Output         : None.9 t1 e0 `  n5 K1 m' T
  59. 59 * Return         : None.
    + l$ G7 t% `& R6 a
  60. 60 *******************************************************************************/5 x: h) ?0 i; b% s" \$ x
  61. 61 void EP3_OUT_Callback(void)
    : r# |- s! U& m& E
  62. 62 {
    & [, L, r% b8 ?& g
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    ) H; d+ d8 {& c$ B. P- r8 s
  64. 64
    ' r7 p, A( q4 r7 d
  65. 65   uint16_t USB_Rx_Cnt;$ J) n; v& u' T! a9 O9 V
  66. 66   ; J+ c# D2 O' G2 S1 S( c* Y; z
  67. 67   /* Get the received data buffer and update the counter */
    . [" p- M# V  Y: [0 t6 ?, @
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);
    ( \  r2 V; i% @( ~* J* ~$ B
  69. 69   * F2 K1 x' X5 T% S7 w7 [% k
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being * m0 G8 M- r; s( t
  71. 71   NAKed till the end of the USART Xfer */
    9 N8 {4 [' d5 e9 W$ h# Z
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);
    ) h8 c. |  O2 v' }; @& s  u) Q
  73. 73 - ?3 O5 w8 Y3 }; V
  74. 74   /* Enable the receive of data on EP3 */, G& u7 M2 v% @; Z0 f
  75. 75   SetEPRxValid(ENDP3);, H+ O" a; B  K1 O$ M
  76. 76
    ) M8 f8 l8 v5 f% r
  77. 77 }
    8 A6 N7 p5 I' F0 r
  78. 78
    0 }" j# u* a5 G: F
  79. 79
    : u$ ~: K3 l: L# G& p/ ]. F
  80. 80 /*******************************************************************************
    7 h. `) U: x- u$ `7 ]+ K3 X
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback
    9 _/ @% {/ R, A6 v$ l
  82. 82 * Description    :
    . [, @4 K& ^* D5 \
  83. 83 * Input          : None.
    4 `6 Q1 B6 X3 \
  84. 84 * Output         : None.9 n+ P$ d* Q) ~/ {5 o8 x" c
  85. 85 * Return         : None.2 x$ C6 ?' R. \; M1 y6 T: V. u9 @
  86. 86 *******************************************************************************/
    3 |+ V# k- [  c, b  W$ b5 \
  87. 87 void SOF_Callback(void)- }2 B& c) F. }  |
  88. 88 {9 j" t6 S& A0 D# Y$ e( |
  89. 89     uint16_t len = 0;3 J# ^% u! P$ k6 A' A+ E
  90. 90
    $ `  g$ O7 o3 N" q- w2 E/ p! Z" w7 b
  91. 91     if(bDeviceState == CONFIGURED)
    0 |3 ^+ {# g8 N0 s  l, Q
  92. 92     {
    9 D8 D7 Z! X" e7 ~
  93. 93         if (0 == txFlg)* `, R6 D+ z$ C" E
  94. 94         {
    ) N" {5 y# D) ~7 ?3 Q
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)3 f3 h# k3 T2 x" Z- e& q
  96. 96             {
    $ ~/ ?1 ?! E" L, E
  97. 97                 /* Reset the frame counter */9 `- x- y* F. `$ N
  98. 98                 FrameCount = 0;
    8 e" `. ?" s" \
  99. 99
    . N6 j0 ]: D0 E9 y& E8 ^2 i; @
  100. 100                 /* Check the data to be sent through IN pipe */4 z- A! S7 h& ~& Q, D
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));7 o- I6 a7 C8 `8 P2 ^
  102. 102 6 d/ b7 H' t! ~# _$ d+ M# q
  103. 103                 if (len > 0)% Q: W( w4 {; @6 W+ V
  104. 104                 {+ z6 I. Z. X, V+ D# ]' q) p- L
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
    8 j4 ?+ g6 \* e: H+ Z* O% k
  106. 106                     SetEPTxCount(ENDP1, len);
    " |% C. e& P& F9 a) ?
  107. 107                     SetEPTxValid(ENDP1);2 i; F" r5 I+ x3 K( K
  108. 108 5 ^1 U: |, K: V9 f
  109. 109                     txFlg = 1;
    , W+ L4 R5 O2 c7 ]' j/ Q. d
  110. 110                 }) x9 X  t! g8 F4 o$ x
  111. 111             }
    ! T9 n: m- |  J* M5 z) L
  112. 112         }
      @: K- c  K0 t6 R
  113. 113     }  * c* V5 ?/ m, K; {9 _9 p
  114. 114 }
    6 U8 g) h6 U7 A2 @9 `
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
( b! W+ }& w+ D3 O) S' }

" k1 b3 i+ f9 S: s  d9 @8 u: W
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。
) ?3 G7 j. B" Z1 R% w, F' N, j
收藏 评论0 发布时间:2022-1-7 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

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