你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32串口发送中断

[复制链接]
万里-363223 发布时间:2014-12-19 08:49
* r) w* \. S3 z
SECTION 1
1 n( {' `& y% r$ G3 O 20141014220925993.jpg
( G8 W+ q$ _: k2 I8 z- O! A
SECTION 2
6 k* a+ h* P$ _; }; \+ w) v
先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下
6 o" V/ ?& S- ^1 c0 U4 O9 E& W4 pvoid USART_SendDataString( u8 *pData )
1 D) L8 @2 v+ [* w# w{9 f2 p- n! A/ `; q- E! a! s
    pDataByte = pData;5 C9 A( K' E0 u4 }3 Y% R
  
3 E4 h: S, [0 r0 O$ `8 N    USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
3 e& ~0 g3 t" }, b, U/ k& v& `    * U( R( A2 [% |7 \* \& y
    USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次, O% m3 h8 M# t4 y
}
  b/ I2 ^5 @$ F- j8 _  g* h( ]. c+ v& O: E( t

# U! [' o# \5 M中断处理函数如下
' x" K! C3 o3 E) \1 m6 [1 Pvoid USART1_IRQHandler(void)4 @2 x/ O6 m$ ^
{5 y1 Z5 w( {; J+ D1 u* D* e
    if( USART_GetITStatus(USART1, USART_IT_TC) == SET  )% t% W+ M! N( p
    {
) {$ j2 k) \/ e" I  {+ j& G' I        if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉
# Z3 v: m3 ^) [- g. B            USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断. clear掉即可,不用关掉TCIE" X8 y; c" X  Y& L1 `/ s
        else/ x. ]3 |9 Y9 _( l5 }( e- h
            USART_SendData(USART1, *pDataByte++ );
3 g% i* J) T" Q5 g6 v( E    }' H: Z1 @* q' f& _# `  T
7 `# G* @( k9 L2 A
}
+ W* Q$ B5 c' H6 L( p9 [
# k5 B# J+ V0 x; [1 N其中u8 *pDataByte;是一个外部指针变量
. |2 P2 t+ V$ b6 i# o( k$ R0 p: ?. p- X
在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。. c- g! l7 F2 P, s# Q
' i- D9 K) d/ o
串口初始化函数如下$ L/ ]- W9 B7 w+ ?
void USART_Config()
8 r; C2 {* n0 @* A# Y6 E{( M1 n8 i/ h8 ~. w: b3 G, I
  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体$ f) H1 F* N# A  z
  
$ f0 D8 ?# P2 g5 Z- r  USART_InitStructure.USART_BaudRate = 9600; //波特率9600; Z. G1 F4 A  s9 r# Z. p3 d0 p
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位6 e- g1 g3 k4 @; A
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位6 Q4 @$ c" o9 P
  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
  G9 U9 T+ J6 E/ k5 K+ q; g  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
9 t$ s, C4 ]" {+ {8 E  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式9 D4 d; K% N8 k( x8 U/ K
  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭/ Y& o0 `& R+ ]' R2 w$ Y6 V
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
7 ^/ x6 g$ N) W  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
2 W( G9 Z( v( Z# m, s9 D9 Q- Y  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;% H0 [  ]9 F: [1 ~) T0 X( Y
  USART_Init(USART1, &USART_InitStructure);//设置到USART1
. W+ L$ ?) x, I' Z+ Y0 p" f1 s  
9 V, |5 ?4 e: q/ l  USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节
0 ^" d. l3 N, H, x: T% I  t
) j: E/ a# l3 v% D  USART_Cmd(USART1, ENABLE); //使能USART1- ~; m* D- O  Y' G
}* J1 k" O+ q" Z% o: ^+ `$ b
这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么??
4 m6 B2 G4 ^7 B, e- F3 d1 v

7 J/ f6 C* ]/ d- X* X
这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.

) J# n1 I- |8 j/ J% `1 m6 \
9 F3 c$ q6 x6 b. |# f( F! `
再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。5 _, V( ~2 M9 R4 t2 n

3 z7 x& W! k$ h! U* Q) J发送函数如下:
! O& O$ k% j0 d' ~void USART_SendDataString( u8 *pData )3 ]7 S/ k5 {) P" u3 G, n& F
{. Q  ]2 H5 R& B' X! F0 s. l
    pDataByte = pData;
6 c2 `9 C7 _: b& K  x7 Q$ E) G    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。 - w4 V; g+ g& E) o8 b. A5 X( F7 t* a
   
9 P0 M. N5 h: h/ T" X}2 N$ ^7 e3 l" t+ l0 w" A

( v1 J# m2 g) e. P中断处理函数如下:
9 A% \0 R  f( X( z+ G' ]8 Rvoid USART1_IRQHandler(void)2 x1 w" W$ S/ F/ ]# o! R
{8 a2 B/ `) \* t0 J. k4 {4 i( }! }
    if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )# M+ C- s4 ~+ w5 S" M
    {
, W0 f& l+ p3 I: X3 V% v8 Q" D        if( *pDataByte == '\0' )//待发送的字节发到末尾NULL了' F( M  q& Y7 U
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断% N; E1 }) J# b* I8 t6 D. B
        else, O) A! F# G6 N: }/ k+ h
            USART_SendData(USART1, *pDataByte++ );2 H; D% d5 v9 S' ]
    }9 W" E) p# g. P& _; B! c. C

/ ^* e  {: I5 M# d9 q2 o. Q}
! E4 }" k7 W3 W5 V  H2 a
; n5 [4 q5 {) _* o# q在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:
1 j" m4 g; m4 y) ]6 c4 Gvoid USART_Config()4 ^' l6 B3 [0 X8 x" _
{# o7 c6 x# q" s" Y3 `# X
  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体
( \9 J. F* v' n9 L8 ~( s: m  6 \1 _) D% r& S1 a5 J
  USART_InitStructure.USART_BaudRate = 9600; //波特率9600
& d5 G: D" c/ l! q3 D7 M1 q  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
% w8 c6 W9 \! X* S3 f  ]/ L  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
/ c3 P" L5 v9 j  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验0 H* @* r- Q" d( \7 l
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制7 ]" l2 o) Q8 d. r+ D( ]. x
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
4 |* i) S/ V% H& D9 Z& q  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭$ L, ~) F8 N, a; K1 n# @
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
" s$ W6 \) I- ^0 v2 w  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;: i: h  I' Y( ?" `: t2 S
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;( A( @# r. {% K2 i. V6 P
/ Y1 i; y- M  K" t2 h
  USART_Init(USART1, &USART_InitStructure);//设置到USART1
8 |. t/ ?0 _8 a  5 j+ n! o6 z9 M
  USART_Cmd(USART1, ENABLE); //使能USART1
; }' S, ]4 y) ?: w  `4 C}$ B) N  Y2 \( H( b- K
/ ^1 p) ~9 a5 e# W7 v" O' g; d; e
SECTION 3
4 N9 j: a5 g* ^; v

4 u. T- ~' R& J1 j- K' {- A! V) z
8 R' F! @: S9 z+ z- P# K, V' C

在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。8 i; N' ]; b8 f. X+ I

# x9 B  c. E3 N% v对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。
0 }  b" D6 \8 D- S* i5 m, A
) n6 S1 E( s9 g$ ^; j' Q: N8 a另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。
0 X5 P; a4 |6 \/ T
: p8 o9 q+ w( H" B  f( E. e0 Y, f* `TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
% ]  b' U0 G6 [% o" q6 @& v7 r; k. i" q
至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。


7 V( h3 G! _' M

SECTION 43 E" p0 _7 ]3 S

       2014-12-19 08 48 55.png 1 x* n( Z# P8 ?- h# v! a
5 g5 d- G9 Y0 B# }$ [1 S

& t6 @" k( y3 y" W: Z. g8 T
赞 收藏 1 评论17 发布时间:2014-12-19 08:49

举报

17个回答
wjandsq 回答时间:2014-12-21 10:53:00
/**9 r, `3 a3 \. }9 n' c
  * @file    usb_endp.c
; v2 g9 b6 a5 P( Q  * @author  wjandcf@gmail.com
  }+ w% x' o6 \3 o+ I  a' V( u7 R  * @version V7.0.2% `- q$ e* V, j2 x" {
  * @date    2014.12.20* f8 j5 w" R! F5 ?/ y
  * @brief   Endpoint routines9 J' s; Y4 K0 F# \
  *  
( O, P. l/ m* n& {1 N) u4 ~  */
) s7 w. |! J2 _1 p/* Includes ------------------------------------------------------------------*/( P) F4 q1 R8 n8 ], d5 C
#include "usb_lib.h") [7 o6 |8 ]) k( A
#include "usb_desc.h"
2 N% C2 T& c2 h3 z2 Y, _$ v#include "usb_mem.h"
, y# _2 J+ {! b1 a4 d" T#include "hw_config.h"
$ _7 ]5 J' y: H3 P: F+ w#include "usb_istr.h"
% z5 N4 N! l! c( X: ?$ M& s#include "usb_pwr.h"  r7 c- I0 G4 e8 D, i
- `8 P# B4 `& J5 x. N' ]4 q* m
/* Private typedef -----------------------------------------------------------*/
$ [& Y: c9 k! B+ s$ d* i/* Private define ------------------------------------------------------------*/
2 j3 {& x9 V) h: A% E0 F2 h2 B$ s1 N. @- `: y; e0 M
/* Interval between sending IN packets in frame number (1 frame = 1ms) */! g) k( G% O% z. u7 @
#define VCOMPORT_IN_FRAME_INTERVAL             5
, F5 g; x1 L  o
) u6 W, A  G& a1 Q, p2 _/* Private macro -------------------------------------------------------------*/- U9 z8 c' S0 P' B" R
/* Private variables ---------------------------------------------------------*/& }+ b( v. r0 K* @4 G- S( z
uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];$ R( {; L, ^5 g1 g7 r6 ~
) D  c* l% k% c2 v+ X
extern uint8_t VCP1_Tx_Buffer1[];      /* VCP1 DMA发送缓冲1 */5 ^% s3 q; z0 G! s4 Y* b% Z9 G
extern uint8_t VCP2_Tx_Buffer1[];      /* VCP2 DMA发送缓冲1 */
# Q1 [& r1 t( X! A" T! eextern uint8_t VCP3_Tx_Buffer1[];      /* VCP3 DMA发送缓冲1 */
$ _/ u6 F3 S$ E. Y1 M2 [
/ l1 \! A6 E  w" |extern uint8_t VCP1_Tx_Buffer2[];      /* VCP1 第二缓冲(临时存放来自PC主机的数据包) */1 m6 Z2 K/ _4 z' o5 ~
extern uint8_t VCP2_Tx_Buffer2[];      /* VCP2 第二缓冲 */
0 {: J, f3 g. X5 `5 L2 ^9 xextern uint8_t VCP3_Tx_Buffer2[];      /* VCP3 第二缓冲 */) w5 |1 D: t) C0 G5 h- n! z
2 y' y9 e8 X9 V2 U" Q
extern uint8_t  Flag_VCP1_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP1_Tx_Buffer1被锁定 *// f4 E+ G: _; C0 ^! Z- c
extern uint8_t  Flag_VCP1_Tx_Buf_Full; /* 1: 表示VCP1_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */& l3 j% R6 e, O! v: }$ M: Q" N. ]
extern uint16_t VCP1_Tx_Buffer_Cnt;    /* VCP1 第二缓冲 收到的字符总数 */
3 d5 ~) m; P8 z$ l3 Z! V; t. y1 ?* {6 c% g$ Z# v
extern uint8_t  Flag_VCP2_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP2_Tx_Buffer1被锁定 */. L. ~- v% u+ S/ K3 d9 ^9 l
extern uint8_t  Flag_VCP2_Tx_Buf_Full; /* 1: 表示VCP2_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */
+ E6 y6 E0 y0 qextern uint16_t VCP2_Tx_Buffer_Cnt;    /* VCP2 第二缓冲 收到的字符总数 */
/ M) R& a# Y: J; b8 q/ X7 |# I& {# p3 m
extern uint8_t  Flag_VCP3_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP3_Tx_Buffer1被锁定 */6 p8 G* x7 w- ]! I2 v1 F
extern uint8_t  Flag_VCP3_Tx_Buf_Full; /* 1: 表示VCP3_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */$ J. U# ~% g' e; n% J5 F- O
extern uint16_t VCP3_Tx_Buffer_Cnt;    /* VCP3 第二缓冲 收到的字符总数 */  m7 B5 \3 Z" P5 P* e0 A) V
9 @9 `5 P: H: G8 e
extern uint8_t  VCP1_Rx_Buffer[];; C) {9 q$ p( I0 J
extern uint32_t VCP1_Rx_ptr_in;
& a6 U" q1 v' |+ rextern uint32_t VCP1_Rx_ptr_out;' `' ^1 T6 e' J' I5 ]$ S
extern uint32_t VCP1_Rx_length;5 M: A+ ], y+ z' g6 ~# {1 x$ x  W
extern uint8_t  CDC1_Tx_State;' A2 q" j+ o8 F* u

