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

STM32串口发送中断

[复制链接]
万里-363223 发布时间:2014-12-19 08:49

6 b( {* ~  c" uSECTION 10 s- Y' I5 z& B& v% ^
20141014220925993.jpg 9 L3 w/ o4 _  N' C3 Q3 X' ?
SECTION 2

! _( D4 d& \' B# ~% m$ ~% z1 }
先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下5 l- _! J" S; B
void USART_SendDataString( u8 *pData )' P7 }' Z( r8 Y* Z  `
{1 w3 u! G( @$ x8 j$ h: U% B
    pDataByte = pData;% U) b4 J, W6 j
  
0 k$ I7 Y, u) y. K: _    USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.7 o  `: D, j/ e& O5 ^; ]/ J
    / W  c4 i) k( G5 }. U2 Q0 V
    USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次
3 Y6 X; X7 v5 T8 C3 H}( ?! _6 O3 S+ ~
* n& a: O4 Y. ?# s* P

% t4 Q* r; p" [$ v" B# O4 X/ k( P# U6 r中断处理函数如下* Y3 t# P! ~( z1 F) Y6 G
void USART1_IRQHandler(void)
1 B4 V2 c9 t# R+ m4 |5 Q8 s% @{
. Q, J& A7 w9 Z( S9 Y2 |    if( USART_GetITStatus(USART1, USART_IT_TC) == SET  )
2 P( i) \4 _* z& r% ?. M, }( F    {4 N# o. G) Y, y" n
        if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉
$ O+ G  J% ?+ E, K6 B            USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断. clear掉即可,不用关掉TCIE) H6 ]/ W9 P; u# @+ n8 z
        else
  f2 M& m5 E  I  E% I; ^& U            USART_SendData(USART1, *pDataByte++ );9 H0 h) z1 ?4 P: g! N; a
    }
) M6 e: N& Z8 s% P; \0 \( S) c6 g0 S7 |  S1 Y2 }6 R
}% i$ F+ P- Z3 P2 W  u+ v  u

2 l3 ?' E4 c9 U8 n. A其中u8 *pDataByte;是一个外部指针变量/ V* k* q, e' O! P$ k
' }. w( j9 T, }( C
在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。1 Y; F6 z6 N9 H0 U
$ ~3 H$ g7 s6 W& v
串口初始化函数如下  @8 `+ h6 E( |4 G- v
void USART_Config()
' K5 J/ \3 f3 E{3 W" [/ \) N4 {) C6 E
  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体- `8 P' t) S% N5 R
  # t$ q& V& N- X0 \2 V- V# W
  USART_InitStructure.USART_BaudRate = 9600; //波特率9600
  d+ s+ e* c* \# S; {5 ^1 ?  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位/ h! I3 Y4 F; g% U- `) o, Q
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位0 c4 w, n3 u, Z! c  S
  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
