串口调试在项目中被使用越来越多,串口资源的紧缺也变的尤为突出。很多本本人群,更是深有体会,不准备一个USB转串口工具就没办法进行开发。本章节来简单概述STM32低端芯片上的USB虚拟串口的移植。在官方DEMO中已经提供了现成的程序,这里对修改方法做简单说明。
q) x/ E+ _ y' O首先打开官方demo我们开始进行移植,第一步复制我们可用的文件,操作如下: Projects\Virtual_COM_Port文件夹下,复制红线部分 图1
4 @# z+ {1 K* T0 g9 z( E* M; |3 m4 T0 l/ `) D* X4 v/ F6 w2 E2 s) Z) a
图2
- y7 O+ {9 E; i* T
) @; P7 y2 }' n) z' C9 j' \我为了方便演示统放在usb/src文件夹下: 图3
6 e, g8 \/ {+ h( x7 h; m5 V% q6 Z( H# P; c7 Z& V& A, w7 C1 y
现在复制USB的库文件,这些文件不需要我们修改: 图4
' @! z* z; X! O/ o* Z* H# ]& N7 u# B! ^9 e" u7 l1 P
上图中的文件统一放在usb/lib文件夹下: 图5 % f) i0 E0 e; i9 E
E3 i5 D3 _3 Y- V0 j, J, M3 E. U3 Q
好了现在所需要的文件我们以复制完了。这里先讲一下DEMO程序的主要工作流程: 图6 % x/ \ v1 }0 ~5 a4 D
由上图可知,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 ------------------------------------------------------------------*/# n- ?( }1 I+ P* Y2 ^1 [9 n! h
- 2 #include "hw_config.h"
# w! o: N4 P" H4 |0 j - 3 #include "usb_lib.h"# N% Z! ?( Q& H3 f6 N5 Y
- 4 #include "usb_istr.h"; V* A( F5 s( ?; Y
- 5
+ g! g% H+ x; I2 B# @3 E - 6
$ C' c! B6 d3 \3 c - 7 /*******************************************************************************
# J1 j1 ~2 @! f% B. J% e/ n, J- o - 8 * Function Name : USB_IRQHandler
' P! E5 M) h7 ?! \ - 9 * Description : This function handles USB Low Priority interrupts1 Y) x& J" W3 L( W" G
- 10 * requests.
1 z/ [9 B) d/ l/ w- B' [( R2 S7 Q. L* J$ K - 11 * Input : None) W$ V" V4 `' X7 {
- 12 * Output : None
( k" A+ C% o7 A( T+ C6 |0 q7 S - 13 * Return : None4 b: i. s% }% k C. d
- 14 *******************************************************************************/. t |' N2 P2 u
- 15 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)|| defined (STM32F37X)6 z$ O4 U( X. F4 h" ? @2 Q/ [
- 16 void USB_LP_IRQHandler(void)6 d) ?* N/ r2 j9 C& X$ s4 B8 N) q# Y
- 17 #else# W/ A7 P5 | ~4 i
- 18 void USB_LP_CAN1_RX0_IRQHandler(void)0 j3 {* K+ B& F8 s3 W+ M
- 19 #endif
$ \% \ O9 Q, W9 T9 H/ l8 X - 20 {
8 @$ U$ |0 v- B) U2 m0 @ - 21 USB_Istr();; o+ Y8 d0 U$ v( S Q1 {
- 22 }
/ q( s: |6 ^6 N- G4 ?3 \* ^1 ~ - 23
; I! c$ A; f2 ]! O/ h - 24 /*******************************************************************************- B* Z- G! ^7 s J7 Z: o/ x
- 25 * Function Name : USB_FS_WKUP_IRQHandler+ y" g& f) { q1 h) H
- 26 * Description : This function handles USB WakeUp interrupt request.
* S- F1 ?( s9 T3 N2 A! Y - 27 * Input : None
: N' ]* F. ]2 B! `5 | L - 28 * Output : None
* U/ r. m# L! V9 L" j6 l - 29 * Return : None
* g& O2 a. G6 o, U4 w7 g4 Y - 30 *******************************************************************************/
; s1 D' D4 x& h0 A - 31
+ m5 B1 L& t0 D/ i( }( w! K - 32 #if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS). o; a5 l! G: l5 v2 q4 G+ h
- 33 void USB_FS_WKUP_IRQHandler(void)- _1 D% s$ `: V' w( ^: w N
- 34 #else* k/ \8 y: {; K. w; `- @
- 35 void USBWakeUp_IRQHandler(void)
. z" {1 p* ~0 [) P0 H! f - 36 #endif" d: B7 o( Q9 `! `8 G' I
- 37 {
; C. t- g5 l* g% x0 ^5 x. ` - 38 EXTI_ClearITPendingBit(EXTI_Line18);5 m. v0 \% _+ w. d, F
- 39 }
复制代码 0 S% d: t3 g2 O2 ~2 K
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 1 @- z v+ b3 O+ Z
1 [3 e8 E5 ?- z4 T' I
关于USB口使能控制引脚,需要根据开发板的引脚定义来修改宏定义platform_config.h文件中,笔者使用的是神舟3号开发板,控制信号刚好和demo相反,所以修改hw_config.c代码如下: 代码3 - 1 /*******************************************************************************
0 \4 F A$ A% w - 2 * Function Name : USB_Cable_Config' i* D/ U$ i% m9 I+ n
- 3 * Description : Software Connection/Disconnection of USB Cable3 M1 W5 x* j8 q% E6 v& P$ W
- 4 * Input : None.
5 ?% l8 y1 X4 b, t/ \ - 5 * Return : Status
, Y5 W+ i1 A3 M' t: ^; O6 L - 6 *******************************************************************************/
9 v0 O; e8 w) {2 g0 E - 7 void USB_Cable_Config (FunctionalState NewState)0 z( k" T8 y" c: x; h; N& S
- 8 {
6 H$ ^1 m4 x- d6 ~ - 9 if (NewState == DISABLE)
|% y4 C( h3 c - 10 {/ ?1 I' j$ N: k. a% D
- 11 GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
- V$ } J! r* o6 c - 12 }
* o' L+ z$ y c - 13 else8 C' c5 z8 T- j0 o! P
- 14 {
1 Y4 m2 e* U0 e$ d0 K: ? - 15 GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);7 O6 x" ], @: e7 G
- 16 }
2 h4 L- M# N; x& w6 J4 D' S2 R - 17 }
复制代码 + ~ k L; {6 m+ V8 t7 W# ]
3,现在修改USB 回调函数中的代码usb_endp.c文件。使用下文代码替换: 代码4 - 1 /* Includes ------------------------------------------------------------------*/0 {8 T: M8 @; ]! J8 i9 r3 x4 q
- 2 #include "usb_lib.h"4 C# }$ M3 i, E- @$ Z. ?
- 3 #include "usb_desc.h", z$ T K: {- O$ u! L( n
- 4 #include "usb_mem.h"
[6 s' I: P- D( t# K( Z- g* O - 5 #include "hw_config.h"
/ M( \- K5 U3 j. Z$ w - 6 #include "usb_istr.h") K: e m- W4 B# ^ Z7 }
- 7 #include "usb_pwr.h"
! @+ n( ~; K7 D5 l - 8 & D& G; K# U8 _9 @& h( v. ?
- 9 /* Private typedef -----------------------------------------------------------*/; L$ }# i [% P4 _ T; z' I
- 10 /* Private define ------------------------------------------------------------*/7 ~6 u7 F5 B/ {/ p' Q0 N* w
- 11
7 l% a. g5 G0 K& r - 12 /* Interval between sending IN packets in frame number (1 frame = 1ms) */
; V% a8 g. Z( M5 B - 13 #define VCOMPORT_IN_FRAME_INTERVAL 5; [/ d. A5 |" K+ P
- 14
+ y% _( M) A0 M, s. i' n' o9 ` - 15 /* Private macro -------------------------------------------------------------*/7 ?! U8 r, x2 Z9 L
- 16 /* Private variables ---------------------------------------------------------*/: b7 h% \, b7 ?* W
- 17 static uint8_t txBuffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};& N% w& _! w/ ]' {8 A
- 18 static volatile uint8_t txFlg = 0;
) a2 k7 o4 l4 p0 R6 s - 19 static volatile uint32_t FrameCount = 0;- p' R! Z8 `5 @' ]7 ]( ^
- 20 + E% Z" e% p( L) `
- 21
% q1 e2 a) s7 F$ _0 C - 22 /* Private function prototypes -----------------------------------------------*/
& B; y5 m5 O9 X9 l - 23 /* Private functions ---------------------------------------------------------*/
+ T* x) a& e. I+ q5 u, T+ N7 k& X$ ?1 t0 I - 24
$ {6 m; Z6 ?5 c- O* v4 _5 K, T - 25 /*******************************************************************************) q; T) P- x0 V9 C) k- ~
- 26 * Function Name : EP1_IN_Callback
9 M: B) n4 q% T2 B6 _: w - 27 * Description :/ L- W* K) j$ j) m
- 28 * Input : None.6 G" |' j& J- W3 l: \9 A+ D
- 29 * Output : None.
; P$ J$ R1 r8 t3 x E, f5 \ - 30 * Return : None.
& I. S; g& N! c. D- y - 31 *******************************************************************************/ y) A; w( V: D. l3 d
- 32 void EP1_IN_Callback (void). p. S. U/ [ h$ W; J
- 33 {
% H' ?& H, K2 c" d8 {0 r: | - 34 uint16_t len = 0;
9 J! k0 r j: g3 K% \ - 35 + `' A' L! k O
- 36 if (1 == txFlg). U/ i. y: {! q9 ]- ~
- 37 {8 Z+ L9 _0 p2 s% c. C: f
- 38 len = USB_TxRead(txBuffter, sizeof(txBuffter));+ N& L3 t) s, @: v. y6 Q, D
- 39
) l; T* j! e8 ^ - 40 if (len > 0). V0 D9 p; \8 M! q
- 41 {
8 T) q: _" Q( F; f9 v. A- b - 42 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);9 _8 d+ z- H# W: @. X8 }
- 43 SetEPTxCount(ENDP1, len);8 I, y( Q$ ^/ F* ?$ V
- 44 SetEPTxValid(ENDP1);
; D0 g7 p1 S7 }4 r) h - 45 FrameCount = 0;
: R( t- I0 u! Q& F5 ] - 46 }6 P9 s6 c3 \5 \! f) l* s
- 47 else
2 f, j) \# y9 I, f: j0 B+ _ - 48 {$ M/ @2 T7 h* g- O
- 49 txFlg = 0;
: [! m% {3 _' d# T2 ? L% I; Q - 50 }
2 e+ w4 p4 r8 R6 | - 51 }
+ s: f( J4 B4 ~3 ]6 n5 y2 u4 _ - 52 }
/ p7 p. y6 ?3 }5 p2 u5 {4 t - 53
8 ^2 B/ O9 N# C" L - 54 /*******************************************************************************3 K2 H; t2 w: v$ ]# y6 q
- 55 * Function Name : EP3_OUT_Callback- u; J" `; W% [3 h
- 56 * Description :
; l' A6 A$ y! ^3 W, V, z - 57 * Input : None.
; A* j. W$ Q3 j, t - 58 * Output : None.* _( N! {7 X& ?; a- p
- 59 * Return : None.
( Q( D+ |5 e5 ^7 J1 D" A - 60 *******************************************************************************/) _6 k! r: F D; Z
- 61 void EP3_OUT_Callback(void)
/ O; F% J" ?! x" a7 o - 62 {/ l1 v2 n! H. o! r8 I; m ]+ g8 \) E
- 63 static uint8_t buffter[VIRTUAL_COM_PORT_DATA_SIZE] = {0};
* P; Y: f3 `/ t - 64 2 p% f, w2 ~8 G8 h& s( ~
- 65 uint16_t USB_Rx_Cnt;
7 \+ h4 [& a& ^9 P1 g1 ? - 66
3 {$ |! h G# B9 ~: V1 ` - 67 /* Get the received data buffer and update the counter */! n+ `/ H9 V0 J, G) ?; E
- 68 USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, buffter);6 M, u. V! w7 l* J! G
- 69 ; A5 t1 X% U6 b) u. K
- 70 /* USB data will be immediately processed, this allow next USB traffic being
; H7 ]3 H T# p" ~6 l - 71 NAKed till the end of the USART Xfer */ s6 j) F' d5 C6 e# g4 c
- 72 USB_RxWrite(buffter, USB_Rx_Cnt);
: {# h+ H- ~, N9 Q) j& N& [; E' M - 73
$ m- f+ V3 y' f9 s: p& } - 74 /* Enable the receive of data on EP3 */) @) X4 @6 W0 l+ @- n* M
- 75 SetEPRxValid(ENDP3);* F+ x$ O* c0 H
- 76 2 ]/ u$ J) ~1 i6 D) F/ N
- 77 }1 u! ]" Q2 Y2 \* n6 s5 [
- 78 # V( ~1 Q3 c& d( f
- 79
$ q# S& Q: E( d7 @# ?; C/ {: c+ n - 80 /*******************************************************************************
1 P& [7 ]3 e. {. j, g - 81 * Function Name : SOF_Callback / INTR_SOFINTR_Callback
& f+ A. v5 }0 o' j' x6 o( X - 82 * Description :
: H# `! R6 c" a- b" |. _* g8 o( A# u - 83 * Input : None., [3 u: J! j% c& x N5 Z4 ]- O) U9 ^
- 84 * Output : None.7 W# T1 G7 F# z; }" C* l
- 85 * Return : None.9 J6 t5 f$ C9 J
- 86 *******************************************************************************// J* f7 o8 h+ X% V1 k
- 87 void SOF_Callback(void)
* |- C4 @: c: O( P$ x7 [ - 88 {
# o& u1 R/ U6 N9 Z) }# _ - 89 uint16_t len = 0;
2 [0 e7 ~6 o. Q7 E" H9 a) {+ Q - 90 & o* B3 ?2 e. Q8 L
- 91 if(bDeviceState == CONFIGURED)
n/ O" ]2 U7 d9 z0 A. F - 92 {
. \8 V0 ~1 J, `& Y' T2 B6 P+ H9 A) [ - 93 if (0 == txFlg)3 c2 I! U2 f. F% m+ {- R
- 94 {
( x& ?: O, e% m) o9 X* A Z* o1 o - 95 if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
6 m% d0 S( Y' D* D: I - 96 {6 I* U+ A0 _: `
- 97 /* Reset the frame counter */
% O* V& m" m g4 R) B - 98 FrameCount = 0;$ t; o' ^. G! C" P1 R- p" y& A
- 99
! z' h. I0 R: P. y8 G( G3 J0 m - 100 /* Check the data to be sent through IN pipe */
. P5 q/ Q4 f- T/ |( q - 101 len = USB_TxRead(txBuffter, sizeof(txBuffter));% h/ e- ^9 I) A7 F1 l+ c
- 102
y! _% |4 d& n! j9 M2 l - 103 if (len > 0)
- e5 g2 D ^6 R8 S - 104 {- N t# E4 C7 v) ^4 h
- 105 UserToPMABufferCopy(txBuffter, ENDP1_TXADDR, len);6 e' b% v- C$ J' s/ ?$ @. k7 v
- 106 SetEPTxCount(ENDP1, len);* T0 u2 m B+ p6 ]; |' g9 `9 {
- 107 SetEPTxValid(ENDP1);, L/ n3 p, E/ Y. D! b
- 108 2 ~: D( t% g* |2 [) s
- 109 txFlg = 1;
; G/ h; J& F9 \* D - 110 }
/ b, @- T h: i7 f% I1 x2 w F& T - 111 }; u# g' R6 R: W s4 ?) y
- 112 }
1 `& c4 _* ?8 ~2 y4 b' k7 j3 Z$ r - 113 } $ B$ U M# ^% Q& N0 `0 `9 f
- 114 }
* O4 y& Y' j- n - 115 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
" ]+ `3 X8 p* e4 y" Q0 ]9 _5 r0 A7 U/ V& r: n7 h* 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也一起存放在上文章开始的链接中。
! R5 i* w7 s' X" {7 I |