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

STM32串口发送中断

[复制链接]
万里-363223 发布时间:2014-12-19 08:49
" V' k' x! J4 |8 M8 Y
SECTION 1
" s# V' n3 `( F0 k/ ?+ a, O8 C 20141014220925993.jpg
$ }  z! `& Y4 A' `( @7 Y
SECTION 2
/ b2 ?$ z, s" [9 b& ?
先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下
8 p' S/ l5 Z1 A5 ^: U( g# fvoid USART_SendDataString( u8 *pData )1 y' Q; I6 O7 x% \. P. a9 W  T9 a
{
( g% Q: i* ]8 ^1 |6 h    pDataByte = pData;+ i) l- d; w% ~7 {, Y
  6 ?9 U2 f; m+ Z9 O4 N+ J5 G
    USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
  q1 e: s7 X: b4 h# l, z   
+ |& P# B! F8 n* `/ l0 C- `    USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次) L! T8 {2 [* T% B/ }( w5 Y/ t" }
}$ E7 @5 N: b. T  c& R
" j; T- [! Z+ F+ \
, N1 `: f& U% l1 F
中断处理函数如下7 _3 ^1 k  Y) {* _
void USART1_IRQHandler(void)% O1 V* ]- M0 W
{. I  s0 M5 j3 l8 O, s
    if( USART_GetITStatus(USART1, USART_IT_TC) == SET  )
: ?  o! E6 d5 f# b/ M3 y; @7 q    {
) g# H, V, U: v3 G# v        if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉
+ ^4 a: q  v- N' X9 m            USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断. clear掉即可,不用关掉TCIE/ v2 r3 b1 |" E& S% P
        else6 A& b8 u& _9 R/ G- g
            USART_SendData(USART1, *pDataByte++ );
# @, Z3 ^" z- D& `/ S$ M1 W( B: g    }5 _, \9 y0 P  |6 k! T2 _, p

9 v8 N. U9 ?" n. O}" D/ k" i* X  R+ _

, C% S! t6 U! M9 G: O9 G: B其中u8 *pDataByte;是一个外部指针变量
0 k- n; K- i  L/ }" E! Q- X$ N  w; Z. c! a; s0 a
在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。- u/ ]' C0 R. I3 S+ }

; H( g) }* V( v7 U. t, M7 ?9 n
串口初始化函数如下1 }3 |8 O9 }( ~6 x, K
void USART_Config()6 [, ?9 X- c9 V+ L0 P! ]
{
/ s% q# }  D8 E# \8 J  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体8 r$ K; c9 ?# `1 M' Z* Q1 \
  
! C0 I$ V+ j) ]) N  USART_InitStructure.USART_BaudRate = 9600; //波特率96005 j( p6 f, S! b4 {. p
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位+ r5 |/ X6 E4 A" W5 H3 R
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
) k9 g& b& B, p  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验7 V# A" _1 D8 p% [: D4 D
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制2 ~2 Z( q, D6 X- B
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
& ~) S* _% t# ~0 C: l1 b  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭
0 J1 J! a0 Y0 b3 p3 N) E, z  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
& s, q2 I0 ^1 z/ R3 D- k7 p4 \  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;6 s4 V( ~, s. o
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
2 j3 w! i. O$ [( O! n, C1 q; x* D% W) m  USART_Init(USART1, &USART_InitStructure);//设置到USART1
, o6 c1 f/ z, F0 W% P  
9 D$ e7 q) |8 O1 a/ j  USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节: C4 `# b  y5 A; e

  P! q* I6 F0 E' k$ o( t) e- |  USART_Cmd(USART1, ENABLE); //使能USART1
' u1 j& f$ I/ l& h0 }# w; R}
% T4 h) x3 A, |这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么??
+ @& ]& P+ ^0 [( C6 E$ k

& U/ D* R( h- ~$ i+ {4 j9 q& n
这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
4 `  V& f4 ?% X' M% F
: f* [& R4 s% s2 X
再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。$ G) w$ J8 m8 u# t3 T( Q

) Q! H# [! Z, l' j# U& d发送函数如下:) @: t; P9 C. T/ m$ T8 W4 f
void USART_SendDataString( u8 *pData )6 ?% T5 Y( N: k' N; d# z( b
{
, x7 T* y! @0 S! b    pDataByte = pData;
+ x) q4 o2 m3 X' K& Y' B+ t3 q- V9 c    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。 & c1 g* i& m/ S5 o2 [/ y
   
, j& t  d; z9 {, W}
  D: k$ `: U5 V* [9 e, y
4 B4 \+ H) t# R/ B. j中断处理函数如下:
' s7 u( e- }  \8 {/ |void USART1_IRQHandler(void)
' u' e5 O2 `) ]8 Y{( h$ p- k3 H* X4 Y' p" z
    if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )5 c: m8 W/ J' I. J* f! N  y/ t9 H' }
    {
- Q4 g: L; _1 w" q& m7 k4 h; o        if( *pDataByte == '\0' )//待发送的字节发到末尾NULL了' x$ x; j* b+ W2 q0 A, c
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断& _1 R' P& T3 `  D
        else; ]6 K* R' w0 ^; z# I
            USART_SendData(USART1, *pDataByte++ );- |. N( Z, ?9 z
    }4 N$ I! u3 }  \