. s  Y% R) D5 P" Mextern uint8_t  VCP2_Rx_Buffer[];
3 p" m. d4 C  i6 ?1 o" {extern uint32_t VCP2_Rx_ptr_in;
% Z, z) U+ x: Rextern uint32_t VCP2_Rx_ptr_out;
  K% z5 I. J/ k9 ^' uextern uint32_t VCP2_Rx_length;- L0 o/ @9 V4 z
extern uint8_t  CDC2_Tx_State;
2 c6 g8 `$ f& |' U$ h: ?+ `
  D, |/ R1 k! ~, ^extern uint8_t  VCP3_Rx_Buffer[];
8 f( R9 y% K" V* k$ i7 sextern uint32_t VCP3_Rx_ptr_in;
' k2 j. X- C$ u7 Nextern uint32_t VCP3_Rx_ptr_out;. e3 r" W  O0 b) _2 g
extern uint32_t VCP3_Rx_length;* o* K8 x- H0 d% ~# B
extern uint8_t  CDC3_Tx_State;! j* b6 B5 L5 o, S
, g) V! o2 r6 e/ a8 {
/* Private function prototypes -----------------------------------------------*/
1 y& p/ q; V0 D# I4 _/ x/* Private functions ---------------------------------------------------------*/. H, ?8 Y: W! e* V! T9 ?
7 A) i, R4 \( v% `1 ]
/* USB的IN端点 发送数据到PC主机 */1 Y! R. j1 M5 N+ E" ?6 N
#define EPx_IN_Callback(ENDPx, CDCx_Tx_State, VCPx_Rx_Buffer, VCPx_Rx_ptr_out, VCPx_Rx_length) {\
4 ]: g$ O' Q: `) Y' _    uint16_t USB_Tx_ptr;\
, t- @5 y# E1 x# k0 K    uint16_t USB_Tx_length;\
( P% Z# x( U$ j( y# b& w9 [2 n    if (CDCx_Tx_State == 1) {\% }' P+ c9 \& `; F0 @/ ]7 A% x
        USB_Tx_ptr = VCPx_Rx_ptr_out;\8 O9 G3 R1 w+ N, H
        if (VCPx_Rx_length == 0) {\% T0 ?: J! N. J+ v
            CDCx_Tx_State = 0;\# n7 y* b* w/ [
            SetEPTxCount(ENDPx,0);\
, W2 h; l. j2 w! H) y- i# q            SetEPTxValid(ENDPx);\/ e& A6 @1 O" h/ D$ r6 @' g
        } else {\
2 \! U) J7 D* s" d            if (VCPx_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE) {\
& B  o; B1 {" P; ?2 Z4 c                USB_Tx_length = VIRTUAL_COM_PORT_DATA_SIZE;\' f4 B5 `, T# \7 `$ N7 p
                VCPx_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;\1 ~6 l; s# o2 M/ l4 l0 T
                VCPx_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;\4 }8 _* a) E; @# u
            } else {\( G) S# n- j3 M; W
                USB_Tx_length = VCPx_Rx_length;\
" i; d7 }0 z: u) }- B! G' Y                VCPx_Rx_ptr_out += VCPx_Rx_length;\
% M3 K0 |: p0 y) q3 v( |- F) E0 W* b                VCPx_Rx_length = 0;\
9 K) x$ I6 D# _" F! S' m            }\& m) D. ~$ @% g5 i' K, p4 B
            USB_SIL_Write(ENDPx, &VCPx_Rx_Buffer[USB_Tx_ptr], USB_Tx_length);\
1 {% z7 s! a3 I/ E            SetEPTxValid(ENDPx);\4 s) r8 h; u( X' Z+ d  y
        }\% u# w% Z8 K; O) X9 l
    }\
