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

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

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

- b5 p  W4 z, u3 A
图2
! b1 L, j. t. P3 }* m1 k8 v
300035202932682.jpg

8 {/ X) z4 v- Q/ X2 M
我为了方便演示统放在usb/src文件夹下:
图3

) L" V% K; d) n* A3 J: \7 r
300035313246246.jpg
$ j9 N/ e1 k1 a) p. j/ W
现在复制USB的库文件,这些文件不需要我们修改:
图4

% ^- z" a: Q9 o+ ]1 p$ X/ n% }3 o
300035404189967.jpg
$ T" i7 `: `- m3 |) K) @8 z: V, G
上图中的文件统一放在usb/lib文件夹下:
图5

6 }1 r+ V; ]8 i3 G( k: \
300036032935279.jpg
% s; X6 x! K; ]
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:
图6
. V. P. e/ j! t
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 ------------------------------------------------------------------*/
    . W; _- p8 P' u6 x9 F' `5 s
  2. 2 #include "hw_config.h"$ U1 L9 T4 T! H9 A! m* J  r  Z
  3. 3 #include "usb_lib.h"( T* D3 W. ~- x
  4. 4 #include "usb_istr.h"
    3 k2 X, p) c# H
  5. 5
    ( T! r* G! J3 p+ |
  6. 6
    : Z5 Y1 ?, z- ^: g; r3 C: k
  7. 7 /*******************************************************************************
    ( S  s8 c/ Q5 }$ Z
  8. 8 * Function Name  : USB_IRQHandler
    $ z9 E  _$ D$ `0 D3 F
  9. 9 * Description    : This function handles USB Low Priority interrupts' @8 m( b" i9 K/ i1 z$ l+ i/ A. m  i
  10. 10 *                  requests.$ ~, `3 I, ?) b% c1 Y3 t8 m* e
  11. 11 * Input          : None
    5 S- _7 U4 U0 n
  12. 12 * Output         : None/ ^+ M6 z" e( c$ ^8 Z
  13. 13 * Return         : None
    5 q' ]) h: v" }& Q; e
  14. 14 *******************************************************************************/
    0 a5 R9 N" z+ I3 z8 a
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X). A+ f, g% x  A# X2 y
  16. 16 void USB_LP_IRQHandler(void)' v7 p5 i2 y( Q- I
  17. 17 #else
      E5 w# }+ ^  ^# {: ]
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void)
    ' l4 ?& ]& o, b( \: q, s
  19. 19 #endif7 t0 M- l2 {8 R3 S
  20. 20 {
    # l6 q* g% H2 F/ v+ X
  21. 21   USB_Istr();9 K( m; V& r1 X- ^2 s9 _
  22. 22 }% f4 c1 B3 v3 `6 {& J! x4 n7 u. o
  23. 23 ! v0 `# l' P' P6 d; [, {
  24. 24 /*******************************************************************************6 X; i3 W( N: [" J, Q4 O1 i
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler7 {9 H% f& j+ l' R8 }
  26. 26 * Description    : This function handles USB WakeUp interrupt request.. a& `8 e, z. m# q% x
  27. 27 * Input          : None* R2 \# V9 u. r/ W$ x  u
  28. 28 * Output         : None
    0 H* j3 R6 E2 e- d1 h1 [7 h
  29. 29 * Return         : None
    ) z8 r( e2 H8 B8 C- i
  30. 30 *******************************************************************************/7 @5 v# s+ V* d' I. U5 ~' W8 Q
  31. 31 5 j' `8 i! b9 ?0 R. e& M' M' i
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
    " W) u, n  i, m/ [* j9 Z, y
  33. 33 void USB_FS_WKUP_IRQHandler(void)
    9 O) ?* c$ A! s; g6 T; k/ `
  34. 34 #else0 |1 o+ K  G6 C8 s0 d$ e6 `) d4 y2 ]
  35. 35 void USBWakeUp_IRQHandler(void)% T' Y4 J: I: y2 [
  36. 36 #endif
    : P7 t: C$ C5 T3 }$ j5 v5 e
  37. 37 {
    6 t( d; k( `8 p- f$ e
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);8 _2 A; F! z3 U
  39. 39 }
复制代码
3 t; t' J* v3 l& S0 z& U5 v! @  I
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
' e7 J- Z# m2 p4 z% N
300038465597900.jpg
) @. H; S) @5 i6 ^: D
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************" g! A# G- V! R5 Y- H1 {, W) l
  2. 2 * Function Name  : USB_Cable_Config
    & A! r6 Y& I( J
  3. 3 * Description    : Software Connection/Disconnection of USB Cable
    - s( b. S" t( t3 ^& m
  4. 4 * Input          : None.
    8 K. i2 q' q! T5 [+ q0 `1 ^
  5. 5 * Return         : Status
    & N, c& ^% K2 k6 k5 l* w" J$ V
  6. 6 *******************************************************************************/
    & m7 s7 i% I* j9 B! f
  7. 7 void USB_Cable_Config (FunctionalState NewState)
    4 q. L6 i1 m. f* z% L/ L9 \
  8. 8 {# Q+ H$ m  Z% w" x9 J4 Y
  9. 9   if (NewState == DISABLE)
    . p2 z$ L4 X  K* q! E
  10. 10   {' ?( _' e9 g9 ^7 S8 ?5 z
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);' x& S9 m3 X- _& f& o
  12. 12   }
    , W2 D; F8 ^7 t: s% ]
  13. 13   else5 R9 _. S$ `/ ~% i" V. t
  14. 14   {# ^2 B7 V0 a) W+ N" w! H2 Q
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);! o9 C7 `  ^+ a5 I3 k9 W& f7 X4 P
  16. 16   }+ a# q' \$ J' i1 Z
  17. 17 }