: t, u9 s* f: n/ Q: ~}
/ Y: \; G0 e# k! t- s3 p
! O& a: j( W0 m% _1 F. z在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:
! `( v, Q( }* A  ~- i& Dvoid USART_Config()
6 T- Q* M, K+ N3 t{
0 |3 B7 ~9 a8 ?+ b$ y/ R3 P  USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体2 \" [& {1 n' [: f( V
  % W; Z6 P2 Q9 G2 i! Z  q9 {2 `( e/ J
  USART_InitStructure.USART_BaudRate = 9600; //波特率9600
2 e9 x; I! w$ ]  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
. i  ]! G, g6 o9 k6 m* @; _. }, ~. Q  USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位! C/ t5 _  d  W9 J* _+ F6 X& d$ G
  USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
$ c9 }$ {4 N  t) p3 ?' n  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
+ ]- V3 Y6 L8 Y0 C  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
2 E) ~4 Q9 D. z# p: b  USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭$ F  v4 g) s! l3 l& o# A! U! k1 c! r
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
4 U3 _/ t/ s0 k% O) {  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
6 U1 x# T0 q/ {0 J$ Q+ A  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;% f& h) U4 j! J6 b

+ S( p; @! q2 e  c  USART_Init(USART1, &USART_InitStructure);//设置到USART1
; y# u6 B0 e" y* R  
" ~( z8 H9 ~! n# {9 F, x" o  USART_Cmd(USART1, ENABLE); //使能USART1
- y. p3 L; ]( d8 M2 a1 q, L2 ~}
9 F5 d5 E8 I% W" G8 d
9 s; I  k. g. y* h+ D1 O( `" ySECTION 3: {" `/ f- B# J" e% T

% O0 S1 ]7 T% e& {1 B$ P% H

0 K# ?% o# M/ [0 |- L  {

在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。
7 O: S% @# |4 s& V2 J
* }$ M0 Q/ b2 h3 e2 r2 V对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。
6 u- n1 c- `' b9 u. Y) g- r& y/ |
另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。* K. v% p8 ?3 K1 U. e# U
3 l/ m; V' C9 a& U. q  R
TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
+ Q) _. A) ]4 P& N& A5 z
7 b# m/ r5 G  b3 _7 e至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

( P  v; d) g  p! }: q% r. k$ H

SECTION 4
+ r4 f% w! i6 c6 V$ Y2 _

       2014-12-19 08 48 55.png
5 W6 u" J, E6 C6 _: ], @. B

9 Y0 i: Y: L! ^& W8 ^# H: }/ ]0 t9 P8 [8 |4 v; R
收藏 1 评论17 发布时间:2014-12-19 08:49

举报

17个回答
wjandsq 回答时间:2014-12-21 10:53:00
/**
+ Z; l7 \  |8 Q* c, a' E, Z* p  * @file    usb_endp.c
9 n" Z7 [* L4 G& v  * @author  wjandcf@gmail.com
( P' n: U, V& A8 ?# D  * @version V7.0.2. c" A2 `9 ]+ |% Z  f
  * @date    2014.12.20
2 V- L2 N, z* @7 X+ H" ]5 W& n  * @brief   Endpoint routines- r, f, k2 l9 g5 a0 B. n
  *  
