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

STM32串口发送中断

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

0 }: G* @" a/ z( {8 X; USECTION 1
& h4 u2 E8 v/ N! l2 a5 y/ k 20141014220925993.jpg
- `* I, z' s: \) l2 |
SECTION 2

5 B  M: @% u- i0 I+ A3 X
先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下
& Y; M% P5 }/ e5 d5 q4 cvoid USART_SendDataString( u8 *pData )/ e6 i& T5 |* x/ c5 L
{' G/ [; B' i- e  k1 G7 t6 s
    pDataByte = pData;- H5 I; R9 U4 Y
  
( z2 e& E( p0 e: M4 I; g$ x. A    USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.2 e: ]; H  U9 w6 r, r% ]  d
      l+ r: E0 u. `/ a. O$ y, s
    USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次
. \0 l$ s3 [" q2 \  H}; I) m, Z1 ^3 K7 I( z( y7 {  j; ~  i- p# c

: e  {' `6 a# v0 M; e8 t& ?& `3 X; C: i/ F: k
中断处理函数如下" t) W6 i8 l4 u
void USART1_IRQHandler(void)
9 I6 d  q" Y4 S4 t) u# Z{( y" I6 F; l/ V' N5 Y
    if( USART_GetITStatus(USART1, USART_IT_TC) == SET  ): J! r. p3 |4 k' y- |
    {& v" @1 Q; v/ f7 P. K
        if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉
7 C, ~4 q( c- B6 b7 ~6 M0 f# }            USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断. clear掉即可,不用关掉TCIE
/ t4 x4 V0 R/ |        else
5 W4 U" t2 d. M: W            USART_SendData(USART1, *pDataByte++ );7 j& s% U- C# g6 t5 ?
    }7 @4 n7 b2 \, s. P, u
( f6 \5 H- `3 ]6 f, s, o
}% R! c7 X) V* z4 Y
. s3 b8 k* d3 o0 [3 S1 Q) G
其中u8 *pDataByte;是一个外部指针变量% q( }4 k' W4 E' R  @

$ G0 E9 z% h* i在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。
9 C/ Q$ s) l3 ]# X5 ~- c* r7 ]
/ \1 B$ n5 ~/ G( y, m  W
串口初始化函数如下7 ?0 N1 T. Q6 N
void USART_Config()
. S" s& w2 w3 R, N{& D! j: ~% X! ]# ~2 }; w
  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体1 B" e3 A2 M$ D1 `0 t
  & v6 [& N' r( \/ g$ ]1 b9 P' ~
  USART_InitStructure.USART_BaudRate = 9600; //波特率9600
9 G6 J/ F8 {; t7 n* \: R. i9 ?  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
, z! V/ F- P/ o8 s* P0 H8 W, B  h  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
4 u2 ?! w; t5 D5 ^5 M  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
0 `  K. a1 W! ?5 o% k" f+ C% b  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
7 B# U$ f7 n1 j% B4 B  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
6 G) l$ y4 F2 U6 A  R  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭1 G8 U" P# A# t6 w) q/ f
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;0 c! r# Y9 |+ T' N& H
  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;# h! ]8 S$ `% q- g* Y
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;  {) F$ I. K- ^
  USART_Init(USART1, &USART_InitStructure);//设置到USART1- l3 t! u- f( E& e$ ?
  
/ [& Q1 a7 v' q1 P/ G% t  USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节
% Q2 T0 `$ p9 `+ J9 T  u, u1 `& S) j5 i0 I. b
  USART_Cmd(USART1, ENABLE); //使能USART1
0 ?" i; g' `4 Q$ D! E8 a- x& w}
8 o- N# q8 x& I, A4 Z; v5 r4 W0 o' Q- f这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么??0 A, Q0 C; _" c7 L

% a, f- }5 b' X. u' x
这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
: L( U9 B# e, M

: h9 c7 L; h) }; o; n
再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。
, v3 H# w' |0 d. r
- I# n) s/ e) u5 B发送函数如下:
, T' S- C# B) w5 c$ P$ q5 gvoid USART_SendDataString( u8 *pData )! B0 |& X& t9 }# a+ }' \
{8 _) v/ F* o1 m& [
    pDataByte = pData;
0 S+ H% D7 Y1 u/ q9 A( V: T& n    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。
8 m  |# A* d. Z/ B) I! F. r7 _+ ?8 W    : E4 ?- _; I, i) D
}
# g4 l& x" o5 ^8 n; O% u9 }/ U4 O+ l% F9 `+ I5 Z% P
中断处理函数如下:
9 D0 s  V3 Y2 p/ i2 M/ S1 D, `void USART1_IRQHandler(void)4 O8 z5 j) z2 O2 W6 C
{; ]* h1 f0 S) O) S! y# a' k8 ]
    if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )
& ^, g" ^2 e' D) X3 i8 b. ?! E    {' Z8 p& [  t# J( X  \7 S- [
        if( *pDataByte == '\0' )//待发送的字节发到末尾NULL了  z4 m  b( i+ I
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断3 ?/ D# G2 v/ Y1 ?$ x: G5 E. y. u( e* T
        else. k) V/ O+ l9 ]) ^/ z2 v6 G  ~$ h
            USART_SendData(USART1, *pDataByte++ );
3 C: {  b& |: w% k    }' c* u3 a! g, K  V

! H4 U. U8 J* K% f' |- }}# G2 _+ E7 [& U4 {0 l8 h

0 ~9 a8 q; i3 j8 ^# C在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:- V! \2 x! D5 U" ?( [
void USART_Config()" J7 A' Y& E% @% J. [9 X
{
0 ^1 t$ q+ P5 A  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体
5 t5 t! |: b3 l+ A  
1 x) N" p( [8 i: i- r  USART_InitStructure.USART_BaudRate = 9600; //波特率9600
4 P! D+ P& |8 B; {% `  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
1 u4 O8 {6 y5 v$ D5 H  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
( I6 [0 w6 L. @- j4 d3 u  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
3 t' \+ V- {; x  |# k" t, Q3 [  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制  y% f- a* T6 T7 X. h) Z
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
+ E* K, X3 f$ {/ a  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭
9 Q# F$ p; n+ ?. w2 i& O  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
0 q+ ?+ m  ?, A  \+ m0 ~* h8 n0 }  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;  D# S- \( M0 G+ U' }
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;5 S8 g! e, M& @6 r+ K- H! r
- d2 x- f  }' ]( D2 y9 m
  USART_Init(USART1, &USART_InitStructure);//设置到USART11 b0 `" E3 T" a( l, d) t
  / i4 ~; {2 j. U6 o/ M
  USART_Cmd(USART1, ENABLE); //使能USART1
; n$ U* D* T8 G1 q5 I1 X/ e5 }}4 c( e. @* q5 a' b2 t
% f4 |8 \% T0 r1 E8 |, u* ^7 g
SECTION 3
6 G$ D, _7 W3 E  y

" ~- g; @4 v8 V% w3 `6 m! ^

6 r3 K8 {* c% d# s% W

在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。( I3 Z* k% h4 u4 t# i1 Z$ _

/ {1 e# U8 t. d+ b* J# h7 Q对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。' P" g( S7 o& u7 x
0 v. a6 d; u6 _& [9 E! k
另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。
, ]# a1 |: L3 q8 j8 ]8 {0 M7 t
" t$ r- t1 O7 S, m! p2 _TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。5 u' d' Z$ A. `/ j/ j" S

8 [, ~! t+ a" R+ G至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

% Z" _- L, i, y0 s' v& n  g4 r4 e' P, p

SECTION 4
! ?6 ?( Z/ x, M( k* m. ?

       2014-12-19 08 48 55.png
0 u  j" Q5 f- J5 [7 E) {/ V: K, U
( R+ ]6 t6 x; A. Y8 p

4 N$ b8 H8 V0 _( v
收藏 1 评论17 发布时间:2014-12-19 08:49

举报

17个回答
wjandsq 回答时间:2014-12-21 10:53:00
/**
8 H2 N6 }0 f: W1 U- g- H7 X. ]9 U  * @file    usb_endp.c
2 j) L, T  p4 I1 Y; D  * @author  wjandcf@gmail.com
2 O: H2 k1 y4 q' J  * @version V7.0.2
0 i6 a4 R# \  R/ b  * @date    2014.12.20. C4 J) p3 b8 i  U6 X$ P, {
  * @brief   Endpoint routines& P6 p( O- @& T# u1 A# G
  *  6 L8 k/ s; y& `8 [1 |0 ^' t
  */, w; T! Y$ k( w2 k1 d
/* Includes ------------------------------------------------------------------*/( J: M( z( t# w* _) P$ `$ b
#include "usb_lib.h"
2 w( p0 t7 [1 u# Q#include "usb_desc.h"
4 p0 U% t2 Z% m$ ^#include "usb_mem.h"
- ^, w$ B, q4 K- M* c9 B( Q#include "hw_config.h"
/ `! B! W0 c) Z" z% D4 T#include "usb_istr.h"
0 D5 b) K5 \$ `0 q' E) t  B#include "usb_pwr.h"
2 l  U; D- j3 p9 a9 I  J5 `2 G/ c& ~8 F1 i9 l% m
/* Private typedef -----------------------------------------------------------*/4 ]9 K  C  j, ]- t, T. m* n
/* Private define ------------------------------------------------------------*/
1 B' Z( s# n! u2 f9 \1 I, d* H9 i# Z$ s- W- b& j1 }6 H0 V
/* Interval between sending IN packets in frame number (1 frame = 1ms) */
5 L; U" V; @. g6 @! h4 V% @6 x#define VCOMPORT_IN_FRAME_INTERVAL             58 S. e3 d) ]& U+ K2 ?
9 N9 P5 b6 a- C. M# J, }8 R
/* Private macro -------------------------------------------------------------*/( u# H! p7 {% f; ~
/* Private variables ---------------------------------------------------------*/" t3 M% v3 u" ^& V
uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];. ?4 h! N4 \  t) P- Q
* S# T; f1 T1 t; J$ b6 s& h
extern uint8_t VCP1_Tx_Buffer1[];      /* VCP1 DMA发送缓冲1 */7 g* q; k3 V7 J+ d3 @% Z0 d7 R, I
extern uint8_t VCP2_Tx_Buffer1[];      /* VCP2 DMA发送缓冲1 */* M0 C! V( w% G$ j- ]' z0 i
extern uint8_t VCP3_Tx_Buffer1[];      /* VCP3 DMA发送缓冲1 */
6 V) f, |+ S. D" }, Q; d& M& i' B% S6 O& F2 a9 {! V2 T
extern uint8_t VCP1_Tx_Buffer2[];      /* VCP1 第二缓冲(临时存放来自PC主机的数据包) */; V$ P$ j% F' ?; D: K  S
extern uint8_t VCP2_Tx_Buffer2[];      /* VCP2 第二缓冲 */
1 b$ A3 s* s1 t$ nextern uint8_t VCP3_Tx_Buffer2[];      /* VCP3 第二缓冲 */
9 c% `  `* K. H  y+ G3 H# u
2 r+ I. ]# f7 O6 I* j+ O) Kextern uint8_t  Flag_VCP1_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP1_Tx_Buffer1被锁定 */# D5 j" i9 D1 u1 @. n; Z. K+ P- {" W
extern uint8_t  Flag_VCP1_Tx_Buf_Full; /* 1: 表示VCP1_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */9 w! N6 v, Z8 o/ O( R
extern uint16_t VCP1_Tx_Buffer_Cnt;    /* VCP1 第二缓冲 收到的字符总数 */' b* Q/ _2 A( X9 Q5 k

9 A& Q; @* ^% r- s2 Q3 h6 J8 r# x" uextern uint8_t  Flag_VCP2_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP2_Tx_Buffer1被锁定 */
, p$ P/ y2 z& Xextern uint8_t  Flag_VCP2_Tx_Buf_Full; /* 1: 表示VCP2_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */9 C: Q: g' u- z
extern uint16_t VCP2_Tx_Buffer_Cnt;    /* VCP2 第二缓冲 收到的字符总数 */; [% y# w) W( `! S; }

$ `$ x8 W$ L* _& |& q# Jextern uint8_t  Flag_VCP3_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP3_Tx_Buffer1被锁定 */3 r9 d. n: c% q, I$ c* R
extern uint8_t  Flag_VCP3_Tx_Buf_Full; /* 1: 表示VCP3_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */
6 h0 R5 L* d$ R$ k9 t, sextern uint16_t VCP3_Tx_Buffer_Cnt;    /* VCP3 第二缓冲 收到的字符总数 */2 P, b% z8 `$ |$ V0 e; |
3 _- o: F4 K  a" n4 m3 M% _
extern uint8_t  VCP1_Rx_Buffer[];+ r5 T  X6 l9 }
extern uint32_t VCP1_Rx_ptr_in;
7 n+ _- W. ~$ m0 y0 l4 {extern uint32_t VCP1_Rx_ptr_out;3 }( ], Y* D! ?  ?8 G/ p
extern uint32_t VCP1_Rx_length;
5 |' [  M0 [% H+ z3 [9 Xextern uint8_t  CDC1_Tx_State;( _+ H3 J9 p1 D
1 z# J" l0 `0 B6 Z
extern uint8_t  VCP2_Rx_Buffer[];
5 l# b$ }8 ]3 g4 fextern uint32_t VCP2_Rx_ptr_in;
0 _! K6 l5 Y' C# [, I  ^1 Lextern uint32_t VCP2_Rx_ptr_out;
1 X5 q4 k( h' y, w' E4 C( bextern uint32_t VCP2_Rx_length;
2 P; P+ d9 T, F4 iextern uint8_t  CDC2_Tx_State;5 [; p" h' w0 {& _! P" B

. B1 r; o0 F" e# c( aextern uint8_t  VCP3_Rx_Buffer[]; # R  X) q8 n& \+ h& [
extern uint32_t VCP3_Rx_ptr_in;
: b! D; E- Q4 l, [' \6 Q4 _extern uint32_t VCP3_Rx_ptr_out;  U: [- g( D9 C1 V1 x
extern uint32_t VCP3_Rx_length;
; L3 n: u& d1 J* Q7 X  l8 L" Dextern uint8_t  CDC3_Tx_State;
, `0 K5 i" I: C+ O  Y; w+ Q) L
" i# U/ E5 o5 |, K$ L, @/* Private function prototypes -----------------------------------------------*/
8 \! X! j6 k; m* x, H( @# w5 T/* Private functions ---------------------------------------------------------*/
. t' f4 {+ t( B+ Z! ]; i! G& ^3 H6 k& w; X
8 {- X% L5 ~/ c6 n( a- S1 v% I; k/* USB的IN端点 发送数据到PC主机 */
( x8 V3 y/ ~8 ]  Y( z7 c" S6 p#define EPx_IN_Callback(ENDPx, CDCx_Tx_State, VCPx_Rx_Buffer, VCPx_Rx_ptr_out, VCPx_Rx_length) {\( z7 l% v3 `6 l# w, @
    uint16_t USB_Tx_ptr;\
6 e) ?, R6 o7 l) |: w$ B# }! \    uint16_t USB_Tx_length;\, u6 K' I+ m; F. M" z
    if (CDCx_Tx_State == 1) {\
8 C5 {9 U/ N( p9 ]        USB_Tx_ptr = VCPx_Rx_ptr_out;\
2 R6 z$ T! C, F+ K3 T) B        if (VCPx_Rx_length == 0) {\8 ?) l, ^9 E4 ]2 H: H" U. K3 ]
            CDCx_Tx_State = 0;\/ d4 U' U: w$ u; E' H' _
            SetEPTxCount(ENDPx,0);\
5 g+ p6 \8 h9 k6 |4 X, w- ?            SetEPTxValid(ENDPx);\7 j  s% \! |# X. x+ B
        } else {\) |9 V( a2 R- X' J  d; K% ]1 v6 _
            if (VCPx_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE) {\
* p7 B8 k5 t8 k+ o* G* i( b% @4 Q7 I                USB_Tx_length = VIRTUAL_COM_PORT_DATA_SIZE;\
5 R; @- ~+ G2 m/ @' T7 s8 t                VCPx_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;\8 i! ?9 ^0 {5 k  U
                VCPx_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;\; r7 x) v/ J  Y& @$ D# L
            } else {\3 v) G" p2 X! |$ ?1 t' L
                USB_Tx_length = VCPx_Rx_length;\3 G/ E7 l/ M) Q" T% @% [
                VCPx_Rx_ptr_out += VCPx_Rx_length;\
; p- `; m7 L$ h  `# s                VCPx_Rx_length = 0;\
7 R- w( L, v$ j* L9 q# t            }\
6 J& W, D1 h: Q1 d            USB_SIL_Write(ENDPx, &VCPx_Rx_Buffer[USB_Tx_ptr], USB_Tx_length);\6 D' ?$ m6 C# K  U( z
            SetEPTxValid(ENDPx);\
6 x0 L. H7 q7 S* p        }\
' l6 m/ @- q" I+ I8 ]% u+ W2 M; Q) Z+ W    }\
2 ]7 Z# V: h* v- Y5 C, J; [5 Q}
) }+ n, S! H! ~6 _! r% V/* USB的OUT端点 通过物理串口向外发送数据(阻塞方式) */( f$ A* A2 a6 N" N9 ?
#define EPx_OUT_Callback(ENDPx, USARTx, GPIOx, GPIO_Pin_x) {\
( o+ k: t, L- u8 @    uint32_t i;\* B7 S8 r0 e) k, @4 @# e/ b
    uint16_t USB_Rx_Cnt;\
0 G7 f; b" R, p1 p+ w    USB_Rx_Cnt = USB_SIL_Read(ENDPx | 0x00, USB_Rx_Buffer); \2 E! n- H  l# m0 {& K; y! I* a
    GPIOx->BSRR = GPIO_Pin_x;\+ v8 B1 y, y! b; Z" J& O  t
    for (i = 0; i < USB_Rx_Cnt; i++) {\, n. F- a! z/ O- E2 g) f4 _" U
        USARTx->DR = *(USB_Rx_Buffer + i);\% S1 H! m/ o4 V, Y* f% [5 u. W5 W6 R9 n
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);\4 R  y" k, e8 T
    }\, X; y8 {+ S+ m; K7 K7 A
    SetEPRxValid(ENDPx);\
+ G0 t2 m* z+ W6 C2 D( p+ E    while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);\
% y  ~: [$ N/ F: h) L  x& y+ H    USART_ClearFlag(USARTx, USART_FLAG_TC);\& M+ Y1 `- n, I' M0 F7 R1 ^
    GPIOx->BRR = GPIO_Pin_x;\
  r& Q( \% S; B% s0 Z( y0 g' j7 @; E6 F}
7 n  z6 e) U$ ]3 U% V, w
. \1 g# d2 [/ U, A# d, J* b$ b/* USB的OUT端点 通过物理串口向外发送数据(DMA方式) */6 j" a9 Z( Q- P9 G2 W" q
#define EPx_OUT_Callback_DMA(Flag_VCPx_Tx_Buf_Use, ENDPx, VCPx_Tx_Buffer1,VCPx_Tx_Buffer2,\& w1 s- f4 Q1 {$ s! |5 C# c. O0 d
    GPIOx, GPIO_Pin_x, DMA1_Channelx, VCPx_Tx_Buffer_Cnt, Flag_VCPx_Tx_Buf_Full) {\
. n. F5 a! j+ F$ |    uint16_t USB_Rx_Cnt;\
- w! U' S5 o- j4 I    if(Flag_VCPx_Tx_Buf_Use == 0){\5 K% |. z6 e( ?
        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\
3 g( t% c! `* N        PMAToUserBufferCopy(&VCPx_Tx_Buffer1[0], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\
2 i6 u! \+ n* `5 H        SetEPRxValid(ENDPx);\6 ^# l& ^! w6 ^4 \1 V
        GPIOx->BSRR = GPIO_Pin_x;\8 W- F6 ?; c/ I$ e4 x5 V& O
        DMA1_Channelx->CNDTR = USB_Rx_Cnt;\7 J$ D3 ^0 w. `0 d' M
        DMA_Cmd(DMA1_Channelx, ENABLE);\( [1 Q. b/ O6 [3 F1 ^$ Z% Z
        Flag_VCPx_Tx_Buf_Use = 1;\
3 E. d5 E" C* t: |        VCPx_Tx_Buffer_Cnt = 0;\7 @9 E, d/ q$ G3 W/ ^# M
    } else {\; l, V# b: O% H0 \: J) ]; Q% ~2 @% H
        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\
& |) N, ]. M5 K! {7 o        if(VCPx_Tx_Buffer_Cnt < (1024-128)){\
2 \6 O/ E# s' W# p  H* E+ {* ]          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\! ~: U& A- M+ [# Y8 E
          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\" e# |) U  V# B! a
          SetEPRxValid(ENDPx);\
' f: n4 x' K! P        } else {\! E' X9 J  {/ N: `6 E9 \( c
          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\7 H: [2 n- x1 t+ `$ m
          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\8 g! G* v- f! V
          Flag_VCPx_Tx_Buf_Full = 1;\4 U0 y+ z( x' o. Z9 g" u. K4 P
        }\
4 \/ N1 ~% J! r3 D) p$ f    }\
2 Z% J# ]) ?) s0 r1 E}( P( h) |2 d! }1 O( f- \5 k2 r  J
/**% {, n$ [7 n+ P% h
* Function Name  : EP2_IN_Callback& E$ U. R1 q* D+ A! N  Q
* Description    :, e+ B* `# R2 }
**/: y- l1 j- q  S# k4 E
void EP2_IN_Callback (void) {
" J, Z& }* C% T4 A7 r: c    EPx_IN_Callback(ENDP2,CDC1_Tx_State,VCP1_Rx_Buffer,VCP1_Rx_ptr_out,VCP1_Rx_length);0 @- \8 K9 M5 D% W0 P4 I8 G+ f  |# z
}9 O0 s! L) i- l; c3 E3 y
/**) q# Y. `( h+ l8 e$ e
* Function Name  : EP4_IN_Callback: f5 f' _8 r% I# M& ?6 A: x: s: J
* Description    :
/ R% u0 M- [* ?# b# q$ N/ W **/
" F2 ]* g3 _3 q2 Cvoid EP4_IN_Callback(void)
& m8 B! g. d) i% C' I' v2 A{
: S) u- f$ N) l" ]    EPx_IN_Callback(ENDP4,CDC2_Tx_State,VCP2_Rx_Buffer,VCP2_Rx_ptr_out,VCP2_Rx_length);
* Y' P) a* J3 W. G6 ]6 r}; }0 g2 O" P4 m* E; L
/**; B/ a9 F; p. H2 u. D5 O! m6 x
* Function Name  : EP6_IN_Callback
9 s. a: p5 [1 g! ~, @3 q. a/ z * Description    : VCP3 向PC主机发送数据
, U$ }4 w; ?! G9 G$ |! L7 T **/5 p  v, u4 u7 A- O; Z, v
void EP6_IN_Callback(void)8 e: n' P' S) A4 h( o7 d/ ]
{
" Q% Y2 G$ t+ O4 f2 T9 j0 ~* h    EPx_IN_Callback(ENDP6,CDC3_Tx_State,VCP3_Rx_Buffer,VCP3_Rx_ptr_out,VCP3_Rx_length);  
. d, j3 d7 R1 R! Z8 m5 g}
. C# ], D0 r0 d( m: S8 _; o/**
$ K1 M/ |1 B' f  f, Z  c * Function Name  : EP2_OUT_Callback& h, ]) Z$ S! p$ y  c
* Description    : VCP1 通过USART1向外送数据' _) m  D% M/ \9 L, q* S/ G2 p- |
**/) T: d6 _% @% j4 L! E6 B
void EP2_OUT_Callback(void) {
7 o, a* g! G* i, I- A#ifdef USB_DMA_SEND
8 w; |4 R4 H' d    EPx_OUT_Callback_DMA(Flag_VCP1_Tx_Buf_Use, ENDP2, VCP1_Tx_Buffer1,VCP1_Tx_Buffer2,\
+ J0 Y7 i, K6 Y' M4 p- `) x/ ]) [& g" h            GPIOB, GPIO_Pin_9, DMA1_Channel4, VCP1_Tx_Buffer_Cnt, Flag_VCP1_Tx_Buf_Full);
& k0 y2 q: h2 k#else
& H% r; D; g3 l, p) d    EPx_OUT_Callback(ENDP2,USART1, GPIOB, GPIO_Pin_9); // DR1 PB9
4 G* _* Q: ?* q# c#endif9 `$ D/ w) B; \0 q- x' z/ Y* j
}+ K0 @% A1 @5 b- X
/*** f$ p+ M' a" i' m
* Function Name  : EP4_OUT_Callback+ X* c$ W3 M0 K
* Description    : VCP2 通过USART2向外送数据% a" ]6 n! S0 k4 d( \$ [+ d
* Input          : None.5 W: A& o2 a. S
* Output         : None.4 g# R$ w" a7 \( x! M
* Return         : None.
* W4 U- N. G8 }9 { **/
( N: n* d, ~) V4 Q8 I% i' V" i4 Nvoid EP4_OUT_Callback(void)
& u8 ^) \# I( N7 U/ N: h{. f5 I: l5 C- L* `: j
#ifdef USB_DMA_SEND
8 N- C) _& R# `3 w& ~  G6 G  p    EPx_OUT_Callback_DMA(Flag_VCP2_Tx_Buf_Use, ENDP4, VCP2_Tx_Buffer1,VCP2_Tx_Buffer2,\
& \+ a6 ^* y# h* H            GPIOB, GPIO_Pin_8, DMA1_Channel7, VCP2_Tx_Buffer_Cnt, Flag_VCP2_Tx_Buf_Full);
0 A# w4 b7 ~. a& A. H7 f! v7 e) C  @5 m#else3 M; a  d4 F& `
    EPx_OUT_Callback(ENDP4,USART2, GPIOB, GPIO_Pin_8); // DR2 PB8
# B% c: p% M: u" r" M& i#endif9 h# k  m+ S7 u1 l' Q$ q/ |* Z6 h( W& M$ U/ W
}
, w* c  E( Q# |* C/**
; [) w/ @$ Q" P0 s * Function Name  : EP6_OUT_Callback0 r9 X: r) R- H% ~- ]
* Description    : VCP3 通过USART3向外送数据7 k, W# g1 Y5 Z; Q! G  G( [
**/+ y( A- ^5 h- G8 _2 p
void EP6_OUT_Callback(void){% u7 p2 [* a# m0 J. V: {  [, f1 Y8 ?
#ifdef USB_DMA_SEND1 s5 y- _( H/ L, N( \* y
    EPx_OUT_Callback_DMA(Flag_VCP3_Tx_Buf_Use, ENDP6, VCP3_Tx_Buffer1,VCP3_Tx_Buffer2,\% X$ g' u5 c! F0 s# D: x9 Z2 |
            GPIOA, GPIO_Pin_5, DMA1_Channel2, VCP3_Tx_Buffer_Cnt, Flag_VCP3_Tx_Buf_Full);
4 P/ b7 e9 {2 S#else  
0 |' k. L5 ?( d7 T    EPx_OUT_Callback(ENDP6,USART3,GPIOA,GPIO_Pin_5);  // DR3 PA5( O) z8 u; r8 p2 v. {3 T
#endif# ?4 Z0 v3 P+ w* |
}
! a$ S- Z4 m6 D, I' N7 |, [0 ^8 R* N2 g/**
! G) ?$ m: p. l( I. G9 S5 V * Function Name  : SOF_Callback / INTR_SOFINTR_Callback6 q5 Y9 t  i# Z, l) D. S# P$ \. m; P
* Description    :* I7 B  c7 y0 V: y3 \7 `4 A4 y' R( }
**/: l9 O7 U5 a0 D- m0 P
void SOF_Callback(void), q8 [4 E- Q3 ?6 `+ p
{7 L  K& f& V1 u; @- c% e; K" c
    static uint32_t FrameCount = 0;7 N& z$ s% U% ~2 _% |( ]+ B  Z
    static uint32_t Led_Count = 0;
8 g# n- C' E6 B; Z' K1 Y# e    if(bDeviceState == CONFIGURED) {. d( Q+ Q, M! o. E- K
        if (FrameCount++ >= VCOMPORT_IN_FRAME_INTERVAL) {
8 ~- R9 ?& ?& u* }# E            /* Reset the frame counter */
. u; n1 m3 ]! N& h3 T            FrameCount = 0;5 ^  z+ r+ @: A( F- H
            /* Check the data to be sent through IN pipe */2 ^; A2 Y9 x3 v) b
            Handle_USBAsynchXfer();  c+ d  G* J$ x( Z1 ~; y* A
        }
9 g5 E3 J; Y8 U4 M3 ?    }
+ T. u; d' q; i! N1 W    if(++Led_Count > 999) {
; `% w! Z5 @0 k! Y        Led_Count = 0;
/ u: G4 T* ~4 h7 L, t# H3 t( v0 j        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0){
6 _7 r+ {! Z. w! g; @/ I  H2 M6 n            GPIOB->BSRR = GPIO_Pin_2; // PB2 H
0 _, O' A3 Q  s$ H( y        } else {0 Q+ Q! Z+ x; S- H% ], A& w! Y
            GPIOB->BRR = GPIO_Pin_2;  // PB2 L
, F8 N- H% L6 E! y, i        }3 X) _. j2 h* c( z1 U
    }
' C# x) P2 c4 F2 H}! }0 M- U9 T& u; Y
+ L6 t* D* c1 s' _6 X
万里-363223 回答时间:2014-12-19 08:51:16
SECTION 4- o/ w, P# ^# r* G/ a. A+ L0 |% I
        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断系统和51单片机一样的简单。, B) `( \1 C( X6 D
        对于接收终端,就是RXNE了,这只在接收完成后才产生,在执行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代码时不会进入ISR。但麻烦的就是发送有关的中断了:TXE或者TC,根据资料和测试的结果,TXE在复位后就是置1的,即在执行USART_ITConfig(USART1, USART_IT_TXE,  ENABLE)后会立即产生中断请求。因此这造成一个麻烦的问题:如果没有真正的发送数据,TXE中断都会发生,而且没有休止,这将占用很大部分的CPU时间,甚至影响其他程序的运行!* o/ w: w$ R5 Q5 ^  u2 |5 f3 B4 q
        因此建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。1 X7 j8 f. ]0 v! Q) c
        对于发送,需要注意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清零。
- [/ M) c( H, o/ v        对于发送单个字符可以考虑不用中断,直接以查询方式完成。
3 y1 B- \% ~5 Q2 a: J        对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。
- z! d# Z0 o* o9 F2 F- T* w       这里简单分析上面代码的执行过程:TXE中断产生于前一个字符从DR送入SR,执行效果是后一个字符送入DR。对于第一种情况,如果是可显示字符,就执行USART_SendData来写DR(也就清零了TXE),当最后一个可显示的字符从DR送入SR之后,产生的TXE中断发现要送入DR的是字符是0x00——这当然不行——此时就关闭TXE中断,字符串发送过程就算结束了。当然这时不能忽略一个隐含的结果:那就是最后一个可显示字符从DR转入SR后TXE是置1的,但关闭了TXE中断,因此只要下次再开启TXE中断就会立即进入ISR。对于第二种情况,其结果和第一种的相同。$ K/ F& Z0 J& |$ b& q
         对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:  R) `; E; N% h7 o- B% T5 ~* @
extern __IO uint8_t TxCounter1;: U' d/ S2 M# k9 [+ `; e0 k9 H
extern uint8_t *TXS;
3 b# A$ Q  |: |. }3 J" Qextern __IO uint8_t TxLen;
; n$ v  \/ E( C& F- ]void USART1_IRQHandler(void)
* u7 M! m( Z/ F7 ]9 T    {
8 Q# l  I/ z7 X' {; F/ b) M        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
) Z9 f$ T- ?3 ?+ e( a. i) @1 f            {                                               ( N' }1 s6 R' G/ E' F( t
                if(TXS[TxCounter1]) //如果是可显示字符/ o8 k  B% ^2 o3 @. Z
                    { USART_SendData(USART1,TXS[TxCounter1++]);}
3 \7 q9 ?9 N/ N                else   //发送完成后关闭TXE中断,
8 P$ q$ e( x3 y2 L/ U+ |; c- i: E                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                        
4 d) ?, n3 u9 m& _$ u+ }/ v" K            }                   # A% w+ [1 h) a+ P9 j
    }
3 k( z9 @' {' ?        对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:
. m% G' ~5 b3 F6 Uvoid USART1_IRQHandler(void)
% J( w3 O8 {6 r6 y9 ?! I    {
3 j- }' ?$ Y& F+ v" X        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。  I1 J" V) w, d9 z& c  V& R
            {                                              ) I' N! _: |+ k; T4 P& z
                if(TxCounter1<TxLen)
0 @6 ?+ |5 A7 Q, N                    { USART_SendData(USART1,TXS[TxCounter1++]);}! ~. M; e/ \' V! D% r  @" L
                else   //发送完成后关闭TXE中断
9 q: f2 Z- K! f3 w0 Z* i' y                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                         0 i" l4 w2 o6 P2 ]) l# k" F8 t
            }                    
, \7 Z9 j% r4 r# h, p    }
3 p: d" s6 ~: j' @2 ]9 j. \9 I        事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!; J0 f8 M+ L1 B: D$ s9 V/ g; Q
        在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:3 J4 e- {: }" `# `+ t
uint32_t *TXS;
+ n5 ?! M. s: P0 Q7 u( zuint8_t TxBuffer1[]="0123456789ABCDEF";* e9 ~7 O8 K! |, f# @" w1 W
uint8_t DST2[]="ASDFGHJKL";8 d! {; u: R7 x/ h* V5 v2 X
__IO uint8_t TxLen = 0x00;
+ B% y% }! G9 c( u2 V     TxLen=8;                               //发送8个字符,最终发送的是01234567
" z( s3 A7 _& D; E& ?5 g    TXS=(uint32_t *)TxBuffer1;   //将TXS指向字符串TxBuffer1
5 X$ ~' c2 l9 \9 J    TxCounter1=0;                     //复位索引值. Q  ]8 Q# C( E
    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);   //启用TXE中断,即开始发送过程
5 u+ L4 M# D! D# k    while(TxCounter1!=TxLen);   //等待发送完成$ K) A" f# K% J( y" g: n

% ^1 s( u( ]# g2 |" O    TXS=(uint32_t *)TxBuffer2;   //同上,最终发送的是ASDFGHJK
1 r6 B( o& @% R* l    TxCounter1=0;9 w8 J* R! l, S/ c7 b
    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);# [3 Z% q$ c* j- n3 j& c
    while(TxCounter1!=TxLen);
* I- U6 J1 |, S        以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。
wamcncn 回答时间:2014-12-19 13:32:32
万里-363223 发表于 2014-12-19 08:51
4 \. {: O" K3 ^/ j0 m8 o8 i9 gSECTION 4
; d, m" n4 ?# e, J        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断 ...
; K, O* _2 L7 n$ r! X
强大的同时,复杂也跟着来了,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
1 P) }# f, P: U5 W- k7 D4 ]强大的同时,复杂也跟着来了,51是基础,STM32是进阶
6 M0 F7 M3 J9 g8 ?
恩恩,没错,鱼和熊掌不可兼得
小贾-370388 回答时间:2014-12-19 15:30:39
充充电: `6 {% d" ~4 r; `: h, t1 g, Z( y
霹雳之火 回答时间: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- s* g4 ?8 M! A6 X9 ^.。。。。总结的好细致。。。。受教了。。不过,51终究是51,用起来简单,当然也就没那么强大了,stm32虽然 ...

" L$ ^& h8 J5 z2 d  |1 g' {- }# G恩恩,没错,还是STM32爽
wjandsq 回答时间:2014-12-21 10:51:09

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

本帖最后由 王建 于 2014-12-21 10:57 编辑 2 ^/ N0 G. T+ K" {5 p+ I

2 U9 i! ?# N- X( B至于接收,根据实际情况而定。
wjandsq 回答时间:2014-12-21 10:59:47
补充下,上一段是USB转多路串口(USB-Multiple-CDC)例程中的部分代码,仅支持STM32F103xx系列。
12下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版