( E* c5 e3 I+ o  K3 \}
% O' R! D; x+ C- o/* USB的OUT端点 通过物理串口向外发送数据(阻塞方式) */
7 O1 E- w4 U- k; g$ I3 O#define EPx_OUT_Callback(ENDPx, USARTx, GPIOx, GPIO_Pin_x) {\0 k8 V9 _% U5 {8 m2 o6 l
    uint32_t i;\
7 `; V9 z2 T6 g; {  h' M- L! v8 _+ k    uint16_t USB_Rx_Cnt;\
1 |1 E, [# H: a* d3 @# {7 R' k  b, e, A    USB_Rx_Cnt = USB_SIL_Read(ENDPx | 0x00, USB_Rx_Buffer); \
- T  |/ l8 u" V) ]    GPIOx->BSRR = GPIO_Pin_x;\
: G! [, f* g% J  M' K0 A! r9 Q    for (i = 0; i < USB_Rx_Cnt; i++) {\
- h% {" k3 v1 m        USARTx->DR = *(USB_Rx_Buffer + i);\
. k4 k' D, J; V" A' S# I! p        while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);\
2 D: s/ u5 Y8 `    }\7 Z/ O- f5 @6 s$ w8 n; d5 k
    SetEPRxValid(ENDPx);\
  T: a/ ]0 z7 C2 W9 g/ v    while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);\
