串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个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
2 L) w, T/ x7 q% K图2 
+ @7 O8 G) }: q% {/ f0 R7 O) X# {% Z% b9 {
我为了方便演示统放在usb/src文件夹下: 图3  ; E4 d2 o- w, Y n9 y0 h1 y
; T' e' n9 e1 i现在复制USB的库文件,这些文件不需要我们修改: 图4  " y( H; r( Z3 C
+ s* S, \+ C' @3 }" I% M( q' z7 |- C上图中的文件统一放在usb/lib文件夹下: 图5 
* O+ c: q* Q' X+ f) c# d/ B
2 V: p& M7 ?, c8 J8 r# I5 c 好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程: 图6  ! @& ~( A7 q. u, n9 V* `
由上图可知,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 ------------------------------------------------------------------*/
+ q6 C4 `+ e! L8 o/ z" Y6 W- c - 2 #include "hw_config.h"
/ f+ c- T4 b" f6 r - 3 #include "usb_lib.h"
' `2 {0 g7 e& j" O - 4 #include "usb_istr.h"% @6 G$ B1 B5 j }3 G9 Z7 k
- 5
. w3 k2 w$ J2 r7 r - 6 2 X( Y; R- P# |2 z p8 b) S
- 7 /*******************************************************************************1 Z2 [& h: Q3 W- p
- 8 * Function Name : USB_IRQHandler
* G% Q* x' C' f$ c a5 {! ^ - 9 * Description : This function handles USB Low Priority interrupts% t, a. q% \3 Z" y9 a
- 10 * requests.1 @# @: I9 b* m/ K0 a
- 11 * Input : None4 \; H/ y+ u4 U( A/ Y( `, ?( M
- 12 * Output : None
. Y" u H* C8 s3 W l - 13 * Return : None
+ z/ q% P6 u% k* K" X% i) I% v) Y4 Q - 14 *******************************************************************************/
' U3 d: \9 L3 G% u4 n& t2 w2 k# I ? - 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)
^; b1 Q: A* z - 16 void USB_LP_IRQHandler(void)2 W" L* M; E; L: p6 S/ b! R! c
- 17 #else
5 D+ z( j- B9 y - 18 void USB_LP_CAN1_RX0_IRQHandler(void), _, m1 ^( i8 K8 f& G' q ]6 r e- F
- 19 #endif' a9 r$ s! ~1 F1 l
- 20 {- F; C$ p* q9 L L$ z
- 21 USB_Istr();( @/ M# d/ x: S+ Q
- 22 }
$ D% `( v7 c$ e2 R# X8 [ - 23 7 Z& v, I4 v0 |; d6 Z8 _) z9 U
- 24 /*******************************************************************************" R% q; P0 V" b, B! O6 L! b
- 25 * Function Name : USB_FS_WKUP_IRQHandler* Z7 ?5 g% d; r
- 26 * Description : This function handles USB WakeUp interrupt request.
- Y' @+ l l4 a+ A% Y" T - 27 * Input : None# }+ }$ T9 j& ~7 ^! K6 L5 U: p/ X
- 28 * Output : None U7 D3 z2 a: f9 \. [5 K; N
- 29 * Return : None3 A+ q0 f1 T7 U+ ?
- 30 *******************************************************************************/
`& q( J5 k H: ?2 y5 u7 e/ `- [ - 31
2 D0 T3 @; b# i% v; w - 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
! f7 ?- C( c, `& d; Q9 E - 33 void USB_FS_WKUP_IRQHandler(void)4 \& j- E5 U, U* N$ I
- 34 #else/ } ~) k% I3 J. C% n+ ?; ]! I) E
- 35 void USBWakeUp_IRQHandler(void)
: a8 E# h$ m- y) ` c' i - 36 #endif
" w. _$ C& X0 w! c V - 37 {
8 V7 u2 `: G- [1 z2 w( ~ - 38 EXTI_ClearITPendingBit(EXTI_Line18);. }5 ]+ Z0 D9 W" Z7 }! `8 G
- 39 }
复制代码
4 ~0 \" U9 A. i* ]2,修改代码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 
' G) U9 Q4 w$ x- @& ~; T
9 e# A% U( R! |4 B关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下: 代码3 - 1 /*******************************************************************************3 d8 ^ w# W1 |; d$ Q
- 2 * Function Name : USB_Cable_Config
7 G$ h' C" c" E/ a( f$ B - 3 * Description : Software Connection/Disconnection of USB Cable; K- c, y$ T Y+ y
- 4 * Input : None.4 L/ j! X: H7 j" {
- 5 * Return : Status
1 r, _. j' ^) q) B" c+ N5 ~ - 6 *******************************************************************************/2 _' B5 n( o# t! n$ S
- 7 void USB_Cable_Config (FunctionalState NewState): Z, \/ g: R) f/ h) ?
- 8 {, y' _4 V3 X+ S& B; d
- 9 if (NewState == DISABLE)
# ~& f* X: B$ i# x8 r - 10 {
9 Z7 U4 F8 i d+ ^ B8 k2 y - 11 GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
* C% G" k6 S( M - 12 }, p$ T4 v4 c" f4 ?
- 13 else3 s# N4 a7 j0 ^/ s8 T% x% s
- 14 {: O- s2 e8 F$ L/ v) U
- 15 GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);9 w* v2 z% b" X, `
- 16 }- j( O5 Q8 w/ V" z! F
- 17 }
复制代码
6 N8 A8 ?6 J# x5 K a' D% ?8 }: x3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换: 代码4 - 1 /* Includes ------------------------------------------------------------------*/# e) [0 f0 @: p. q2 _+ k
- 2 #include "usb_lib.h"2 w C3 D1 P$ N' c% ~
- 3 #include "usb_desc.h", H h3 a1 p4 D6 m& r, h9 C
- 4 #include "usb_mem.h"' `, ~, @2 v* U+ ]2 [/ H. U J7 u
- 5 #include "hw_config.h"
- ]0 ?- R A1 k/ k8 w/ s9 P - 6 #include "usb_istr.h"
. T! u# f4 n& p- C6 v - 7 #include "usb_pwr.h"
; \5 I$ y& P' x( k - 8 . H) Y& Q: D% A, g; g
- 9 /* Private typedef -----------------------------------------------------------*/
/ i7 h9 u/ ~8 D6 e# p: g - 10 /* Private define ------------------------------------------------------------*/8 y+ _8 p$ h" c; R
- 11 + k6 s O9 q0 g5 O/ Y, t( Z X2 {
- 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
; f: o. p M% @8 m1 b - 13 #define VCOMPORT_IN_FRAME_INTERVAL 5
3 k0 _- Q2 V. b0 O9 x - 14
! w; ~( ^8 E& X - 15 /* Private macro -------------------------------------------------------------*/
- e6 t8 p' Y5 M; i* u! O - 16 /* Private variables ---------------------------------------------------------*/
+ L; D4 f; R- R% h# W - 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};3 u9 ?1 i* |7 k& d. M& I9 w8 I
- 18 static volatile uint8_t txFlg = 0;
4 d. f0 K1 K# N0 T; S - 19 static volatile uint32_t FrameCount = 0;1 B- o' @5 d& E; \4 B4 O
- 20
' c% @: }7 `* l) j# e - 21
7 t8 A) _% ^6 s- L+ Y% l - 22 /* Private function prototypes -----------------------------------------------*/
: H* {9 R2 }. S0 w - 23 /* Private functions ---------------------------------------------------------*/. K l# M. p9 C6 f- T
- 24 1 K2 m+ k1 `/ Z n$ R, J
- 25 /*******************************************************************************: I8 j; a0 N7 R/ ?* a
- 26 * Function Name : EP1_IN_Callback. V) v6 @) `) W$ f) ^
- 27 * Description :" l0 k' c) L" `. n1 y
- 28 * Input : None.
7 A$ Z7 P# s0 O' \' h - 29 * Output : None.0 y$ A/ x: d. J; g# E w: m2 _& s- a
- 30 * Return : None.
4 |6 `* I9 C9 N' [ - 31 *******************************************************************************/) p* m7 \9 S8 Z; G
- 32 void EP1_IN_Callback (void)
& ^; F: X' ]$ @& F5 L/ K2 G - 33 {
& T; D9 V- o# [: E" V. J% t1 H - 34 uint16_t len = 0;
7 z9 f( M* I$ u: f3 a. n - 35
2 }% W# Z5 S: W% v6 L3 b - 36 if (1 == txFlg)
: C5 L; l2 E: r- F7 P; u% g7 f - 37 {
3 e( W4 \( t( D: T$ d# }6 M5 p7 M2 Z - 38 len = USB_TxRead(txBuffter, sizeof(txBuffter));9 H9 [' K$ X/ d' T
- 39
2 W: v% {1 i, p. b; e - 40 if (len > 0)
8 {: ]: c$ F u- O - 41 {
H0 R& M% G8 p8 x - 42 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);& w- R V/ H. i& W
- 43 SetEPTxCount(ENDP1, len);
N7 Q1 O% @" r; R ^1 [. h# m - 44 SetEPTxValid(ENDP1); * g0 {! ^. f5 v$ M) ?
- 45 FrameCount = 0;7 T- Q6 _4 F8 n1 w a
- 46 }
2 D: P @6 H6 r( f; y: \4 d - 47 else
/ U/ c5 ~" l7 q! Z - 48 {5 w" J7 a }; t
- 49 txFlg = 0;
: r- E% M( J, n6 e - 50 }+ A4 y4 |! c0 a$ r* m
- 51 }
' X t! f% _& Z) N - 52 }
- W# y# [$ Q) d w* R2 G6 H& b3 Q f. D - 53 L1 m* q$ E( O( t/ M2 z- o# g! \
- 54 /*******************************************************************************
G! I( P( ?* N, k0 Q - 55 * Function Name : EP3_OUT_Callback
. |. a7 T" J S! ~2 |! o+ ^& B - 56 * Description :4 O6 P2 R) k. f! w- o- I$ P
- 57 * Input : None.
+ g5 ]7 B" F P' e - 58 * Output : None.
/ F+ z4 f( A5 }; K - 59 * Return : None.& f3 L2 Q8 }6 A* I k
- 60 *******************************************************************************/
: d9 a) y m3 c! B) C, c - 61 void EP3_OUT_Callback(void)$ x% p5 s# j$ z. c2 g; G
- 62 {) ~, K. }3 z1 @, R# Y
- 63 static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};0 }% a" T- Z- G
- 64 1 M# ]2 {; Z2 W5 m* X: s/ k
- 65 uint16_t USB_Rx_Cnt;; U" S% e$ F J' C3 R* F" E
- 66
- F/ S& V: y! I) v - 67 /* Get the received data buffer and update the counter *// \9 d7 p/ f; ~ n
- 68 USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);4 n- y- ]) V6 G3 F6 C. l
- 69
) [, e; }$ L9 u" D* P# { - 70 /* USB data will be immediately processed, this allow next USB traffic being o7 F2 }- X& x+ Y" q# d
- 71 NAKed till the end of the USART Xfer */: }% \+ e, T% |7 g! L J P
- 72 USB_RxWrite(buffter, USB_Rx_Cnt);
y0 t) ~. e7 I3 A - 73 , G( ~) |- S6 ^# p }
- 74 /* Enable the receive of data on EP3 */# r) J( V9 c8 k4 q8 D
- 75 SetEPRxValid(ENDP3);
9 h v9 Y1 I- A5 K7 }: { - 76 8 f6 T& ?$ X4 v- V, w
- 77 }6 S& `: u/ B/ x Y( V
- 78
: y0 _0 a& l" |% j' ^. B% t - 79
. U5 a" ^' M3 i' q6 `0 z5 ` - 80 /*******************************************************************************- {' `7 K8 q: ~1 `: f. r" _1 X
- 81 * Function Name : SOF_Callback / INTR_SOFINTR_Callback" E8 x9 ^6 v4 R" e5 \* e
- 82 * Description :
9 E* I% G$ S7 M0 z - 83 * Input : None.
4 j5 Z: N' e- h" m0 q8 n9 ~1 i - 84 * Output : None.
+ }- h1 L' I1 s( X8 z- C - 85 * Return : None., I8 W/ j, W: S+ P( q, k+ j
- 86 *******************************************************************************/
[8 P: [9 @6 i9 @' } - 87 void SOF_Callback(void)! u" {& U$ E' T: n
- 88 {
, {: X7 x: o c: } - 89 uint16_t len = 0;- q3 W4 L7 F! c1 I
- 90
4 T$ B4 A$ c H% h, S - 91 if(bDeviceState == CONFIGURED)8 |+ _6 P+ X8 u* n( V
- 92 {
- J9 f+ x- @' n$ ?# H% H - 93 if (0 == txFlg)+ Y8 B# B# H/ B5 H
- 94 {4 Z! P* }8 F. J0 P: [. {; S
- 95 if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
0 z7 Q' N% M' d% Q& X8 ]* f - 96 {$ \5 b( Q( L8 B+ _' i% e
- 97 /* Reset the frame counter */6 Y1 ~. E* G W( ] Y
- 98 FrameCount = 0;
2 \* b' q& W. B8 z% {2 |, ~; @/ K - 99
; V; c7 n Z k& V9 s - 100 /* Check the data to be sent through IN pipe */
4 ~2 U. ?) p( D$ L - 101 len = USB_TxRead(txBuffter, sizeof(txBuffter));
% S2 a2 e+ N6 s$ j - 102
* ?( J. E9 b1 g, F- D6 K% I" ? - 103 if (len > 0)( u- j: g+ q0 s: @/ c- B
- 104 {2 c7 }' x& U9 D: x+ i
- 105 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
( ]7 [- h% ^& c, l- e- A @ - 106 SetEPTxCount(ENDP1, len);
' k! {' I+ L7 @0 u8 \ - 107 SetEPTxValid(ENDP1);
, D- x6 b0 G/ P8 ?% g% _) d. R8 i - 108
\3 j6 p b- s/ P2 T+ s3 v+ H" X - 109 txFlg = 1;$ F9 m0 e. x. k" `
- 110 }) ]: D p9 ^! Q4 {8 r' v4 v
- 111 }7 c4 q# I4 V2 d2 E6 Y; B
- 112 }
3 o; l0 k9 U' j3 ] - 113 } ! Q* l( s; J+ k) Z" V
- 114 }
2 U. C' T6 }; Y) P* f) z- r - 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 |