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

STM32串口发送中断

[复制链接]
万里-363223 发布时间:2014-12-19 08:49
0 E! x1 F' L* [+ E7 C
SECTION 1; S5 R+ z  u0 E3 }% q+ G! F
20141014220925993.jpg
# I, P6 E* o& [; B$ V
SECTION 2
! e' g7 G, a! C2 L
先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下6 @7 B5 u  X6 x7 U9 f
void USART_SendDataString( u8 *pData )
$ f/ E/ M8 T: q# [# W* q{
+ P2 y: @  o$ V& L    pDataByte = pData;" j! T/ B9 E, |9 c- Y8 ~# m6 J
  0 I% t4 L. G# Q
    USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
& v+ I+ g* f4 G    7 @' q, ^' _/ y' _5 N3 ?
    USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次
* h4 n4 p, p& i! x- H( y}& @5 ?) \3 O  G! ~4 W

8 A$ B1 W( Q+ u/ D9 J- E/ g" }/ u2 _% k4 r$ @4 i
中断处理函数如下
3 @: x4 ~2 p- @void USART1_IRQHandler(void)1 \+ S3 I9 ], v0 j
{/ Y% Q! w+ n. ?1 P. m
    if( USART_GetITStatus(USART1, USART_IT_TC) == SET  ). G6 |. M1 p+ N$ I
    {
' q# G7 c2 L/ _9 a. G* R        if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉7 k7 X4 _$ ^7 C! g6 F2 h8 H
            USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断. clear掉即可,不用关掉TCIE
" ^' I' i$ ~6 E9 z) V1 o  E& }        else
3 F9 F# |* p! _' M% F            USART_SendData(USART1, *pDataByte++ );% O* D+ x5 t* [1 j( i9 E
    }- m- x! x* D7 ^4 J7 d
7 Q$ L' }5 V; Y4 X; u: H; t( E" n
}2 n& q+ A8 Y3 F3 A* `8 [0 z
: N- Z! a1 K' l/ B' D
其中u8 *pDataByte;是一个外部指针变量: L/ ?& \8 e2 ?3 J: g

2 j9 G9 V. ?* E9 e8 M! h在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。/ ]: y7 G% j4 C  L$ I1 q
, n/ L% d1 z7 V; C# R
串口初始化函数如下1 `% j8 l6 o7 j+ r0 C9 ~8 q
void USART_Config()
& J1 [9 m% i( G' Z$ g1 k7 A{" D1 h0 f# g# `& e& J, i5 _- A2 ~; M
  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体
( l. l9 d" Z( |- u) u: w- j7 `  
6 o) C; s5 O+ G  USART_InitStructure.USART_BaudRate = 9600; //波特率9600
5 f, ]: @' D: u1 X& ^) S# z- K  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位- p5 v6 j" Y4 ]
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位& J% E6 e. D8 j
  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
5 z7 s, D( [9 v! i  ?( A) R' S  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
. H. ^! B  O# w8 T+ F: K  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式, d( A3 I# h/ i# F" k6 g+ L; C
  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭( |7 s, u. F8 o9 E  i3 ]
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
3 z8 a9 u9 p7 |3 d  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;/ u% z/ e; r/ j- e  ?* j! Y
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;) ~3 F  Y9 ?$ `$ e' O8 }
  USART_Init(USART1, &USART_InitStructure);//设置到USART17 d$ ~  v; E( v
  * }+ P- j$ j+ y: g+ Y$ b) g
  USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节
; Q' W/ N5 z3 {0 c- @4 w" m! v
; e( |& _  D, T) \+ g9 P  USART_Cmd(USART1, ENABLE); //使能USART1% Z- R9 u' i; }( O1 _) [$ a# D$ J7 O
}) @( `* {9 x6 F# G; c/ O
这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么??
+ w3 {% h' \/ [# ^% o
. y- ~: m  r5 G
这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.

6 n9 j* m8 q: P) l
1 _) ^1 b+ f6 R  o$ x
再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。3 J: a* d5 C" t) f2 q9 C6 u$ D
# V$ {0 n# F8 Z
发送函数如下:
' n& C" R. y9 N9 O3 k0 jvoid USART_SendDataString( u8 *pData ); {& O8 ^2 x; A& Q! {
{
. D- x/ a" {, v4 P$ b( h9 \( H& c; o9 k    pDataByte = pData;& g- j4 u7 J# x
    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。 - ~6 ^5 l' D& k( z0 p& [5 H
    4 |) y- V" H% f; _8 M4 ~" M& X
}
" h( o# v3 a/ \4 e$ b- Y# [3 ]2 ], o, m
* T- p8 b; C% g& ~5 \% t9 U3 D中断处理函数如下:
4 p+ F" o% `1 u, @3 E) `5 yvoid USART1_IRQHandler(void)
) ^; {9 R  K" f0 l" G8 |  s{
  [2 g6 g0 S4 L% g( @% d+ {    if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )
+ T( ]1 W4 D2 G  K8 Z    {; s- R5 C6 ]$ w5 v
        if( *pDataByte == '\0' )//待发送的字节发到末尾NULL了1 }3 T0 ]5 m- d: K. c- M
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断% J! [$ [3 j6 b' {6 L
        else
% U2 C  c) F7 z1 p4 a8 S- E' _! \            USART_SendData(USART1, *pDataByte++ );3 A5 }/ O! C9 [, t9 V
    }
1 a1 M( }* j$ W* l0 @. F* E" G) U# _$ z6 |8 @: R% h
}& s" J. K/ J/ K9 \' y, V: H* @
! y& H* n5 N* d8 H& G5 j, k! @
在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:
" E5 t% K7 @) q$ T+ _' Q' jvoid USART_Config()
# g: N) l" @1 i{
7 W  o5 d1 Y7 I3 d6 E/ D2 Z, P+ M  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体
( ?! u1 H2 y& _) O2 V! ?1 p+ }/ L  0 L0 r" x& ]& Z  W/ P3 n6 o7 f' [
  USART_InitStructure.USART_BaudRate = 9600; //波特率9600
9 r( Z" ^3 ^. _* r0 h% o  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
& ]! X0 P- C! B, a8 y0 I, h  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位: |$ C& O* F7 s2 M: o! Z5 K2 x
  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验; @) ~; V1 v: j7 M6 R* [
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制3 B3 e5 s' Y0 R$ c6 w, x* H7 r
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
/ b8 b0 s4 L& Z2 v  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭
/ a8 S- X6 J9 m; L  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
$ [- l+ ]8 p0 O$ Q  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;0 y5 Y+ a8 f$ b
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;( R$ `  b0 B& p3 H

/ p  p: u: E- h; `& F! X  USART_Init(USART1, &USART_InitStructure);//设置到USART1
8 P! e; g7 C( @% P3 v' @  1 m, U7 z4 B9 ~, ?  q* Y
  USART_Cmd(USART1, ENABLE); //使能USART1* W, |) t8 M9 U) ?
}
  s; E  B. L7 F5 K# z0 H. ?2 n6 x; ^! a& s& |8 }7 h
SECTION 3
/ M  t0 a- E" ~) K1 S

( q4 J/ g( r) L" |: F
# ]9 ?6 @4 x: o% z' i

在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。0 W0 k* y/ E! W0 G% C  L

+ j6 q0 F4 X4 u# {( u对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。% A) R6 C! s2 l7 `# q

; R4 I+ o) g6 U另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。
6 c; L  q3 j0 K- r( o4 q
' M$ l, v" r+ z! `- i6 ~* YTXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
4 B" X: n- q# f2 l+ Q' H+ I4 B
2 Y- y7 g6 F' ]  Y! R. H% z/ X- J- {至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。


# ^: S% }, W- U. C3 x# ~. c

SECTION 4
4 V" n7 ^  Q  Z0 q) j

       2014-12-19 08 48 55.png
/ L, V. @7 ^- t  E5 n' |

+ ^0 l9 P1 k- ?& a1 ^
- ~6 V7 ]. Y; b2 G8 c. }
收藏 1 评论17 发布时间:2014-12-19 08:49

举报

17个回答
wjandsq 回答时间:2014-12-21 10:53:00
/**
+ @" a0 ~/ k/ L* U- A: J* m  * @file    usb_endp.c
% G/ b+ y! F; Z: k  * @author  wjandcf@gmail.com
1 O. H( K* }2 t4 Z: @4 a  * @version V7.0.2$ c/ n8 `* Z. e5 d& ]
  * @date    2014.12.20
; g- V4 ?0 _5 {# T0 i7 e8 a% J0 q7 ^* o  * @brief   Endpoint routines" X3 j, z  N& K  o
  *  
: p: P3 k5 e, ]  J& ^; A! r# D+ F  */
( k$ l; J! g( D, \* W# r$ i* A& c/* Includes ------------------------------------------------------------------*/1 _$ f- W; f& j/ t
#include "usb_lib.h"
! T  ^2 N) U  _& A# i' y9 P8 p4 @4 K#include "usb_desc.h"
. V% `& D. X: J$ q#include "usb_mem.h"
" w8 w( ]1 P0 V1 H#include "hw_config.h"* G$ j3 A+ ]4 E1 c2 v3 P- L8 F
#include "usb_istr.h"; b/ q* V. ]. k" I+ j0 l
#include "usb_pwr.h"
4 g1 l, ?* F: U4 [0 n& }! W7 ?' z" ?4 D9 l7 y2 r) x
/* Private typedef -----------------------------------------------------------*/
: z( a& l  D4 j& ]6 v1 M+ d% X( t5 n& q/* Private define ------------------------------------------------------------*/$ O! g. \+ Z9 Q

5 c: @# T. g8 S8 ]/* Interval between sending IN packets in frame number (1 frame = 1ms) */5 `5 r( {, i+ E2 i4 M1 c/ X  s: u
#define VCOMPORT_IN_FRAME_INTERVAL             53 W+ p. o6 d, y9 p
! S1 @- Y. y: F8 K1 e1 P) n/ L
/* Private macro -------------------------------------------------------------*/
( A; i5 s; A, b% F+ I! |2 p/* Private variables ---------------------------------------------------------*/4 r$ s! _4 }+ X0 k0 v  R; }
uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];- Q9 |9 p+ z4 H/ Y; @" j9 X# U

- _1 t: r, m- Q! ~( `% vextern uint8_t VCP1_Tx_Buffer1[];      /* VCP1 DMA发送缓冲1 */
$ r  W" k% e) G1 X# mextern uint8_t VCP2_Tx_Buffer1[];      /* VCP2 DMA发送缓冲1 */
. U4 l4 J9 w- [8 ~extern uint8_t VCP3_Tx_Buffer1[];      /* VCP3 DMA发送缓冲1 *// ?! [; J! c6 M$ F. b- @

- @; e- C& X% Oextern uint8_t VCP1_Tx_Buffer2[];      /* VCP1 第二缓冲(临时存放来自PC主机的数据包) *// `- b% a6 ^4 T! ~
extern uint8_t VCP2_Tx_Buffer2[];      /* VCP2 第二缓冲 */" f' s, k/ Z' [. G- l
extern uint8_t VCP3_Tx_Buffer2[];      /* VCP3 第二缓冲 */
) n6 H1 i* q. B# x" H' |9 e  @1 ~* k* B$ E5 \
extern uint8_t  Flag_VCP1_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP1_Tx_Buffer1被锁定 */, w  m: \/ D, s& q
extern uint8_t  Flag_VCP1_Tx_Buf_Full; /* 1: 表示VCP1_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */
9 T8 W  \6 k* W4 `2 wextern uint16_t VCP1_Tx_Buffer_Cnt;    /* VCP1 第二缓冲 收到的字符总数 */
9 w9 G% S; G6 F# F, B5 ?8 ~/ r0 z
+ _, g$ g! w3 B6 vextern uint8_t  Flag_VCP2_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP2_Tx_Buffer1被锁定 */, @! T1 s# D7 }0 J
extern uint8_t  Flag_VCP2_Tx_Buf_Full; /* 1: 表示VCP2_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */
( p% {: O+ w5 s0 a/ s, J5 k! Pextern uint16_t VCP2_Tx_Buffer_Cnt;    /* VCP2 第二缓冲 收到的字符总数 */0 w! M# X5 y* K! X+ l
# }1 h" j' _4 @7 l1 l3 u! @
extern uint8_t  Flag_VCP3_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP3_Tx_Buffer1被锁定 */
; R# s: b1 F4 y* Q3 j# q9 dextern uint8_t  Flag_VCP3_Tx_Buf_Full; /* 1: 表示VCP3_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */  p: X( }& n: d, A6 s
extern uint16_t VCP3_Tx_Buffer_Cnt;    /* VCP3 第二缓冲 收到的字符总数 */& z! C. t3 d3 i# o- g" p; O

' i) f) C6 ?; \" x# r: H4 Qextern uint8_t  VCP1_Rx_Buffer[];, e4 g! m" Z6 V; l7 [
extern uint32_t VCP1_Rx_ptr_in;$ G) r% R3 a8 \4 m- }
extern uint32_t VCP1_Rx_ptr_out;1 T' I( p( X* K3 p9 |
extern uint32_t VCP1_Rx_length;
! k' S' {  X) Oextern uint8_t  CDC1_Tx_State;
( B1 Z( v* j) O- Y5 u/ d
8 L. N4 D& |7 Q5 V  p; F) qextern uint8_t  VCP2_Rx_Buffer[];
* p& X* V' j8 a7 t& u/ Vextern uint32_t VCP2_Rx_ptr_in;
4 `; j& ?, b# `% z2 x) b8 |( Q5 Pextern uint32_t VCP2_Rx_ptr_out;6 c8 X' A5 b. z9 V: F+ e. `
extern uint32_t VCP2_Rx_length;
; K) }4 m0 V$ }" Y4 Xextern uint8_t  CDC2_Tx_State;0 m* ^/ \0 l* {
0 g4 M2 y( t; F9 Y* R: e  |
extern uint8_t  VCP3_Rx_Buffer[]; * ]) Z! X( J) Q6 u* O2 _$ p
extern uint32_t VCP3_Rx_ptr_in;
; B1 S. k  n: k+ ~) Hextern uint32_t VCP3_Rx_ptr_out;  C6 k3 A; _( S9 g% [7 I: Q1 h  @
extern uint32_t VCP3_Rx_length;
: q& K. }  c3 T" J! q. ]extern uint8_t  CDC3_Tx_State;) F' F$ s2 B6 n( K$ U, i$ h4 ^4 `
9 Q; i% T8 o3 E- V6 {/ T1 g8 J
/* Private function prototypes -----------------------------------------------*/# P' K9 E; F7 `% a& ?
/* Private functions ---------------------------------------------------------*/
5 O7 U" ^+ N& [& K2 S: G! i  ~5 H+ U3 X: o9 }0 J" o1 d
/* USB的IN端点 发送数据到PC主机 */8 A+ g( W7 a7 U$ h' _3 ^
#define EPx_IN_Callback(ENDPx, CDCx_Tx_State, VCPx_Rx_Buffer, VCPx_Rx_ptr_out, VCPx_Rx_length) {\' l% n3 _; j- f$ p) j
    uint16_t USB_Tx_ptr;\
) P/ h- A" k/ k9 ]% `    uint16_t USB_Tx_length;\% k$ T5 x* T% X! x
    if (CDCx_Tx_State == 1) {\( ^" P* [4 _! D) Z* }
        USB_Tx_ptr = VCPx_Rx_ptr_out;\
4 D7 j0 w# h7 p        if (VCPx_Rx_length == 0) {\
' x6 Y3 e: K# [8 }# F. ~1 s            CDCx_Tx_State = 0;\
/ i. ]& K5 [& z( C            SetEPTxCount(ENDPx,0);\$ S4 @- ]7 o( j/ ?7 ~! Y
            SetEPTxValid(ENDPx);\
* N! r$ g6 k$ T        } else {\% g9 c$ s; c5 E4 Q
            if (VCPx_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE) {\
0 {4 M( l) H# G% D- g8 N- h                USB_Tx_length = VIRTUAL_COM_PORT_DATA_SIZE;\
: r, u6 Z! A! d% T7 z& u                VCPx_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;\  p' `, p4 }: m: m2 N# h+ c) J
                VCPx_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;\
/ _& \9 }: i# L4 H2 S5 h2 y5 Y            } else {\2 `+ r  s4 l* J; R, G( J
                USB_Tx_length = VCPx_Rx_length;\
" n  E: A" ~2 T9 Z! d8 l; B                VCPx_Rx_ptr_out += VCPx_Rx_length;\
9 h9 [7 R9 U/ w  S$ k1 ^- |3 [                VCPx_Rx_length = 0;\
( v# t6 G% G# G, b. f/ c1 e  l            }\
0 ~8 M" p4 z, S3 D+ X% {8 Q  U            USB_SIL_Write(ENDPx, &VCPx_Rx_Buffer[USB_Tx_ptr], USB_Tx_length);\
5 u* _* v; g& w; Q5 w$ H            SetEPTxValid(ENDPx);\
/ a, f  O  o8 ?- H+ V: F' }4 m3 t        }\
' q8 q7 |, ?7 R8 i" T    }\
7 n7 Y) h2 |1 {. c  Z8 M6 q}/ @- \! M. W4 l1 \
/* USB的OUT端点 通过物理串口向外发送数据(阻塞方式) */" l* H, Y2 M' O( c2 r
#define EPx_OUT_Callback(ENDPx, USARTx, GPIOx, GPIO_Pin_x) {\
. O2 D* N# \! _% s, R    uint32_t i;\
+ t8 B: S% I8 H  I    uint16_t USB_Rx_Cnt;\
# a& B0 N! l; _" e" ^; @: l    USB_Rx_Cnt = USB_SIL_Read(ENDPx | 0x00, USB_Rx_Buffer); \" ^0 h$ V" f) L! W" x8 J+ a
    GPIOx->BSRR = GPIO_Pin_x;\# O9 P# e$ [. P) B5 [. q# }6 B
    for (i = 0; i < USB_Rx_Cnt; i++) {\
% ?4 H" a( W* |/ h% Q# u6 T! a$ y        USARTx->DR = *(USB_Rx_Buffer + i);\$ o. L) T3 Y0 C5 {0 ~6 ^0 s
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);\
1 ~8 b+ @* {! e2 |' f2 I    }\- q* o0 c, @% v1 m* p/ r# N
    SetEPRxValid(ENDPx);\
* I; M# j0 U$ X! L( z0 Y; C: {! C    while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);\
4 y# g# a1 Y8 F+ @4 c2 O- |+ l" _    USART_ClearFlag(USARTx, USART_FLAG_TC);\
2 `! `  K1 H% _4 t! H$ a) f    GPIOx->BRR = GPIO_Pin_x;\3 v- x; [- |5 ?" d$ p4 t! q
}8 s; V" w) {. {% {

" X( s: K, \/ @* q/* USB的OUT端点 通过物理串口向外发送数据(DMA方式) */
' J4 H3 w$ |6 N0 f2 U: O#define EPx_OUT_Callback_DMA(Flag_VCPx_Tx_Buf_Use, ENDPx, VCPx_Tx_Buffer1,VCPx_Tx_Buffer2,\
1 M- O% p+ T  ~0 _    GPIOx, GPIO_Pin_x, DMA1_Channelx, VCPx_Tx_Buffer_Cnt, Flag_VCPx_Tx_Buf_Full) {\, Y4 R. b  C5 \/ `+ e
    uint16_t USB_Rx_Cnt;\
5 l( E( b- P& ?    if(Flag_VCPx_Tx_Buf_Use == 0){\( B; H9 j2 A0 B) _  H  k
        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\1 A$ q: ~9 X0 m. v: Z9 v
        PMAToUserBufferCopy(&VCPx_Tx_Buffer1[0], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\0 G/ ~* E5 V# m3 L0 f6 k& p
        SetEPRxValid(ENDPx);\
- T: A7 \( L- a$ |  ~        GPIOx->BSRR = GPIO_Pin_x;\
* S, t; d) J! o1 x) K        DMA1_Channelx->CNDTR = USB_Rx_Cnt;\- j+ k, m3 j& n+ F! f. {4 L$ T
        DMA_Cmd(DMA1_Channelx, ENABLE);\
* [5 p3 b# @, a* g) _8 W        Flag_VCPx_Tx_Buf_Use = 1;\+ j$ w% K7 M; k! y, I
        VCPx_Tx_Buffer_Cnt = 0;\
4 N* ~( `  r. s' V3 u: k    } else {\* ~. u% d$ R9 e
        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\4 p3 I/ v, j. |" ^) K) U5 Y5 X0 ?
        if(VCPx_Tx_Buffer_Cnt < (1024-128)){\
1 Z1 S6 b) ~- G0 y& B* `( y8 g          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\0 V) X" w8 t! J3 l- G
          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\9 v# ?" \$ M% X6 k$ B1 t
          SetEPRxValid(ENDPx);\
0 y, k& i5 Y+ p& M8 }        } else {\" b* A" F1 i; d3 ^' r0 j+ F
          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\3 _9 k0 v& z" f3 H; u" n
          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\: G# t' `7 b. D1 J
          Flag_VCPx_Tx_Buf_Full = 1;\+ i! z6 p+ |! z
        }\5 f8 S4 E/ f! j
    }\% z/ c: O2 e+ {( R. W/ a
}
8 u0 t2 |8 R8 z' W( ?2 x3 _/**7 J8 a# t9 k, z8 J- A5 X8 D
* Function Name  : EP2_IN_Callback
2 B; o6 @* Z, S! O! j" g' Z% k * Description    :
9 V! I7 O" g9 \5 v **/6 H4 d7 {$ N( H
void EP2_IN_Callback (void) {
8 }1 o' D  ~$ Q$ V1 E  Z    EPx_IN_Callback(ENDP2,CDC1_Tx_State,VCP1_Rx_Buffer,VCP1_Rx_ptr_out,VCP1_Rx_length);0 [  y4 G" j4 G0 t  _$ a
}* i. Z/ j( ]' c+ q
/**) U9 w; f+ ~" B) u
* Function Name  : EP4_IN_Callback1 j) k+ A1 Z7 v# N" B
* Description    :
  V# y$ f" W- i/ \: a' V **/
) {/ b) @* z3 H* }6 I) e. xvoid EP4_IN_Callback(void)7 r; |% z5 S% W4 F' T1 W) {
{9 s7 R. t/ h7 A/ N' p
    EPx_IN_Callback(ENDP4,CDC2_Tx_State,VCP2_Rx_Buffer,VCP2_Rx_ptr_out,VCP2_Rx_length);
0 c+ N' k! [5 n6 g) s}
7 ~3 j: Q, s# ]: O3 M/ K9 o/**
6 U% t: Y' @6 ]# [+ P * Function Name  : EP6_IN_Callback
. O% q1 z) _. c8 I4 ^7 _ * Description    : VCP3 向PC主机发送数据
7 W$ w, S+ ^0 e2 Q8 y) K0 O **/
" R; _' N4 E/ }+ V- i  {, H2 o# M5 |void EP6_IN_Callback(void): t4 W; X3 V' Q- m
{4 ^  T9 @4 ?, _
    EPx_IN_Callback(ENDP6,CDC3_Tx_State,VCP3_Rx_Buffer,VCP3_Rx_ptr_out,VCP3_Rx_length);  
; o' j, c9 |) j6 ^}
* `% z' H4 d- [% T/**
6 K8 Q) U9 v8 ~) t+ R * Function Name  : EP2_OUT_Callback
7 M1 x5 k, m# Q1 b2 y& y& A * Description    : VCP1 通过USART1向外送数据
; j% S1 N4 P0 A0 R2 P: m' j+ X **/( R" h2 {! Y; B: r8 j0 J. s; d
void EP2_OUT_Callback(void) {  B! ~  V) C) f% c: j+ v
#ifdef USB_DMA_SEND
  {3 y* {  F( f) i, z" C- k    EPx_OUT_Callback_DMA(Flag_VCP1_Tx_Buf_Use, ENDP2, VCP1_Tx_Buffer1,VCP1_Tx_Buffer2,\% }; m7 s+ R; j
            GPIOB, GPIO_Pin_9, DMA1_Channel4, VCP1_Tx_Buffer_Cnt, Flag_VCP1_Tx_Buf_Full);
; G  s/ D! n; G: u3 p. G#else& k( b& V0 t, N! {% E5 v. I: `( V8 D
    EPx_OUT_Callback(ENDP2,USART1, GPIOB, GPIO_Pin_9); // DR1 PB9! w0 U5 ]& N& i2 ]! e& ^: }# V  q
#endif" T& ?* U5 v$ M  Y" h( V
}4 L' p3 a: {# N. s6 J- j
/**
. ?8 v. P- t, R, B$ j* y5 E * Function Name  : EP4_OUT_Callback2 E$ V4 B  e8 M7 A  D! c
* Description    : VCP2 通过USART2向外送数据
1 y/ O% `) w+ Q& ] * Input          : None.
3 m! L9 p) H9 Z9 w. @, ~ * Output         : None.1 }# `( W8 c% y8 F, q6 {/ z/ z. S
* Return         : None.
: o% H0 m2 r. j; C **/4 o, [6 g3 s7 N6 C1 ~
void EP4_OUT_Callback(void)
& b" L  w. d7 Q' }, x1 a5 T: C{
3 Q( B: Y% F4 ]% y3 \( L#ifdef USB_DMA_SEND
& ~5 l( T, k* ]+ g    EPx_OUT_Callback_DMA(Flag_VCP2_Tx_Buf_Use, ENDP4, VCP2_Tx_Buffer1,VCP2_Tx_Buffer2,\
+ q: D7 p& H! N# ?. z( q' n5 J            GPIOB, GPIO_Pin_8, DMA1_Channel7, VCP2_Tx_Buffer_Cnt, Flag_VCP2_Tx_Buf_Full);% \/ D8 C  s/ c% n
#else
& i+ q* P1 I& q; \9 I    EPx_OUT_Callback(ENDP4,USART2, GPIOB, GPIO_Pin_8); // DR2 PB8
$ B; u6 x( u3 ~#endif
. D! y! n4 \' z% p3 ~}" N0 v' v& H) @3 R
/**7 L. K9 Y) J- J, V: W" M" g
* Function Name  : EP6_OUT_Callback
" l1 @  C, `" T+ ]$ b * Description    : VCP3 通过USART3向外送数据
; e" ?7 s8 w+ W **/8 U6 M; p' A& @, p( h1 Y& D
void EP6_OUT_Callback(void){2 X! c2 ]: `9 h' n4 B9 W/ W9 h
#ifdef USB_DMA_SEND" H# m& `1 S, m. q
    EPx_OUT_Callback_DMA(Flag_VCP3_Tx_Buf_Use, ENDP6, VCP3_Tx_Buffer1,VCP3_Tx_Buffer2,\
( D+ z6 o' e. G2 r            GPIOA, GPIO_Pin_5, DMA1_Channel2, VCP3_Tx_Buffer_Cnt, Flag_VCP3_Tx_Buf_Full);
8 [; {2 \! T2 m4 f& P#else  
" P0 |. k7 V$ b    EPx_OUT_Callback(ENDP6,USART3,GPIOA,GPIO_Pin_5);  // DR3 PA53 y2 i' a# F( g9 ~3 E& i
#endif
5 l, K; s2 F+ o}
5 V6 o- l+ z4 Y/**( P- p& O# S- r1 L% W# A
* Function Name  : SOF_Callback / INTR_SOFINTR_Callback
& ?: F$ U6 F4 e2 G) I8 M* @3 Y * Description    :
( x* G0 ^2 c6 S1 P. O8 g+ q **/2 ^8 r- x4 ^& Z+ s' x. L8 e* L1 l
void SOF_Callback(void)
2 S0 H0 @( K( M' |! C- H8 X) l) T{1 C4 t8 b% L$ W: g! y* ~
    static uint32_t FrameCount = 0;
0 r) ]) ?- Y' `* V7 Y6 I# b    static uint32_t Led_Count = 0;
4 |6 n) D' ~7 a2 {% D6 U4 ]  }    if(bDeviceState == CONFIGURED) {. q  |# J8 I& w) T
        if (FrameCount++ >= VCOMPORT_IN_FRAME_INTERVAL) {
6 k$ _. U7 ~# c& C/ b2 m            /* Reset the frame counter *// H8 w' X, A/ {; T4 \9 R
            FrameCount = 0;
! Z% F$ _/ i0 A            /* Check the data to be sent through IN pipe */
- B" A3 _4 v3 q. l: N            Handle_USBAsynchXfer();( K# i) G: p# _3 g" D$ O
        }' v- I4 Z" d2 x, E" _, s9 \0 R6 \& _
    }
8 t6 E9 R9 h% P' m    if(++Led_Count > 999) {/ o5 d/ l( U* U5 [: D1 ]
        Led_Count = 0;
5 Y: }% g$ J- }7 F( _/ i/ q        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0){3 ^/ O& X3 ^9 F
            GPIOB->BSRR = GPIO_Pin_2; // PB2 H9 ^& X8 s. V% d1 V) I% ]/ A
        } else {
' g3 z4 }! g* ?# E            GPIOB->BRR = GPIO_Pin_2;  // PB2 L, A2 O1 V) H$ ^& D
        }/ [4 T& O: h6 k4 }' P! G! k4 t5 J9 n
    }
0 ~+ H3 O0 y' f9 ^9 O}
+ V: q: \" F+ A: \9 ^% K' t( L3 H4 F0 |/ D
万里-363223 回答时间:2014-12-19 08:51:16
SECTION 4
6 N* }5 v1 y& t. W1 q9 S        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断系统和51单片机一样的简单。& J( t. Y# t# q/ R! R% z
        对于接收终端,就是RXNE了,这只在接收完成后才产生,在执行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代码时不会进入ISR。但麻烦的就是发送有关的中断了:TXE或者TC,根据资料和测试的结果,TXE在复位后就是置1的,即在执行USART_ITConfig(USART1, USART_IT_TXE,  ENABLE)后会立即产生中断请求。因此这造成一个麻烦的问题:如果没有真正的发送数据,TXE中断都会发生,而且没有休止,这将占用很大部分的CPU时间,甚至影响其他程序的运行!
4 [; ^0 a; u7 q, I" Q: b+ d        因此建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。
- X4 y4 \  d& Z% Z( S& Q: |        对于发送,需要注意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清零。8 K- _) v; N6 X0 A" L* \0 L& M
        对于发送单个字符可以考虑不用中断,直接以查询方式完成。
' {- `& D0 i/ `        对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。9 M, d1 K8 `5 M% `" E
       这里简单分析上面代码的执行过程:TXE中断产生于前一个字符从DR送入SR,执行效果是后一个字符送入DR。对于第一种情况,如果是可显示字符,就执行USART_SendData来写DR(也就清零了TXE),当最后一个可显示的字符从DR送入SR之后,产生的TXE中断发现要送入DR的是字符是0x00——这当然不行——此时就关闭TXE中断,字符串发送过程就算结束了。当然这时不能忽略一个隐含的结果:那就是最后一个可显示字符从DR转入SR后TXE是置1的,但关闭了TXE中断,因此只要下次再开启TXE中断就会立即进入ISR。对于第二种情况,其结果和第一种的相同。
2 D* j4 {& u: Y3 o         对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:
% s  @2 U. n, [; w  m$ c6 A( w/ Iextern __IO uint8_t TxCounter1;
% i( W$ Q! w& i5 @/ Kextern uint8_t *TXS;
4 u" N- Y  S5 iextern __IO uint8_t TxLen; + X- I: `/ Z( I' `
void USART1_IRQHandler(void)
- ?3 j; p* N# W9 W$ K0 y  d1 H    {  A0 f* P( O  H+ T/ \
        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
7 x# k$ F4 T5 g* F* ^  ^' z            {                                               
/ ]9 T. F6 o' p8 ^9 x                if(TXS[TxCounter1]) //如果是可显示字符
$ Z3 Z% K& j; o3 E/ l                    { USART_SendData(USART1,TXS[TxCounter1++]);}
3 m+ a, t- x. {2 h; q( n+ ~8 t                else   //发送完成后关闭TXE中断,. Z! Q( |5 A3 M
                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                        ; I2 F, }7 C1 u- B" _
            }                  
) c1 ]+ \- {3 C- X5 p' _& E    }7 K3 T% d* y. Q; k+ X4 {. {
        对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:* q' N" ?5 \$ Q( `$ K
void USART1_IRQHandler(void)
' Q  v$ F2 c1 b- b: [    {
# x( S) T3 ^5 K5 H: x6 y/ j        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。
$ s# D; J" E9 _* W/ r7 `. e            {                                             
8 _/ S% {* p0 g% k" n" K                if(TxCounter1<TxLen), Z4 Q4 h3 U' T3 Y: T
                    { USART_SendData(USART1,TXS[TxCounter1++]);}( M6 k0 G! g& c3 `- v1 o. f5 H
                else   //发送完成后关闭TXE中断
1 m- U& A1 z3 x1 m                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                         $ j/ r6 Y$ K/ w
            }                    
9 |0 q  V  T* u1 d    }. P6 x5 G- j2 E3 ~
        事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!
  A* K2 \% h+ D# Q! E- h        在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:
; R) r5 V7 l% B) S  buint32_t *TXS;
' i, J  l6 p$ Z7 {uint8_t TxBuffer1[]="0123456789ABCDEF";
$ }/ a: q  [% R' {uint8_t DST2[]="ASDFGHJKL";
! Q( ?6 U7 c6 g, o__IO uint8_t TxLen = 0x00;
+ k2 O6 N* l! y/ i     TxLen=8;                               //发送8个字符,最终发送的是012345673 B2 ]8 q3 \  v$ d
    TXS=(uint32_t *)TxBuffer1;   //将TXS指向字符串TxBuffer13 c9 w/ A7 T$ O3 {( b6 k. A, y
    TxCounter1=0;                     //复位索引值7 h0 m, k' J; Q0 d
    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);   //启用TXE中断,即开始发送过程6 U, G# `' v! Q% Y( y1 y+ Q. u
    while(TxCounter1!=TxLen);   //等待发送完成" G: Y2 H& H( `
  u0 N$ Z3 T; Q# u  E, y! ]
    TXS=(uint32_t *)TxBuffer2;   //同上,最终发送的是ASDFGHJK
. U  j5 Q3 e6 h0 O# ^    TxCounter1=0;  S+ O. H0 ?4 w! m* @0 h  U' Q! y: j
    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);
4 v+ |2 o. ^& I    while(TxCounter1!=TxLen);: r% y; O3 V1 I# \0 H4 J
        以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。
wamcncn 回答时间:2014-12-19 13:32:32
万里-363223 发表于 2014-12-19 08:51
% S, p' M+ R& f: a. wSECTION 4! Y7 X" r  j4 H* T5 c; w
        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断 ...
; Z8 S. |2 t  t$ Y9 ^
强大的同时,复杂也跟着来了,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 k, s, g" h$ e: j2 g强大的同时,复杂也跟着来了,51是基础,STM32是进阶
$ s: ~5 E4 p% N% e, g( L1 v& g
恩恩,没错,鱼和熊掌不可兼得
小贾-370388 回答时间:2014-12-19 15:30:39
充充电" S% Q7 m( h, I" @& V* a
霹雳之火 回答时间: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:182 |/ a7 P3 i$ ^( l/ q. s
.。。。。总结的好细致。。。。受教了。。不过,51终究是51,用起来简单,当然也就没那么强大了,stm32虽然 ...
* i# z2 d8 I" O5 U' ^
恩恩,没错,还是STM32爽
wjandsq 回答时间:2014-12-21 10:51:09

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

本帖最后由 王建 于 2014-12-21 10:57 编辑 . K- {  f) |0 U1 j

8 j% Z$ @+ q7 \/ d至于接收,根据实际情况而定。
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 手机版