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

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

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

; o+ V. @  r% J, m; s# z3 @- f; t& Z
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下:
Projects\Virtual_COM_Port文件夹下,复制红线部分
图1

! h$ z+ i! \8 w  g. `& ^+ n
300035032772082.jpg
) j* }3 k1 {" |- m2 g
图2
# ^" `$ w- S1 T
300035202932682.jpg
; ^' i, Q' \* b" Z; U) P
我为了方便演示统放在usb/src文件夹下:
图3

$ o0 R6 ~7 P5 w) U
300035313246246.jpg

( L4 ~! m3 J1 C. \, o9 L# [* w0 r
现在复制USB的库文件,这些文件不需要我们修改:
图4
$ T. O( o5 z' o" \+ |6 c, p8 m; C
300035404189967.jpg
6 |4 \0 f. K& K, e* }" i) C
上图中的文件统一放在usb/lib文件夹下:
图5
; a% {4 d8 N% }( b
300036032935279.jpg

9 N1 A9 Q# ~# f6 \/ \2 O1 {9 v
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:
图6
, E- J; d' m8 @
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 ------------------------------------------------------------------*/. Z  D9 v; N) K' ]3 p- M# A
  2. 2 #include "hw_config.h"
    # e2 T+ f2 z) ]9 }6 k
  3. 3 #include "usb_lib.h"& ]$ {9 b7 I; H* T2 H
  4. 4 #include "usb_istr.h"
    9 C$ w3 Q; j3 N- Z7 t
  5. 5 8 L3 v* Y8 r$ e, B0 E- G) d3 e
  6. 6
    , u6 _) N5 B& P3 f, C4 o' \$ H1 N
  7. 7 /*******************************************************************************2 _* M: @$ p) K9 D, R* o. T
  8. 8 * Function Name  : USB_IRQHandler+ V# J7 V! @" ~. U! b: x# X+ v
  9. 9 * Description    : This function handles USB Low Priority interrupts
    2 T. H& P8 J. U/ s2 U9 \2 n- e* y
  10. 10 *                  requests.; \7 z4 N5 S  Z( a* `5 \' |. ^
  11. 11 * Input          : None
    4 I  L6 X& p2 v& {2 |+ W) \
  12. 12 * Output         : None
    ( y- c( H- @! B3 v
  13. 13 * Return         : None
    : H  U! U( c! x* d; k/ n) h
  14. 14 *******************************************************************************/
    4 k6 E& z# g3 B/ e( z( `- r# r
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)$ H" i+ n6 U$ n5 ~
  16. 16 void USB_LP_IRQHandler(void), F; j# X( P1 ^8 i/ e+ h! l
  17. 17 #else4 i8 a6 w! B3 `' E& I
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void)
    ( L/ t3 b; F; e6 K1 [' |
  19. 19 #endif
    3 W' u% W) G; e8 i: n( y
  20. 20 {3 ~. l" M% U) D1 A& `
  21. 21   USB_Istr();' C, f; D/ K& I/ k. c' [" M- _
  22. 22 }; H- p9 W& L3 b+ {0 J0 X& i/ ^' C
  23. 23 6 w7 |7 s, H, D
  24. 24 /*******************************************************************************
    , {8 l; T' ?& Q0 e$ O
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler. t9 Z7 `1 Q# b3 [4 \
  26. 26 * Description    : This function handles USB WakeUp interrupt request.
    3 U8 w3 c3 F5 H4 G: y, T5 r$ p
  27. 27 * Input          : None! w2 ]/ s/ p  h# S: m8 V
  28. 28 * Output         : None
    * |9 A! E9 {! G  z4 {
  29. 29 * Return         : None$ E+ l& N9 `& L1 w
  30. 30 *******************************************************************************/; d8 \9 |' T5 E8 S1 F, f
  31. 31
      Q+ U" |& a8 x1 T
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
    , B8 y& m; m+ \! |; v$ z; E9 I. |
  33. 33 void USB_FS_WKUP_IRQHandler(void)
    ) x# y0 }$ F1 A. H4 r3 ^" C
  34. 34 #else
    3 p, Z0 h( [+ \. A! |- q& q2 r
  35. 35 void USBWakeUp_IRQHandler(void)
    + i8 _2 @" @  `# c5 z& J
  36. 36 #endif$ W$ [0 t0 V0 z% b& i1 [" N
  37. 37 {
    * M& P4 }! ~4 f! p: E8 ?
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);9 `  ]# ~5 O1 ~2 V4 {6 _# ~% `% x$ N/ c3 m
  39. 39 }
复制代码

1 W. {! ^  o4 K6 ~- _3 ?1 t) r
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

& k% ?8 p0 [- z" O9 [: t1 V7 Q; n
300038465597900.jpg

, C2 @; @) w( T, d4 g: ?
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************) j* x$ |( v; T1 a
  2. 2 * Function Name  : USB_Cable_Config
    6 t. a5 x9 ]  z) A7 o# O2 ?
  3. 3 * Description    : Software Connection/Disconnection of USB Cable# h# |" e5 \% [$ h
  4. 4 * Input          : None.9 J7 l) E  [- K* B( ?0 C
  5. 5 * Return         : Status
    + a. c3 C# z3 t0 @
  6. 6 *******************************************************************************/
    ) i& T5 Z4 m5 y
  7. 7 void USB_Cable_Config (FunctionalState NewState)
    ' \) `3 |. C4 O( S
  8. 8 {
    ( Q4 k) [4 }9 v. Y! Y5 D( l! b
  9. 9   if (NewState == DISABLE)
    : s9 `. s/ A9 h. M$ n/ h9 |
  10. 10   {
    ; o6 I3 b- i* p! I
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    + w( e5 Y; q2 P3 n; p4 _
  12. 12   }' c: C1 S- N/ O0 E  W
  13. 13   else
    + A1 s8 }+ c3 \2 p
  14. 14   {
    ( B  I& {( p3 P7 X, ], X& ]' I
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);" k( u5 w, p8 O: l
  16. 16   }& a4 G; B1 B6 }0 c3 F5 r2 b: \% A
  17. 17 }
复制代码

, }2 {3 J- r' b- w7 k
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/  E2 h0 k+ L0 Z; C4 e# u
  2.   2 #include "usb_lib.h"/ [" |$ f7 h# ^3 ^& F
  3.   3 #include "usb_desc.h"* g1 _% h% q! T
  4.   4 #include "usb_mem.h"
    - `, ^* \# D" A
  5.   5 #include "hw_config.h"
    - e& ]3 s, z& ~' ]; g
  6.   6 #include "usb_istr.h"9 v' m2 R' F( K7 d: |" x1 O
  7.   7 #include "usb_pwr.h"
    - i0 C* w# B6 Z) O
  8.   8 - |1 W( Z! A* f2 |. ]. N1 C) D+ M
  9.   9 /* Private typedef -----------------------------------------------------------*/0 i/ h4 v- }0 I5 [% L5 t$ d
  10. 10 /* Private define ------------------------------------------------------------*/
    . Z# V. x, B8 B) M0 B+ C
  11. 11 1 }  P) [/ J6 X1 l. |& M
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */8 r  t% B4 d. z7 |+ J& E2 \
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             5
    # e! h: Z5 J( s4 U+ T. |1 i3 N
  14. 14
    : a6 W1 p3 ?; J$ B/ [0 ~$ O% B
  15. 15 /* Private macro -------------------------------------------------------------*/: n; H% U- r9 u  ~5 x) E
  16. 16 /* Private variables ---------------------------------------------------------*/1 ^+ W" |4 {' p$ x1 p2 `
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    5 I- U/ _$ S: M
  18. 18 static volatile uint8_t txFlg = 0;
    $ K  n& f! ?5 d' N4 R5 T  ^, o; w
  19. 19 static volatile uint32_t FrameCount = 0;7 j( s2 J, s! `, ^6 R# A1 Y$ V8 E
  20. 20 / x8 f) B6 l. A( u) z) |  L
  21. 21 9 ~* j- s, K1 I1 T" e) ^5 q8 N
  22. 22 /* Private function prototypes -----------------------------------------------*/
    ' K' ]* l4 ^* @: l
  23. 23 /* Private functions ---------------------------------------------------------*/
    & ?2 T) Z4 G1 K3 p+ h+ q3 T
  24. 24
    # c" ~/ N! r5 R, _2 t8 E2 s# _# w
  25. 25 /*******************************************************************************
    & u6 ]& ]0 B: B' }7 o7 z. K
  26. 26 * Function Name  : EP1_IN_Callback4 X# L- m4 ]" e8 L5 `
  27. 27 * Description    :9 N+ t7 Q( q, q' j9 A" R$ {" b
  28. 28 * Input          : None.
    3 i/ R/ A* {( a8 ~7 @
  29. 29 * Output         : None.
      Q1 Z; }# Q1 z7 _
  30. 30 * Return         : None.
      Y( o7 K1 }9 }+ \/ P5 q) n
  31. 31 *******************************************************************************/- e. {* B' r3 p8 T' c! @4 [1 t
  32. 32 void EP1_IN_Callback (void)6 u: ~$ R! S# l. Z3 E( {) d
  33. 33 {' g1 y5 f3 `) V2 ]' b* H
  34. 34     uint16_t len = 0;3 a  R" {4 f7 |7 G
  35. 35     
    2 c" H2 U& e6 H  Y* k7 R
  36. 36     if (1 == txFlg)% b+ l( v2 G  V# l9 U3 n. Q; z' z
  37. 37     {" |; }1 N: l/ j& P1 n( {  v3 b! g' i
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));
    " i5 y) }% n  ?3 d  E+ J
  39. 39
    ; D0 g% P* K" C6 u/ a' e& ]9 l
  40. 40         if (len > 0)
    ! M$ c& C% g8 E9 j, ]) r. g: F* [
  41. 41         {# k+ Q( h& W! q4 Z- W5 X- B
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
    , T' h, H/ N4 t1 V% H
  43. 43             SetEPTxCount(ENDP1, len);: a5 D8 W+ l9 b# [7 p1 P
  44. 44             SetEPTxValid(ENDP1); / I# e. O, C) `, H( b! j9 G, d/ w/ S
  45. 45             FrameCount = 0;; h) X6 i7 y5 Y( S
  46. 46         }# G' H( }, S0 ?9 m. |& R
  47. 47         else2 z; V1 |3 m) A1 r, |) u
  48. 48         {( Q+ l* Z3 {+ J( U- _8 v; j
  49. 49             txFlg = 0;
    5 H, T: J6 h& L
  50. 50         }
    6 R( s* S* y8 g/ C+ s" E
  51. 51     }2 R1 p1 Q1 g/ M) R0 t& q
  52. 52 }
    2 F2 r5 y8 i  }0 ]* c2 ~* L
  53. 53 . O: ^5 q) e+ L( Z
  54. 54 /*******************************************************************************1 p( o& }! o0 p
  55. 55 * Function Name  : EP3_OUT_Callback
    7 }, U" i( Y4 b) G3 W
  56. 56 * Description    :, ~) E& m- a6 s4 g: v
  57. 57 * Input          : None.5 o0 O: ~* I8 H, ~2 W
  58. 58 * Output         : None.3 W" o  h+ B* x3 R* a) ^% ^
  59. 59 * Return         : None.
    - A0 v6 s5 b# l# P
  60. 60 *******************************************************************************/+ _* n8 O" ?6 r# @) b. p
  61. 61 void EP3_OUT_Callback(void)! J7 f) {0 y+ V8 I& B
  62. 62 {
    ( H( a( m- j3 `  W: J1 a% Z' `
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};8 o, N& u' _, B# @+ ~0 R; a" ^) ]
  64. 64 ( Z9 ^, F7 n: Q/ P& z: J  U* ~
  65. 65   uint16_t USB_Rx_Cnt;
    / n* v+ g1 q% C8 b
  66. 66   
    % z* I# v9 Y1 j
  67. 67   /* Get the received data buffer and update the counter */
    9 h' J+ M; w6 r9 D8 }
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);" t$ j, e! e0 H
  69. 69   
    6 ~, t) Q0 F) ]' ~; ?( H
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being   L$ Q: v8 S2 G1 x! v5 l) p0 i
  71. 71   NAKed till the end of the USART Xfer */, {' \$ J5 B- U) R
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);
    8 p. `, f0 I# [2 J2 q7 j  O3 H
  73. 73
    ) ]+ d* q8 R/ ^; X* V/ c6 V) F4 E
  74. 74   /* Enable the receive of data on EP3 */2 j' V) ]+ \/ ^: d& N/ {# u
  75. 75   SetEPRxValid(ENDP3);
    " {1 W! b1 H& ^, [: n/ q# G
  76. 76
    % x( v, y2 {5 c7 u2 I
  77. 77 }% [) K6 J, e/ `% j
  78. 78 . c# I9 `# F' v' l5 ]: q
  79. 79 : X5 ]- B  c7 \: l7 P- O
  80. 80 /*******************************************************************************2 m+ L' Z; [$ r: w: t+ b9 z
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback
    ! V! a( ?4 M7 X- t5 E) a% a
  82. 82 * Description    :
    3 s8 R8 z& M4 w5 a
  83. 83 * Input          : None.) W0 y% c) C) M
  84. 84 * Output         : None.. n# z* }* x# p1 _
  85. 85 * Return         : None.
    , f' c6 k) _+ s3 m* I" g, t* t* x
  86. 86 *******************************************************************************/4 C3 d5 \0 {, U6 ?
  87. 87 void SOF_Callback(void)
    2 d" o3 W! T9 q$ i/ y; K6 i9 d
  88. 88 {' x+ m- Z- A' a) _/ F
  89. 89     uint16_t len = 0;! i. ?: t% Y9 W) A7 X
  90. 90 1 q- L( w. I+ u) ^. ~$ F/ U
  91. 91     if(bDeviceState == CONFIGURED)
    ! i+ g, L* n; }! j0 @
  92. 92     {
    ; y, ^  F( m* o1 M& T) R
  93. 93         if (0 == txFlg)
    : K) x: f. `- `0 x/ I
  94. 94         {
    $ r& A' f& W+ f/ J# G
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
    / |9 P, F' o: C1 Z7 z" m6 {# N1 W
  96. 96             {, R' x/ ~) G' ]
  97. 97                 /* Reset the frame counter */$ }, a2 ?+ m5 p6 V4 N, H) P
  98. 98                 FrameCount = 0;
    ( d) [3 s& F, i! n4 {
  99. 99
    , a0 e$ k8 a0 x+ r: l' b- o
  100. 100                 /* Check the data to be sent through IN pipe */
    - R8 C4 M. X0 b2 ^3 \$ N: d; V, A
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));
    + s/ n, P" @2 D- N& w# l; A+ L
  102. 102
      n$ U, x5 e) }8 @0 K1 O
  103. 103                 if (len > 0)0 j% N; g3 x: ?5 l* a
  104. 104                 {- L7 Z, _) K& g  M
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);! T, y1 D: y% c
  106. 106                     SetEPTxCount(ENDP1, len);: P9 I, P/ s, g6 ?2 t7 C
  107. 107                     SetEPTxValid(ENDP1);
    4 O# i( ?1 h+ C1 N' l6 ?; s
  108. 108 / ?. I) f3 M5 Y, c% q
  109. 109                     txFlg = 1;3 K2 F8 ]. c6 E& c; h
  110. 110                 }0 B7 r& M7 K2 r8 t
  111. 111             }
    / Y3 P2 H/ I4 @3 b+ \7 N  P$ h
  112. 112         }& g. d1 p& _0 G. Z" u1 n
  113. 113     }  # o5 |  F+ {* ?4 w+ @
  114. 114 }
      r  x( q$ K3 L. Y# I5 Y. i
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
4 p* H) k" A- m
$ {& X4 I! o) R4 H8 J
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。
+ P% q" o5 i: _$ w$ a9 p4 q
赞 收藏 评论0 发布时间:2022-1-7 19:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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