( y  K9 m3 o5 n; z, m  */9 a+ g0 Y$ l4 g
/* Includes ------------------------------------------------------------------*/6 y: ~- w6 y& u
#include "usb_lib.h"0 }" m" a7 |% C! J9 ]
#include "usb_desc.h"
- c# P4 Z6 F! {#include "usb_mem.h"$ V' L& @3 {2 i: p; p- v
#include "hw_config.h"
( L2 Z3 U3 N  `  d9 W+ S! B6 ^#include "usb_istr.h"6 z' E! J( {- r; S
#include "usb_pwr.h"/ M4 k0 `, _  T, o9 }
% Q2 e: t) ]/ X' b8 I; T
/* Private typedef -----------------------------------------------------------*/
9 R2 m! m  `. ]/ _3 x/* Private define ------------------------------------------------------------*/" m: O* [  t) t3 F! _- O
- d8 I  `. Z! F( }: P$ [# {
/* Interval between sending IN packets in frame number (1 frame = 1ms) */
/ e- P' X% m9 x1 `1 F#define VCOMPORT_IN_FRAME_INTERVAL             5
  o, ?- G8 q8 D2 f% J: v5 D$ k
& ~# D* j" o! b  {) q# f% K/* Private macro -------------------------------------------------------------*/9 b3 y3 C& F" w7 @4 P4 W
/* Private variables ---------------------------------------------------------*/
" b/ b, \7 Y$ ]/ r/ U1 H/ Luint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];! e% n+ g3 L% {+ L3 W" d- D

+ I3 z) f6 @+ q# y; H' kextern uint8_t VCP1_Tx_Buffer1[];      /* VCP1 DMA发送缓冲1 */
: v" y6 I, K( }. Y6 W# ^* h2 Fextern uint8_t VCP2_Tx_Buffer1[];      /* VCP2 DMA发送缓冲1 */
+ b, D. o: Y0 ^3 u6 _3 @extern uint8_t VCP3_Tx_Buffer1[];      /* VCP3 DMA发送缓冲1 */. |& G/ x( J! i
* G6 V# J. [0 @  P- Y# A! |( v
extern uint8_t VCP1_Tx_Buffer2[];      /* VCP1 第二缓冲(临时存放来自PC主机的数据包) */4 O* F/ \4 U$ b1 P  Z! N3 U+ O8 N
extern uint8_t VCP2_Tx_Buffer2[];      /* VCP2 第二缓冲 */. [9 t2 [* {5 e. e% q) `# L
extern uint8_t VCP3_Tx_Buffer2[];      /* VCP3 第二缓冲 */7 j6 I9 K3 T' H, f2 z4 C

- [8 ]6 w4 u! x4 d) _extern uint8_t  Flag_VCP1_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP1_Tx_Buffer1被锁定 */) Z5 q3 V3 D: `, j
extern uint8_t  Flag_VCP1_Tx_Buf_Full; /* 1: 表示VCP1_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */( Z/ I+ G7 D% m% K
extern uint16_t VCP1_Tx_Buffer_Cnt;    /* VCP1 第二缓冲 收到的字符总数 */
; H1 L) u. j5 R4 S# h" S, K# {6 j& v7 _3 P5 Q
extern uint8_t  Flag_VCP2_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP2_Tx_Buffer1被锁定 */
& K* \1 w3 ~! f4 n9 I: j- S+ G9 p) Vextern uint8_t  Flag_VCP2_Tx_Buf_Full; /* 1: 表示VCP2_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */; z  o7 |! I; A
extern uint16_t VCP2_Tx_Buffer_Cnt;    /* VCP2 第二缓冲 收到的字符总数 */
9 e% Y$ h1 w/ B: b% J) p. h8 X/ ]# G* e% [# t) s! N* R
extern uint8_t  Flag_VCP3_Tx_Buf_Use;  /* 1: 表示DMA发送已启动,VCP3_Tx_Buffer1被锁定 */
% d. H4 F( w7 ^5 cextern uint8_t  Flag_VCP3_Tx_Buf_Full; /* 1: 表示VCP3_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */
4 `" p9 y) D2 jextern uint16_t VCP3_Tx_Buffer_Cnt;    /* VCP3 第二缓冲 收到的字符总数 */5 B" o5 t8 {1 A7 w" C- R6 N

" ~6 U/ j! f- j* X, \$ U0 v  qextern uint8_t  VCP1_Rx_Buffer[];5 r, j4 O8 F9 {1 ^; L
extern uint32_t VCP1_Rx_ptr_in;
6 y0 f3 G1 w( y. s1 `! xextern uint32_t VCP1_Rx_ptr_out;! c* M8 b4 N- n0 u- y. }. }: @
extern uint32_t VCP1_Rx_length;& U: P* o: F' }3 w
extern uint8_t  CDC1_Tx_State;
  d1 r3 e6 r3 r$ \" k+ R0 c
