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

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

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

2 L) w, T/ x7 q% K
图2

+ @7 O8 G) }: q% {/ f0 R
300035202932682.jpg
7 O) X# {% Z% b9 {
我为了方便演示统放在usb/src文件夹下:
图3
; E4 d2 o- w, Y  n9 y0 h1 y
300035313246246.jpg

; T' e' n9 e1 i
现在复制USB的库文件,这些文件不需要我们修改:
图4
" y( H; r( Z3 C
300035404189967.jpg

+ s* S, \+ C' @3 }" I% M( q' z7 |- C
上图中的文件统一放在usb/lib文件夹下:
图5

* O+ c: q* Q' X+ f) c# d/ B
300036032935279.jpg

2 V: p& M7 ?, c8 J8 r# I5 c
         好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程:
图6
! @& ~( A7 q. u, n9 V* `
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 ------------------------------------------------------------------*/
    + q6 C4 `+ e! L8 o/ z" Y6 W- c
  2. 2 #include "hw_config.h"
    / f+ c- T4 b" f6 r
  3. 3 #include "usb_lib.h"
    ' `2 {0 g7 e& j" O
  4. 4 #include "usb_istr.h"% @6 G$ B1 B5 j  }3 G9 Z7 k
  5. 5
    . w3 k2 w$ J2 r7 r
  6. 6 2 X( Y; R- P# |2 z  p8 b) S
  7. 7 /*******************************************************************************1 Z2 [& h: Q3 W- p
  8. 8 * Function Name  : USB_IRQHandler
    * G% Q* x' C' f$ c  a5 {! ^
  9. 9 * Description    : This function handles USB Low Priority interrupts% t, a. q% \3 Z" y9 a
  10. 10 *                  requests.1 @# @: I9 b* m/ K0 a
  11. 11 * Input          : None4 \; H/ y+ u4 U( A/ Y( `, ?( M
  12. 12 * Output         : None
    . Y" u  H* C8 s3 W  l
  13. 13 * Return         : None
    + z/ q% P6 u% k* K" X% i) I% v) Y4 Q
  14. 14 *******************************************************************************/
    ' U3 d: \9 L3 G% u4 n& t2 w2 k# I  ?
  15. 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)
      ^; b1 Q: A* z
  16. 16 void USB_LP_IRQHandler(void)2 W" L* M; E; L: p6 S/ b! R! c
  17. 17 #else
    5 D+ z( j- B9 y
  18. 18 void USB_LP_CAN1_RX0_IRQHandler(void), _, m1 ^( i8 K8 f& G' q  ]6 r  e- F
  19. 19 #endif' a9 r$ s! ~1 F1 l
  20. 20 {- F; C$ p* q9 L  L$ z
  21. 21   USB_Istr();( @/ M# d/ x: S+ Q
  22. 22 }
    $ D% `( v7 c$ e2 R# X8 [
  23. 23 7 Z& v, I4 v0 |; d6 Z8 _) z9 U
  24. 24 /*******************************************************************************" R% q; P0 V" b, B! O6 L! b
  25. 25 * Function Name  : USB_FS_WKUP_IRQHandler* Z7 ?5 g% d; r
  26. 26 * Description    : This function handles USB WakeUp interrupt request.
    - Y' @+ l  l4 a+ A% Y" T
  27. 27 * Input          : None# }+ }$ T9 j& ~7 ^! K6 L5 U: p/ X
  28. 28 * Output         : None  U7 D3 z2 a: f9 \. [5 K; N
  29. 29 * Return         : None3 A+ q0 f1 T7 U+ ?
  30. 30 *******************************************************************************/
      `& q( J5 k  H: ?2 y5 u7 e/ `- [
  31. 31
    2 D0 T3 @; b# i% v; w
  32. 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
    ! f7 ?- C( c, `& d; Q9 E
  33. 33 void USB_FS_WKUP_IRQHandler(void)4 \& j- E5 U, U* N$ I
  34. 34 #else/ }  ~) k% I3 J. C% n+ ?; ]! I) E
  35. 35 void USBWakeUp_IRQHandler(void)
    : a8 E# h$ m- y) `  c' i
  36. 36 #endif
    " w. _$ C& X0 w! c  V
  37. 37 {
    8 V7 u2 `: G- [1 z2 w( ~
  38. 38   EXTI_ClearITPendingBit(EXTI_Line18);. }5 ]+ Z0 D9 W" Z7 }! `8 G
  39. 39 }
复制代码

4 ~0 \" U9 A. 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

' G) U9 Q4 w$ x- @& ~; T
300038465597900.jpg

9 e# A% U( R! |4 B
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下:
代码3
  1. 1 /*******************************************************************************3 d8 ^  w# W1 |; d$ Q
  2. 2 * Function Name  : USB_Cable_Config
    7 G$ h' C" c" E/ a( f$ B
  3. 3 * Description    : Software Connection/Disconnection of USB Cable; K- c, y$ T  Y+ y
  4. 4 * Input          : None.4 L/ j! X: H7 j" {
  5. 5 * Return         : Status
    1 r, _. j' ^) q) B" c+ N5 ~
  6. 6 *******************************************************************************/2 _' B5 n( o# t! n$ S
  7. 7 void USB_Cable_Config (FunctionalState NewState): Z, \/ g: R) f/ h) ?
  8. 8 {, y' _4 V3 X+ S& B; d
  9. 9   if (NewState == DISABLE)
    # ~& f* X: B$ i# x8 r
  10. 10   {
    9 Z7 U4 F8 i  d+ ^  B8 k2 y
  11. 11     GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    * C% G" k6 S( M
  12. 12   }, p$ T4 v4 c" f4 ?
  13. 13   else3 s# N4 a7 j0 ^/ s8 T% x% s
  14. 14   {: O- s2 e8 F$ L/ v) U
  15. 15     GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);9 w* v2 z% b" X, `
  16. 16   }- j( O5 Q8 w/ V" z! F
  17. 17 }
复制代码

6 N8 A8 ?6 J# x5 K  a' D% ?8 }: x
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换:
代码4
  1.   1 /* Includes ------------------------------------------------------------------*/# e) [0 f0 @: p. q2 _+ k
  2.   2 #include "usb_lib.h"2 w  C3 D1 P$ N' c% ~
  3.   3 #include "usb_desc.h", H  h3 a1 p4 D6 m& r, h9 C
  4.   4 #include "usb_mem.h"' `, ~, @2 v* U+ ]2 [/ H. U  J7 u
  5.   5 #include "hw_config.h"
    - ]0 ?- R  A1 k/ k8 w/ s9 P
  6.   6 #include "usb_istr.h"
    . T! u# f4 n& p- C6 v
  7.   7 #include "usb_pwr.h"
    ; \5 I$ y& P' x( k
  8.   8 . H) Y& Q: D% A, g; g
  9.   9 /* Private typedef -----------------------------------------------------------*/
    / i7 h9 u/ ~8 D6 e# p: g
  10. 10 /* Private define ------------------------------------------------------------*/8 y+ _8 p$ h" c; R
  11. 11 + k6 s  O9 q0 g5 O/ Y, t( Z  X2 {
  12. 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
    ; f: o. p  M% @8 m1 b
  13. 13 #define VCOMPORT_IN_FRAME_INTERVAL             5
    3 k0 _- Q2 V. b0 O9 x
  14. 14
    ! w; ~( ^8 E& X
  15. 15 /* Private macro -------------------------------------------------------------*/
    - e6 t8 p' Y5 M; i* u! O
  16. 16 /* Private variables ---------------------------------------------------------*/
    + L; D4 f; R- R% h# W
  17. 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};3 u9 ?1 i* |7 k& d. M& I9 w8 I
  18. 18 static volatile uint8_t txFlg = 0;
    4 d. f0 K1 K# N0 T; S
  19. 19 static volatile uint32_t FrameCount = 0;1 B- o' @5 d& E; \4 B4 O
  20. 20
    ' c% @: }7 `* l) j# e
  21. 21
    7 t8 A) _% ^6 s- L+ Y% l
  22. 22 /* Private function prototypes -----------------------------------------------*/
    : H* {9 R2 }. S0 w
  23. 23 /* Private functions ---------------------------------------------------------*/. K  l# M. p9 C6 f- T
  24. 24 1 K2 m+ k1 `/ Z  n$ R, J
  25. 25 /*******************************************************************************: I8 j; a0 N7 R/ ?* a
  26. 26 * Function Name  : EP1_IN_Callback. V) v6 @) `) W$ f) ^
  27. 27 * Description    :" l0 k' c) L" `. n1 y
  28. 28 * Input          : None.
    7 A$ Z7 P# s0 O' \' h
  29. 29 * Output         : None.0 y$ A/ x: d. J; g# E  w: m2 _& s- a
  30. 30 * Return         : None.
    4 |6 `* I9 C9 N' [
  31. 31 *******************************************************************************/) p* m7 \9 S8 Z; G
  32. 32 void EP1_IN_Callback (void)
    & ^; F: X' ]$ @& F5 L/ K2 G
  33. 33 {
    & T; D9 V- o# [: E" V. J% t1 H
  34. 34     uint16_t len = 0;
    7 z9 f( M* I$ u: f3 a. n
  35. 35     
    2 }% W# Z5 S: W% v6 L3 b
  36. 36     if (1 == txFlg)
    : C5 L; l2 E: r- F7 P; u% g7 f
  37. 37     {
    3 e( W4 \( t( D: T$ d# }6 M5 p7 M2 Z
  38. 38         len = USB_TxRead(txBuffter, sizeof(txBuffter));9 H9 [' K$ X/ d' T
  39. 39
    2 W: v% {1 i, p. b; e
  40. 40         if (len > 0)
    8 {: ]: c$ F  u- O
  41. 41         {
      H0 R& M% G8 p8 x
  42. 42             UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);& w- R  V/ H. i& W
  43. 43             SetEPTxCount(ENDP1, len);
      N7 Q1 O% @" r; R  ^1 [. h# m
  44. 44             SetEPTxValid(ENDP1); * g0 {! ^. f5 v$ M) ?
  45. 45             FrameCount = 0;7 T- Q6 _4 F8 n1 w  a
  46. 46         }
    2 D: P  @6 H6 r( f; y: \4 d
  47. 47         else
    / U/ c5 ~" l7 q! Z
  48. 48         {5 w" J7 a  }; t
  49. 49             txFlg = 0;
    : r- E% M( J, n6 e
  50. 50         }+ A4 y4 |! c0 a$ r* m
  51. 51     }
    ' X  t! f% _& Z) N
  52. 52 }
    - W# y# [$ Q) d  w* R2 G6 H& b3 Q  f. D
  53. 53   L1 m* q$ E( O( t/ M2 z- o# g! \
  54. 54 /*******************************************************************************
      G! I( P( ?* N, k0 Q
  55. 55 * Function Name  : EP3_OUT_Callback
    . |. a7 T" J  S! ~2 |! o+ ^& B
  56. 56 * Description    :4 O6 P2 R) k. f! w- o- I$ P
  57. 57 * Input          : None.
    + g5 ]7 B" F  P' e
  58. 58 * Output         : None.
    / F+ z4 f( A5 }; K
  59. 59 * Return         : None.& f3 L2 Q8 }6 A* I  k
  60. 60 *******************************************************************************/
    : d9 a) y  m3 c! B) C, c
  61. 61 void EP3_OUT_Callback(void)$ x% p5 s# j$ z. c2 g; G
  62. 62 {) ~, K. }3 z1 @, R# Y
  63. 63   static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};0 }% a" T- Z- G
  64. 64 1 M# ]2 {; Z2 W5 m* X: s/ k
  65. 65   uint16_t USB_Rx_Cnt;; U" S% e$ F  J' C3 R* F" E
  66. 66   
    - F/ S& V: y! I) v
  67. 67   /* Get the received data buffer and update the counter *// \9 d7 p/ f; ~  n
  68. 68   USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);4 n- y- ]) V6 G3 F6 C. l
  69. 69   
    ) [, e; }$ L9 u" D* P# {
  70. 70   /* USB data will be immediately processed, this allow next USB traffic being   o7 F2 }- X& x+ Y" q# d
  71. 71   NAKed till the end of the USART Xfer */: }% \+ e, T% |7 g! L  J  P
  72. 72   USB_RxWrite(buffter, USB_Rx_Cnt);
      y0 t) ~. e7 I3 A
  73. 73 , G( ~) |- S6 ^# p  }
  74. 74   /* Enable the receive of data on EP3 */# r) J( V9 c8 k4 q8 D
  75. 75   SetEPRxValid(ENDP3);
    9 h  v9 Y1 I- A5 K7 }: {
  76. 76 8 f6 T& ?$ X4 v- V, w
  77. 77 }6 S& `: u/ B/ x  Y( V
  78. 78
    : y0 _0 a& l" |% j' ^. B% t
  79. 79
    . U5 a" ^' M3 i' q6 `0 z5 `
  80. 80 /*******************************************************************************- {' `7 K8 q: ~1 `: f. r" _1 X
  81. 81 * Function Name  : SOF_Callback / INTR_SOFINTR_Callback" E8 x9 ^6 v4 R" e5 \* e
  82. 82 * Description    :
    9 E* I% G$ S7 M0 z
  83. 83 * Input          : None.
    4 j5 Z: N' e- h" m0 q8 n9 ~1 i
  84. 84 * Output         : None.
    + }- h1 L' I1 s( X8 z- C
  85. 85 * Return         : None., I8 W/ j, W: S+ P( q, k+ j
  86. 86 *******************************************************************************/
      [8 P: [9 @6 i9 @' }
  87. 87 void SOF_Callback(void)! u" {& U$ E' T: n
  88. 88 {
    , {: X7 x: o  c: }
  89. 89     uint16_t len = 0;- q3 W4 L7 F! c1 I
  90. 90
    4 T$ B4 A$ c  H% h, S
  91. 91     if(bDeviceState == CONFIGURED)8 |+ _6 P+ X8 u* n( V
  92. 92     {
    - J9 f+ x- @' n$ ?# H% H
  93. 93         if (0 == txFlg)+ Y8 B# B# H/ B5 H
  94. 94         {4 Z! P* }8 F. J0 P: [. {; S
  95. 95             if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
    0 z7 Q' N% M' d% Q& X8 ]* f
  96. 96             {$ \5 b( Q( L8 B+ _' i% e
  97. 97                 /* Reset the frame counter */6 Y1 ~. E* G  W( ]  Y
  98. 98                 FrameCount = 0;
    2 \* b' q& W. B8 z% {2 |, ~; @/ K
  99. 99
    ; V; c7 n  Z  k& V9 s
  100. 100                 /* Check the data to be sent through IN pipe */
    4 ~2 U. ?) p( D$ L
  101. 101                 len = USB_TxRead(txBuffter, sizeof(txBuffter));
    % S2 a2 e+ N6 s$ j
  102. 102
    * ?( J. E9 b1 g, F- D6 K% I" ?
  103. 103                 if (len > 0)( u- j: g+ q0 s: @/ c- B
  104. 104                 {2 c7 }' x& U9 D: x+ i
  105. 105                     UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
    ( ]7 [- h% ^& c, l- e- A  @
  106. 106                     SetEPTxCount(ENDP1, len);
    ' k! {' I+ L7 @0 u8 \
  107. 107                     SetEPTxValid(ENDP1);
    , D- x6 b0 G/ P8 ?% g% _) d. R8 i
  108. 108
      \3 j6 p  b- s/ P2 T+ s3 v+ H" X
  109. 109                     txFlg = 1;$ F9 m0 e. x. k" `
  110. 110                 }) ]: D  p9 ^! Q4 {8 r' v4 v
  111. 111             }7 c4 q# I4 V2 d2 E6 Y; B
  112. 112         }
    3 o; l0 k9 U' j3 ]
  113. 113     }  ! Q* l( s; J+ k) Z" V
  114. 114 }
    2 U. C' T6 }; Y) P* f) z- r
  115. 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码

9 h' E5 X  e$ w
& v$ w& X  z# m2 m4 u. 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也一起存放在上文章开始的链接中。

9 ^( Q3 x. L9 `1 F
收藏 评论0 发布时间:2022-1-7 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

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