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

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

[复制链接]
STMCU小助手 发布时间:2022-1-7 19:00
串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。
* H4 O- ?7 p- b* ?# c3 H7 ~9 x
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下:
Projects\Virtual_COM_Port文件夹下,复制红线部分
图1
+ Q# A2 A( e" u* B8 F) q5 \- K
300035032772082.jpg
! v' `. l, ?" e( s$ a, r1 F% x8 d
图2

: t# Y; l! q# T: O' y( v
300035202932682.jpg

  l+ m6 |4 N* x
我为了方便演示统放在usb/src文件夹下:
图3

3 i- {' G5 X# V. @9 ?& H) u
300035313246246.jpg

6 }  M9 ~- L7 x. C4 e
现在复制USB的库文件,这些文件不需要我们修改:
图4
! [! N$ ]7 k4 G# r2 h3 i0 q
300035404189967.jpg
7 a+ a5 N8 {0 i% y: h6 m( f
上图中的文件统一放在usb/lib文件夹下:
图5
' b, B9 }2 y. |
300036032935279.jpg
' @2 h. g& A/ n; r" E* b, V' @
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:
图6
* m, r$ A& [  H0 `
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 ------------------------------------------------------------------*/
    . C8 ^! n  B2 U0 F* H
  2. 2 #include "hw_config.h"6 p- H) D) U/ T- J0 _4 {
  3. 3 #include "usb_lib.h"
    ( w3 C+ v! h4 Z! K7 B! D
  4. 4 #include "usb_istr.h"
    # G8 z4 I$ }. l4 i
  5. 5
    ) i/ Q$ g- X( C/ {/ N0 ?
  6. 6 ; Z6 s. X! C0 E, r4 U% a$ L+ m
  7. 7 /*******************************************************************************
    8 y9 I  `; Z/ p- j7 d
  8. 8 * Function Name  : USB_IRQHandler
    3 Q8 I3 |* z9 [& W
  9. 9 * Description    : This function handles USB Low Priority interrupts
    + R3 m* A. \; c. \
  10. 10 *                  requests." N4 `  y& e3 }$ c. H6 f: A
  11. 11 * Input          : None
    : {8 q+ N" U- ?5 {
  12. 12 * Output         : None
    8 J) `& V( h$ b* W  ]
  13. 13 * Return         : None
    : @( [/ m- y; ^, m' i
  14. 14 *******************************************************************************/2 O1 J; w! k; I
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)) ^" X" n' T: W
  16. 16 void USB_LP_IRQHandler(void)7 }: V7 P( p5 `5 @9 e7 p
  17. 17 #else1 ^. f  [/ y8 W7 S! B
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void); ~/ P8 }2 G8 H- l- b+ g
  19. 19 #endif
    ( A5 ?( r# @5 h: F8 j$ Z0 I9 T
  20. 20 {  ~( u1 G: j/ e$ W" V; J$ [
  21. 21   USB_Istr();5 g2 I7 R  e( N% E& M& c
  22. 22 }% J- n; s* ~+ N- i% N/ v
  23. 23
    . N4 T# j9 O  J* Y
  24. 24 /*******************************************************************************
    $ @% n6 }8 H6 p
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler/ v% ~  U; E8 G9 ]! W) ^
  26. 26 * Description    : This function handles USB WakeUp interrupt request.
    1 X, W* k8 n' }8 E( M8 M
  27. 27 * Input          : None* N% Z2 b  Y, i
  28. 28 * Output         : None- {7 P% P4 g* Q  h2 z, b
  29. 29 * Return         : None% U3 Q4 B0 J3 ?
  30. 30 *******************************************************************************/1 i1 K* I: ]$ c
  31. 31 ; f- B* y1 Y: E5 N5 L
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
    3 e6 z2 ^( X3 ~3 Q
  33. 33 void USB_FS_WKUP_IRQHandler(void); {* ?9 y/ C( i
  34. 34 #else
    & m; v. x* k. b2 D( p. u
  35. 35 void USBWakeUp_IRQHandler(void). O0 y: q. R7 k: b' {! l2 R5 z
  36. 36 #endif
    5 x0 @9 d3 C( }! N+ L
  37. 37 {
    3 y  p8 A6 U, r; A( S2 ]
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);, }9 d# `- k2 O: d6 E
  39. 39 }
复制代码
# y2 \2 C( G1 k2 n: I: C
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

2 h0 J" R) n+ V4 J1 r9 r$ M' G$ o
300038465597900.jpg
3 ?3 H' b2 y- \6 |  ?  _
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************
    ) P* ^6 E& \- D+ k6 w& i
  2. 2 * Function Name  : USB_Cable_Config
      ^* z- v6 G) k( c8 O  c+ u
  3. 3 * Description    : Software Connection/Disconnection of USB Cable
    * @4 w% P* |9 G. h. e
  4. 4 * Input          : None.
    5 Z( q( f- a) j: F# R4 E4 a
  5. 5 * Return         : Status& ?. h. N. X3 L" j) M2 r( Q
  6. 6 *******************************************************************************/* Y# X0 U  g' w/ C3 A
  7. 7 void USB_Cable_Config (FunctionalState NewState)
    4 `: L0 L5 Q! T1 m' G! n8 Z
  8. 8 {
    # C+ h  c2 h5 e
  9. 9   if (NewState == DISABLE)6 j  p) @" f) A& h
  10. 10   {: h( L) i1 R/ b1 P9 D
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    7 Z7 ~6 k5 O+ q
  12. 12   }& J. u5 V: w5 K! p- d( ~
  13. 13   else1 a" l$ ?& Q/ K* d2 U  q! D4 N/ X! d
  14. 14   {
    ' O: q8 J9 |  I5 g* j" f, m" P
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);& J  H0 z% _/ J. E/ J. {9 u
  16. 16   }
    + }2 o% \( A9 }3 |: F
  17. 17 }
复制代码

  v8 ~$ n8 ?. j9 O5 \5 w1 O
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/
    & S6 K% {, I6 J! G4 E
  2.   2 #include "usb_lib.h"
    0 N; M8 K! l0 Y/ l, i8 a
  3.   3 #include "usb_desc.h"3 c) |  S( A5 Y" V% F- G
  4.   4 #include "usb_mem.h"1 b: K0 q/ p: L$ e; M) l( ]- M
  5.   5 #include "hw_config.h"# K4 z9 [3 Y/ s- K
  6.   6 #include "usb_istr.h"* d8 b+ R* K( ?4 }: f9 T
  7.   7 #include "usb_pwr.h"& w3 f/ D7 F7 G4 P9 j  D
  8.   8
    1 ]0 B5 w5 l" R4 Y% P, x/ v9 W4 X
  9.   9 /* Private typedef -----------------------------------------------------------*/
    ( l; D8 h  Z" {* ?( ^- f. q
  10. 10 /* Private define ------------------------------------------------------------*/
    # d+ \! [2 i0 s, x# w: |
  11. 11 ; e# K9 S4 ~$ [' m8 @. o! g' k
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */) a0 d$ ~9 e7 x. g
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             56 T7 j: w' t2 y1 Q$ `+ I5 T9 v
  14. 14
    " a) V6 X2 Z) g7 t8 V& h; F1 q+ b
  15. 15 /* Private macro -------------------------------------------------------------*/% D' ]6 w9 l, U9 O0 r
  16. 16 /* Private variables ---------------------------------------------------------*/
    . V+ U$ e9 t! t- Z2 N; J
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    2 W9 n! w6 ]. D5 A' E' r
  18. 18 static volatile uint8_t txFlg = 0;; f( ?' ?' n* g
  19. 19 static volatile uint32_t FrameCount = 0;
    % b' D; X# A1 h% U7 N* u
  20. 20
    & ^! S1 R6 b1 _  j2 c7 M7 y2 {. A
  21. 21 9 f' u. v- S$ M7 X8 U; E# X
  22. 22 /* Private function prototypes -----------------------------------------------*/9 h6 d( D" I9 w( V- V: d
  23. 23 /* Private functions ---------------------------------------------------------*/
    . m0 p! e; j4 Y* G: \
  24. 24 # B, [8 \" @* ?, ]% b/ V
  25. 25 /*******************************************************************************' j* W9 E3 Y4 j1 R
  26. 26 * Function Name  : EP1_IN_Callback
    9 Y) [/ s3 j  M; W
  27. 27 * Description    :
    % S+ E4 u7 T, E' i( q
  28. 28 * Input          : None.
    8 o1 j9 h6 l$ }1 y
  29. 29 * Output         : None.8 N7 ^$ s1 u9 u, i) e
  30. 30 * Return         : None.5 E% ?4 L, B- o; `! e
  31. 31 *******************************************************************************/
    % B/ H, T3 c" h; U' n! e  a/ _
  32. 32 void EP1_IN_Callback (void)
    ' L8 J6 |5 T0 F# Y
  33. 33 {! ?/ l) q1 z  G( }  \) q! Y1 p
  34. 34     uint16_t len = 0;1 k' m$ H* v" T/ R6 W  `( ], c
  35. 35     
    / H) [* ]# ?$ ~! I8 ]# G5 T9 Y2 x
  36. 36     if (1 == txFlg)% Q0 U  B) O  Z& x7 O! g
  37. 37     {$ O" M' S6 R9 j1 c# g& G
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));
    2 o2 D2 u9 O% u* B. j6 H! h) ^
  39. 39
    ' _- A8 A* D$ k  o4 h& L+ w
  40. 40         if (len > 0)
    9 F+ u' `5 I( Y; |, X
  41. 41         {
    ' k9 O$ }8 q4 ~! {
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
    8 k8 [7 E4 Z5 L( v2 y+ l$ O
  43. 43             SetEPTxCount(ENDP1, len);
    - s5 `+ o! I! i& S
  44. 44             SetEPTxValid(ENDP1);
    . V5 Y  J/ l& q7 ?9 l' K1 U& t
  45. 45             FrameCount = 0;
    8 x: m* v9 y% |' g
  46. 46         }
    2 Y9 Z. }+ D4 q* i3 `, Z! Z9 R
  47. 47         else- l: r" \* O6 O
  48. 48         {0 W8 |  q+ |0 s( x! j5 t
  49. 49             txFlg = 0;  ^, X% k) `! N6 x# j) t: A
  50. 50         }0 d" x5 T8 v. Z" g5 l
  51. 51     }- \9 K0 b" r/ l+ Q1 N* {1 I
  52. 52 }
    9 U  Q7 W' r+ ]+ R: m3 C
  53. 53
    " N4 l; l6 N: \' E  x: D$ ]
  54. 54 /*******************************************************************************% |6 c& J: A; {
  55. 55 * Function Name  : EP3_OUT_Callback7 Y2 O: ~* E7 s, z8 u
  56. 56 * Description    :# ?5 w! d; Y. X% h5 X3 T' J
  57. 57 * Input          : None.
    ( P  l* X5 E- Z! Z3 b0 ^
  58. 58 * Output         : None.
    0 B* ]3 f+ h1 V, Z6 N" h
  59. 59 * Return         : None.
    2 ^* |2 W/ J$ N* ]( ^
  60. 60 *******************************************************************************/$ c5 G6 _% S* h* ~" u$ M) p( _8 a( Z
  61. 61 void EP3_OUT_Callback(void); Q3 T$ s' Z! L
  62. 62 {
    , C+ c, L, @0 C4 z8 ^$ k3 `. L6 o* E
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    0 `0 A! F) l+ \$ y2 }+ w: O
  64. 64
    / e; u5 A" b0 M3 ^/ ^/ f
  65. 65   uint16_t USB_Rx_Cnt;
    ! T, h) J1 q" r: g1 d
  66. 66   
    5 h5 R3 }, g( Q
  67. 67   /* Get the received data buffer and update the counter */$ h* \( i& M; Z2 K; R
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);4 }, }3 m2 `" U$ _1 H
  69. 69   7 H5 f  d3 f: h; r1 ]6 W
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being
    & J/ h6 p0 v2 H+ G9 R
  71. 71   NAKed till the end of the USART Xfer */
    + S' y8 H  x% P! K
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);# y' [0 {; W' S' y1 R
  73. 73
    ! m# U; ^' n- u) |2 k1 ?1 q! p/ g
  74. 74   /* Enable the receive of data on EP3 */
    - A! W2 j! p+ M2 z$ B2 G
  75. 75   SetEPRxValid(ENDP3);
    4 W& ~/ ~( [; L  d
  76. 76
    1 k: S6 F9 J; G! R$ g. B  L7 q
  77. 77 }
    5 U; ^# u8 u2 w, e7 N# Q
  78. 78
    2 v$ A' S3 u' Q( T
  79. 79
    # U6 H" X7 a$ N4 I/ ~* r% [
  80. 80 /*******************************************************************************
      {  P9 f) j. c4 F# U2 M$ K
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback0 F7 V, u  O$ h, _7 X3 p) U$ _
  82. 82 * Description    :4 ^  K3 B$ I$ h% f
  83. 83 * Input          : None.
    - B# D; g) j4 q1 x
  84. 84 * Output         : None.# y3 ]- [0 y5 _) m+ v' K, l
  85. 85 * Return         : None.
    2 J1 F! E* L' q% _
  86. 86 *******************************************************************************/+ a- g7 l) f: x
  87. 87 void SOF_Callback(void)' O( r. D- o2 V) Y$ k
  88. 88 {
    : B  }# k: u+ ^! E) G! U# m2 _" Y; G
  89. 89     uint16_t len = 0;
    1 D2 z7 Q. h  e$ g, S  @- `. `1 Y% X
  90. 90 6 D! @8 `' h" W  m
  91. 91     if(bDeviceState == CONFIGURED)
    ( e) @& |: y1 b7 l; h) c% T
  92. 92     {' L- B4 x* C* A
  93. 93         if (0 == txFlg)4 X- o) J# F+ F
  94. 94         {
    " C7 i% |4 L, h) m2 F
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
    ; W' ]: H; q1 b7 R( E0 f7 k9 d
  96. 96             {
    # {) d, `6 ]# I$ \3 @# @  K  }, v
  97. 97                 /* Reset the frame counter */6 L: d" G2 \3 p
  98. 98                 FrameCount = 0;  c: J6 t6 Z8 @, U. b! Z
  99. 99 # K) t9 o6 O3 M6 d
  100. 100                 /* Check the data to be sent through IN pipe */
    4 Y' o! g; f! t: k; F6 L# E
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));3 M0 U. @. [# I3 R
  102. 102 : E  w& w# k& f, g, R
  103. 103                 if (len > 0)
    " g, H! M0 y3 g9 v) ^7 ~
  104. 104                 {" G' Z9 M! X7 t+ d/ b
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);+ z4 V# T( `" B" E7 Y& o
  106. 106                     SetEPTxCount(ENDP1, len);
    $ g2 P2 Q4 o. q* L/ a  g/ J  p2 U
  107. 107                     SetEPTxValid(ENDP1);0 T  f- d! B+ o0 b- ^6 Y/ k, P' v# {
  108. 108 % |7 Q6 v  }/ D' `" w
  109. 109                     txFlg = 1;
    ; G, J" ~/ {2 j/ D
  110. 110                 }3 M- j# I1 s4 U
  111. 111             }
    3 R. X1 x7 Z' J4 Y* u: b) i
  112. 112         }+ L' A) m& s0 e2 J
  113. 113     }  
    # f' P/ J) K6 I
  114. 114 }
    # S  b  ]0 }" T0 B/ t7 K
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码

. l4 a" E' ?) V% S1 R: u" V: P  J9 ]8 U
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。

  \* t  F0 ]+ l$ @
收藏 评论0 发布时间:2022-1-7 19:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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