- P4 t! Q4 x5 K# h2 F% Hextern uint8_t  VCP2_Rx_Buffer[]; ! ?# p5 j4 z1 ^8 y
extern uint32_t VCP2_Rx_ptr_in;
5 n) G- l! a3 X' R" i2 G( ^2 q4 Dextern uint32_t VCP2_Rx_ptr_out;; h1 j! }. i& t0 z" [& S
extern uint32_t VCP2_Rx_length;& v  F. W' K7 V* H8 D- D4 e% f
extern uint8_t  CDC2_Tx_State;
8 H& M2 h6 j+ J0 e, @
: I1 ~) f% @( [+ E4 Sextern uint8_t  VCP3_Rx_Buffer[]; & S6 n1 ?) i; o. q, K
extern uint32_t VCP3_Rx_ptr_in;) _- U/ [4 A7 k; n( n0 S3 g* l
extern uint32_t VCP3_Rx_ptr_out;
0 D6 E4 \2 V% ?' q) l3 l3 Uextern uint32_t VCP3_Rx_length;
# U" I, q/ J5 P( j! z8 Cextern uint8_t  CDC3_Tx_State;
5 e7 D* A0 @2 z, _- F4 A+ k0 A8 i8 ?5 C4 W4 @6 J
/* Private function prototypes -----------------------------------------------*/
' N& q# L) ]. R/* Private functions ---------------------------------------------------------*/
. F+ k/ p- ~6 }6 B1 N+ g
8 J/ C* B5 ?0 O3 ^! |4 |! X/* USB的IN端点 发送数据到PC主机 */
! i0 ?. }; }" k#define EPx_IN_Callback(ENDPx, CDCx_Tx_State, VCPx_Rx_Buffer, VCPx_Rx_ptr_out, VCPx_Rx_length) {\
! a3 r. U* _% D7 R' I    uint16_t USB_Tx_ptr;\3 M+ u0 b6 W4 P' w
    uint16_t USB_Tx_length;\, r3 F0 x+ P* w, ~7 `9 }) A
    if (CDCx_Tx_State == 1) {\* o2 U: R4 \8 O
        USB_Tx_ptr = VCPx_Rx_ptr_out;\
6 |# P$ _, D0 f* R/ _- O+ g        if (VCPx_Rx_length == 0) {\! t) ?/ j* D0 X) K; e
            CDCx_Tx_State = 0;\
) v0 r1 r, n: G! r/ r' n6 k4 j            SetEPTxCount(ENDPx,0);\- J) P; e: c4 B: H# ~( X8 `5 C
            SetEPTxValid(ENDPx);\$ }% e+ p: P1 Z. l3 n9 B0 \0 R  m
        } else {\
3 ^4 N0 I3 z& Z3 K* g# b/ I9 K            if (VCPx_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE) {\
% ?4 a8 n+ i+ M0 S                USB_Tx_length = VIRTUAL_COM_PORT_DATA_SIZE;\
# J* b6 n; a' r( |9 b. I# D                VCPx_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;\
5 U: R: q" {2 m5 H/ ?                VCPx_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;\
( s8 v: }" H' C9 ~& i+ z/ N8 I            } else {\
$ c. H! b+ M0 Q4 [) b5 T' e' U  F                USB_Tx_length = VCPx_Rx_length;\
' L) ]: O" U7 u, [                VCPx_Rx_ptr_out += VCPx_Rx_length;\
" z3 H2 O& F( A* Z1 l+ B                VCPx_Rx_length = 0;\
  ]9 X, M% P& ^; w5 |# p! u1 a' A% j            }\
; s% Z) b" C9 x            USB_SIL_Write(ENDPx, &VCPx_Rx_Buffer[USB_Tx_ptr], USB_Tx_length);\- N9 _. F0 g8 B8 x' d, K' h. U; a5 s' @4 Y
            SetEPTxValid(ENDPx);\$ Y+ k$ D5 y) _& `
        }\5 l! k) X) X: u. {( Z
    }\
/ o4 h6 q9 q3 q( A}/ U5 S8 _5 I' {8 W; A. x6 a; E7 s
/* USB的OUT端点 通过物理串口向外发送数据(阻塞方式) */
3 O+ r: X: I" M$ Y0 Y#define EPx_OUT_Callback(ENDPx, USARTx, GPIOx, GPIO_Pin_x) {\& ~8 t  Q  A  v( u" L8 c9 I" G; I
    uint32_t i;\- f# m/ G+ q; [) s  E: @% U6 x2 [
    uint16_t USB_Rx_Cnt;\
9 S- y% ~9 v' b/ A4 m% u1 |% D% B6 P    USB_Rx_Cnt = USB_SIL_Read(ENDPx | 0x00, USB_Rx_Buffer); \# R- a4 W% `5 ^1 P7 _
    GPIOx->BSRR = GPIO_Pin_x;\4 z' ?9 G/ [7 V
    for (i = 0; i < USB_Rx_Cnt; i++) {\
  J0 ]" r2 j8 n1 Z! d* q, X1 f0 R        USARTx->DR = *(USB_Rx_Buffer + i);\
3 c; p6 [5 S  g/ t9 Z0 ?        while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);\
) H9 y9 F' `5 v4 n/ O& i    }\! n/ [2 d, U, L+ ^8 u, A# R
    SetEPRxValid(ENDPx);\
