|
串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。 5 @( N2 K. l+ C! i; j1 V
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下: Projects\Virtual_COM_Port文件夹下,复制红线部分 图1 - M! C+ V& k. e% e; X2 _+ I
' f8 {; C5 m! ^! k O9 [: d图2  - @7 |. Y6 T- ~( O c) S x
. z5 T( k* W% y* e2 J# W- `! d T1 z2 [我为了方便演示统放在usb/src文件夹下: 图3  6 m& ^1 ~" ?! e$ o# m- H' z0 Z% i
8 m- }( }* K) I
现在复制USB的库文件,这些文件不需要我们修改: 图4 
* C0 K! j; }: r& b6 ^3 e3 r' F0 G) a8 o6 X" {7 D# E) ]* z- R
上图中的文件统一放在usb/lib文件夹下: 图5 
$ F1 N9 _3 N7 T$ V' ~- J$ u& n, K5 p) k
好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程: 图6  5 _7 l) k0 N: ~
由上图可知,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 ------------------------------------------------------------------*/9 W0 u: {3 \: s
- 2 #include "hw_config.h"" l p4 k5 s/ c: e, Y1 `5 y9 [
- 3 #include "usb_lib.h"2 Y# Y& e+ T, P& F1 `" o2 \
- 4 #include "usb_istr.h"
7 z2 C( p7 H2 x: g2 [+ _ - 5
, @$ @ p5 a/ ~: _& y - 6 2 d+ z- z" M8 Z0 B
- 7 /*******************************************************************************
3 q/ w. U6 B8 i0 P( W2 g/ e2 g - 8 * Function Name : USB_IRQHandler$ ~/ q# l% n$ D( k5 _' y8 U
- 9 * Description : This function handles USB Low Priority interrupts
+ ~' z2 W5 U1 k - 10 * requests.
; R+ e' D2 D y- v - 11 * Input : None6 f4 c6 B! s' u8 v. k) d
- 12 * Output : None
y+ B& H( Y" {% ?4 Y3 Z - 13 * Return : None2 ]' x+ ~" C# Z1 n
- 14 *******************************************************************************/4 e: N) K' X; ~& [
- 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)
) U; @0 M" Z) s p( w1 e4 U - 16 void USB_LP_IRQHandler(void)/ d3 {: w3 K% b9 }6 K3 N1 e k1 m+ R
- 17 #else
# N; ]) t; f! R, r - 18 void USB_LP_CAN1_RX0_IRQHandler(void)9 c/ n1 z- R9 k* T: t0 {
- 19 #endif) n, D& W' q% \( f
- 20 {
4 ]5 P6 ~7 y* |4 [9 L! ` - 21 USB_Istr();
' F& ?3 M! K9 @ - 22 }
& u2 M' P% @* @! n% \* Y5 C! [ - 23 , X l2 f/ K f
- 24 /*******************************************************************************
8 e0 c1 V! |, Z' b4 H5 ^0 m - 25 * Function Name : USB_FS_WKUP_IRQHandler
( Y% q* L% e5 l5 w - 26 * Description : This function handles USB WakeUp interrupt request.
1 c0 @, J9 z+ v: \" [0 E - 27 * Input : None$ d$ `) u' L0 |+ G9 N
- 28 * Output : None: a5 I- J2 i. h+ t: \* _5 x/ {; ?
- 29 * Return : None! u* A0 G0 _+ _4 C8 D9 ^4 M
- 30 *******************************************************************************/4 n; Q y8 T) Q3 o, |
- 31
, c8 L+ E& W, B7 I6 x Q+ S - 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)- A1 h5 ~/ s1 x* S/ @& J9 e
- 33 void USB_FS_WKUP_IRQHandler(void)+ ~- B' d" |& m: g
- 34 #else
* U/ v! ^+ W$ C4 W* i/ b$ T - 35 void USBWakeUp_IRQHandler(void)
" g7 c0 L0 g6 ?5 u8 i - 36 #endif- c3 [7 |" N8 p$ e) i% v, j8 P* E
- 37 {
3 \0 V. T& Q! T9 Y - 38 EXTI_ClearITPendingBit(EXTI_Line18);
% C; [3 m4 Z+ G; }6 g" ]3 ` - 39 }
复制代码 5 L; A; z! V1 v" n5 }, P$ k, p3 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  5 R7 {3 H# j- ?, T/ Q! r
5 q8 E. p8 D1 D1 n8 k/ r
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下: 代码3 - 1 /*******************************************************************************
( y- f0 |1 o, P6 z; c6 }4 d I - 2 * Function Name : USB_Cable_Config
) u/ S. e- G% R& }8 ?/ B* S7 X, L* t% Z - 3 * Description : Software Connection/Disconnection of USB Cable- d# y2 \, T4 L0 H: I0 c
- 4 * Input : None.9 E5 S, a( I# p
- 5 * Return : Status
$ f- b; W& x# g6 P - 6 *******************************************************************************/! ]0 \, K3 _, B
- 7 void USB_Cable_Config (FunctionalState NewState)
$ @) O8 |6 Z' p5 ~4 R% E8 C - 8 {5 I; F& x+ a; V/ w: @/ P3 A5 `) Z, w
- 9 if (NewState == DISABLE)5 G f) S8 V4 h7 E
- 10 {
7 W2 [' ?) Q/ v4 a/ \- z - 11 GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);# F i @* j" ^
- 12 }
, d( O* U9 y4 V - 13 else
- Z% O. s4 Q; |) k2 x - 14 {5 D7 Z7 p9 ^5 T- g- G5 f; n2 o
- 15 GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);3 @4 ?+ v" n# ^
- 16 }
3 @$ K8 ^. w0 y/ p, v2 c; w# w - 17 }
复制代码
2 ~3 |5 k* \2 e4 S3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换: 代码4 - 1 /* Includes ------------------------------------------------------------------*/
. R3 ]- o6 K1 l4 B) ] - 2 #include "usb_lib.h"
, a; ? E* }$ }2 s - 3 #include "usb_desc.h"
N; m' D V0 |+ D - 4 #include "usb_mem.h"
+ }& u6 Q5 M. A! C - 5 #include "hw_config.h"" }% L5 r5 {- q% c/ w
- 6 #include "usb_istr.h"* Q8 {4 \4 P3 h% F3 K( U
- 7 #include "usb_pwr.h"5 G# i' B' c$ v, r* ~- M9 w# B
- 8 7 B" j0 E6 B3 S6 U( b% f. G& A5 S
- 9 /* Private typedef -----------------------------------------------------------*/
6 q' m/ p+ U/ c0 v) u8 i: F) U - 10 /* Private define ------------------------------------------------------------*/& g6 w9 G4 e: O+ D
- 11 ; f* R* D! Z. n+ v, W$ q5 f- t
- 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */, y9 w2 `/ h+ q9 z+ T
- 13 #define VCOMPORT_IN_FRAME_INTERVAL 5* b0 N& f- e( y& t) b: ^
- 14
) \( l1 g7 w* e! p4 s6 x - 15 /* Private macro -------------------------------------------------------------*/
& \( i- w5 K- X - 16 /* Private variables ---------------------------------------------------------*/
3 w0 n$ a( t) f6 d( B - 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};2 q( ~8 \. H% i3 S; [
- 18 static volatile uint8_t txFlg = 0;
/ P- a( q E8 P. w$ a - 19 static volatile uint32_t FrameCount = 0;
" {6 O9 s; }; Z7 V - 20
$ [) ]5 y! G3 T; o - 21
+ q8 l9 z& u' W# p( g7 F- y - 22 /* Private function prototypes -----------------------------------------------*/: K) r u! C- f' ?0 j0 P
- 23 /* Private functions ---------------------------------------------------------*/
1 p( x' f4 i$ N- c6 ? - 24 ; e4 L+ e7 l& M' a3 C1 k$ H
- 25 /*******************************************************************************" V8 `, Q/ j+ k' f7 O1 t
- 26 * Function Name : EP1_IN_Callback
; V2 m: J* D6 @3 A0 ~* N0 e; w - 27 * Description :
5 V% B; i6 y: \0 N1 g/ T- { ]$ } - 28 * Input : None.: A4 N7 @% S0 D4 [+ o* r
- 29 * Output : None.
2 D! j4 t' P$ t8 ]6 {( N9 t9 w+ w - 30 * Return : None.
$ S. J0 g" T, g8 a* p - 31 *******************************************************************************// z, I0 _# L8 r: p. m- u" E [
- 32 void EP1_IN_Callback (void)
3 X4 b9 z! g2 |+ T! k - 33 {5 P/ S4 A: c( V8 {, `. T
- 34 uint16_t len = 0;+ A, @0 r: Y6 {8 J* A% g
- 35
3 [. B% H/ F. }9 |3 L; S - 36 if (1 == txFlg)1 s0 }$ F- u5 A+ w1 y- b: @
- 37 {' L2 a* @7 o0 `; }3 o8 D
- 38 len = USB_TxRead(txBuffter, sizeof(txBuffter));
6 k; k! {. Z+ l9 k - 39 ' ^2 i: C8 z0 G; u9 a( H/ Y! q8 {" i
- 40 if (len > 0)
+ ]2 i" P K+ H! L% d9 @/ ? - 41 {
* b6 b1 r3 W3 i Q$ g9 V - 42 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);* w: t& t1 \2 x' d+ [% D
- 43 SetEPTxCount(ENDP1, len);
, s/ s' D, i) ]7 j( o& w - 44 SetEPTxValid(ENDP1);
E0 I H$ \; [! J - 45 FrameCount = 0;
4 v6 X, g! x8 f; `* D4 u% r' G j% ` - 46 }: z; y+ ?* v( [* \0 X; T7 \
- 47 else
1 b$ V2 W1 x6 l5 D B: K2 p - 48 {
5 ^% M" W) O6 M) K- L9 X - 49 txFlg = 0;
0 r1 Q ]5 `5 o$ Y - 50 }2 q2 ]. ^% T- m( V# W$ m
- 51 }
9 J& h' U% r2 S$ e9 ]! c* ] - 52 }
+ |1 |5 i8 v1 W! Q) T - 53
6 x; n: O0 C2 c - 54 /*******************************************************************************4 V$ x* ?( G( X+ R$ m
- 55 * Function Name : EP3_OUT_Callback
4 c! ` \0 W) O% L4 t1 W: I4 y2 Y - 56 * Description :
$ [) l7 B& ^5 H* N' Y - 57 * Input : None.& Y8 c$ w5 z& }9 I: w9 r
- 58 * Output : None.
+ F& l) h* s x: A" q6 h - 59 * Return : None.) X E( q& Z- f2 m3 S3 [
- 60 *******************************************************************************/" m: a! n5 e# X2 u
- 61 void EP3_OUT_Callback(void)* | _4 [6 b% w6 t1 }
- 62 {
7 t! r) b2 X3 A+ y; N - 63 static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
5 | `4 F# C! T% M& ?8 T( K7 ]1 M - 64 - E2 \# I% I% l
- 65 uint16_t USB_Rx_Cnt;% q9 P0 z7 ?3 u! E G& r( w# H
- 66
0 h' Z9 X, m9 J; A& \( F5 g - 67 /* Get the received data buffer and update the counter */
0 B( ?3 n& \( V Z. |: Z - 68 USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);
1 E) L1 D2 [ W( K2 ?+ N - 69 9 C: l# L8 R1 `
- 70 /* USB data will be immediately processed, this allow next USB traffic being 0 D% x: ?. i. Y! o5 [9 n; B* f4 ?8 k
- 71 NAKed till the end of the USART Xfer */2 \5 i2 f& |9 A) L: b1 b
- 72 USB_RxWrite(buffter, USB_Rx_Cnt);( [1 F3 i7 D5 q( M. x T& E% @" v
- 73
* B3 R& D, ~1 f) z2 ]; u# f - 74 /* Enable the receive of data on EP3 */+ J3 ~; A8 f% ^& ]3 ^
- 75 SetEPRxValid(ENDP3);
1 {# [1 V" K" h# k" N. N - 76 $ a0 M1 y* B) ?3 x
- 77 }4 \$ C1 U5 N$ e4 v5 }% s9 W% [( r
- 78
. Y' D, D' ]7 s% ? - 79
, t6 c( h6 L% q. P0 R' v - 80 /*******************************************************************************5 c) S( j7 D: H
- 81 * Function Name : SOF_Callback / INTR_SOFINTR_Callback' q9 l) ]! v+ {( w6 k
- 82 * Description :
[ H# Z1 u4 P/ G3 L/ S - 83 * Input : None.
/ b3 w8 f6 g$ N( ^0 J - 84 * Output : None.& d( m/ c( c4 `6 T& d' p+ G
- 85 * Return : None., a9 T+ [# D7 P! x; e
- 86 *******************************************************************************/
1 E+ N" x! a7 ]# L! N8 M( A - 87 void SOF_Callback(void)
0 `5 q" i: B4 j/ D% r! ^$ j - 88 {/ \3 R! o8 a s* I. p7 U' s" F, G
- 89 uint16_t len = 0;
( J# @& }; N- N; H! G1 A2 l - 90 7 C* s1 ~6 l% n( _; k
- 91 if(bDeviceState == CONFIGURED)
+ I& @ A0 O9 |; E' z" G* L# X, { - 92 {( V N1 A. e h0 K
- 93 if (0 == txFlg)
$ e' H( g6 w+ I9 u: H6 o - 94 {
" g! ]) N' k3 Q! D, T; b, r - 95 if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
0 L9 h$ P# @' c1 d9 Z- P- m# } - 96 {
$ T- F: N( h \& i) c! } - 97 /* Reset the frame counter */
; [ n* |/ V+ M+ S3 T7 }2 C - 98 FrameCount = 0;
$ N t/ S4 @' D* O% b* [7 @4 @& w - 99 $ c! @7 d1 [3 b P( f3 x6 w
- 100 /* Check the data to be sent through IN pipe */! A5 \! ~# T( ]# Q
- 101 len = USB_TxRead(txBuffter, sizeof(txBuffter));
( j- l/ o! P1 Q+ U0 a& f - 102
9 p# H* W3 }. `& P - 103 if (len > 0)+ H4 d; i, a3 q6 u( H
- 104 {. Q6 V7 t E [& Z3 w+ i' s: M
- 105 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);) S; { A1 l5 F8 P
- 106 SetEPTxCount(ENDP1, len);! n- L( l& M: {! W+ e* |$ B
- 107 SetEPTxValid(ENDP1);
* i: X* Q( o1 E) w - 108
, e6 h1 p5 ]* R( b7 g - 109 txFlg = 1;
: x& b& r) X7 j) M g1 p# r - 110 }# q. ~' S% [) a9 J, h- b) a5 S9 L
- 111 }# S3 k* N" T: S4 n0 V! ^
- 112 }
) d( Q; j! q7 V! f8 \ - 113 } 3 P4 F E) s7 B( W2 t( V# }: v- p
- 114 }
" W1 [) L+ ?" j4 m% e/ d - 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
8 F! ^* c( v" F* N. W4 G) e
: y! I% q# E# p这里讲下大概意思,函数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也一起存放在上文章开始的链接中。 : d% }: k3 J, ]0 U. y
|