, N9 m3 E6 a3 `& ^7 J6 W6 v    USART_ClearFlag(USARTx, USART_FLAG_TC);\
4 V; F: k1 C. R0 e2 W    GPIOx->BRR = GPIO_Pin_x;\9 l; M- W/ c7 y
}
+ N7 Q" ^+ S0 d2 k1 m) S
& ]3 [3 D  N. m+ i5 @+ e$ W) o/* USB的OUT端点 通过物理串口向外发送数据(DMA方式) */
! v6 x: N" h$ K0 W# Q#define EPx_OUT_Callback_DMA(Flag_VCPx_Tx_Buf_Use, ENDPx, VCPx_Tx_Buffer1,VCPx_Tx_Buffer2,\$ w6 X( l9 x* c+ K: A  {2 c
    GPIOx, GPIO_Pin_x, DMA1_Channelx, VCPx_Tx_Buffer_Cnt, Flag_VCPx_Tx_Buf_Full) {\
0 L# z. Q" n8 m7 h# ]0 Y    uint16_t USB_Rx_Cnt;\4 T. k/ K: `2 a7 o, D! F
    if(Flag_VCPx_Tx_Buf_Use == 0){\
* Z4 w, _3 Q$ Q/ f9 @        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\* x1 ^) G- k1 C; ^9 Q6 z
        PMAToUserBufferCopy(&VCPx_Tx_Buffer1[0], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\# y0 H/ v8 ?7 d+ k/ o
        SetEPRxValid(ENDPx);\8 O' n/ o! @4 n* H" C  D
        GPIOx->BSRR = GPIO_Pin_x;\0 A  ]. {& c% q" t/ C7 L
        DMA1_Channelx->CNDTR = USB_Rx_Cnt;\; s5 l4 ^+ H: Q3 B, D
        DMA_Cmd(DMA1_Channelx, ENABLE);\
9 Z6 O& s. ^6 ~- X! q8 N$ |8 I% v7 _        Flag_VCPx_Tx_Buf_Use = 1;\
* R& h3 T. F) r/ ^6 u$ ?        VCPx_Tx_Buffer_Cnt = 0;\
% T/ O- j0 k7 e    } else {\3 e( V- p  W& Y' F: U1 S+ ?
        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\2 K% N- ?% o7 l0 L
        if(VCPx_Tx_Buffer_Cnt < (1024-128)){\6 N8 _5 q& S7 E& g
          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\1 G& ?% r0 J  z5 v: n$ R
          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\
7 x* R: K9 A- r8 F4 ?" \          SetEPRxValid(ENDPx);\
  J9 D% G/ ?  o. {4 D- W7 l" R$ I        } else {\# y1 Y! [, @2 Z% c
          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\' B# \& B/ ^& @. Z" Y. D+ a
          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\
% L  H$ _, Q5 G8 E! `+ _          Flag_VCPx_Tx_Buf_Full = 1;\, j" R0 n) z. b) n: Y" [2 C
        }\8 c  t7 n4 O# y; J/ x& K9 h$ N# d
    }\" x% G: K( _) b; R4 g/ U3 G
}
/ v. z% D: h# ?& i/**/ p3 @9 K! v7 M. p0 I! f
* Function Name  : EP2_IN_Callback
' [3 ^5 G$ v3 d" Y * Description    :( P1 M6 v; H/ S' o2 ]/ Q0 ]1 n
**/
4 v  H2 C" c. U' J- K9 fvoid EP2_IN_Callback (void) {
/ i4 N. T( u% F6 d1 Y' N% D* u    EPx_IN_Callback(ENDP2,CDC1_Tx_State,VCP1_Rx_Buffer,VCP1_Rx_ptr_out,VCP1_Rx_length);) @  b$ K8 J" m1 F; e
}
! k8 n6 ?. f2 {; g/ C! U+ Y/**0 L9 ^! j% T2 \" L! Z8 q
* Function Name  : EP4_IN_Callback
+ s. F) r) p4 r2 w5 w- V * Description    :7 T- R6 @8 C: i: a
**/# i6 m2 r: G9 b) w% |" i( g
void EP4_IN_Callback(void)( \( Q* G1 x1 y. P$ s1 l
{
8 i6 B7 V/ B$ X& l    EPx_IN_Callback(ENDP4,CDC2_Tx_State,VCP2_Rx_Buffer,VCP2_Rx_ptr_out,VCP2_Rx_length);
3 u& _: F9 [2 F/ Y- J  [}
5 R* X' I5 T/ V8 U2 Q/**# Y& B$ s! A# |4 h2 u
* Function Name  : EP6_IN_Callback
  J( v* Y1 Q0 Y * Description    : VCP3 向PC主机发送数据9 C  {! v4 M: m' j+ x, t
**/
7 @, D7 o$ ]$ x0 Svoid EP6_IN_Callback(void)
9 D9 q* I  e* r: F5 Z. A' u5 ?( I9 E{; J' m1 }. E& L; \3 i  m2 |
    EPx_IN_Callback(ENDP6,CDC3_Tx_State,VCP3_Rx_Buffer,VCP3_Rx_ptr_out,VCP3_Rx_length);  
# Z( K9 _' \- f}) @  {$ u% a2 C! t+ @8 Y
/**
" W$ L. \+ j; }. \* w" a * Function Name  : EP2_OUT_Callback7 H  m. p0 L; ~$ [
* Description    : VCP1 通过USART1向外送数据
/ A; D# C! W# U6 j. @$ f5 |  I **/
9 ?$ R% g+ ~. ?/ Xvoid EP2_OUT_Callback(void) {
+ ~8 o. f3 W& C1 E#ifdef USB_DMA_SEND. v$ n/ X. i$ ^" F1 Z9 t
    EPx_OUT_Callback_DMA(Flag_VCP1_Tx_Buf_Use, ENDP2, VCP1_Tx_Buffer1,VCP1_Tx_Buffer2,\5 f1 y. ~# W/ M% D7 \
            GPIOB, GPIO_Pin_9, DMA1_Channel4, VCP1_Tx_Buffer_Cnt, Flag_VCP1_Tx_Buf_Full);
! w1 R8 @$ ?$ j! T$ b. ]3 D& p4 v; `#else
8 m  o& V7 Z0 m& C0 ^    EPx_OUT_Callback(ENDP2,USART1, GPIOB, GPIO_Pin_9); // DR1 PB9
2 Q* e, R" ?0 {0 v" W#endif
  G5 ^1 P, \9 V}
' }3 Y! q: s& O; |8 {# @/**7 p$ Z- X. B9 Y3 {+ O, o
* Function Name  : EP4_OUT_Callback
  `4 {3 z! B# L6 e0 z- m" E* E * Description    : VCP2 通过USART2向外送数据  i: i1 W( t3 h) T! s' w( p
* Input          : None.5 s7 ~! U/ b8 ~) ?! C7 u3 E7 c) Z2 N# {. ^
* Output         : None.
; O- L, W1 p% h. G * Return         : None.% x4 {' z: u6 o) g& N! R
**/
& H) I; i+ D  ]. N  F2 f- Nvoid EP4_OUT_Callback(void)
+ ?( {) Y) P8 _1 E{5 F2 k1 E4 U% C% [. s& p
#ifdef USB_DMA_SEND/ |, o% i9 R/ i6 _% b% f) `8 O
    EPx_OUT_Callback_DMA(Flag_VCP2_Tx_Buf_Use, ENDP4, VCP2_Tx_Buffer1,VCP2_Tx_Buffer2,\
1 k9 C* N; J. b  b3 }/ T  ~6 w! U( ]            GPIOB, GPIO_Pin_8, DMA1_Channel7, VCP2_Tx_Buffer_Cnt, Flag_VCP2_Tx_Buf_Full);
( X+ g' b0 X5 }! H#else  `6 C' O: `1 W1 }
    EPx_OUT_Callback(ENDP4,USART2, GPIOB, GPIO_Pin_8); // DR2 PB8
5 J7 Q0 L4 c/ K#endif5 v9 D2 y, W0 n8 k
}
: h* P/ K9 v; n; Y- D/**. L# {& F0 ~2 Z3 _
* Function Name  : EP6_OUT_Callback
4 ?! L4 v3 D2 e6 R * Description    : VCP3 通过USART3向外送数据
6 n$ _+ w2 f; _  n **/4 ^* n, F0 L  F1 ^
void EP6_OUT_Callback(void){
# b  m5 G  g. [! U3 U, ^#ifdef USB_DMA_SEND% d; m1 ~! O+ y6 s! O. _' l
    EPx_OUT_Callback_DMA(Flag_VCP3_Tx_Buf_Use, ENDP6, VCP3_Tx_Buffer1,VCP3_Tx_Buffer2,\
# T/ q7 K6 P( n            GPIOA, GPIO_Pin_5, DMA1_Channel2, VCP3_Tx_Buffer_Cnt, Flag_VCP3_Tx_Buf_Full);7 R" A0 F/ F' t$ F
#else  
- N) b7 Q) V+ d; k- }! s* @4 W    EPx_OUT_Callback(ENDP6,USART3,GPIOA,GPIO_Pin_5);  // DR3 PA5
! q$ I% Q: G7 }#endif& Y% q& `; v: L  r; k. A; d
}
- s, U  f. Z/ F/**
3 n2 k/ A( w, [# f0 I * Function Name  : SOF_Callback / INTR_SOFINTR_Callback) O$ O$ U. t$ P+ S
* Description    :
7 [- N1 U6 s$ `5 h3 d **/) `# G2 ], E7 r" J0 D  Y- u
void SOF_Callback(void)
& l3 B* g% p! B2 K% ?{, k  j" a/ E) J; Z
    static uint32_t FrameCount = 0;
! Z' y% s& R, V: N3 _3 y/ v6 ~2 m    static uint32_t Led_Count = 0;6 N. G; ?1 l/ U3 [4 F
    if(bDeviceState == CONFIGURED) {* ^# c! J7 @7 R  n& g3 {
        if (FrameCount++ >= VCOMPORT_IN_FRAME_INTERVAL) {* n6 m/ k+ X! Z0 W
            /* Reset the frame counter */% X, E' T( V; z( U& M
            FrameCount = 0;  Z0 Y$ o; y) O4 d' A/ l+ b
            /* Check the data to be sent through IN pipe */; ?/ q2 L. m5 f3 X8 n. ?8 Y( M8 }
            Handle_USBAsynchXfer();
, h5 \0 @  t, s# b% U- R# S; D, e        }
9 W0 ~% b' t) N) H  p8 ]$ c8 z# L2 [    }- _7 a% o; r2 T9 w8 A1 I% A. y
    if(++Led_Count > 999) {
1 Z9 {8 d- y" ^5 f        Led_Count = 0;2 s) b0 n# a. d! b; B( X' W( k
        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0){# d3 }$ K6 j+ h2 ~3 Q* I
            GPIOB->BSRR = GPIO_Pin_2; // PB2 H
8 S- q1 _+ U, |" \, B+ w; o; }        } else {1 }/ Z& }5 B6 I" E2 j
            GPIOB->BRR = GPIO_Pin_2;  // PB2 L
/ j  l5 i0 y8 d! h* E# y( G        }7 p$ M/ A! b9 B( j" t! R
    }
4 v* I5 @- A3 s8 c0 V1 b$ i}% `/ P! h' ?7 I- W* Y* ~/ F

