|
串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。
; o+ V. @ r% J, m; s# z3 @- f; t& Z首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下: Projects\Virtual_COM_Port文件夹下,复制红线部分 图1
! h$ z+ i! \8 w g. `& ^+ n) j* }3 k1 {" |- m2 g
图2  # ^" `$ w- S1 T
; ^' i, Q' \* b" Z; U) P
我为了方便演示统放在usb/src文件夹下: 图3 
$ o0 R6 ~7 P5 w) U
( L4 ~! m3 J1 C. \, o9 L# [* w0 r现在复制USB的库文件,这些文件不需要我们修改: 图4  $ T. O( o5 z' o" \+ |6 c, p8 m; C
6 |4 \0 f. K& K, e* }" i) C
上图中的文件统一放在usb/lib文件夹下: 图5  ; a% {4 d8 N% }( b
9 N1 A9 Q# ~# f6 \/ \2 O1 {9 v 好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程: 图6  , E- J; d' m8 @
由上图可知,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 ------------------------------------------------------------------*/. Z D9 v; N) K' ]3 p- M# A
- 2 #include "hw_config.h"
# e2 T+ f2 z) ]9 }6 k - 3 #include "usb_lib.h"& ]$ {9 b7 I; H* T2 H
- 4 #include "usb_istr.h"
9 C$ w3 Q; j3 N- Z7 t - 5 8 L3 v* Y8 r$ e, B0 E- G) d3 e
- 6
, u6 _) N5 B& P3 f, C4 o' \$ H1 N - 7 /*******************************************************************************2 _* M: @$ p) K9 D, R* o. T
- 8 * Function Name : USB_IRQHandler+ V# J7 V! @" ~. U! b: x# X+ v
- 9 * Description : This function handles USB Low Priority interrupts
2 T. H& P8 J. U/ s2 U9 \2 n- e* y - 10 * requests.; \7 z4 N5 S Z( a* `5 \' |. ^
- 11 * Input : None
4 I L6 X& p2 v& {2 |+ W) \ - 12 * Output : None
( y- c( H- @! B3 v - 13 * Return : None
: H U! U( c! x* d; k/ n) h - 14 *******************************************************************************/
4 k6 E& z# g3 B/ e( z( `- r# r - 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)$ H" i+ n6 U$ n5 ~
- 16 void USB_LP_IRQHandler(void), F; j# X( P1 ^8 i/ e+ h! l
- 17 #else4 i8 a6 w! B3 `' E& I
- 18 void USB_LP_CAN1_RX0_IRQHandler(void)
( L/ t3 b; F; e6 K1 [' | - 19 #endif
3 W' u% W) G; e8 i: n( y - 20 {3 ~. l" M% U) D1 A& `
- 21 USB_Istr();' C, f; D/ K& I/ k. c' [" M- _
- 22 }; H- p9 W& L3 b+ {0 J0 X& i/ ^' C
- 23 6 w7 |7 s, H, D
- 24 /*******************************************************************************
, {8 l; T' ?& Q0 e$ O - 25 * Function Name : USB_FS_WKUP_IRQHandler. t9 Z7 `1 Q# b3 [4 \
- 26 * Description : This function handles USB WakeUp interrupt request.
3 U8 w3 c3 F5 H4 G: y, T5 r$ p - 27 * Input : None! w2 ]/ s/ p h# S: m8 V
- 28 * Output : None
* |9 A! E9 {! G z4 { - 29 * Return : None$ E+ l& N9 `& L1 w
- 30 *******************************************************************************/; d8 \9 |' T5 E8 S1 F, f
- 31
Q+ U" |& a8 x1 T - 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
, B8 y& m; m+ \! |; v$ z; E9 I. | - 33 void USB_FS_WKUP_IRQHandler(void)
) x# y0 }$ F1 A. H4 r3 ^" C - 34 #else
3 p, Z0 h( [+ \. A! |- q& q2 r - 35 void USBWakeUp_IRQHandler(void)
+ i8 _2 @" @ `# c5 z& J - 36 #endif$ W$ [0 t0 V0 z% b& i1 [" N
- 37 {
* M& P4 }! ~4 f! p: E8 ? - 38 EXTI_ClearITPendingBit(EXTI_Line18);9 ` ]# ~5 O1 ~2 V4 {6 _# ~% `% x$ N/ c3 m
- 39 }
复制代码
1 W. {! ^ o4 K6 ~- _3 ?1 t) r2,修改代码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 
& k% ?8 p0 [- z" O9 [: t1 V7 Q; n
, C2 @; @) w( T, d4 g: ?关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下: 代码3 - 1 /*******************************************************************************) j* x$ |( v; T1 a
- 2 * Function Name : USB_Cable_Config
6 t. a5 x9 ] z) A7 o# O2 ? - 3 * Description : Software Connection/Disconnection of USB Cable# h# |" e5 \% [$ h
- 4 * Input : None.9 J7 l) E [- K* B( ?0 C
- 5 * Return : Status
+ a. c3 C# z3 t0 @ - 6 *******************************************************************************/
) i& T5 Z4 m5 y - 7 void USB_Cable_Config (FunctionalState NewState)
' \) `3 |. C4 O( S - 8 {
( Q4 k) [4 }9 v. Y! Y5 D( l! b - 9 if (NewState == DISABLE)
: s9 `. s/ A9 h. M$ n/ h9 | - 10 {
; o6 I3 b- i* p! I - 11 GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
+ w( e5 Y; q2 P3 n; p4 _ - 12 }' c: C1 S- N/ O0 E W
- 13 else
+ A1 s8 }+ c3 \2 p - 14 {
( B I& {( p3 P7 X, ], X& ]' I - 15 GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);" k( u5 w, p8 O: l
- 16 }& a4 G; B1 B6 }0 c3 F5 r2 b: \% A
- 17 }
复制代码
, }2 {3 J- r' b- w7 k3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换: 代码4 - 1 /* Includes ------------------------------------------------------------------*/ E2 h0 k+ L0 Z; C4 e# u
- 2 #include "usb_lib.h"/ [" |$ f7 h# ^3 ^& F
- 3 #include "usb_desc.h"* g1 _% h% q! T
- 4 #include "usb_mem.h"
- `, ^* \# D" A - 5 #include "hw_config.h"
- e& ]3 s, z& ~' ]; g - 6 #include "usb_istr.h"9 v' m2 R' F( K7 d: |" x1 O
- 7 #include "usb_pwr.h"
- i0 C* w# B6 Z) O - 8 - |1 W( Z! A* f2 |. ]. N1 C) D+ M
- 9 /* Private typedef -----------------------------------------------------------*/0 i/ h4 v- }0 I5 [% L5 t$ d
- 10 /* Private define ------------------------------------------------------------*/
. Z# V. x, B8 B) M0 B+ C - 11 1 } P) [/ J6 X1 l. |& M
- 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */8 r t% B4 d. z7 |+ J& E2 \
- 13 #define VCOMPORT_IN_FRAME_INTERVAL 5
# e! h: Z5 J( s4 U+ T. |1 i3 N - 14
: a6 W1 p3 ?; J$ B/ [0 ~$ O% B - 15 /* Private macro -------------------------------------------------------------*/: n; H% U- r9 u ~5 x) E
- 16 /* Private variables ---------------------------------------------------------*/1 ^+ W" |4 {' p$ x1 p2 `
- 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
5 I- U/ _$ S: M - 18 static volatile uint8_t txFlg = 0;
$ K n& f! ?5 d' N4 R5 T ^, o; w - 19 static volatile uint32_t FrameCount = 0;7 j( s2 J, s! `, ^6 R# A1 Y$ V8 E
- 20 / x8 f) B6 l. A( u) z) | L
- 21 9 ~* j- s, K1 I1 T" e) ^5 q8 N
- 22 /* Private function prototypes -----------------------------------------------*/
' K' ]* l4 ^* @: l - 23 /* Private functions ---------------------------------------------------------*/
& ?2 T) Z4 G1 K3 p+ h+ q3 T - 24
# c" ~/ N! r5 R, _2 t8 E2 s# _# w - 25 /*******************************************************************************
& u6 ]& ]0 B: B' }7 o7 z. K - 26 * Function Name : EP1_IN_Callback4 X# L- m4 ]" e8 L5 `
- 27 * Description :9 N+ t7 Q( q, q' j9 A" R$ {" b
- 28 * Input : None.
3 i/ R/ A* {( a8 ~7 @ - 29 * Output : None.
Q1 Z; }# Q1 z7 _ - 30 * Return : None.
Y( o7 K1 }9 }+ \/ P5 q) n - 31 *******************************************************************************/- e. {* B' r3 p8 T' c! @4 [1 t
- 32 void EP1_IN_Callback (void)6 u: ~$ R! S# l. Z3 E( {) d
- 33 {' g1 y5 f3 `) V2 ]' b* H
- 34 uint16_t len = 0;3 a R" {4 f7 |7 G
- 35
2 c" H2 U& e6 H Y* k7 R - 36 if (1 == txFlg)% b+ l( v2 G V# l9 U3 n. Q; z' z
- 37 {" |; }1 N: l/ j& P1 n( { v3 b! g' i
- 38 len = USB_TxRead(txBuffter, sizeof(txBuffter));
" i5 y) }% n ?3 d E+ J - 39
; D0 g% P* K" C6 u/ a' e& ]9 l - 40 if (len > 0)
! M$ c& C% g8 E9 j, ]) r. g: F* [ - 41 {# k+ Q( h& W! q4 Z- W5 X- B
- 42 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
, T' h, H/ N4 t1 V% H - 43 SetEPTxCount(ENDP1, len);: a5 D8 W+ l9 b# [7 p1 P
- 44 SetEPTxValid(ENDP1); / I# e. O, C) `, H( b! j9 G, d/ w/ S
- 45 FrameCount = 0;; h) X6 i7 y5 Y( S
- 46 }# G' H( }, S0 ?9 m. |& R
- 47 else2 z; V1 |3 m) A1 r, |) u
- 48 {( Q+ l* Z3 {+ J( U- _8 v; j
- 49 txFlg = 0;
5 H, T: J6 h& L - 50 }
6 R( s* S* y8 g/ C+ s" E - 51 }2 R1 p1 Q1 g/ M) R0 t& q
- 52 }
2 F2 r5 y8 i }0 ]* c2 ~* L - 53 . O: ^5 q) e+ L( Z
- 54 /*******************************************************************************1 p( o& }! o0 p
- 55 * Function Name : EP3_OUT_Callback
7 }, U" i( Y4 b) G3 W - 56 * Description :, ~) E& m- a6 s4 g: v
- 57 * Input : None.5 o0 O: ~* I8 H, ~2 W
- 58 * Output : None.3 W" o h+ B* x3 R* a) ^% ^
- 59 * Return : None.
- A0 v6 s5 b# l# P - 60 *******************************************************************************/+ _* n8 O" ?6 r# @) b. p
- 61 void EP3_OUT_Callback(void)! J7 f) {0 y+ V8 I& B
- 62 {
( H( a( m- j3 ` W: J1 a% Z' ` - 63 static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};8 o, N& u' _, B# @+ ~0 R; a" ^) ]
- 64 ( Z9 ^, F7 n: Q/ P& z: J U* ~
- 65 uint16_t USB_Rx_Cnt;
/ n* v+ g1 q% C8 b - 66
% z* I# v9 Y1 j - 67 /* Get the received data buffer and update the counter */
9 h' J+ M; w6 r9 D8 } - 68 USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);" t$ j, e! e0 H
- 69
6 ~, t) Q0 F) ]' ~; ?( H - 70 /* USB data will be immediately processed, this allow next USB traffic being L$ Q: v8 S2 G1 x! v5 l) p0 i
- 71 NAKed till the end of the USART Xfer */, {' \$ J5 B- U) R
- 72 USB_RxWrite(buffter, USB_Rx_Cnt);
8 p. `, f0 I# [2 J2 q7 j O3 H - 73
) ]+ d* q8 R/ ^; X* V/ c6 V) F4 E - 74 /* Enable the receive of data on EP3 */2 j' V) ]+ \/ ^: d& N/ {# u
- 75 SetEPRxValid(ENDP3);
" {1 W! b1 H& ^, [: n/ q# G - 76
% x( v, y2 {5 c7 u2 I - 77 }% [) K6 J, e/ `% j
- 78 . c# I9 `# F' v' l5 ]: q
- 79 : X5 ]- B c7 \: l7 P- O
- 80 /*******************************************************************************2 m+ L' Z; [$ r: w: t+ b9 z
- 81 * Function Name : SOF_Callback / INTR_SOFINTR_Callback
! V! a( ?4 M7 X- t5 E) a% a - 82 * Description :
3 s8 R8 z& M4 w5 a - 83 * Input : None.) W0 y% c) C) M
- 84 * Output : None.. n# z* }* x# p1 _
- 85 * Return : None.
, f' c6 k) _+ s3 m* I" g, t* t* x - 86 *******************************************************************************/4 C3 d5 \0 {, U6 ?
- 87 void SOF_Callback(void)
2 d" o3 W! T9 q$ i/ y; K6 i9 d - 88 {' x+ m- Z- A' a) _/ F
- 89 uint16_t len = 0;! i. ?: t% Y9 W) A7 X
- 90 1 q- L( w. I+ u) ^. ~$ F/ U
- 91 if(bDeviceState == CONFIGURED)
! i+ g, L* n; }! j0 @ - 92 {
; y, ^ F( m* o1 M& T) R - 93 if (0 == txFlg)
: K) x: f. `- `0 x/ I - 94 {
$ r& A' f& W+ f/ J# G - 95 if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
/ |9 P, F' o: C1 Z7 z" m6 {# N1 W - 96 {, R' x/ ~) G' ]
- 97 /* Reset the frame counter */$ }, a2 ?+ m5 p6 V4 N, H) P
- 98 FrameCount = 0;
( d) [3 s& F, i! n4 { - 99
, a0 e$ k8 a0 x+ r: l' b- o - 100 /* Check the data to be sent through IN pipe */
- R8 C4 M. X0 b2 ^3 \$ N: d; V, A - 101 len = USB_TxRead(txBuffter, sizeof(txBuffter));
+ s/ n, P" @2 D- N& w# l; A+ L - 102
n$ U, x5 e) }8 @0 K1 O - 103 if (len > 0)0 j% N; g3 x: ?5 l* a
- 104 {- L7 Z, _) K& g M
- 105 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);! T, y1 D: y% c
- 106 SetEPTxCount(ENDP1, len);: P9 I, P/ s, g6 ?2 t7 C
- 107 SetEPTxValid(ENDP1);
4 O# i( ?1 h+ C1 N' l6 ?; s - 108 / ?. I) f3 M5 Y, c% q
- 109 txFlg = 1;3 K2 F8 ]. c6 E& c; h
- 110 }0 B7 r& M7 K2 r8 t
- 111 }
/ Y3 P2 H/ I4 @3 b+ \7 N P$ h - 112 }& g. d1 p& _0 G. Z" u1 n
- 113 } # o5 | F+ {* ?4 w+ @
- 114 }
r x( q$ K3 L. Y# I5 Y. i - 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码 4 p* H) k" A- m
$ {& X4 I! o) R4 H8 J
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。 + P% q" o5 i: _$ w$ a9 p4 q
|