|
串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。 * H4 O- ?7 p- b* ?# c3 H7 ~9 x
首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下: Projects\Virtual_COM_Port文件夹下,复制红线部分 图1 + Q# A2 A( e" u* B8 F) q5 \- K
! v' `. l, ?" e( s$ a, r1 F% x8 d
图2 
: t# Y; l! q# T: O' y( v
l+ m6 |4 N* x我为了方便演示统放在usb/src文件夹下: 图3 
3 i- {' G5 X# V. @9 ?& H) u
6 } M9 ~- L7 x. C4 e现在复制USB的库文件,这些文件不需要我们修改: 图4  ! [! N$ ]7 k4 G# r2 h3 i0 q
7 a+ a5 N8 {0 i% y: h6 m( f
上图中的文件统一放在usb/lib文件夹下: 图5  ' b, B9 }2 y. |
' @2 h. g& A/ n; r" E* b, V' @
好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程: 图6  * m, r$ A& [ H0 `
由上图可知,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 ------------------------------------------------------------------*/
. C8 ^! n B2 U0 F* H - 2 #include "hw_config.h"6 p- H) D) U/ T- J0 _4 {
- 3 #include "usb_lib.h"
( w3 C+ v! h4 Z! K7 B! D - 4 #include "usb_istr.h"
# G8 z4 I$ }. l4 i - 5
) i/ Q$ g- X( C/ {/ N0 ? - 6 ; Z6 s. X! C0 E, r4 U% a$ L+ m
- 7 /*******************************************************************************
8 y9 I `; Z/ p- j7 d - 8 * Function Name : USB_IRQHandler
3 Q8 I3 |* z9 [& W - 9 * Description : This function handles USB Low Priority interrupts
+ R3 m* A. \; c. \ - 10 * requests." N4 ` y& e3 }$ c. H6 f: A
- 11 * Input : None
: {8 q+ N" U- ?5 { - 12 * Output : None
8 J) `& V( h$ b* W ] - 13 * Return : None
: @( [/ m- y; ^, m' i - 14 *******************************************************************************/2 O1 J; w! k; I
- 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)) ^" X" n' T: W
- 16 void USB_LP_IRQHandler(void)7 }: V7 P( p5 `5 @9 e7 p
- 17 #else1 ^. f [/ y8 W7 S! B
- 18 void USB_LP_CAN1_RX0_IRQHandler(void); ~/ P8 }2 G8 H- l- b+ g
- 19 #endif
( A5 ?( r# @5 h: F8 j$ Z0 I9 T - 20 { ~( u1 G: j/ e$ W" V; J$ [
- 21 USB_Istr();5 g2 I7 R e( N% E& M& c
- 22 }% J- n; s* ~+ N- i% N/ v
- 23
. N4 T# j9 O J* Y - 24 /*******************************************************************************
$ @% n6 }8 H6 p - 25 * Function Name : USB_FS_WKUP_IRQHandler/ v% ~ U; E8 G9 ]! W) ^
- 26 * Description : This function handles USB WakeUp interrupt request.
1 X, W* k8 n' }8 E( M8 M - 27 * Input : None* N% Z2 b Y, i
- 28 * Output : None- {7 P% P4 g* Q h2 z, b
- 29 * Return : None% U3 Q4 B0 J3 ?
- 30 *******************************************************************************/1 i1 K* I: ]$ c
- 31 ; f- B* y1 Y: E5 N5 L
- 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
3 e6 z2 ^( X3 ~3 Q - 33 void USB_FS_WKUP_IRQHandler(void); {* ?9 y/ C( i
- 34 #else
& m; v. x* k. b2 D( p. u - 35 void USBWakeUp_IRQHandler(void). O0 y: q. R7 k: b' {! l2 R5 z
- 36 #endif
5 x0 @9 d3 C( }! N+ L - 37 {
3 y p8 A6 U, r; A( S2 ] - 38 EXTI_ClearITPendingBit(EXTI_Line18);, }9 d# `- k2 O: d6 E
- 39 }
复制代码 # y2 \2 C( G1 k2 n: I: C
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 
2 h0 J" R) n+ V4 J1 r9 r$ M' G$ o3 ?3 H' b2 y- \6 | ? _
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下: 代码3 - 1 /*******************************************************************************
) P* ^6 E& \- D+ k6 w& i - 2 * Function Name : USB_Cable_Config
^* z- v6 G) k( c8 O c+ u - 3 * Description : Software Connection/Disconnection of USB Cable
* @4 w% P* |9 G. h. e - 4 * Input : None.
5 Z( q( f- a) j: F# R4 E4 a - 5 * Return : Status& ?. h. N. X3 L" j) M2 r( Q
- 6 *******************************************************************************/* Y# X0 U g' w/ C3 A
- 7 void USB_Cable_Config (FunctionalState NewState)
4 `: L0 L5 Q! T1 m' G! n8 Z - 8 {
# C+ h c2 h5 e - 9 if (NewState == DISABLE)6 j p) @" f) A& h
- 10 {: h( L) i1 R/ b1 P9 D
- 11 GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
7 Z7 ~6 k5 O+ q - 12 }& J. u5 V: w5 K! p- d( ~
- 13 else1 a" l$ ?& Q/ K* d2 U q! D4 N/ X! d
- 14 {
' O: q8 J9 | I5 g* j" f, m" P - 15 GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);& J H0 z% _/ J. E/ J. {9 u
- 16 }
+ }2 o% \( A9 }3 |: F - 17 }
复制代码
v8 ~$ n8 ?. j9 O5 \5 w1 O3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换: 代码4 - 1 /* Includes ------------------------------------------------------------------*/
& S6 K% {, I6 J! G4 E - 2 #include "usb_lib.h"
0 N; M8 K! l0 Y/ l, i8 a - 3 #include "usb_desc.h"3 c) | S( A5 Y" V% F- G
- 4 #include "usb_mem.h"1 b: K0 q/ p: L$ e; M) l( ]- M
- 5 #include "hw_config.h"# K4 z9 [3 Y/ s- K
- 6 #include "usb_istr.h"* d8 b+ R* K( ?4 }: f9 T
- 7 #include "usb_pwr.h"& w3 f/ D7 F7 G4 P9 j D
- 8
1 ]0 B5 w5 l" R4 Y% P, x/ v9 W4 X - 9 /* Private typedef -----------------------------------------------------------*/
( l; D8 h Z" {* ?( ^- f. q - 10 /* Private define ------------------------------------------------------------*/
# d+ \! [2 i0 s, x# w: | - 11 ; e# K9 S4 ~$ [' m8 @. o! g' k
- 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */) a0 d$ ~9 e7 x. g
- 13 #define VCOMPORT_IN_FRAME_INTERVAL 56 T7 j: w' t2 y1 Q$ `+ I5 T9 v
- 14
" a) V6 X2 Z) g7 t8 V& h; F1 q+ b - 15 /* Private macro -------------------------------------------------------------*/% D' ]6 w9 l, U9 O0 r
- 16 /* Private variables ---------------------------------------------------------*/
. V+ U$ e9 t! t- Z2 N; J - 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
2 W9 n! w6 ]. D5 A' E' r - 18 static volatile uint8_t txFlg = 0;; f( ?' ?' n* g
- 19 static volatile uint32_t FrameCount = 0;
% b' D; X# A1 h% U7 N* u - 20
& ^! S1 R6 b1 _ j2 c7 M7 y2 {. A - 21 9 f' u. v- S$ M7 X8 U; E# X
- 22 /* Private function prototypes -----------------------------------------------*/9 h6 d( D" I9 w( V- V: d
- 23 /* Private functions ---------------------------------------------------------*/
. m0 p! e; j4 Y* G: \ - 24 # B, [8 \" @* ?, ]% b/ V
- 25 /*******************************************************************************' j* W9 E3 Y4 j1 R
- 26 * Function Name : EP1_IN_Callback
9 Y) [/ s3 j M; W - 27 * Description :
% S+ E4 u7 T, E' i( q - 28 * Input : None.
8 o1 j9 h6 l$ }1 y - 29 * Output : None.8 N7 ^$ s1 u9 u, i) e
- 30 * Return : None.5 E% ?4 L, B- o; `! e
- 31 *******************************************************************************/
% B/ H, T3 c" h; U' n! e a/ _ - 32 void EP1_IN_Callback (void)
' L8 J6 |5 T0 F# Y - 33 {! ?/ l) q1 z G( } \) q! Y1 p
- 34 uint16_t len = 0;1 k' m$ H* v" T/ R6 W `( ], c
- 35
/ H) [* ]# ?$ ~! I8 ]# G5 T9 Y2 x - 36 if (1 == txFlg)% Q0 U B) O Z& x7 O! g
- 37 {$ O" M' S6 R9 j1 c# g& G
- 38 len = USB_TxRead(txBuffter, sizeof(txBuffter));
2 o2 D2 u9 O% u* B. j6 H! h) ^ - 39
' _- A8 A* D$ k o4 h& L+ w - 40 if (len > 0)
9 F+ u' `5 I( Y; |, X - 41 {
' k9 O$ }8 q4 ~! { - 42 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);
8 k8 [7 E4 Z5 L( v2 y+ l$ O - 43 SetEPTxCount(ENDP1, len);
- s5 `+ o! I! i& S - 44 SetEPTxValid(ENDP1);
. V5 Y J/ l& q7 ?9 l' K1 U& t - 45 FrameCount = 0;
8 x: m* v9 y% |' g - 46 }
2 Y9 Z. }+ D4 q* i3 `, Z! Z9 R - 47 else- l: r" \* O6 O
- 48 {0 W8 | q+ |0 s( x! j5 t
- 49 txFlg = 0; ^, X% k) `! N6 x# j) t: A
- 50 }0 d" x5 T8 v. Z" g5 l
- 51 }- \9 K0 b" r/ l+ Q1 N* {1 I
- 52 }
9 U Q7 W' r+ ]+ R: m3 C - 53
" N4 l; l6 N: \' E x: D$ ] - 54 /*******************************************************************************% |6 c& J: A; {
- 55 * Function Name : EP3_OUT_Callback7 Y2 O: ~* E7 s, z8 u
- 56 * Description :# ?5 w! d; Y. X% h5 X3 T' J
- 57 * Input : None.
( P l* X5 E- Z! Z3 b0 ^ - 58 * Output : None.
0 B* ]3 f+ h1 V, Z6 N" h - 59 * Return : None.
2 ^* |2 W/ J$ N* ]( ^ - 60 *******************************************************************************/$ c5 G6 _% S* h* ~" u$ M) p( _8 a( Z
- 61 void EP3_OUT_Callback(void); Q3 T$ s' Z! L
- 62 {
, C+ c, L, @0 C4 z8 ^$ k3 `. L6 o* E - 63 static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
0 `0 A! F) l+ \$ y2 }+ w: O - 64
/ e; u5 A" b0 M3 ^/ ^/ f - 65 uint16_t USB_Rx_Cnt;
! T, h) J1 q" r: g1 d - 66
5 h5 R3 }, g( Q - 67 /* Get the received data buffer and update the counter */$ h* \( i& M; Z2 K; R
- 68 USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);4 }, }3 m2 `" U$ _1 H
- 69 7 H5 f d3 f: h; r1 ]6 W
- 70 /* USB data will be immediately processed, this allow next USB traffic being
& J/ h6 p0 v2 H+ G9 R - 71 NAKed till the end of the USART Xfer */
+ S' y8 H x% P! K - 72 USB_RxWrite(buffter, USB_Rx_Cnt);# y' [0 {; W' S' y1 R
- 73
! m# U; ^' n- u) |2 k1 ?1 q! p/ g - 74 /* Enable the receive of data on EP3 */
- A! W2 j! p+ M2 z$ B2 G - 75 SetEPRxValid(ENDP3);
4 W& ~/ ~( [; L d - 76
1 k: S6 F9 J; G! R$ g. B L7 q - 77 }
5 U; ^# u8 u2 w, e7 N# Q - 78
2 v$ A' S3 u' Q( T - 79
# U6 H" X7 a$ N4 I/ ~* r% [ - 80 /*******************************************************************************
{ P9 f) j. c4 F# U2 M$ K - 81 * Function Name : SOF_Callback / INTR_SOFINTR_Callback0 F7 V, u O$ h, _7 X3 p) U$ _
- 82 * Description :4 ^ K3 B$ I$ h% f
- 83 * Input : None.
- B# D; g) j4 q1 x - 84 * Output : None.# y3 ]- [0 y5 _) m+ v' K, l
- 85 * Return : None.
2 J1 F! E* L' q% _ - 86 *******************************************************************************/+ a- g7 l) f: x
- 87 void SOF_Callback(void)' O( r. D- o2 V) Y$ k
- 88 {
: B }# k: u+ ^! E) G! U# m2 _" Y; G - 89 uint16_t len = 0;
1 D2 z7 Q. h e$ g, S @- `. `1 Y% X - 90 6 D! @8 `' h" W m
- 91 if(bDeviceState == CONFIGURED)
( e) @& |: y1 b7 l; h) c% T - 92 {' L- B4 x* C* A
- 93 if (0 == txFlg)4 X- o) J# F+ F
- 94 {
" C7 i% |4 L, h) m2 F - 95 if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
; W' ]: H; q1 b7 R( E0 f7 k9 d - 96 {
# {) d, `6 ]# I$ \3 @# @ K }, v - 97 /* Reset the frame counter */6 L: d" G2 \3 p
- 98 FrameCount = 0; c: J6 t6 Z8 @, U. b! Z
- 99 # K) t9 o6 O3 M6 d
- 100 /* Check the data to be sent through IN pipe */
4 Y' o! g; f! t: k; F6 L# E - 101 len = USB_TxRead(txBuffter, sizeof(txBuffter));3 M0 U. @. [# I3 R
- 102 : E w& w# k& f, g, R
- 103 if (len > 0)
" g, H! M0 y3 g9 v) ^7 ~ - 104 {" G' Z9 M! X7 t+ d/ b
- 105 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);+ z4 V# T( `" B" E7 Y& o
- 106 SetEPTxCount(ENDP1, len);
$ g2 P2 Q4 o. q* L/ a g/ J p2 U - 107 SetEPTxValid(ENDP1);0 T f- d! B+ o0 b- ^6 Y/ k, P' v# {
- 108 % |7 Q6 v }/ D' `" w
- 109 txFlg = 1;
; G, J" ~/ {2 j/ D - 110 }3 M- j# I1 s4 U
- 111 }
3 R. X1 x7 Z' J4 Y* u: b) i - 112 }+ L' A) m& s0 e2 J
- 113 }
# f' P/ J) K6 I - 114 }
# S b ]0 }" T0 B/ t7 K - 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
. l4 a" E' ?) V% S1 R: u" V: P J9 ]8 U
这里讲下大概意思,函数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也一起存放在上文章开始的链接中。
\* t F0 ]+ l$ @ |