, ?/ G  j4 |. O6 {; a. ?
万里-363223 回答时间:2014-12-19 08:51:16
SECTION 4) v( b* f9 D% {& }; O) Y& X* E
        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断系统和51单片机一样的简单。) L- R0 V# @' k% E
        对于接收终端,就是RXNE了,这只在接收完成后才产生,在执行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代码时不会进入ISR。但麻烦的就是发送有关的中断了:TXE或者TC,根据资料和测试的结果,TXE在复位后就是置1的,即在执行USART_ITConfig(USART1, USART_IT_TXE,  ENABLE)后会立即产生中断请求。因此这造成一个麻烦的问题:如果没有真正的发送数据,TXE中断都会发生,而且没有休止,这将占用很大部分的CPU时间,甚至影响其他程序的运行!
" }" m: q& [4 w  u1 _* @$ v* R        因此建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。: u" @' k: t2 Y" U6 \6 N
        对于发送,需要注意TXE和TC的差别——这里简单描述一下,假设串口数据寄存器是DR、串口移位寄存器是SR以及TXD引脚TXDpin,其关系是DR->SR->TXDpin。当DR中的数据转移到SR中时TXE置1,如果有数据写入DR时就能将TXE置0;如果SR中的数据全部通过TXDpin移出并且没有数据进入DR,则TC置1。并且需要注意TXE只能通过写DR来置0,不能直接将其清零,而TC可以直接将其写1清零。
- g& |9 k0 c' N! F7 U3 T        对于发送单个字符可以考虑不用中断,直接以查询方式完成。
& X" w7 Y# ?3 v0 K4 O2 b) T( N; f        对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。
3 B% ^- `5 U0 g( |, _6 ]; ~' G       这里简单分析上面代码的执行过程:TXE中断产生于前一个字符从DR送入SR,执行效果是后一个字符送入DR。对于第一种情况,如果是可显示字符,就执行USART_SendData来写DR(也就清零了TXE),当最后一个可显示的字符从DR送入SR之后,产生的TXE中断发现要送入DR的是字符是0x00——这当然不行——此时就关闭TXE中断,字符串发送过程就算结束了。当然这时不能忽略一个隐含的结果:那就是最后一个可显示字符从DR转入SR后TXE是置1的,但关闭了TXE中断,因此只要下次再开启TXE中断就会立即进入ISR。对于第二种情况,其结果和第一种的相同。
. |- Q9 U% q* k9 Z7 O         对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:
  S5 {) F& }7 K+ d/ l- Fextern __IO uint8_t TxCounter1;
: U* j) O* {, }" g* textern uint8_t *TXS;
4 Z  e6 g4 J$ H6 x' ?* \) o5 fextern __IO uint8_t TxLen; 7 J) _" G% f; w; j( l
void USART1_IRQHandler(void)1 x( g1 V  E0 P5 U! A- w
    {
3 G5 @$ F$ A. _" o1 }        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
! {. m8 j/ ?5 ^  B/ u7 Z* p            {                                               - U# J# \; d+ }% Q% g* n8 z1 F
                if(TXS[TxCounter1]) //如果是可显示字符8 J0 Q9 e) s3 u/ C( s" _
                    { USART_SendData(USART1,TXS[TxCounter1++]);}5 q8 t$ O9 P( N5 S
                else   //发送完成后关闭TXE中断,5 i; K8 l# `0 n0 b, V: y4 Z5 M
                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                        
6 u( S& J/ W( @/ L3 i. ]/ M  X2 N9 G            }                   4 z% b& v( q  [
    }
7 c, R. L2 Q8 `9 x& y        对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:6 v3 C! s1 J! x, L; u+ W
void USART1_IRQHandler(void)) G$ i$ y/ f9 v9 b2 T8 j, c
    {9 L# g' j. t" E; h% X/ r
        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。
: [% d% i& W8 `  ^& t            {                                              8 E+ U2 C/ M$ E) D( C* Z
                if(TxCounter1<TxLen): v0 c. P) H5 j; \; |
                    { USART_SendData(USART1,TXS[TxCounter1++]);}
+ {& i+ e# c5 }, N: E* l                else   //发送完成后关闭TXE中断: F3 {) |& U( V5 [. h0 S# z
                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                         
) y* A& z7 P2 E; k            }                    " @. d1 c/ L2 U9 l. g% r/ h( o
    }0 X8 H) @7 i" L% Z: z
        事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!2 Z3 |3 B6 \7 f& ?+ P& v
        在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:
