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

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

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

  q) x/ E+ _  y' O
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下:
Projects\Virtual_COM_Port文件夹下,复制红线部分
图1

4 @# z+ {1 K* T0 g9 z( E* M; |3 m4 T0 l
300035032772082.jpg
/ `) D* X4 v/ F6 w2 E2 s) Z) a
图2

- y7 O+ {9 E; i* T
300035202932682.jpg

) @; P7 y2 }' n) z' C9 j' \
我为了方便演示统放在usb/src文件夹下:
图3

6 e, g8 \/ {+ h( x7 h; m5 V% q6 Z
300035313246246.jpg
( H# P; c7 Z& V& A, w7 C1 y
现在复制USB的库文件,这些文件不需要我们修改:
图4

' @! z* z; X! O/ o* Z* H# ]& N
300035404189967.jpg
7 u# B! ^9 e" u7 l1 P
上图中的文件统一放在usb/lib文件夹下:
图5
% f) i0 E0 e; i9 E
300036032935279.jpg
  E3 i5 D3 _3 Y- V0 j, J, M3 E. U3 Q
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:
图6
% x/ \  v1 }0 ~5 a4 D
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 ------------------------------------------------------------------*/# n- ?( }1 I+ P* Y2 ^1 [9 n! h
  2. 2 #include "hw_config.h"
    # w! o: N4 P" H4 |0 j
  3. 3 #include "usb_lib.h"# N% Z! ?( Q& H3 f6 N5 Y
  4. 4 #include "usb_istr.h"; V* A( F5 s( ?; Y
  5. 5
    + g! g% H+ x; I2 B# @3 E
  6. 6
    $ C' c! B6 d3 \3 c
  7. 7 /*******************************************************************************
    # J1 j1 ~2 @! f% B. J% e/ n, J- o
  8. 8 * Function Name  : USB_IRQHandler
    ' P! E5 M) h7 ?! \
  9. 9 * Description    : This function handles USB Low Priority interrupts1 Y) x& J" W3 L( W" G
  10. 10 *                  requests.
    1 z/ [9 B) d/ l/ w- B' [( R2 S7 Q. L* J$ K
  11. 11 * Input          : None) W$ V" V4 `' X7 {
  12. 12 * Output         : None
    ( k" A+ C% o7 A( T+ C6 |0 q7 S
  13. 13 * Return         : None4 b: i. s% }% k  C. d
  14. 14 *******************************************************************************/. t  |' N2 P2 u
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)6 z$ O4 U( X. F4 h" ?  @2 Q/ [
  16. 16 void USB_LP_IRQHandler(void)6 d) ?* N/ r2 j9 C& X$ s4 B8 N) q# Y
  17. 17 #else# W/ A7 P5 |  ~4 i
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void)0 j3 {* K+ B& F8 s3 W+ M
  19. 19 #endif
    $ \% \  O9 Q, W9 T9 H/ l8 X
  20. 20 {
    8 @$ U$ |0 v- B) U2 m0 @
  21. 21   USB_Istr();; o+ Y8 d0 U$ v( S  Q1 {
  22. 22 }
    / q( s: |6 ^6 N- G4 ?3 \* ^1 ~
  23. 23
    ; I! c$ A; f2 ]! O/ h
  24. 24 /*******************************************************************************- B* Z- G! ^7 s  J7 Z: o/ x
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler+ y" g& f) {  q1 h) H
  26. 26 * Description    : This function handles USB WakeUp interrupt request.
    * S- F1 ?( s9 T3 N2 A! Y
  27. 27 * Input          : None
    : N' ]* F. ]2 B! `5 |  L
  28. 28 * Output         : None
    * U/ r. m# L! V9 L" j6 l
  29. 29 * Return         : None
    * g& O2 a. G6 o, U4 w7 g4 Y
  30. 30 *******************************************************************************/
    ; s1 D' D4 x& h0 A
  31. 31
    + m5 B1 L& t0 D/ i( }( w! K
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS). o; a5 l! G: l5 v2 q4 G+ h
  33. 33 void USB_FS_WKUP_IRQHandler(void)- _1 D% s$ `: V' w( ^: w  N
  34. 34 #else* k/ \8 y: {; K. w; `- @
  35. 35 void USBWakeUp_IRQHandler(void)
    . z" {1 p* ~0 [) P0 H! f
  36. 36 #endif" d: B7 o( Q9 `! `8 G' I
  37. 37 {
    ; C. t- g5 l* g% x0 ^5 x. `
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);5 m. v0 \% _+ w. d, F
  39. 39 }
复制代码
0 S% d: t3 g2 O2 ~2 K
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
1 @- z  v+ b3 O+ Z
300038465597900.jpg
1 [3 e8 E5 ?- z4 T' I
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************
    0 \4 F  A$ A% w
  2. 2 * Function Name  : USB_Cable_Config' i* D/ U$ i% m9 I+ n
  3. 3 * Description    : Software Connection/Disconnection of USB Cable3 M1 W5 x* j8 q% E6 v& P$ W
  4. 4 * Input          : None.
    5 ?% l8 y1 X4 b, t/ \
  5. 5 * Return         : Status
    , Y5 W+ i1 A3 M' t: ^; O6 L
  6. 6 *******************************************************************************/
    9 v0 O; e8 w) {2 g0 E
  7. 7 void USB_Cable_Config (FunctionalState NewState)0 z( k" T8 y" c: x; h; N& S
  8. 8 {
    6 H$ ^1 m4 x- d6 ~
  9. 9   if (NewState == DISABLE)
      |% y4 C( h3 c
  10. 10   {/ ?1 I' j$ N: k. a% D
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    - V$ }  J! r* o6 c
  12. 12   }
    * o' L+ z$ y  c
  13. 13   else8 C' c5 z8 T- j0 o! P
  14. 14   {
    1 Y4 m2 e* U0 e$ d0 K: ?
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);7 O6 x" ], @: e7 G
  16. 16   }
    2 h4 L- M# N; x& w6 J4 D' S2 R
  17. 17 }
复制代码
+ ~  k  L; {6 m+ V8 t7 W# ]
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/0 {8 T: M8 @; ]! J8 i9 r3 x4 q
  2.   2 #include "usb_lib.h"4 C# }$ M3 i, E- @$ Z. ?
  3.   3 #include "usb_desc.h", z$ T  K: {- O$ u! L( n
  4.   4 #include "usb_mem.h"
      [6 s' I: P- D( t# K( Z- g* O
  5.   5 #include "hw_config.h"
    / M( \- K5 U3 j. Z$ w
  6.   6 #include "usb_istr.h") K: e  m- W4 B# ^  Z7 }
  7.   7 #include "usb_pwr.h"
    ! @+ n( ~; K7 D5 l
  8.   8 & D& G; K# U8 _9 @& h( v. ?
  9.   9 /* Private typedef -----------------------------------------------------------*/; L$ }# i  [% P4 _  T; z' I
  10. 10 /* Private define ------------------------------------------------------------*/7 ~6 u7 F5 B/ {/ p' Q0 N* w
  11. 11
    7 l% a. g5 G0 K& r
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
    ; V% a8 g. Z( M5 B
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             5; [/ d. A5 |" K+ P
  14. 14
    + y% _( M) A0 M, s. i' n' o9 `
  15. 15 /* Private macro -------------------------------------------------------------*/7 ?! U8 r, x2 Z9 L
  16. 16 /* Private variables ---------------------------------------------------------*/: b7 h% \, b7 ?* W
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};& N% w& _! w/ ]' {8 A
  18. 18 static volatile uint8_t txFlg = 0;
    ) a2 k7 o4 l4 p0 R6 s
  19. 19 static volatile uint32_t FrameCount = 0;- p' R! Z8 `5 @' ]7 ]( ^
  20. 20 + E% Z" e% p( L) `
  21. 21
    % q1 e2 a) s7 F$ _0 C
  22. 22 /* Private function prototypes -----------------------------------------------*/
    & B; y5 m5 O9 X9 l
  23. 23 /* Private functions ---------------------------------------------------------*/
    + T* x) a& e. I+ q5 u, T+ N7 k& X$ ?1 t0 I
  24. 24
    $ {6 m; Z6 ?5 c- O* v4 _5 K, T
  25. 25 /*******************************************************************************) q; T) P- x0 V9 C) k- ~
  26. 26 * Function Name  : EP1_IN_Callback
    9 M: B) n4 q% T2 B6 _: w
  27. 27 * Description    :/ L- W* K) j$ j) m
  28. 28 * Input          : None.6 G" |' j& J- W3 l: \9 A+ D
  29. 29 * Output         : None.
    ; P$ J$ R1 r8 t3 x  E, f5 \
  30. 30 * Return         : None.
    & I. S; g& N! c. D- y
  31. 31 *******************************************************************************/  y) A; w( V: D. l3 d
  32. 32 void EP1_IN_Callback (void). p. S. U/ [  h$ W; J
  33. 33 {
    % H' ?& H, K2 c" d8 {0 r: |
  34. 34     uint16_t len = 0;
    9 J! k0 r  j: g3 K% \
  35. 35     + `' A' L! k  O
  36. 36     if (1 == txFlg). U/ i. y: {! q9 ]- ~
  37. 37     {8 Z+ L9 _0 p2 s% c. C: f
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));+ N& L3 t) s, @: v. y6 Q, D
  39. 39
    ) l; T* j! e8 ^
  40. 40         if (len > 0). V0 D9 p; \8 M! q
  41. 41         {
    8 T) q: _" Q( F; f9 v. A- b
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);9 _8 d+ z- H# W: @. X8 }
  43. 43             SetEPTxCount(ENDP1, len);8 I, y( Q$ ^/ F* ?$ V
  44. 44             SetEPTxValid(ENDP1);
    ; D0 g7 p1 S7 }4 r) h
  45. 45             FrameCount = 0;
    : R( t- I0 u! Q& F5 ]
  46. 46         }6 P9 s6 c3 \5 \! f) l* s
  47. 47         else
    2 f, j) \# y9 I, f: j0 B+ _
  48. 48         {$ M/ @2 T7 h* g- O
  49. 49             txFlg = 0;
    : [! m% {3 _' d# T2 ?  L% I; Q
  50. 50         }
    2 e+ w4 p4 r8 R6 |
  51. 51     }
    + s: f( J4 B4 ~3 ]6 n5 y2 u4 _
  52. 52 }
    / p7 p. y6 ?3 }5 p2 u5 {4 t
  53. 53
    8 ^2 B/ O9 N# C" L
  54. 54 /*******************************************************************************3 K2 H; t2 w: v$ ]# y6 q
  55. 55 * Function Name  : EP3_OUT_Callback- u; J" `; W% [3 h
  56. 56 * Description    :
    ; l' A6 A$ y! ^3 W, V, z
  57. 57 * Input          : None.
    ; A* j. W$ Q3 j, t
  58. 58 * Output         : None.* _( N! {7 X& ?; a- p
  59. 59 * Return         : None.
    ( Q( D+ |5 e5 ^7 J1 D" A
  60. 60 *******************************************************************************/) _6 k! r: F  D; Z
  61. 61 void EP3_OUT_Callback(void)
    / O; F% J" ?! x" a7 o
  62. 62 {/ l1 v2 n! H. o! r8 I; m  ]+ g8 \) E
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
    * P; Y: f3 `/ t
  64. 64 2 p% f, w2 ~8 G8 h& s( ~
  65. 65   uint16_t USB_Rx_Cnt;
    7 \+ h4 [& a& ^9 P1 g1 ?
  66. 66   
    3 {$ |! h  G# B9 ~: V1 `
  67. 67   /* Get the received data buffer and update the counter */! n+ `/ H9 V0 J, G) ?; E
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);6 M, u. V! w7 l* J! G
  69. 69   ; A5 t1 X% U6 b) u. K
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being
    ; H7 ]3 H  T# p" ~6 l
  71. 71   NAKed till the end of the USART Xfer */  s6 j) F' d5 C6 e# g4 c
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);
    : {# h+ H- ~, N9 Q) j& N& [; E' M
  73. 73
    $ m- f+ V3 y' f9 s: p& }
  74. 74   /* Enable the receive of data on EP3 */) @) X4 @6 W0 l+ @- n* M
  75. 75   SetEPRxValid(ENDP3);* F+ x$ O* c0 H
  76. 76 2 ]/ u$ J) ~1 i6 D) F/ N
  77. 77 }1 u! ]" Q2 Y2 \* n6 s5 [
  78. 78 # V( ~1 Q3 c& d( f
  79. 79
    $ q# S& Q: E( d7 @# ?; C/ {: c+ n
  80. 80 /*******************************************************************************
    1 P& [7 ]3 e. {. j, g
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback
    & f+ A. v5 }0 o' j' x6 o( X
  82. 82 * Description    :
    : H# `! R6 c" a- b" |. _* g8 o( A# u
  83. 83 * Input          : None., [3 u: J! j% c& x  N5 Z4 ]- O) U9 ^
  84. 84 * Output         : None.7 W# T1 G7 F# z; }" C* l
  85. 85 * Return         : None.9 J6 t5 f$ C9 J
  86. 86 *******************************************************************************// J* f7 o8 h+ X% V1 k
  87. 87 void SOF_Callback(void)
    * |- C4 @: c: O( P$ x7 [
  88. 88 {
    # o& u1 R/ U6 N9 Z) }# _
  89. 89     uint16_t len = 0;
    2 [0 e7 ~6 o. Q7 E" H9 a) {+ Q
  90. 90 & o* B3 ?2 e. Q8 L
  91. 91     if(bDeviceState == CONFIGURED)
      n/ O" ]2 U7 d9 z0 A. F
  92. 92     {
    . \8 V0 ~1 J, `& Y' T2 B6 P+ H9 A) [
  93. 93         if (0 == txFlg)3 c2 I! U2 f. F% m+ {- R
  94. 94         {
    ( x& ?: O, e% m) o9 X* A  Z* o1 o
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
    6 m% d0 S( Y' D* D: I
  96. 96             {6 I* U+ A0 _: `
  97. 97                 /* Reset the frame counter */
    % O* V& m" m  g4 R) B
  98. 98                 FrameCount = 0;$ t; o' ^. G! C" P1 R- p" y& A
  99. 99
    ! z' h. I0 R: P. y8 G( G3 J0 m
  100. 100                 /* Check the data to be sent through IN pipe */
    . P5 q/ Q4 f- T/ |( q
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));% h/ e- ^9 I) A7 F1 l+ c
  102. 102
      y! _% |4 d& n! j9 M2 l
  103. 103                 if (len > 0)
    - e5 g2 D  ^6 R8 S
  104. 104                 {- N  t# E4 C7 v) ^4 h
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);6 e' b% v- C$ J' s/ ?$ @. k7 v
  106. 106                     SetEPTxCount(ENDP1, len);* T0 u2 m  B+ p6 ]; |' g9 `9 {
  107. 107                     SetEPTxValid(ENDP1);, L/ n3 p, E/ Y. D! b
  108. 108 2 ~: D( t% g* |2 [) s
  109. 109                     txFlg = 1;
    ; G/ h; J& F9 \* D
  110. 110                 }
    / b, @- T  h: i7 f% I1 x2 w  F& T
  111. 111             }; u# g' R6 R: W  s4 ?) y
  112. 112         }
    1 `& c4 _* ?8 ~2 y4 b' k7 j3 Z$ r
  113. 113     }  $ B$ U  M# ^% Q& N0 `0 `9 f
  114. 114 }
    * O4 y& Y' j- n
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码

" ]+ `3 X8 p* e4 y" Q0 ]9 _5 r0 A7 U/ V& r: n7 h* Z
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。

! R5 i* w7 s' X" {7 I
收藏 评论0 发布时间:2022-1-7 19:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版