1 t& q8 e$ s. W* f; c$ G# E; ?  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
6 P3 E/ y; v( y0 t, H% [# z" F1 l  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
, n6 ^3 ?% _# {" O: W; u  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭6 y2 Q5 ^4 o# l
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
# k" [3 D  b" o  r/ {- G' `; ~. R  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;- d. X/ ]% y( @5 U
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
% V7 e. `- W' [- O- B: j! m  USART_Init(USART1, &USART_InitStructure);//设置到USART1
$ A/ e7 e' t1 F$ s/ W  
; Z4 H0 G9 {( i9 X  USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节
  m" t' S; |4 I( p( O+ z7 F0 R4 y! Y% L3 c/ Q) H3 F+ B+ ~
  USART_Cmd(USART1, ENABLE); //使能USART1
! K  P* T& O( m* B9 }}
# \7 ^/ w% a4 d/ q: G这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么??
) Q- F% i& f. k' E$ Q2 B! r/ F
1 I2 E; h: g' G' c, v  `
这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
9 y! W, E$ Y8 d4 p

8 e+ J( A) @8 S. h1 [
再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。
0 h5 E) {* n: R( b1 S9 i' }# Q" G7 [5 X/ B
发送函数如下:6 v1 o1 r$ A8 j; U& Y: D) H: c& I9 E3 c( C
void USART_SendDataString( u8 *pData ). k% M! z* n/ {+ j4 `" k0 C( j" J
{) [) O- e$ z1 ?( u' i/ `7 k
    pDataByte = pData;
" r& y2 j- M4 H% X    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。 # C  ^$ A+ M$ S9 A, Z4 H& h, G" W! W* I! Q
      w' ]0 ?* ?) ?" {6 r' b- |. d3 f
}
9 c; L  x- ^9 C, o: y3 M8 K* t. F8 e
中断处理函数如下:+ J. \& ~" `- r
void USART1_IRQHandler(void)7 d1 r0 m7 N, ^- W6 M( _$ a
{
% R5 ~: R/ H0 z7 v& C( X    if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )
. ]9 [. _# O. x; q    {
+ v; Q; T1 A6 f% S        if( *pDataByte == '\0' )//待发送的字节发到末尾NULL了/ O* g6 M4 ~3 e3 K+ |
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断9 Z' N0 x! W5 [
        else
) p$ r' H" y( N            USART_SendData(USART1, *pDataByte++ );; W7 ^2 g# D6 d9 s( @
    }
8 E. y9 L& V: B4 h
1 z7 X/ h% Z1 R. n7 |}
; a" {% l5 O3 t. U& [% ]$ s% b: i  [: p: T
在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:
. Z& e+ ]4 ~3 r' J1 a& L7 bvoid USART_Config()1 c) u6 ?* ~) Y/ {5 t* h$ Q
{6 K/ a3 k. _" l6 W
  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体8 s" d7 C5 C6 [$ n5 h& x0 M
  ( S( W( W* L$ L( |
  USART_InitStructure.USART_BaudRate = 9600; //波特率9600  O6 G- l9 r- {1 p/ \! b
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位3 j$ `" Y$ o+ u" U: E) D2 P& ~$ b  T
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
; L  L1 r; Z) _6 W3 ]  Y  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
" b" n& D! x* _- Y3 E3 H& S  A, x  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
8 W- d2 L+ I( o& P5 \# p8 s  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
+ I3 E4 Q- f' b+ i, z9 Y4 s* W  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭) E" l) I/ I! T4 n7 y" s# ]) ^
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;( m7 V7 ]8 I8 x
  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
  d/ ?! b; Y4 U7 A0 G+ g$ |3 \& G  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;1 C# S6 C' S3 c3 U9 ?' I. v

, H' Y  h5 x1 {' Y7 o  USART_Init(USART1, &USART_InitStructure);//设置到USART1; r$ |% ], L! p5 }
  
- F! A2 L. t. E. T  USART_Cmd(USART1, ENABLE); //使能USART1$ D: q/ b- ~8 W) B& I# F
}3 g$ C$ |  _2 Z; o3 H. M: `/ ~2 t

6 Q9 g. x" ]" n- _SECTION 3# M' Y. m( H% `8 B& j/ u, [1 P
7 }3 l+ f8 i+ P- E7 s% I& Y( V

& R8 s) j, _0 J8 X( f

在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。
) J! I* }2 o# u2 \
9 h# ]8 Z. F' m2 t: f对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。
/ B( d3 q" b, ?7 n
! O2 o& Y( s6 l6 D另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。
8 a5 `1 o3 X) P  ]( n' o1 Z4 l/ j+ B/ D
TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
# E1 t) A. `, m& f  [. j8 t0 {- e% P- L9 f7 ?, S
至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

3 w: D* H5 o# j& P% j2 E/ a

SECTION 4% S! E9 u3 m# W/ Z+ M% O5 ?

       2014-12-19 08 48 55.png , P( e& C: [. k
' \8 q* r' _, d( K- n( I

7 K! k7 ]5 C0 x1 Z3 \/ G- d
赞 收藏 1 评论17 发布时间:2014-12-19 08:49

举报

17个回答
wjandsq 回答时间:2014-12-21 10:53:00
/**# N+ ~7 I+ g8 C% Q4 U' d
  * @file    usb_endp.c: J' H$ i# @" A+ l! ]+ }# u
  * @author  wjandcf@gmail.com6 S6 s" \# v& _0 b7 C0 T# ?
  * @version V7.0.2
& O7 N0 D; K2 e4 \: D  * @date    2014.12.20. s0 f, X/ N' ^
  * @brief   Endpoint routines0 y4 ^/ K: t7 e% f$ k3 ]
  *  ' G. y. ^3 P6 p0 N8 u
  */
, r7 P" x: Z! E0 {/* Includes ------------------------------------------------------------------*/
$ `, Y( }) `: Q, [, U. p8 S7 T#include "usb_lib.h"
! F; j  T4 T  A4 g! D, b- l! Q, R#include "usb_desc.h"
/ ~. ^8 P* H" ?# X: a( M  B; u#include "usb_mem.h"
8 x3 h! E& V9 U7 {* h; X; U8 E#include "hw_config.h"7 X3 a2 q' ]/ o$ u3 }; m
#include "usb_istr.h"1 `$ i3 E" p: e  a9 B. n
#include "usb_pwr.h"
% v9 t9 ?( d4 `& K: q1 D( S$ M2 \  m3 C& K
/* Private typedef -----------------------------------------------------------*/. }& g4 `& @: a- e% O6 E& L
/* Private define ------------------------------------------------------------*/
% @7 @2 G# q8 n7 A2 l6 ~, B% V) W% z1 [2 W' `
/* Interval between sending IN packets in frame number (1 frame = 1ms) */
) O+ ~2 Y0 A- d1 s/ G#define VCOMPORT_IN_FRAME_INTERVAL             5
4 }3 S6 k- @, o7 v9 i# @# _7 Q8 R$ f" [1 Q4 U
/* Private macro -------------------------------------------------------------*/
) O% G1 t# D- a/ h0 A8 a; f/* Private variables ---------------------------------------------------------*/! {6 p% D  D# H/ v6 b/ ?* R2 |1 B1 Q
uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];( S; X- C* S  c+ h- D
! V4 d' L; r- j! c+ m+ P
extern uint8_t VCP1_Tx_Buffer1[];      /* VCP1 DMA发送缓冲1 */5 m- n6 J0 F" b4 }# a1 G* G
extern uint8_t VCP2_Tx_Buffer1[];      /* VCP2 DMA发送缓冲1 */: r  x' V' E$ F& g+ `* N' N
extern uint8_t VCP3_Tx_Buffer1[];      /* VCP3 DMA发送缓冲1 */
* V7 a5 E' ?) C; ]2 k
8 u2 T# R( u& m/ k4 f* Pextern uint8_t VCP1_Tx_Buffer2[];      /* VCP1 第二缓冲(临时存放来自PC主机的数据包) */
6 e/ M" A* t2 a! Bextern uint8_t VCP2_Tx_Buffer2[];      /* VCP2 第二缓冲 */
) n. L9 A; k! Q3 t. W6 K% cextern uint8_t VCP3_Tx_Buffer2[];      /* VCP3 第二缓冲 */
0 D$ x- i$ {7 s9 S  X$ r& L& a! j- n, b# E
extern uint8_t  Flag_VCP1_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP1_Tx_Buffer1被锁定 */
4 \8 R, p. w, c9 c# e0 t# `' Aextern uint8_t  Flag_VCP1_Tx_Buf_Full; /* 1: 表示VCP1_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */! J, ?9 w8 A5 w. Q* g
extern uint16_t VCP1_Tx_Buffer_Cnt;    /* VCP1 第二缓冲 收到的字符总数 */4 g# ]; E& p4 {. v

( r/ Z1 X) y* X: y3 s/ {extern uint8_t  Flag_VCP2_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP2_Tx_Buffer1被锁定 */; q* r" G7 |* x8 Y: \, A+ L# c
extern uint8_t  Flag_VCP2_Tx_Buf_Full; /* 1: 表示VCP2_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */
2 J5 ?! Y$ Z+ `, M9 Wextern uint16_t VCP2_Tx_Buffer_Cnt;    /* VCP2 第二缓冲 收到的字符总数 */' ^  {4 ^* U) O3 b3 V3 q: P

7 A( I" T' ~$ m, X: ]extern uint8_t  Flag_VCP3_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP3_Tx_Buffer1被锁定 */0 v, y. @1 f1 B% D; o  G) f0 D# Z
extern uint8_t  Flag_VCP3_Tx_Buf_Full; /* 1: 表示VCP3_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */4 p. s! z: r9 y( `, Q( H# F# F
extern uint16_t VCP3_Tx_Buffer_Cnt;    /* VCP3 第二缓冲 收到的字符总数 */
/ y8 r/ L7 d5 F: _6 w: ~0 ]8 T5 n$ q- r
extern uint8_t  VCP1_Rx_Buffer[];" J. K% D( [/ y2 T( G) [. U0 s
extern uint32_t VCP1_Rx_ptr_in;
+ Q; m0 H7 ]1 [5 }3 Y; e) bextern uint32_t VCP1_Rx_ptr_out;7 y4 h. _$ E: P( b1 r. z: @4 K9 K
extern uint32_t VCP1_Rx_length;
6 V7 s5 a" J' Zextern uint8_t  CDC1_Tx_State;* Z- V' h! y. i' {

7 s( O3 z  W. C, o! n+ g- E, ?extern uint8_t  VCP2_Rx_Buffer[];
, w$ Y% v2 t+ U' K$ ~extern uint32_t VCP2_Rx_ptr_in;# a- k$ E$ @1 h2 d6 ]$ a
extern uint32_t VCP2_Rx_ptr_out;- d8 o0 R; Q) z' g% C& v. v
extern uint32_t VCP2_Rx_length;
: x8 o, `8 s: G# ^/ yextern uint8_t  CDC2_Tx_State;7 [$ c1 K1 ~; R2 [, f! |6 L

/ {: ]9 g  s9 F+ Q" Jextern uint8_t  VCP3_Rx_Buffer[]; & U3 W% X+ H* T! T( h- ]. ?
extern uint32_t VCP3_Rx_ptr_in;. f& O7 y5 d" }' b5 n& h7 ~
extern uint32_t VCP3_Rx_ptr_out;
5 H9 H/ O) v8 Z- p3 H3 cextern uint32_t VCP3_Rx_length;
2 V% r, H) }' A% n, n! N) Zextern uint8_t  CDC3_Tx_State;( N( w% M: b) m' [& S) @
( H& K+ q. z, X& z7 y1 W, G, h
/* Private function prototypes -----------------------------------------------*/
, b  V% n0 w* A8 [. j6 O- [/* Private functions ---------------------------------------------------------*/% b9 u  X  B) Y! X6 ?

0 `; c( d5 Z, I" L6 C  ^# C, O# n+ M3 b/* USB的IN端点 发送数据到PC主机 */
* n$ i& E" P9 p1 I5 j#define EPx_IN_Callback(ENDPx, CDCx_Tx_State, VCPx_Rx_Buffer, VCPx_Rx_ptr_out, VCPx_Rx_length) {\( z  X3 C$ q) J" P) C1 W# g4 O
    uint16_t USB_Tx_ptr;\" e3 s  p: j$ D$ w! j% t
    uint16_t USB_Tx_length;\6 o) ~/ V* T8 }2 q. r
    if (CDCx_Tx_State == 1) {\! C' m9 K1 F9 I! M
        USB_Tx_ptr = VCPx_Rx_ptr_out;\- D. t$ z/ k7 B* x
        if (VCPx_Rx_length == 0) {\
: ~- z7 v' u& Q. d, b            CDCx_Tx_State = 0;\1 z$ V9 O; G9 [. s2 o
            SetEPTxCount(ENDPx,0);\
( Z. V- Y, P% |+ I' K            SetEPTxValid(ENDPx);\0 c1 j! S: G7 @  H0 j- H
        } else {\3 f2 `$ J5 v7 G" k3 k9 y
            if (VCPx_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE) {\" G  Q/ S  `! k/ K2 j7 g' Z
                USB_Tx_length = VIRTUAL_COM_PORT_DATA_SIZE;\
, `. d& T+ p6 e0 N. ]                VCPx_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;\
6 `  t- g5 B9 @. I; h6 j* W+ |6 r                VCPx_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;\% S/ |5 i$ g0 d
            } else {\% G/ l2 a! s9 `& l, ~& f, l
                USB_Tx_length = VCPx_Rx_length;\9 F- U/ u4 W) n0 B1 y5 ?7 l. D
                VCPx_Rx_ptr_out += VCPx_Rx_length;\1 v6 j1 V+ B: r, Q/ k2 S  U
                VCPx_Rx_length = 0;\
3 c- [. Z% H5 O# |# a            }\% S/ c* N! p! q0 v. W7 _! F
            USB_SIL_Write(ENDPx, &VCPx_Rx_Buffer[USB_Tx_ptr], USB_Tx_length);\
9 B# L4 k" H; d6 c! v2 i2 ~            SetEPTxValid(ENDPx);\- ]+ A6 Y% a/ v2 R
        }\
, B+ _5 [' V) I% E  p    }\1 d$ h! t9 z4 s6 g/ Q
}
' }$ K& H* ^: r! |5 B7 ~/* USB的OUT端点 通过物理串口向外发送数据(阻塞方式) */. G: l$ z" i7 v) |; C* u% p: L/ e1 z
#define EPx_OUT_Callback(ENDPx, USARTx, GPIOx, GPIO_Pin_x) {\
2 {: z6 u! N( a1 V0 ^* N    uint32_t i;\; \+ s7 U+ a# G1 R. Y5 s
    uint16_t USB_Rx_Cnt;\
( `9 b+ w. j- V  c) D    USB_Rx_Cnt = USB_SIL_Read(ENDPx | 0x00, USB_Rx_Buffer); \+ v9 R9 J7 t  l# M: S
    GPIOx->BSRR = GPIO_Pin_x;\3 n4 A0 |. o4 r0 j' V# f' E
    for (i = 0; i < USB_Rx_Cnt; i++) {\
0 M: E; W( B0 p" Q" R/ h        USARTx->DR = *(USB_Rx_Buffer + i);\
* Q% Y9 `( `, C7 n4 Z8 v3 r' h        while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);\
% ?( P- S" K- B" p" i- G    }\+ G; s" _* B. D4 o3 S) E8 g
    SetEPRxValid(ENDPx);\( b4 F  s; b. X5 i+ s
    while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);\8 {5 v* i* S' D- R% c0 L
    USART_ClearFlag(USARTx, USART_FLAG_TC);\  K4 L! M% ~" B$ T
    GPIOx->BRR = GPIO_Pin_x;\
% @4 U* n  r& K; p% J}
2 s+ t6 ^/ i& J* ]1 h+ `+ ^+ |, z
/* USB的OUT端点 通过物理串口向外发送数据(DMA方式) */2 q6 U- V# r" b; M% n; G
#define EPx_OUT_Callback_DMA(Flag_VCPx_Tx_Buf_Use, ENDPx, VCPx_Tx_Buffer1,VCPx_Tx_Buffer2,\
8 ~6 Q2 p" c, u! _( I, r    GPIOx, GPIO_Pin_x, DMA1_Channelx, VCPx_Tx_Buffer_Cnt, Flag_VCPx_Tx_Buf_Full) {\% c! X: j# ?+ B( z, h& x
    uint16_t USB_Rx_Cnt;\/ J1 }" A* l- J5 @: z5 f' \( ~
    if(Flag_VCPx_Tx_Buf_Use == 0){\
$ i* a* C( l6 }7 {. F, g4 n        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\% |) n' d' ?7 @- k# @# z$ K9 ^
        PMAToUserBufferCopy(&VCPx_Tx_Buffer1[0], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\2 U' a. y+ Z1 D. C6 N
        SetEPRxValid(ENDPx);\3 w! R' g+ n! J# R  G- e0 s  H
        GPIOx->BSRR = GPIO_Pin_x;\  n& s& p$ r# q# e
        DMA1_Channelx->CNDTR = USB_Rx_Cnt;\; h/ S  ]) h- S4 t7 ^9 W
        DMA_Cmd(DMA1_Channelx, ENABLE);\) P# s, g- d2 v" \4 R8 U& N
        Flag_VCPx_Tx_Buf_Use = 1;\
4 h# d6 j5 S8 G' ?2 [' ?        VCPx_Tx_Buffer_Cnt = 0;\" E+ z# a3 B; m3 K3 ^$ p, t
    } else {\
# a8 z. Q7 V5 H0 R        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\
6 ^- K4 }, s1 e& T* @/ B4 i2 C        if(VCPx_Tx_Buffer_Cnt < (1024-128)){\
. f6 }/ _( K; p+ b2 C( ?          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\
% v* o1 T& J0 U; h6 O5 @3 @/ i& f! s          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\' V1 [0 N' |' I, F7 Z3 R
          SetEPRxValid(ENDPx);\# @$ Z& T) Z. l/ v; F
        } else {\
1 J. R. S( r- ]$ s9 z5 g* \7 G" x0 X          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\
; ^" }2 J& {. a. F          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\
3 c! |- b: y& H/ Q5 x+ y/ T          Flag_VCPx_Tx_Buf_Full = 1;\$ n6 Q; V) R0 k  c6 }
        }\
0 f% r, C( J$ x    }\
1 X# [3 _% f5 m8 n3 P}* N+ A' B+ G# E- l( ^0 n9 j
/**
6 ?1 X9 h6 a" F4 L2 |3 _ * Function Name  : EP2_IN_Callback
; i4 E" I- J2 Y  Q- m7 D5 B. K4 Q * Description    :6 m, j5 [6 N' e
**/, Y8 G6 T9 ^9 b% b9 d/ ^  z0 [
void EP2_IN_Callback (void) {! r6 F& k5 ?( T4 X/ F# j! y) A
    EPx_IN_Callback(ENDP2,CDC1_Tx_State,VCP1_Rx_Buffer,VCP1_Rx_ptr_out,VCP1_Rx_length);
  }, Z4 ?( k/ k. l/ W6 Q7 ?}
+ e) G, N! Z6 Y. F# S. {9 @& ^/**
+ D; x. c+ z2 i0 B0 @( w * Function Name  : EP4_IN_Callback# Z6 U8 K! y" U: z. G. O- Y
* Description    :
7 ]1 C/ I3 e, U! S' X **/+ B- `' y; ~9 a" c) u% U) Z
void EP4_IN_Callback(void)( p% n$ B& {) x
{
* k, f* i$ M. R/ }& L    EPx_IN_Callback(ENDP4,CDC2_Tx_State,VCP2_Rx_Buffer,VCP2_Rx_ptr_out,VCP2_Rx_length);
6 h0 h7 |0 Y1 H" N}
# y$ A! c( j- r0 j/**
/ J4 O0 V/ V+ F  q$ b) I * Function Name  : EP6_IN_Callback
9 m( G& w# ?! U! Z! u9 Z- R. ^& K * Description    : VCP3 向PC主机发送数据
$ t: Y& s+ `" C9 d2 ?6 ` **/
, [, H: n* d2 z0 i, C" x- o; [void EP6_IN_Callback(void)
  W" K2 ^* q6 y% ^7 U+ d$ e- J{
) ?  `! i* `, w( n$ R    EPx_IN_Callback(ENDP6,CDC3_Tx_State,VCP3_Rx_Buffer,VCP3_Rx_ptr_out,VCP3_Rx_length);  : v# [# y3 k% J8 o* ?( r* b
}
# h7 ?2 @. C6 f3 q/ j& ?/**
2 b$ l; M. ?' [ * Function Name  : EP2_OUT_Callback$ ^8 D* @! S& Q3 q; Q' J) `2 S3 y8 e1 B
* Description    : VCP1 通过USART1向外送数据
: P, o/ U- [* D9 I) f. i% {% p6 X2 J **/
9 f' a7 s" s5 Ovoid EP2_OUT_Callback(void) {
% L- b$ a% n6 q* o/ F#ifdef USB_DMA_SEND
9 C7 s+ B/ Z8 p- M    EPx_OUT_Callback_DMA(Flag_VCP1_Tx_Buf_Use, ENDP2, VCP1_Tx_Buffer1,VCP1_Tx_Buffer2,\( S2 l, l: A1 m! ]/ }* r  H# i
            GPIOB, GPIO_Pin_9, DMA1_Channel4, VCP1_Tx_Buffer_Cnt, Flag_VCP1_Tx_Buf_Full);0 F% O1 B1 L$ Q# H, M+ Y
#else2 G- A% K' Y5 e$ R$ h; i
    EPx_OUT_Callback(ENDP2,USART1, GPIOB, GPIO_Pin_9); // DR1 PB9  {# N# I) z6 S" q
#endif
5 M# J2 c! M$ ?% a. O}7 A$ V( u3 t/ b, c, E
/**2 P* ]5 N2 u* z) b5 D
* Function Name  : EP4_OUT_Callback% N( v3 R% f9 W* B8 _4 Z7 J
* Description    : VCP2 通过USART2向外送数据. D9 g$ X+ L1 A1 S3 u
* Input          : None.8 u4 g$ S" k* ^& S+ f8 D
* Output         : None.
2 Q! {; a4 n3 H * Return         : None.
+ e! @1 u4 a: B **/6 L" J; J* ^7 J9 T0 D& s, B2 B( \
void EP4_OUT_Callback(void)1 J$ f) `- d6 p4 V. X% S. i/ z9 q
{
; t. Y& p5 p+ z5 d#ifdef USB_DMA_SEND3 U  h3 W% m  W  M+ r
    EPx_OUT_Callback_DMA(Flag_VCP2_Tx_Buf_Use, ENDP4, VCP2_Tx_Buffer1,VCP2_Tx_Buffer2,\7 ]( }: Q4 F2 K/ D
            GPIOB, GPIO_Pin_8, DMA1_Channel7, VCP2_Tx_Buffer_Cnt, Flag_VCP2_Tx_Buf_Full);: A  A' s! G; q0 H& d1 i
#else
" I; }9 W( ~* x* s6 I    EPx_OUT_Callback(ENDP4,USART2, GPIOB, GPIO_Pin_8); // DR2 PB8
$ {5 O  C/ N, p, O( J#endif1 v( b9 r2 M  {$ D8 |- h
}
7 c% Q8 z7 z7 C: e9 ~9 p  ?/**
/ G  l, w9 n0 ] * Function Name  : EP6_OUT_Callback; i2 z6 M! j) q0 m! k+ N, z- t
* Description    : VCP3 通过USART3向外送数据
% p& e" m/ ~3 a( P' _3 L **/
* H8 f. S$ \. A+ G0 k; n/ Vvoid EP6_OUT_Callback(void){- Y: C. ~; J1 ?+ R+ k+ A
#ifdef USB_DMA_SEND( u4 @" C; D' I3 c' U
    EPx_OUT_Callback_DMA(Flag_VCP3_Tx_Buf_Use, ENDP6, VCP3_Tx_Buffer1,VCP3_Tx_Buffer2,\' x7 w7 Z/ S, e0 s" Q1 H) i( h
            GPIOA, GPIO_Pin_5, DMA1_Channel2, VCP3_Tx_Buffer_Cnt, Flag_VCP3_Tx_Buf_Full);: i( x& ~' E" ?! c$ o
#else  % t. ]9 A7 [: {& p7 E) A" x! l# s
    EPx_OUT_Callback(ENDP6,USART3,GPIOA,GPIO_Pin_5);  // DR3 PA5
5 T# R* |& d; q- S#endif" q6 O- c, Z. U3 K$ m' h% r
}
# D  ]0 A/ U1 O( ~1 a( C4 q/**
6 s( H, Z8 w& q* E * Function Name  : SOF_Callback / INTR_SOFINTR_Callback- K" R2 T+ q( ]/ X
* Description    :) P( ~8 Z1 {/ R$ A0 `1 {. |) ?
**/% `( P/ a- p0 D& p
void SOF_Callback(void)
3 h" a* @% C2 N# E/ w, [{
! R& K9 h! P$ D/ i* w% F    static uint32_t FrameCount = 0;
4 E4 I$ q& n4 g! S  V    static uint32_t Led_Count = 0;
1 A- D; q" U: D6 V) h% r8 r    if(bDeviceState == CONFIGURED) {* @  H$ l3 o- y
        if (FrameCount++ >= VCOMPORT_IN_FRAME_INTERVAL) {
3 U+ o6 f$ g& s            /* Reset the frame counter */7 P6 X/ O! x2 V
            FrameCount = 0;
% C* D8 ]& A3 o' J7 t# J            /* Check the data to be sent through IN pipe */$ @; t: `5 k9 X$ J! T
            Handle_USBAsynchXfer();
/ Y! B6 ^- D9 w5 c        }
& t7 o$ h& i# X+ Z: v/ Y. K    }, x! l2 O8 F4 M' d; p, O. s2 e
    if(++Led_Count > 999) {* }' u1 W% s# U4 X, Y( }, _
        Led_Count = 0;
; Y) x# N9 S+ q' u5 K: T4 q        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0){5 `( X* L4 t1 L
            GPIOB->BSRR = GPIO_Pin_2; // PB2 H
# R" b1 d4 x& h8 `1 U        } else {+ M9 w% b: j* ^1 y7 ~# r8 p  F' {
            GPIOB->BRR = GPIO_Pin_2;  // PB2 L* Y' n, U* V- s, |6 u2 A
        }
& b+ q0 m, P5 q3 t. v- ^    }, ^3 \4 j  ^, k
}
4 e) R1 c' G0 B7 F# o8 g8 k
- L: f2 ?) r* c" ]
万里-363223 回答时间:2014-12-19 08:51:16
SECTION 4
* m; h5 I( T4 m: ~        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断系统和51单片机一样的简单。7 \$ u% X, l, @  D  \
        对于接收终端,就是RXNE了,这只在接收完成后才产生,在执行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代码时不会进入ISR。但麻烦的就是发送有关的中断了:TXE或者TC,根据资料和测试的结果,TXE在复位后就是置1的,即在执行USART_ITConfig(USART1, USART_IT_TXE,  ENABLE)后会立即产生中断请求。因此这造成一个麻烦的问题:如果没有真正的发送数据,TXE中断都会发生,而且没有休止,这将占用很大部分的CPU时间,甚至影响其他程序的运行!
. |1 r. @! a# q7 _9 Z) L, P        因此建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。2 j/ C/ B0 a2 M/ D7 Z
        对于发送,需要注意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清零。2 A/ c# g9 }# \2 ^8 V, Y1 P5 m
        对于发送单个字符可以考虑不用中断,直接以查询方式完成。% t7 p1 s; H0 x$ s8 O3 ?
        对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。$ Z8 t3 l( ^. p- H8 Z
       这里简单分析上面代码的执行过程:TXE中断产生于前一个字符从DR送入SR,执行效果是后一个字符送入DR。对于第一种情况,如果是可显示字符,就执行USART_SendData来写DR(也就清零了TXE),当最后一个可显示的字符从DR送入SR之后,产生的TXE中断发现要送入DR的是字符是0x00——这当然不行——此时就关闭TXE中断,字符串发送过程就算结束了。当然这时不能忽略一个隐含的结果:那就是最后一个可显示字符从DR转入SR后TXE是置1的,但关闭了TXE中断,因此只要下次再开启TXE中断就会立即进入ISR。对于第二种情况,其结果和第一种的相同。1 B! K' g, W/ U  c- [) Y" d& r
         对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:
! z, O3 q  k: _8 X& A3 uextern __IO uint8_t TxCounter1;% _5 n* [/ j0 E! W
extern uint8_t *TXS;" N+ m3 s% s  D# I+ o8 M
extern __IO uint8_t TxLen; ( V2 k+ k0 j9 a+ Z+ I8 w# e' C
void USART1_IRQHandler(void)
* K" v& h9 P7 a# G    {/ f. P) ^& R  c8 L$ |& r3 Q
        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET), \8 e0 V' C( A+ b1 z% u3 Z
            {                                               0 l8 V% ^% N% X: Y1 o! B
                if(TXS[TxCounter1]) //如果是可显示字符/ r/ C4 Y: Z/ F( m2 ~
                    { USART_SendData(USART1,TXS[TxCounter1++]);}1 T- C8 i+ G; K
                else   //发送完成后关闭TXE中断,4 y# d) ^8 S, A: O- p# ?' t
                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                        
* ?, H; s! p: u) m' e7 M/ d# c% I            }                  
: `1 E* q8 y( A. l8 y- Y    }. o- X0 f8 j3 o* K8 O1 v$ _$ Q
        对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:
3 j# t+ ?9 E* G9 B2 v. ~1 E- [void USART1_IRQHandler(void); w' A6 z( H: w. H: g7 Y- V( `
    {* L3 ]' z) H5 N3 M( l
        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。3 S9 h$ R# }9 {5 y* z
            {                                             
7 }6 G- G; P9 R2 A0 z0 U                if(TxCounter1<TxLen)
. W# V# E* n' P4 J- v                    { USART_SendData(USART1,TXS[TxCounter1++]);}& V3 I9 I4 a1 M. ~$ y! G1 b
                else   //发送完成后关闭TXE中断
* g+ S! o9 a$ g7 z- Y2 z% [7 ^4 g                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                         * x" S( u5 W8 R# w. z; ]9 }4 L
            }                    & i( T2 l: |+ M, ]7 x! w
    }
, c) J0 e4 I+ S- W9 N/ p        事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!
+ G; h, |6 {# S        在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:6 y! H3 _* |/ h" N
uint32_t *TXS;
4 x8 e! W4 x9 g& f5 y- V3 |, S; luint8_t TxBuffer1[]="0123456789ABCDEF";: T4 i" M9 g! I# t
uint8_t DST2[]="ASDFGHJKL";6 o9 b7 {) R' @7 w; S3 M
__IO uint8_t TxLen = 0x00;# w) }- s; k  H; l0 K& w& n' w
     TxLen=8;                               //发送8个字符,最终发送的是01234567) K/ x  y$ C+ e  I% S
    TXS=(uint32_t *)TxBuffer1;   //将TXS指向字符串TxBuffer1
. T3 i4 w& `# L& B& P    TxCounter1=0;                     //复位索引值
# X5 K. k7 B, q0 T) E5 A2 `    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);   //启用TXE中断,即开始发送过程* Z$ Y- Z. O' e$ Z( s' ^
    while(TxCounter1!=TxLen);   //等待发送完成5 K9 e. }$ a" ^! N4 |8 e- E0 e& b; x
7 N7 g  W- v/ f3 f2 S  i
    TXS=(uint32_t *)TxBuffer2;   //同上,最终发送的是ASDFGHJK
) U3 m) E$ ]' n6 l    TxCounter1=0;
0 x9 Z! C; N9 t    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);
( S4 x) y) P! N, ~# T' w    while(TxCounter1!=TxLen);* Y% V# g! u- Z& g& n8 m
        以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。
wamcncn 回答时间:2014-12-19 13:32:32
万里-363223 发表于 2014-12-19 08:51
& k/ d. m6 D% j' s4 OSECTION 4
! v; d9 Q2 j+ R, C& t+ O        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断 ...
  l! V. x% q4 g+ o, 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# a; E: A8 Q* W( }; F
强大的同时,复杂也跟着来了,51是基础,STM32是进阶

% a- \/ D/ \4 r+ G7 ]8 U恩恩,没错,鱼和熊掌不可兼得
小贾-370388 回答时间:2014-12-19 15:30:39
充充电+ L3 C1 H5 z+ O4 F4 v
霹雳之火 回答时间: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/ m' U) ~1 l6 N$ x/ U0 M5 L9 p
.。。。。总结的好细致。。。。受教了。。不过,51终究是51,用起来简单,当然也就没那么强大了,stm32虽然 ...
, J* P* o+ S& L: t0 x
恩恩,没错,还是STM32爽
wjandsq 回答时间:2014-12-21 10:51:09

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

本帖最后由 王建 于 2014-12-21 10:57 编辑 4 _6 ]3 z7 c* E/ w$ s

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