( N  D" i* n) juint32_t *TXS;4 f% J9 A* f2 I/ b' L/ q( _
uint8_t TxBuffer1[]="0123456789ABCDEF";" H8 H' r2 x  A& z2 X: C4 c& ?
uint8_t DST2[]="ASDFGHJKL";
5 |5 X. G& X% [( G) ]) Z) B/ {__IO uint8_t TxLen = 0x00;
' w1 ]0 ~6 X) F     TxLen=8;                               //发送8个字符,最终发送的是01234567; Z) G) N" }# X1 Z3 Z) z* G
    TXS=(uint32_t *)TxBuffer1;   //将TXS指向字符串TxBuffer1! O: P" e  t+ F8 q2 x$ o7 e5 F' S" r
    TxCounter1=0;                     //复位索引值3 b5 L: Z% D( T
    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);   //启用TXE中断,即开始发送过程( A* V% g, U- I
    while(TxCounter1!=TxLen);   //等待发送完成* e7 C4 K8 t4 X
$ ~! u4 O1 Y! Z
    TXS=(uint32_t *)TxBuffer2;   //同上,最终发送的是ASDFGHJK# }) N& `$ W% a" i
    TxCounter1=0;4 h3 e+ J& S  e! h5 ^" n) {
    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);
1 P8 K5 m$ Q# W  D4 m/ B. h& [3 Z    while(TxCounter1!=TxLen);# p6 u% d+ d4 W: o
        以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。