. w  h! `+ Z* l2 x* @    while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);\, J4 Y& {% e! a1 K
    USART_ClearFlag(USARTx, USART_FLAG_TC);\" h) Y/ F# ?1 ]5 f+ [- F: p
    GPIOx->BRR = GPIO_Pin_x;\
( E/ P* w& U) v. X! N4 F! A}
3 n% N3 r& b4 _9 t1 F
! w8 I- a8 [* M/ t/ @% |7 S/* USB的OUT端点 通过物理串口向外发送数据(DMA方式) */
. f1 a8 b7 b/ V1 j' p0 j#define EPx_OUT_Callback_DMA(Flag_VCPx_Tx_Buf_Use, ENDPx, VCPx_Tx_Buffer1,VCPx_Tx_Buffer2,\
. ]; ]' C6 o, {, T    GPIOx, GPIO_Pin_x, DMA1_Channelx, VCPx_Tx_Buffer_Cnt, Flag_VCPx_Tx_Buf_Full) {\
+ z. E9 `! ]9 f! V  N    uint16_t USB_Rx_Cnt;\
, a  H2 H2 \" c* p% t; F) p. V    if(Flag_VCPx_Tx_Buf_Use == 0){\; E- k1 z+ P1 V+ f2 [( E& P! x
        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\4 [' H6 ?6 T" h0 L/ l/ p) n, q
        PMAToUserBufferCopy(&VCPx_Tx_Buffer1[0], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\
$ y1 p5 f- `9 r- o: p/ V( p        SetEPRxValid(ENDPx);\
  @7 M; L2 }1 J# z        GPIOx->BSRR = GPIO_Pin_x;\
2 X3 x' _9 P: h" ]" @( v- B        DMA1_Channelx->CNDTR = USB_Rx_Cnt;\" |5 J& k2 G# Q8 a3 C! L
        DMA_Cmd(DMA1_Channelx, ENABLE);\
; `8 y4 [9 j  c. E4 w! W        Flag_VCPx_Tx_Buf_Use = 1;\
& u  D4 p0 x  V5 t        VCPx_Tx_Buffer_Cnt = 0;\
- c0 |( A3 {- t, K8 y7 j" Z    } else {\$ x5 y: i! e, k$ q5 w8 i
        USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\
( s  M5 ?( _! ?+ m6 _- [! j        if(VCPx_Tx_Buffer_Cnt < (1024-128)){\, U8 }( j  N; p6 g) J+ c% O( K) m
          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\7 O5 a' m" I9 \9 O4 @
          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\9 A6 w6 _6 m( g" g/ A
          SetEPRxValid(ENDPx);\+ a- e' x* o9 A; \1 O+ [1 N: A
        } else {\
0 ]- f; ]- G/ t2 i          PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\
7 j4 h8 A  ?& z) l+ A- B# S          VCPx_Tx_Buffer_Cnt += USB_Rx_Cnt;\. A, C* y# ]" h2 v
          Flag_VCPx_Tx_Buf_Full = 1;\0 j/ i" a9 M6 v% `, o0 C+ V3 r
        }\
, {6 a" y- M: |9 b* r$ X' G    }\6 d: n0 m$ k' `2 J8 l$ M" s* r# n7 R
}; ~# G* P4 T* w% R& c+ X
/**, |+ I/ E2 i0 @- F, h' s& g% u% j
* Function Name  : EP2_IN_Callback
+ p% Z  l7 v% C% e1 u * Description    :
4 F- L8 w" R1 i3 W! i, S **/7 p( ]% l6 f  O" u
void EP2_IN_Callback (void) {7 Q+ W7 C- r2 c, I* E
    EPx_IN_Callback(ENDP2,CDC1_Tx_State,VCP1_Rx_Buffer,VCP1_Rx_ptr_out,VCP1_Rx_length);
8 S6 B& h4 F. u}: u0 e2 N  a6 b' o. N( l
/**
3 A& F, [2 l3 Z0 U- K& p  o% z; B * Function Name  : EP4_IN_Callback
# P2 Q" W" s( K. c& m * Description    :( e) D8 W' `# O7 k
**/& z( h0 V, k, s3 s
void EP4_IN_Callback(void)4 z* `1 l. i3 ]  X! L
{
9 Q. l2 `" ^" n# W    EPx_IN_Callback(ENDP4,CDC2_Tx_State,VCP2_Rx_Buffer,VCP2_Rx_ptr_out,VCP2_Rx_length);3 m  z) F2 w" U0 q& _* ~  t
}
9 @+ c4 [: Z' u4 k3 R3 k8 o/**3 R5 b! g+ e' g6 S* J0 W. P
* Function Name  : EP6_IN_Callback
8 Q/ R4 A$ u& R' s' } * Description    : VCP3 向PC主机发送数据
  a: a1 H" y! g **/" f1 a- z& L' O: n2 o+ I
void EP6_IN_Callback(void)
! U5 G5 D5 I* v5 s' s# S- t, o{
7 b' g1 L% ~1 x( i4 h# {4 q4 L    EPx_IN_Callback(ENDP6,CDC3_Tx_State,VCP3_Rx_Buffer,VCP3_Rx_ptr_out,VCP3_Rx_length);  ' @* z1 ]4 e# n/ l
}* h0 Q: x" J$ s$ ^- f
/**- P" C2 d. @, z) [0 n, ]
* Function Name  : EP2_OUT_Callback
" {3 I* L1 i( M/ Q: z0 a9 D * Description    : VCP1 通过USART1向外送数据
4 m" y) U8 S% \0 h) g, ^! a **/( L2 y5 v) e% R
void EP2_OUT_Callback(void) {
9 [2 J& R$ C) C/ Q& F( o#ifdef USB_DMA_SEND
% T" V4 |2 q1 D3 q7 E" n% k6 G6 ?    EPx_OUT_Callback_DMA(Flag_VCP1_Tx_Buf_Use, ENDP2, VCP1_Tx_Buffer1,VCP1_Tx_Buffer2,\, U) |( W$ f" q1 M/ M
            GPIOB, GPIO_Pin_9, DMA1_Channel4, VCP1_Tx_Buffer_Cnt, Flag_VCP1_Tx_Buf_Full);
$ B' g1 Q* W( G1 w! \7 P#else
1 W2 @' R; U; ?9 j4 X0 r' b    EPx_OUT_Callback(ENDP2,USART1, GPIOB, GPIO_Pin_9); // DR1 PB9) U, S( R$ F( G1 [( R
#endif7 e' v0 X8 \8 q" i6 U
}
5 y1 Y; L" _1 u1 D3 D1 ^, C/ r/**
/ x5 t8 d0 n  { * Function Name  : EP4_OUT_Callback; u! w) A) B  Q) L( f! v! a
* Description    : VCP2 通过USART2向外送数据
( Z/ E3 r' I; S# a# Z2 { * Input          : None.6 c% K9 i& b, M5 I  v/ M
* Output         : None.
) D9 \1 Q- Y8 I; f8 x$ X * Return         : None.
" \$ f- L" o+ h( z  R) C" M2 u **/
4 J, n# G4 g( Nvoid EP4_OUT_Callback(void)
& t: Y* P* Y  p{! [# V. E! W& G  l- x# H: ~: t
#ifdef USB_DMA_SEND
0 y. T$ k/ e5 S# @, A& Z, N    EPx_OUT_Callback_DMA(Flag_VCP2_Tx_Buf_Use, ENDP4, VCP2_Tx_Buffer1,VCP2_Tx_Buffer2,\1 \& l! P: W$ H  X
            GPIOB, GPIO_Pin_8, DMA1_Channel7, VCP2_Tx_Buffer_Cnt, Flag_VCP2_Tx_Buf_Full);% A! B- x0 t6 ?7 t$ Z9 A
#else& s5 b* T5 S% L: [
    EPx_OUT_Callback(ENDP4,USART2, GPIOB, GPIO_Pin_8); // DR2 PB8
( f/ q9 r4 z5 s* K1 M; s' `- _8 |#endif
& ^4 K9 c. k& G: r( j+ l* ~1 B}
+ v3 w  C0 ^( c/ w, {/**3 P& o" U3 m5 s. v. t/ X7 V
* Function Name  : EP6_OUT_Callback1 Y, L2 t0 R# z2 T$ E
* Description    : VCP3 通过USART3向外送数据7 i7 N3 q1 u+ `; G
**/, w, F5 {1 H" Y& A
void EP6_OUT_Callback(void){1 G# B+ }4 X! V/ D8 \& h! |
#ifdef USB_DMA_SEND* y8 n! r- U2 U1 S" }# m
    EPx_OUT_Callback_DMA(Flag_VCP3_Tx_Buf_Use, ENDP6, VCP3_Tx_Buffer1,VCP3_Tx_Buffer2,\% C- W; H9 S6 P
            GPIOA, GPIO_Pin_5, DMA1_Channel2, VCP3_Tx_Buffer_Cnt, Flag_VCP3_Tx_Buf_Full);% z% ?7 \5 x8 v9 d
#else  5 H2 q6 y, n/ ~% P
    EPx_OUT_Callback(ENDP6,USART3,GPIOA,GPIO_Pin_5);  // DR3 PA5
3 C) x+ |. v0 ?1 x: V4 W#endif
( L# J8 m+ G/ J! k9 Z) E}
' f9 O3 t: L3 L/ ^/**$ a, z' V$ M  o* a) I
* Function Name  : SOF_Callback / INTR_SOFINTR_Callback* f! [" T; l, Q! U  b; I
* Description    :0 ~8 I' g% ]1 s  ~8 _( X
**/
) q& g- }6 \! q7 B9 a7 T# pvoid SOF_Callback(void)9 z6 [* L7 C" F- H
{
7 _! v: T3 @$ G9 ^) }    static uint32_t FrameCount = 0;
  H4 X- \& B" I) P5 \7 m    static uint32_t Led_Count = 0;
, y4 Y4 c7 A6 x8 V- a/ S7 Q. E    if(bDeviceState == CONFIGURED) {
1 R7 m7 z5 I$ s/ O* w: p        if (FrameCount++ >= VCOMPORT_IN_FRAME_INTERVAL) {/ J9 I( Y) d0 c
            /* Reset the frame counter */
5 y9 Q* v; }. T/ O8 k0 Y* ^            FrameCount = 0;
; i8 h& x2 k* V1 r  f7 V            /* Check the data to be sent through IN pipe */
9 H0 L+ w/ c& h# R& n% E            Handle_USBAsynchXfer();" V7 d  [, V6 y5 k& v0 C0 d; [
        }0 \7 X8 T' p8 n5 B$ E. j
    }
6 z6 J1 g/ {+ p( A% L$ T1 b& s! l    if(++Led_Count > 999) {+ ?8 _" Y% m. Z% g2 x" j
        Led_Count = 0;% A+ Y* o/ ^& o  f2 D
        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0){
5 f* a* Z6 c- h' ?5 [8 Q( [            GPIOB->BSRR = GPIO_Pin_2; // PB2 H7 a$ d0 g' F' m( _0 {  R8 b& h' m
        } else {
( L0 v1 v' x* `            GPIOB->BRR = GPIO_Pin_2;  // PB2 L
+ h& P( i9 i2 v$ E5 C9 y        }9 d& _8 j$ k/ {# v
    }
% s, M4 i& o. q% y  D! C2 t" L4 m}- c  I0 ~# Y5 T% ~
/ A) I0 ]) t9 N5 `2 \
万里-363223 回答时间:2014-12-19 08:51:16
SECTION 4( z8 r# |- W. U7 ]6 f
        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断系统和51单片机一样的简单。
: A' Q4 |& c( S2 Q) Q        对于接收终端,就是RXNE了,这只在接收完成后才产生,在执行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代码时不会进入ISR。但麻烦的就是发送有关的中断了:TXE或者TC,根据资料和测试的结果,TXE在复位后就是置1的,即在执行USART_ITConfig(USART1, USART_IT_TXE,  ENABLE)后会立即产生中断请求。因此这造成一个麻烦的问题:如果没有真正的发送数据,TXE中断都会发生,而且没有休止,这将占用很大部分的CPU时间,甚至影响其他程序的运行!
( X6 _/ g; \: ^5 Q/ I+ I" S        因此建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。2 ]3 t* T: b. ~. b- {$ 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清零。
6 @! l9 A# Z, P! {3 A        对于发送单个字符可以考虑不用中断,直接以查询方式完成。9 f1 s3 r/ P% }
        对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。
4 _1 d& W% R& j% N7 C       这里简单分析上面代码的执行过程:TXE中断产生于前一个字符从DR送入SR,执行效果是后一个字符送入DR。对于第一种情况,如果是可显示字符,就执行USART_SendData来写DR(也就清零了TXE),当最后一个可显示的字符从DR送入SR之后,产生的TXE中断发现要送入DR的是字符是0x00——这当然不行——此时就关闭TXE中断,字符串发送过程就算结束了。当然这时不能忽略一个隐含的结果:那就是最后一个可显示字符从DR转入SR后TXE是置1的,但关闭了TXE中断,因此只要下次再开启TXE中断就会立即进入ISR。对于第二种情况,其结果和第一种的相同。
0 V; i' Q& T6 d, @$ t" e2 n1 H1 W         对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:
& k* A7 ~" J: Dextern __IO uint8_t TxCounter1;2 R$ B2 n6 ~8 d
extern uint8_t *TXS;
/ l$ `1 C% ]( s* ^- i2 Mextern __IO uint8_t TxLen;
% ?2 N9 V$ P' ]2 tvoid USART1_IRQHandler(void)
! o% z" F8 g( C6 G* @4 U& u    {
! b$ s, T) @0 u% K        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)% f5 t5 o/ n" ^
            {                                               
5 N7 h* f3 V9 K$ p                if(TXS[TxCounter1]) //如果是可显示字符
, L) L' ]2 c# Y7 ~# _                    { USART_SendData(USART1,TXS[TxCounter1++]);}- o+ u) Y& s( R
                else   //发送完成后关闭TXE中断,
# o/ Z+ o, H0 A                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                        
  p, A, q, s* g# r3 ^  |: A# U            }                   8 O, s/ P; j3 z( j9 d
    }
" P7 b, X: h1 g; v" A' k: l        对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:2 Y( h8 O' `% U1 G, T
void USART1_IRQHandler(void)
. C5 @2 H3 q& U    {, N4 O. X9 A( L
        if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。  r9 B, s+ M+ V8 o, k
            {                                              ' p) L, ]7 Y# {. |  _* j, Q: w2 r' v
                if(TxCounter1<TxLen)
' @) T$ N( R7 w  t. l9 j                    { USART_SendData(USART1,TXS[TxCounter1++]);}
) a: y0 z8 A, v: M: y1 Y* q                else   //发送完成后关闭TXE中断- c/ l  j  R0 ^6 \. o) K
                    { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                         % v% G* ~* _/ ]8 A8 y
            }                    
% w$ y. _. M6 ~    }! b6 q+ Z  x- V* `4 A5 }( k
        事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!( o5 |5 m, J& y, `; }" v
        在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:. ~+ \! P* j/ k  M
uint32_t *TXS;
( m8 M9 e/ m  l1 s$ Tuint8_t TxBuffer1[]="0123456789ABCDEF";
- S7 c. @1 ~* k) S* wuint8_t DST2[]="ASDFGHJKL";
: f2 W* S5 C/ ^__IO uint8_t TxLen = 0x00;
6 C/ g( g2 [0 t9 D/ _& [     TxLen=8;                               //发送8个字符,最终发送的是01234567
/ V$ h5 ]' h/ \! j1 v: [' T    TXS=(uint32_t *)TxBuffer1;   //将TXS指向字符串TxBuffer1
, A; G4 N) U2 I( z' I4 A, T    TxCounter1=0;                     //复位索引值
& L; B- m5 \5 ]. V: H    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);   //启用TXE中断,即开始发送过程$ P# X# B& B9 {
    while(TxCounter1!=TxLen);   //等待发送完成
0 @4 }* {: b, ]/ D9 z) y0 c. r
0 i! s" @/ ?2 L. m. L    TXS=(uint32_t *)TxBuffer2;   //同上,最终发送的是ASDFGHJK
0 ^" Y- }+ |0 O% R2 w8 i! Y    TxCounter1=0;
. r: C6 x7 m; R; n! k( b/ S: H    USART_ITConfig(USART1, USART_IT_TXE,ENABLE);4 j9 U1 U2 D2 i; S4 R
    while(TxCounter1!=TxLen);& x6 |" G# @. L" [- p7 O
        以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。
wamcncn 回答时间:2014-12-19 13:32:32
万里-363223 发表于 2014-12-19 08:516 M& _2 H* b" o
SECTION 4
9 l/ e! h& z# y* D2 d        总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断 ...

9 u; d# ?% |9 E0 B' ~强大的同时,复杂也跟着来了,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; M! L7 k1 `. p( W4 P/ X
强大的同时,复杂也跟着来了,51是基础,STM32是进阶

+ l. B- M# U" n! U/ ]恩恩,没错,鱼和熊掌不可兼得
小贾-370388 回答时间:2014-12-19 15:30:39
充充电
9 B$ A7 z! Q9 |3 _2 s- S+ f
霹雳之火 回答时间: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# u' _+ [/ j6 W
.。。。。总结的好细致。。。。受教了。。不过,51终究是51,用起来简单,当然也就没那么强大了,stm32虽然 ...

8 \7 N8 p9 ~6 _. I3 }7 R恩恩,没错,还是STM32爽
wjandsq 回答时间:2014-12-21 10:51:09

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

本帖最后由 王建 于 2014-12-21 10:57 编辑
  s  l  w6 n1 x/ z1 G# J+ {  F5 R* K7 y5 }0 f0 r
至于接收,根据实际情况而定。
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 手机版