
" V' k' x! J4 |8 M8 Y SECTION 1 ![]() SECTION 2 / b2 ?$ z, s" [9 b& ? 先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下 void USART_SendDataString( u8 *pData )1 y' Q; I6 O7 x% \. P. a9 W T9 a { 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个字节的数据.网友提供. 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 ) { if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉 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++ ); }5 _, \9 y0 P |6 k! T2 _, p }" D/ k" i* X R+ _ 其中u8 *pDataByte;是一个外部指针变量 ! Q- X$ N w; Z. c! a; s0 a 在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。- u/ ]' C0 R. I3 S+ } 串口初始化函数如下1 }3 |8 O9 }( ~6 x, K void USART_Config()6 [, ?9 X- c9 V+ L0 P! ] { USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体8 r$ K; c9 ?# `1 M' Z* Q1 \ 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位停止位 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;//输入加输出模式 USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭 USART_InitStructure.USART_CPOL = USART_CPOL_Low; USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;6 s4 V( ~, s. o USART_InitStructure.USART_LastBit = USART_LastBit_Disable; USART_Init(USART1, &USART_InitStructure);//设置到USART1 USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节: C4 `# b y5 A; e USART_Cmd(USART1, ENABLE); //使能USART1 } 这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么?? 这里笔者可以给出解释,你看下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发送函数如下:) @: t; P9 C. T/ m$ T8 W4 f void USART_SendDataString( u8 *pData )6 ?% T5 Y( N: k' N; d# z( b { pDataByte = pData; USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。 & c1 g* i& m/ S5 o2 [/ y } 中断处理函数如下: void USART1_IRQHandler(void) {( 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' } { 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 } \ } 在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下: void USART_Config() { 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 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位 USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位! C/ t5 _ d W9 J* _+ F6 X& d$ G USART_InitStructure.USART_Parity = USART_Parity_No;//无校验 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式 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; USART_InitStructure.USART_CPHA = USART_CPHA_2Edge; USART_InitStructure.USART_LastBit = USART_LastBit_Disable;% f& h) U4 j! J6 b USART_Init(USART1, &USART_InitStructure);//设置到USART1 USART_Cmd(USART1, ENABLE); //使能USART1 } SECTION 3: {" `/ f- B# J" e% T 在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。 ( P v; d) g p! }: q% r. k$ H SECTION 4 ![]() 8 ^# H: }/ ]0 t9 P8 [8 |4 v; R |
* @file usb_endp.c
* @author wjandcf@gmail.com
* @version V7.0.2. c" A2 `9 ]+ |% Z f
* @date 2014.12.20
* @brief Endpoint routines- r, f, k2 l9 g5 a0 B. n
*
*/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"
#include "usb_mem.h"$ V' L& @3 {2 i: p; p- v
#include "hw_config.h"
#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 -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/" m: O* [ t) t3 F! _- O
- d8 I `. Z! F( }: P$ [# {
/* Interval between sending IN packets in frame number (1 frame = 1ms) */
#define VCOMPORT_IN_FRAME_INTERVAL 5
/* Private macro -------------------------------------------------------------*/9 b3 y3 C& F" w7 @4 P4 W
/* Private variables ---------------------------------------------------------*/
uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];! e% n+ g3 L% {+ L3 W" d- D
extern uint8_t VCP1_Tx_Buffer1[]; /* VCP1 DMA发送缓冲1 */
extern uint8_t VCP2_Tx_Buffer1[]; /* VCP2 DMA发送缓冲1 */
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
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 第二缓冲 收到的字符总数 */
# {6 j& v7 _3 P5 Q
extern uint8_t Flag_VCP2_Tx_Buf_Use; /* 1: 表示DMA发送已启动,VCP2_Tx_Buffer1被锁定 */
extern uint8_t Flag_VCP2_Tx_Buf_Full; /* 1: 表示VCP2_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */; z o7 |! I; A
extern uint16_t VCP2_Tx_Buffer_Cnt; /* VCP2 第二缓冲 收到的字符总数 */
8 X/ ]# G* e% [# t) s! N* R
extern uint8_t Flag_VCP3_Tx_Buf_Use; /* 1: 表示DMA发送已启动,VCP3_Tx_Buffer1被锁定 */
extern uint8_t Flag_VCP3_Tx_Buf_Full; /* 1: 表示VCP3_Tx_Buffer2数据可能已放满,暂时不能再从主机接收数据 */
extern uint16_t VCP3_Tx_Buffer_Cnt; /* VCP3 第二缓冲 收到的字符总数 */5 B" o5 t8 {1 A7 w" C- R6 N
extern uint8_t VCP1_Rx_Buffer[];5 r, j4 O8 F9 {1 ^; L
extern uint32_t VCP1_Rx_ptr_in;
extern 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;
extern uint8_t VCP2_Rx_Buffer[]; ! ?# p5 j4 z1 ^8 y
extern uint32_t VCP2_Rx_ptr_in;
extern 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;
extern 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;
extern uint32_t VCP3_Rx_length;
extern uint8_t CDC3_Tx_State;
+ k0 A8 i8 ?5 C4 W4 @6 J
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/* USB的IN端点 发送数据到PC主机 */
#define EPx_IN_Callback(ENDPx, CDCx_Tx_State, VCPx_Rx_Buffer, VCPx_Rx_ptr_out, VCPx_Rx_length) {\
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;\
if (VCPx_Rx_length == 0) {\! t) ?/ j* D0 X) K; e
CDCx_Tx_State = 0;\
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 {\
if (VCPx_Rx_length > VIRTUAL_COM_PORT_DATA_SIZE) {\
USB_Tx_length = VIRTUAL_COM_PORT_DATA_SIZE;\
VCPx_Rx_ptr_out += VIRTUAL_COM_PORT_DATA_SIZE;\
VCPx_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;\
} else {\
USB_Tx_length = VCPx_Rx_length;\
VCPx_Rx_ptr_out += VCPx_Rx_length;\
VCPx_Rx_length = 0;\
}\
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
}\
}/ U5 S8 _5 I' {8 W; A. x6 a; E7 s
/* USB的OUT端点 通过物理串口向外发送数据(阻塞方式) */
#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;\
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++) {\
USARTx->DR = *(USB_Rx_Buffer + i);\
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);\
}\! n/ [2 d, U, L+ ^8 u, A# R
SetEPRxValid(ENDPx);\
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;\
}
/* USB的OUT端点 通过物理串口向外发送数据(DMA方式) */
#define EPx_OUT_Callback_DMA(Flag_VCPx_Tx_Buf_Use, ENDPx, VCPx_Tx_Buffer1,VCPx_Tx_Buffer2,\
GPIOx, GPIO_Pin_x, DMA1_Channelx, VCPx_Tx_Buffer_Cnt, Flag_VCPx_Tx_Buf_Full) {\
uint16_t USB_Rx_Cnt;\
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);\
SetEPRxValid(ENDPx);\
GPIOx->BSRR = GPIO_Pin_x;\
DMA1_Channelx->CNDTR = USB_Rx_Cnt;\" |5 J& k2 G# Q8 a3 C! L
DMA_Cmd(DMA1_Channelx, ENABLE);\
Flag_VCPx_Tx_Buf_Use = 1;\
VCPx_Tx_Buffer_Cnt = 0;\
} else {\$ x5 y: i! e, k$ q5 w8 i
USB_Rx_Cnt = GetEPRxCount(ENDPx & 0x7F);\
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 {\
PMAToUserBufferCopy(&VCPx_Tx_Buffer2[VCPx_Tx_Buffer_Cnt], GetEPRxAddr(ENDPx & 0x7F), USB_Rx_Cnt);\
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 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
* Description :
**/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);
}: u0 e2 N a6 b' o. N( l
/**
* Function Name : EP4_IN_Callback
* Description :( e) D8 W' `# O7 k
**/& z( h0 V, k, s3 s
void EP4_IN_Callback(void)4 z* `1 l. i3 ] X! L
{
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
}
/**3 R5 b! g+ e' g6 S* J0 W. P
* Function Name : EP6_IN_Callback
* Description : VCP3 向PC主机发送数据
**/" f1 a- z& L' O: n2 o+ I
void EP6_IN_Callback(void)
{
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
* Description : VCP1 通过USART1向外送数据
**/( L2 y5 v) e% R
void EP2_OUT_Callback(void) {
#ifdef USB_DMA_SEND
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);
#else
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
}
/**
* Function Name : EP4_OUT_Callback; u! w) A) B Q) L( f! v! a
* Description : VCP2 通过USART2向外送数据
* Input : None.6 c% K9 i& b, M5 I v/ M
* Output : None.
* Return : None.
**/
void EP4_OUT_Callback(void)
{! [# V. E! W& G l- x# H: ~: t
#ifdef USB_DMA_SEND
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
#endif
}
/**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
#endif
}
/**$ 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
**/
void SOF_Callback(void)9 z6 [* L7 C" F- H
{
static uint32_t FrameCount = 0;
static uint32_t Led_Count = 0;
if(bDeviceState == CONFIGURED) {
if (FrameCount++ >= VCOMPORT_IN_FRAME_INTERVAL) {/ J9 I( Y) d0 c
/* Reset the frame counter */
FrameCount = 0;
/* Check the data to be sent through IN pipe */
Handle_USBAsynchXfer();" V7 d [, V6 y5 k& v0 C0 d; [
}0 \7 X8 T' p8 n5 B$ E. j
}
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){
GPIOB->BSRR = GPIO_Pin_2; // PB2 H7 a$ d0 g' F' m( _0 { R8 b& h' m
} else {
GPIOB->BRR = GPIO_Pin_2; // PB2 L
}9 d& _8 j$ k/ {# v
}
}- c I0 ~# Y5 T% ~
/ A) I0 ]) t9 N5 `2 \
总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断系统和51单片机一样的简单。
对于接收终端,就是RXNE了,这只在接收完成后才产生,在执行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代码时不会进入ISR。但麻烦的就是发送有关的中断了:TXE或者TC,根据资料和测试的结果,TXE在复位后就是置1的,即在执行USART_ITConfig(USART1, USART_IT_TXE, ENABLE)后会立即产生中断请求。因此这造成一个麻烦的问题:如果没有真正的发送数据,TXE中断都会发生,而且没有休止,这将占用很大部分的CPU时间,甚至影响其他程序的运行!
因此建议的是在初始化时不好启用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清零。
对于发送单个字符可以考虑不用中断,直接以查询方式完成。9 f1 s3 r/ P% }
对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。
这里简单分析上面代码的执行过程:TXE中断产生于前一个字符从DR送入SR,执行效果是后一个字符送入DR。对于第一种情况,如果是可显示字符,就执行USART_SendData来写DR(也就清零了TXE),当最后一个可显示的字符从DR送入SR之后,产生的TXE中断发现要送入DR的是字符是0x00——这当然不行——此时就关闭TXE中断,字符串发送过程就算结束了。当然这时不能忽略一个隐含的结果:那就是最后一个可显示字符从DR转入SR后TXE是置1的,但关闭了TXE中断,因此只要下次再开启TXE中断就会立即进入ISR。对于第二种情况,其结果和第一种的相同。
对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:
extern __IO uint8_t TxCounter1;2 R$ B2 n6 ~8 d
extern uint8_t *TXS;
extern __IO uint8_t TxLen;
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)% f5 t5 o/ n" ^
{
if(TXS[TxCounter1]) //如果是可显示字符
{ USART_SendData(USART1,TXS[TxCounter1++]);}- o+ u) Y& s( R
else //发送完成后关闭TXE中断,
{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}
} 8 O, s/ P; j3 z( j9 d
}
对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:2 Y( h8 O' `% U1 G, T
void USART1_IRQHandler(void)
{, 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)
{ USART_SendData(USART1,TXS[TxCounter1++]);}
else //发送完成后关闭TXE中断- c/ l j R0 ^6 \. o) K
{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);} % v% G* ~* _/ ]8 A8 y
}
}! 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;
uint8_t TxBuffer1[]="0123456789ABCDEF";
uint8_t DST2[]="ASDFGHJKL";
__IO uint8_t TxLen = 0x00;
TxLen=8; //发送8个字符,最终发送的是01234567
TXS=(uint32_t *)TxBuffer1; //将TXS指向字符串TxBuffer1
TxCounter1=0; //复位索引值
USART_ITConfig(USART1, USART_IT_TXE,ENABLE); //启用TXE中断,即开始发送过程$ P# X# B& B9 {
while(TxCounter1!=TxLen); //等待发送完成
TXS=(uint32_t *)TxBuffer2; //同上,最终发送的是ASDFGHJK
TxCounter1=0;
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的器件。
强大的同时,复杂也跟着来了,51是基础,STM32是进阶
恩恩,没错,鱼和熊掌不可兼得
恩恩,没错,还是STM32爽
STM32的串口通信,最好使用DMA方式发送。
1 G# J+ { F5 R* K7 y5 }0 f0 r
至于接收,根据实际情况而定。