wamcncn 回答时间:2014-12-19 13:32:32
万里-363223 发表于 2014-12-19 08:511 U8 b2 F& M# X+ u
SECTION 4, A& A& C/ l! k1 q) Q: u$ X
        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断 ...
# }# W2 \" [; N( B1 d6 p: u" }
强大的同时,复杂也跟着来了,51是基础,STM32是进阶
万里-363223 回答时间:2014-12-19 08:50:11
发帖子,受到字数限制了
废鱼 回答时间:2014-12-19 09:28:07
谢谢楼主分享。
巅峰残狼 回答时间:2014-12-19 13:16:21
太牛了,谢谢分享
wamcncn 回答时间:2014-12-19 13:30:09
学习,楼主总结的不错,谢谢分享
万里-363223 回答时间:2014-12-19 14:25:00
wambob 发表于 2014-12-19 13:32% s/ m. G. A9 d# Y
强大的同时,复杂也跟着来了,51是基础,STM32是进阶

$ h; U! T* K- ], x恩恩,没错,鱼和熊掌不可兼得
小贾-370388 回答时间:2014-12-19 15:30:39
充充电
% z9 {, Z) B! G- y5 v3 F6 E. h
霹雳之火 回答时间:2014-12-19 23:35:32
谢谢分享,楼主辛苦啦,果断顶起
奔跑小蜗牛 回答时间:2014-12-20 08:18:54
.。。。。总结的好细致。。。。受教了。。不过,51终究是51,用起来简单,当然也就没那么强大了,stm32虽然复杂,那只是在配置和工作原理上,都熟悉了,性能绝对够不错,万事开头难,上手了就好了。
万里-363223 回答时间:2014-12-21 09:39:00
奔跑小蜗牛 发表于 2014-12-20 08:18
# \) c9 q# S3 u8 q1 U& q+ {: T2 G.。。。。总结的好细致。。。。受教了。。不过,51终究是51,用起来简单,当然也就没那么强大了,stm32虽然 ...
- f+ E8 S3 s- r+ q. U- y9 }
恩恩,没错,还是STM32爽
wjandsq 回答时间:2014-12-21 10:51:09

STM32的串口通信,最好使用DMA方式发送。

本帖最后由 王建 于 2014-12-21 10:57 编辑
8 Y# Y  S( ?7 C. B8 d. B+ e& S/ o$ b3 s+ D% l
至于接收,根据实际情况而定。
wjandsq 回答时间:2014-12-21 10:59:47
补充下,上一段是USB转多路串口(USB-Multiple-CDC)例程中的部分代码,仅支持STM32F103xx系列。
12下一页
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版