复制代码
2 O6 D* i4 J' u
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/
    $ L! G2 D; [1 [' f& n9 u" Z0 e) ]
  2.   2 #include "usb_lib.h"% I  N  A$ [* D
  3.   3 #include "usb_desc.h", e7 a9 W7 Q2 ~+ O8 g+ \' `
  4.   4 #include "usb_mem.h"% x5 w! x9 T$ ^' g3 Y
  5.   5 #include "hw_config.h"
      V3 j, w7 {6 i+ U0 Q5 ?" d
  6.   6 #include "usb_istr.h"9 d3 Z7 r$ [! H/ [& ?+ a
  7.   7 #include "usb_pwr.h"
    1 f! {; [7 J1 l# c
  8.   8 8 q9 n0 }% J+ v- m, H) R/ C# C+ j
  9.   9 /* Private typedef -----------------------------------------------------------*/
    9 |) O9 e' C2 }* `+ {: z  }& S, A
  10. 10 /* Private define ------------------------------------------------------------*/) V: W  k7 b7 D. c. |2 U2 T
  11. 11
    / `: S; n( N3 n8 v
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
      G9 p( ~: R9 A
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             5
    5 _0 H8 {% s$ t  q) K! s- J& T& D
  14. 14
    5 h& f. c3 {( O
  15. 15 /* Private macro -------------------------------------------------------------*/
    ) L# v3 h0 g8 V- R1 L* Y" g* K* G  v
  16. 16 /* Private variables ---------------------------------------------------------*/
    & k9 R" q1 J2 n7 _
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    ! Y' f# X1 m- y3 M5 V
  18. 18 static volatile uint8_t txFlg = 0;
    1 @& Y1 V9 t+ q
  19. 19 static volatile uint32_t FrameCount = 0;! ?6 F9 x/ L5 e1 [. Q" l
  20. 20
    + F% U) W/ Q+ q2 {4 Y
  21. 21
    1 \, R' G- F+ g5 m, J* L
  22. 22 /* Private function prototypes -----------------------------------------------*/# R) p+ T4 q+ }
  23. 23 /* Private functions ---------------------------------------------------------*/: j: ?9 G9 H2 _+ U
  24. 24 2 u6 ^4 I% L! j! K) Y3 C5 z
  25. 25 /*******************************************************************************/ b; \6 d) C. a
  26. 26 * Function Name  : EP1_IN_Callback
    / v) F3 V6 G2 }8 K/ o+ L
  27. 27 * Description    :
    , y0 L1 G6 Z; @1 S% ~' K
  28. 28 * Input          : None.( n6 ^  k) p! _! a& }6 k  D& a9 \
  29. 29 * Output         : None.
    ) b8 y; p- T' X
  30. 30 * Return         : None./ }0 _/ ]! E. u- h: @
  31. 31 *******************************************************************************/
    4 F8 v% f8 }% N" p/ C9 b* Q3 V
  32. 32 void EP1_IN_Callback (void)& Z& e$ b  K% o! O, `
  33. 33 {
    8 I' |5 a. h% }" D% I
  34. 34     uint16_t len = 0;, ?$ u& c, W' }# E- F/ Y
  35. 35       O+ j* b0 s! f
  36. 36     if (1 == txFlg)# V$ [( w  E* B
  37. 37     {+ I( [8 q& T1 _
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));
    ( }+ t9 x# N' B- F) G/ ?
  39. 39
    " O1 K5 G5 O1 U
  40. 40         if (len > 0)
    - F, ^+ l# H5 g/ H, M0 ]
  41. 41         {9 x+ O( H! ~" j. I7 G
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
      a7 l7 E5 o# \% a0 f/ z
  43. 43             SetEPTxCount(ENDP1, len);
    1 e& |  R  I+ H' ^0 b$ j+ I5 r
  44. 44             SetEPTxValid(ENDP1); : ^- g+ ]+ I4 U) ]! K
  45. 45             FrameCount = 0;3 n: J! k* u0 [# Q
  46. 46         }$ o9 \- m9 D. f7 t* Y# b+ t
  47. 47         else- x1 f0 r6 h9 @. P0 D. E$ }, ]+ @
  48. 48         {
    5 F3 H. \. _! U! g" e
  49. 49             txFlg = 0;$ Z! v! Z' q$ U0 i7 T8 ?# n
  50. 50         }
    2 d& G- s; b  C/ U& f
  51. 51     }
    . e) ?3 ~: Z+ Z8 D. j
  52. 52 }
    , L+ f- H) ?# z+ B
  53. 53 ) R" T- W! Y* i5 x$ y
  54. 54 /*******************************************************************************" {% F. T4 @: z4 B
  55. 55 * Function Name  : EP3_OUT_Callback: \  E( a4 h# R  a
  56. 56 * Description    :
    ; R) [5 _* {9 W  b( {/ @& p! x
  57. 57 * Input          : None.+ R3 ^$ _6 K9 d$ N( y, m, J# q6 P
  58. 58 * Output         : None.3 D4 d" Z4 |6 L! g
  59. 59 * Return         : None.
    8 ]3 q4 ]& k/ {5 I/ b
  60. 60 *******************************************************************************/+ K. V) X5 F3 r( a
  61. 61 void EP3_OUT_Callback(void)
    9 s- y  n/ X2 P
  62. 62 {( h' W' y/ H. H1 `; i
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    % }- ~/ h- J9 M
  64. 64
    ( Q7 ^* _% @/ D# s
  65. 65   uint16_t USB_Rx_Cnt;3 f3 }0 u: ]8 F% s8 [4 H$ I
  66. 66   : u1 \: z8 t% A/ E! I
  67. 67   /* Get the received data buffer and update the counter */7 T* \# {0 F; y; @- `
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);) G& \: s: _# W& R* x5 L2 |8 ]
  69. 69   
    % |% a  Q! q! [
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being 3 B1 q1 W0 p: W- Z1 v
  71. 71   NAKed till the end of the USART Xfer */5 F* H+ b( N1 q
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);7 a% [; G. _* l/ }
  73. 73 & T/ x, A5 j( W3 K# m, j5 h
  74. 74   /* Enable the receive of data on EP3 */$ V# |7 C& a1 E  j- D+ S& d
  75. 75   SetEPRxValid(ENDP3);. ?5 b% Z7 m: O  M4 O. P
  76. 76
    * C6 O) ~+ N6 H+ C) C, Q
  77. 77 }
    5 H2 t- I1 I7 Y% B5 Q$ B9 v% a
  78. 78
    / _# [1 V' _1 j9 j1 K" G% j( A
  79. 79 9 l+ l- D' U: H
  80. 80 /*******************************************************************************  y3 T! H  `4 [
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback
    ! r6 ~6 E+ L; x& E4 K
  82. 82 * Description    :
    6 s! S$ h) a  t  m' e( [; J' t( {' v
  83. 83 * Input          : None.
    3 i2 d* V: e: w: v) f0 Q, v
  84. 84 * Output         : None.
    % ~7 w6 [4 S! Z3 S# E6 K
  85. 85 * Return         : None.
      E2 O( A: y2 O3 r. a; o3 P
  86. 86 *******************************************************************************/
    " G2 ~+ d! Z5 \$ b+ Q* X  B. u8 Z  d
  87. 87 void SOF_Callback(void)+ E2 Y, e( j/ X
  88. 88 {4 o7 n6 n$ X& }, ~7 z
  89. 89     uint16_t len = 0;- [3 d8 d: M# f1 e
  90. 90 - \& o' I, c5 }' m& h- a
  91. 91     if(bDeviceState == CONFIGURED)
    + X0 W! Y) m1 J# ^+ m  [# h
  92. 92     {
    4 T7 L! k; |; `# o6 K
  93. 93         if (0 == txFlg): _* \( ?7 E% V. x! i2 S. V
  94. 94         {
    $ z; F& Q$ z; N
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
    5 M) [5 [( z) F
  96. 96             {& O/ R3 j" Q% g7 N7 {8 F- U8 T
  97. 97                 /* Reset the frame counter */
    ( H9 |9 z/ z4 n9 Y) U) ]- n) F. ~
  98. 98                 FrameCount = 0;
    9 F1 X/ @0 f! j# h( u- S
  99. 99 6 d* I" N' k2 V; t5 W
  100. 100                 /* Check the data to be sent through IN pipe */" }' [: Q, u6 g& ^
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));
    . V5 Z; m, f8 n; U: a
  102. 102 3 U+ r9 X! v( G9 d- @6 o
  103. 103                 if (len > 0)% K$ D# P7 {, x" x7 s
  104. 104                 {7 D9 h# t8 @& Z% j) z
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);: `( |6 u0 \) x
  106. 106                     SetEPTxCount(ENDP1, len);" T; ~* P% A, V( M- @' h
  107. 107                     SetEPTxValid(ENDP1);
      K, M8 A% z5 b  a0 T
  108. 108 , H0 r6 X7 A8 y8 q. }
  109. 109                     txFlg = 1;; n8 T$ A6 G3 [- F; e  b
  110. 110                 }
    ' ]2 `- s$ m! T+ d6 H# E" w
  111. 111             }$ X  B+ c8 V  k7 Q" y; d
  112. 112         }: S' Y. H, W3 o* W/ X8 ~1 a, E  a+ l
  113. 113     }  
    8 g- N2 E& I& Y4 ^! v5 a- t5 ]
  114. 114 }
    ' t% T6 C! L- M& u
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
, t  R8 ^$ H  ?" t9 `# h
2 ]% X2 w5 R9 y8 b
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。

6 X6 `4 h" T4 ^) Q
收藏 评论0 发布时间:2022-1-7 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

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