串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。 4 G/ ]) J# e K S) \& R6 f
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下: Projects\Virtual_COM_Port文件夹下,复制红线部分 图1
* a# Y( R- Q4 Z) Q
. A A6 ^2 R" a0 J图2  5 ^2 G @" e" C
! r. M; y" C! [1 C( B/ L我为了方便演示统放在usb/src文件夹下: 图3  : h' g" Z3 h5 p, @
* C9 @3 p3 ~ A3 A. w: R! f% C0 g现在复制USB的库文件,这些文件不需要我们修改: 图4 
- l9 y' k, [* A4 ~* Q3 v! s/ ~+ M* I/ J
. c' ~( \! O3 Q+ }上图中的文件统一放在usb/lib文件夹下: 图5 
, M+ }4 { b4 [% h$ _& M& w3 M9 [4 P0 G/ D1 C- @
好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程: 图6  # M3 L2 x, H' a8 R
由上图可知,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 /* Includes ------------------------------------------------------------------*/" m$ `' g" ^9 B
- 2 #include "hw_config.h"
0 M" j& f1 |" S9 D - 3 #include "usb_lib.h"' w x* U* N3 }, ]0 q/ S, {4 a
- 4 #include "usb_istr.h"
8 ~8 Q) M) T5 M! q0 p - 5 ' x/ u0 v& r# `( n& d8 o
- 6
! b ]1 K' ^7 V# G - 7 /*******************************************************************************: I% u) ]# W7 w* v3 g; m
- 8 * Function Name : USB_IRQHandler
C; _4 ^/ U' g' { - 9 * Description : This function handles USB Low Priority interrupts2 s& i; ?* E0 m( d; ~
- 10 * requests.; O6 Z1 q3 ~6 m( p9 X9 w* A
- 11 * Input : None; g8 w5 G \/ G- d1 v6 J& e+ D
- 12 * Output : None0 t5 Y( t. K* u
- 13 * Return : None
: T& O% M Y: p# y+ k - 14 *******************************************************************************/
8 h* |+ C* ]9 `4 f4 O( K - 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)
1 B. f/ |+ \. ^' p - 16 void USB_LP_IRQHandler(void)
. ` N) k: ^8 u* @ - 17 #else
5 q5 R* Q. p( V' j7 j7 v7 y9 d* f+ _ - 18 void USB_LP_CAN1_RX0_IRQHandler(void)
/ u7 T) A& L, w - 19 #endif$ g- L% R) U: P
- 20 {
' @8 ^/ J: T. G! O+ p - 21 USB_Istr();
5 `$ S' V% q' I" x4 \+ K9 f - 22 }7 t/ E. `0 n# O9 m1 a2 S3 k( O- N
- 23
: ^5 a% x- f& V" K7 n - 24 /*******************************************************************************$ X& {8 M f7 \2 `2 y
- 25 * Function Name : USB_FS_WKUP_IRQHandler! q) k" w+ L8 r8 A, m B% e& s8 P
- 26 * Description : This function handles USB WakeUp interrupt request.* Y7 d I+ g/ e' F/ { B# Y
- 27 * Input : None; c% g, [% z% I* a
- 28 * Output : None
3 Y) E7 B9 o4 a' C" ] - 29 * Return : None
( ]3 O, L! C% M. y) t - 30 *******************************************************************************/
, \: x% W$ U8 ~8 I0 o - 31 : K" j/ l7 o$ n0 N- _+ Y9 Z* P
- 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)' e' w% i9 T3 O0 n: `* |4 @% O
- 33 void USB_FS_WKUP_IRQHandler(void)
" x2 [7 Y0 w8 X1 X" | - 34 #else: y% t0 w' n4 a' C
- 35 void USBWakeUp_IRQHandler(void)8 l# I6 ~1 ]* Q; S
- 36 #endif \% H" G& C+ }: i3 ? ^
- 37 {: t. }: u3 p) N( g# J( H. D
- 38 EXTI_ClearITPendingBit(EXTI_Line18);2 D; |$ b& T. e, \
- 39 }
复制代码
! w. I' G2 E4 C+ a2,修改代码hw_config.c删除无用代码,新建立2组,读FIFO和写FIFO的函数。后面会用到。 代码如下: 代码2 - <img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" border="0" alt=""> View Code
复制代码这里要讲一下为什么要屏蔽SystemInit(),因为demo只运行虚拟串口功能,在USB未插入的情况下,是进入低功耗状态,插入时从低功耗状态退出后会调用此函数。当然我们在项目中一般不会这样,系统是否运行和插USB接口没有联系。所以我在下文中把进入低功耗代码屏蔽了,自然也就不用唤醒代码了。 图7  9 I4 s- g& U+ e0 H( }6 `9 J
: K- ^, ]3 I% M4 J, w2 F
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下: 代码3 - 1 /*******************************************************************************
$ V% O0 C& X1 M0 R - 2 * Function Name : USB_Cable_Config
) r1 k- f3 p1 h1 ?( Q2 M1 S - 3 * Description : Software Connection/Disconnection of USB Cable: ^" r. {# D, w7 p
- 4 * Input : None.1 B" B% A3 v& W `+ e. }4 R! j1 d
- 5 * Return : Status9 }' \3 U* F4 V( F' j
- 6 *******************************************************************************/
( V' c H& n) e8 I - 7 void USB_Cable_Config (FunctionalState NewState), I, V0 L) I$ b
- 8 {' r8 z% \7 L. }: W! R8 ?) `
- 9 if (NewState == DISABLE)6 F) c6 X+ q3 a4 B6 m o2 W" h
- 10 {
0 z* A) L0 N# r - 11 GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
0 N+ ^1 K. _0 l" H' k1 p - 12 }. f* F) t I" m% g2 I3 N& q
- 13 else
+ u9 m. [+ A* X$ u/ q% u: T - 14 {, n8 U" o; ]7 G: M. X2 l
- 15 GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
, @0 X$ U8 r$ ]/ h - 16 }6 p5 T5 M% a; \" E" q( j& V
- 17 }
复制代码 . b$ t9 i- Z) c5 N i" u
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换: 代码4 - 1 /* Includes ------------------------------------------------------------------*/: v+ a/ R$ Y$ x' `: m3 Q* Z
- 2 #include "usb_lib.h"/ O9 n+ ], F+ ]* U8 N
- 3 #include "usb_desc.h"; ]) H5 W' p1 G3 ]! I( ^ J( C
- 4 #include "usb_mem.h"- \: d# i2 B0 b- H& R1 b# x
- 5 #include "hw_config.h"
. J5 `) l% o5 |( @& n$ I - 6 #include "usb_istr.h"
7 s: j# [2 r# w% k+ T, Q - 7 #include "usb_pwr.h") A/ G3 w: h* m( [/ ~
- 8
& j1 z1 B! p2 k' `+ } N - 9 /* Private typedef -----------------------------------------------------------*/
) B3 {9 U/ @( P2 H$ y5 o, i# e - 10 /* Private define ------------------------------------------------------------*/
, O6 ^$ U' X4 c - 11
9 m. x- H9 X- e, V1 u - 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
8 x. x5 j `6 o2 c* m4 y( {" Q - 13 #define VCOMPORT_IN_FRAME_INTERVAL 5
4 k0 f0 W. \( r+ f' M - 14 4 A! g2 Z" t- l6 D! A
- 15 /* Private macro -------------------------------------------------------------*/
' x7 t$ Y/ {7 T! w5 } - 16 /* Private variables ---------------------------------------------------------*/9 I1 o! Y, D' K6 {1 r
- 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};/ Q& ~: V0 h- g! f
- 18 static volatile uint8_t txFlg = 0;
; Y0 R. f9 \9 u - 19 static volatile uint32_t FrameCount = 0;
! z0 y# G. I8 g( q - 20 9 d7 e7 C9 M& e" f* w
- 21 + C0 ]5 d- U. Q0 _9 E
- 22 /* Private function prototypes -----------------------------------------------*/
1 H/ C; G3 N0 ^/ l. k5 C - 23 /* Private functions ---------------------------------------------------------*/. l0 }3 A$ |& U' I6 ?5 f
- 24 " c, x$ p! V0 s, s+ j" P2 O
- 25 /*******************************************************************************
& l, q: \7 F }4 p; z* c6 U - 26 * Function Name : EP1_IN_Callback |! {* L" j+ \+ ]+ b( Z, ~4 r2 k& ]
- 27 * Description :# k1 O) r& l# N: r# v
- 28 * Input : None.
4 C2 {+ H' c5 ^9 w% ^& m' v. G3 i - 29 * Output : None.) E' K3 o7 f7 }; j, k. t
- 30 * Return : None.; Q3 \ _( j; F2 b* J
- 31 *******************************************************************************/
N* p, R" N: n) R - 32 void EP1_IN_Callback (void)
8 \+ a" g8 ?" k( j% E5 x - 33 {" i2 X# l0 ~) {- U) k5 W
- 34 uint16_t len = 0;
]# P g& H! j! ~' p - 35 ' y7 A+ r: o- A/ C8 i& \
- 36 if (1 == txFlg)
; M# g' d, D- K; t2 m - 37 {
3 @ C' q. g2 Q, K5 k R; l K3 V ^+ p - 38 len = USB_TxRead(txBuffter, sizeof(txBuffter));" {. G- p6 l4 a4 B5 [1 t& S; R
- 39
8 k- R- s0 Z' v" n9 { - 40 if (len > 0)
: a+ K% |) T, K- y8 X8 h+ k - 41 {
5 z: ^/ Y/ L: J% G" n - 42 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
5 x6 r7 e4 m8 P' G' ?8 o9 F - 43 SetEPTxCount(ENDP1, len);
% E1 @3 }3 K$ A6 Z' o - 44 SetEPTxValid(ENDP1);
7 q6 X- [' [$ G" X* q/ H - 45 FrameCount = 0;
* M1 \1 E4 P' t) c2 s+ C - 46 }
1 T, P8 y8 A+ ]! {% e8 u+ \ - 47 else
7 x. K- Z5 a; O O - 48 {9 K X' j/ B2 x5 E/ v4 m8 z- z& T2 y
- 49 txFlg = 0;
& G: n, _' F% X" u6 Q, M3 \5 A - 50 }$ E) p1 f i. S3 R
- 51 }
. X, i* ~/ L0 X6 D5 ^ - 52 }& E* u. r9 I7 p& w% z
- 53
. C4 x% D7 V% H a& h+ W; q - 54 /*******************************************************************************% ~9 }( K" T- A0 y& O" o6 o
- 55 * Function Name : EP3_OUT_Callback4 I$ C# X/ H6 S/ q0 C9 x
- 56 * Description :$ B }4 {6 {% \
- 57 * Input : None.
# }" r' O6 Z6 K) h - 58 * Output : None.9 t1 e0 ` n5 K1 m' T
- 59 * Return : None.
+ l$ G7 t% `& R6 a - 60 *******************************************************************************/5 x: h) ?0 i; b% s" \$ x
- 61 void EP3_OUT_Callback(void)
: r# |- s! U& m& E - 62 {
& [, L, r% b8 ?& g - 63 static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
) H; d+ d8 {& c$ B. P- r8 s - 64
' r7 p, A( q4 r7 d - 65 uint16_t USB_Rx_Cnt;$ J) n; v& u' T! a9 O9 V
- 66 ; J+ c# D2 O' G2 S1 S( c* Y; z
- 67 /* Get the received data buffer and update the counter */
. [" p- M# V Y: [0 t6 ?, @ - 68 USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);
( \ r2 V; i% @( ~* J* ~$ B - 69 * F2 K1 x' X5 T% S7 w7 [% k
- 70 /* USB data will be immediately processed, this allow next USB traffic being * m0 G8 M- r; s( t
- 71 NAKed till the end of the USART Xfer */
9 N8 {4 [' d5 e9 W$ h# Z - 72 USB_RxWrite(buffter, USB_Rx_Cnt);
) h8 c. | O2 v' }; @& s u) Q - 73 - ?3 O5 w8 Y3 }; V
- 74 /* Enable the receive of data on EP3 */, G& u7 M2 v% @; Z0 f
- 75 SetEPRxValid(ENDP3);, H+ O" a; B K1 O$ M
- 76
) M8 f8 l8 v5 f% r - 77 }
8 A6 N7 p5 I' F0 r - 78
0 }" j# u* a5 G: F - 79
: u$ ~: K3 l: L# G& p/ ]. F - 80 /*******************************************************************************
7 h. `) U: x- u$ `7 ]+ K3 X - 81 * Function Name : SOF_Callback / INTR_SOFINTR_Callback
9 _/ @% {/ R, A6 v$ l - 82 * Description :
. [, @4 K& ^* D5 \ - 83 * Input : None.
4 `6 Q1 B6 X3 \ - 84 * Output : None.9 n+ P$ d* Q) ~/ {5 o8 x" c
- 85 * Return : None.2 x$ C6 ?' R. \; M1 y6 T: V. u9 @
- 86 *******************************************************************************/
3 |+ V# k- [ c, b W$ b5 \ - 87 void SOF_Callback(void)- }2 B& c) F. } |
- 88 {9 j" t6 S& A0 D# Y$ e( |
- 89 uint16_t len = 0;3 J# ^% u! P$ k6 A' A+ E
- 90
$ ` g$ O7 o3 N" q- w2 E/ p! Z" w7 b - 91 if(bDeviceState == CONFIGURED)
0 |3 ^+ {# g8 N0 s l, Q - 92 {
9 D8 D7 Z! X" e7 ~ - 93 if (0 == txFlg)* `, R6 D+ z$ C" E
- 94 {
) N" {5 y# D) ~7 ?3 Q - 95 if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)3 f3 h# k3 T2 x" Z- e& q
- 96 {
$ ~/ ?1 ?! E" L, E - 97 /* Reset the frame counter */9 `- x- y* F. `$ N
- 98 FrameCount = 0;
8 e" `. ?" s" \ - 99
. N6 j0 ]: D0 E9 y& E8 ^2 i; @ - 100 /* Check the data to be sent through IN pipe */4 z- A! S7 h& ~& Q, D
- 101 len = USB_TxRead(txBuffter, sizeof(txBuffter));7 o- I6 a7 C8 `8 P2 ^
- 102 6 d/ b7 H' t! ~# _$ d+ M# q
- 103 if (len > 0)% Q: W( w4 {; @6 W+ V
- 104 {+ z6 I. Z. X, V+ D# ]' q) p- L
- 105 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
8 j4 ?+ g6 \* e: H+ Z* O% k - 106 SetEPTxCount(ENDP1, len);
" |% C. e& P& F9 a) ? - 107 SetEPTxValid(ENDP1);2 i; F" r5 I+ x3 K( K
- 108 5 ^1 U: |, K: V9 f
- 109 txFlg = 1;
, W+ L4 R5 O2 c7 ]' j/ Q. d - 110 }) x9 X t! g8 F4 o$ x
- 111 }
! T9 n: m- | J* M5 z) L - 112 }
@: K- c K0 t6 R - 113 } * c* V5 ?/ m, K; {9 _9 p
- 114 }
6 U8 g) h6 U7 A2 @9 ` - 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码 ( b! W+ }& w+ D3 O) S' }
" k1 b3 i+ f9 S: s d9 @8 u: W这里讲下大概意思,函数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也一起存放在上文章开始的链接中。 ) ?3 G7 j. B" Z1 R% w, F' N, j
|