
0 E! x1 F' L* [+ E7 C SECTION 1; S5 R+ z u0 E3 }% q+ G! F ![]() 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 ) { pDataByte = pData;" j! T/ B9 E, |9 c- Y8 ~# m6 J 0 I% t4 L. G# Q USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供. 7 @' q, ^' _/ y' _5 N3 ? USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次 }& @5 ?) \3 O G! ~4 W - E/ g" }/ u2 _% k4 r$ @4 i 中断处理函数如下 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 { 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 else 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 在中断处理程序中,发送完该字符串后,不用关闭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() {" D1 h0 f# g# `& e& J, i5 _- A2 ~; M USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体 USART_InitStructure.USART_BaudRate = 9600; //波特率9600 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;//无校验 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制 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; 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中断必须放在这里,否则还是会丢失第一字节 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()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么?? . y- ~: m r5 G 这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供. 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 发送函数如下: void USART_SendDataString( u8 *pData ); {& O8 ^2 x; A& Q! { { 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 } 中断处理函数如下: void USART1_IRQHandler(void) { if( USART_GetITStatus(USART1, USART_IT_TXE) == SET ) {; 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 USART_SendData(USART1, *pDataByte++ );3 A5 }/ O! C9 [, t9 V } " G) U# _$ z6 |8 @: R% h }& s" J. K/ J/ K9 \' y, V: H* @ ! y& H* n5 N* d8 H& G5 j, k! @ 在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下: void USART_Config() { USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体 0 L0 r" x& ]& Z W/ P3 n6 o7 f' [ USART_InitStructure.USART_BaudRate = 9600; //波特率9600 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位 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;//输入加输出模式 USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭 USART_InitStructure.USART_CPOL = USART_CPOL_Low; 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 USART_Init(USART1, &USART_InitStructure);//设置到USART1 1 m, U7 z4 B9 ~, ? q* Y USART_Cmd(USART1, ENABLE); //使能USART1* W, |) t8 M9 U) ? } 6 x; ^! a& s& |8 }7 h SECTION 3 # ]9 ?6 @4 x: o% z' i 在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。0 W0 k* y/ E! W0 G% C L
SECTION 4 ![]() |
* @file usb_endp.c
* @author wjandcf@gmail.com
* @version V7.0.2$ c/ n8 `* Z. e5 d& ]
* @date 2014.12.20
* @brief Endpoint routines" X3 j, z N& K o
*
*/
/* Includes ------------------------------------------------------------------*/1 _$ f- W; f& j/ t
#include "usb_lib.h"
#include "usb_desc.h"
#include "usb_mem.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"
! W7 ?' z" ?4 D9 l7 y2 r) x
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/$ O! g. \+ Z9 Q
/* 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 -------------------------------------------------------------*/
/* 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
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 *// ?! [; J! c6 M$ F. b- @
extern 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 第二缓冲 */
" 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数据可能已放满,暂时不能再从主机接收数据 */
extern uint16_t VCP1_Tx_Buffer_Cnt; /* VCP1 第二缓冲 收到的字符总数 */
extern 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数据可能已放满,暂时不能再从主机接收数据 */
extern 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被锁定 */
extern 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
extern 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;
extern uint8_t CDC1_Tx_State;
extern uint8_t VCP2_Rx_Buffer[];
extern uint32_t VCP2_Rx_ptr_in;
extern uint32_t VCP2_Rx_ptr_out;6 c8 X' A5 b. z9 V: F+ e. `
extern uint32_t VCP2_Rx_length;
extern 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;
extern uint32_t VCP3_Rx_ptr_out; C6 k3 A; _( S9 g% [7 I: Q1 h @
extern uint32_t VCP3_Rx_length;
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 ---------------------------------------------------------*/
: 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;\
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;\
if (VCPx_Rx_length == 0) {\
CDCx_Tx_State = 0;\
SetEPTxCount(ENDPx,0);\$ S4 @- ]7 o( j/ ?7 ~! Y
SetEPTxValid(ENDPx);\
} else {\% g9 c$ s; c5 E4 Q
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;\ p' `, p4 }: m: m2 N# h+ c) J
VCPx_Rx_length -= VIRTUAL_COM_PORT_DATA_SIZE;\
} else {\2 `+ r s4 l* J; R, G( J
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);\
SetEPTxValid(ENDPx);\
}\
}\
}/ @- \! M. W4 l1 \
/* USB的OUT端点 通过物理串口向外发送数据(阻塞方式) */" l* H, Y2 M' O( c2 r
#define EPx_OUT_Callback(ENDPx, USARTx, GPIOx, GPIO_Pin_x) {\
uint32_t i;\
uint16_t USB_Rx_Cnt;\
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++) {\
USARTx->DR = *(USB_Rx_Buffer + i);\$ o. L) T3 Y0 C5 {0 ~6 ^0 s
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);\
}\- q* o0 c, @% v1 m* p/ r# N
SetEPRxValid(ENDPx);\
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);\
USART_ClearFlag(USARTx, USART_FLAG_TC);\
GPIOx->BRR = GPIO_Pin_x;\3 v- x; [- |5 ?" d$ p4 t! q
}8 s; V" w) {. {% {
/* 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) {\, Y4 R. b C5 \/ `+ e
uint16_t USB_Rx_Cnt;\
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);\
GPIOx->BSRR = GPIO_Pin_x;\
DMA1_Channelx->CNDTR = USB_Rx_Cnt;\- j+ k, m3 j& n+ F! f. {4 L$ T
DMA_Cmd(DMA1_Channelx, ENABLE);\
Flag_VCPx_Tx_Buf_Use = 1;\+ j$ w% K7 M; k! y, I
VCPx_Tx_Buffer_Cnt = 0;\
} 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)){\
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);\
} 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
}
/**7 J8 a# t9 k, z8 J- A5 X8 D
* Function Name : EP2_IN_Callback
* Description :
**/6 H4 d7 {$ N( H
void EP2_IN_Callback (void) {
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 :
**/
void 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);
}
/**
* Function Name : EP6_IN_Callback
* Description : VCP3 向PC主机发送数据
**/
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);
}
/**
* Function Name : EP2_OUT_Callback
* Description : VCP1 通过USART1向外送数据
**/( 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
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);
#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
/**
* Function Name : EP4_OUT_Callback2 E$ V4 B e8 M7 A D! c
* Description : VCP2 通过USART2向外送数据
* Input : None.
* Output : None.1 }# `( W8 c% y8 F, q6 {/ z/ z. S
* Return : None.
**/4 o, [6 g3 s7 N6 C1 ~
void EP4_OUT_Callback(void)
{
#ifdef USB_DMA_SEND
EPx_OUT_Callback_DMA(Flag_VCP2_Tx_Buf_Use, ENDP4, VCP2_Tx_Buffer1,VCP2_Tx_Buffer2,\
GPIOB, GPIO_Pin_8, DMA1_Channel7, VCP2_Tx_Buffer_Cnt, Flag_VCP2_Tx_Buf_Full);% \/ D8 C s/ c% n
#else
EPx_OUT_Callback(ENDP4,USART2, GPIOB, GPIO_Pin_8); // DR2 PB8
#endif
}" N0 v' v& H) @3 R
/**7 L. K9 Y) J- J, V: W" M" g
* Function Name : EP6_OUT_Callback
* Description : VCP3 通过USART3向外送数据
**/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,\
GPIOA, GPIO_Pin_5, DMA1_Channel2, VCP3_Tx_Buffer_Cnt, Flag_VCP3_Tx_Buf_Full);
#else
EPx_OUT_Callback(ENDP6,USART3,GPIOA,GPIO_Pin_5); // DR3 PA53 y2 i' a# F( g9 ~3 E& i
#endif
}
/**( P- p& O# S- r1 L% W# A
* Function Name : SOF_Callback / INTR_SOFINTR_Callback
* Description :
**/2 ^8 r- x4 ^& Z+ s' x. L8 e* L1 l
void SOF_Callback(void)
{1 C4 t8 b% L$ W: g! y* ~
static uint32_t FrameCount = 0;
static uint32_t Led_Count = 0;
if(bDeviceState == CONFIGURED) {. q |# J8 I& w) T
if (FrameCount++ >= VCOMPORT_IN_FRAME_INTERVAL) {
/* Reset the frame counter *// H8 w' X, A/ {; T4 \9 R
FrameCount = 0;
/* Check the data to be sent through IN pipe */
Handle_USBAsynchXfer();( K# i) G: p# _3 g" D$ O
}' v- I4 Z" d2 x, E" _, s9 \0 R6 \& _
}
if(++Led_Count > 999) {/ o5 d/ l( U* U5 [: D1 ]
Led_Count = 0;
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 {
GPIOB->BRR = GPIO_Pin_2; // PB2 L, A2 O1 V) H$ ^& D
}/ [4 T& O: h6 k4 }' P! G! k4 t5 J9 n
}
}
( L3 H4 F0 |/ D
总的来说,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时间,甚至影响其他程序的运行!
因此建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。
对于发送,需要注意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
对于发送单个字符可以考虑不用中断,直接以查询方式完成。
对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用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。对于第二种情况,其结果和第一种的相同。
对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:
extern __IO uint8_t TxCounter1;
extern uint8_t *TXS;
extern __IO uint8_t TxLen; + X- I: `/ Z( I' `
void USART1_IRQHandler(void)
{ A0 f* P( O H+ T/ \
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
{
if(TXS[TxCounter1]) //如果是可显示字符
{ USART_SendData(USART1,TXS[TxCounter1++]);}
else //发送完成后关闭TXE中断,. Z! Q( |5 A3 M
{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);} ; I2 F, }7 C1 u- B" _
}
}7 K3 T% d* y. Q; k+ X4 {. {
对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:* q' N" ?5 \$ Q( `$ K
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。
{
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中断
{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);} $ j/ r6 Y$ K/ w
}
}. P6 x5 G- j2 E3 ~
事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!
在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:
uint32_t *TXS;
uint8_t TxBuffer1[]="0123456789ABCDEF";
uint8_t DST2[]="ASDFGHJKL";
__IO uint8_t TxLen = 0x00;
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
TxCounter1=0; S+ O. H0 ?4 w! m* @0 h U' Q! y: j
USART_ITConfig(USART1, USART_IT_TXE,ENABLE);
while(TxCounter1!=TxLen);: r% y; O3 V1 I# \0 H4 J
以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。
强大的同时,复杂也跟着来了,51是基础,STM32是进阶
恩恩,没错,鱼和熊掌不可兼得
恩恩,没错,还是STM32爽
STM32的串口通信,最好使用DMA方式发送。
至于接收,根据实际情况而定。