在研究STM32串口接收发送中断的时候找到不少不错的资料,现在备份在这里。以供自己查阅,以及方便其他人。 TC ====TXE 顺便预告下最近会写个有关串口处理数据的帖子,从查询和中断方面以及数据处理的方式,从队列以及FIFO方面写起。
7 ^, r" v9 y( Q5 A5 oSECTION 1 - /*
: T, |2 x$ g5 g6 C - 调试STM32串口过程中发现一个奇怪的问题,初始化串口1口,使能串口发送完成中断后,立刻就进入了发送完成中断。
+ ]* @0 F R" A1 P. F - 仔细的查阅了STM32手册中的串口部分的介绍:
. W! c$ A k0 @2 \- U- c+ n3 } - , V; }# C' k" S$ ^. ?4 M) D _% r
- 以下是字符发送的配置过程,注意第6点,在设置USART_CR1中的TE位时,会发送一个空闲帧作为第一次数据发送,所以即便你执行了USART_ClearFlag(USART1, USART_FLAG_TC); (这个函数肯定在空闲帧数据发送完成前执行),所以当空闲帧发送完后,就进入发送完成中断。4 Q( \( `+ @* z. E
- ) F1 p9 q5 i5 e. U# [
- 配置步骤:
' X5 H! a) g. Z2 i - 1. 通过在USART_CR1寄存器上置位UE位来激活USART+ F( g8 F8 M& ~7 T1 l
- 2. 编程USART_CR1的M位来定义字长。
% p0 m% E- F$ Q- L, F( Q - 3. 在USART_CR2中编程停止位的位数。$ n- n' o# ~2 N/ g
- 4. 如果采用多缓冲器通信,配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中! P: ~- n8 T5 ?6 ~, z7 }+ p
- 的描述配置DMA寄存器。
" y2 ~0 i3 H3 _ v u0 C - 5. 利用USART_BRR寄存器选择要求的波特率。
6 ?( _# N0 w) f - 6. 设置USART_CR1中的TE位,发送一个空闲帧作为第一次数据发送。
0 D3 _* X$ }5 r; V. T - 7. 把要发送的数据写进USART_DR寄存器(此动作清除TXE位)。在只有一个缓冲器的情况9 _: O1 i, r8 i5 H! D
- 下,对每个待发送的数据重复步骤7。
0 l. M6 d1 ], k3 c) Z( } [# Z - 8. 在USART_DR寄存器中写入最后一个数据字后,要等待TC=1,它表示最后一个数据帧的9 L8 U& v; j1 ]8 j
- 传输结束。当需要关闭USART或需要进入停机模式之前,需要确认传输结束,避免破坏6 j& G% {3 Z: D3 k) m
- 最后一次传输。
2 H, Q Y: {3 `" j0 d5 X, {7 K - */5 _7 z4 d4 O2 T; R4 y' T
- //解决的办法:3 u7 z# V- L+ L1 m+ j4 D: V
- //方法一1 \' C# j" ]. v9 ` ]
- //在执行5 U& Y3 H( C2 E& |; l$ {) L
- USART_ITConfig(USART1, USART_IT_TC, ENABLE); + n+ q; ]7 [' z4 e! k9 Q7 ~
- //之前,先延时一段时间,基本上比一个字符发送的时间长一点就可以了,然后再执行( G p( R9 E$ d% u1 V* y. E
- USART_ClearFlag(USART1, USART_FLAG_TC);2 n" ~+ a9 K! B+ R* M# l; b: `2 P( p6 E
-
/ j+ e+ i: C2 v9 ?- C* l - //方法二:. p6 g1 S u# N1 W4 k7 p, E, g8 M9 S
- //在执行6 h5 _1 R# e/ @1 ?2 W
- USART_ITConfig(USART1, USART_IT_TC, ENABLE);% |5 W/ r- {$ h* [* B3 J' x
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET)
) y: Q: l% h7 l4 | - {
2 \; z" t! E0 [9 Z. o - ; //等待空闲帧发送完成后 再清零发送标志
! C( |* W% _% }7 P; d - }% f7 Y! A' k, z, u6 j& q6 H
- USART_ClearFlag(USART1,USART_FLAG_TC);
复制代码 & J. E4 r) j7 x* f8 s. R: Y
- R9 G! K D/ ^6 d- E4 p1 MSECTION 2
3 {$ f7 o1 C) A, {% P% J: y% ^8 r先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下
7 e; p, G0 R, ]& x# ] G. K$ u
0 c) N$ R: b9 E! _: G$ ]- /*******' _, ]9 \$ I1 s/ S
- 功能:中断方式发送字符串.采用判断TC的方式.即 判断 发送后中断 位.
" O6 U7 n$ j' y0 `5 w+ j - 输入:字符串的首地址# ~6 ~! s" J' q* k5 _+ C/ r! `5 f( W
- 输出:无2 d6 S: P# G- ~, H9 T `* M
- *******/% j, r9 ?8 u9 Q) b. {
- void USART_SendDataString( u8 *pData )
7 {- S4 |+ `. M% X6 u& R# x# N - {* P, r5 u# {& D& ~$ ?6 p
- pDataByte = pData;, G! @* b& E5 R& h! U6 J' z
-
, L1 W1 h0 a/ x3 h! Y8 O) v; I i - USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
! g( [2 ~2 Y) |! p0 A U$ Y - 5 D: _3 k2 m7 r5 a* U
- USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次3 R3 ~# ?! l& O. q/ d
- }
复制代码 ) ^" ^. W2 z& _. C
中断处理函数如下 ; _. C$ w% g: f4 U& _, s4 J, ^+ N
- /********7 }. f+ P& e$ P% h
- * Function Name : USART1_IRQHandler9 }9 l2 c G, U; A
- * Description : This function handles USART1 global interrupt request.9 I3 J P( s i5 ^0 }* [
- * Input : None4 G5 N$ P! h; h( P+ m
- * Output : None
9 F1 G# E1 |# t' p7 o- c/ i - * Return : None) R" l8 Z. n8 _- T& Q
- *********/
2 @& r! `& t! O6 k6 K - void USART1_IRQHandler(void)
) t% S% R9 v9 P- U+ V2 c Q - {
$ y! @, k B# Q0 o4 v4 M - if( USART_GetITStatus(USART1, USART_IT_TC) == SET )
P, `6 P4 b# V6 Z* e - {- o" M2 s9 _1 |& @6 K
- if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉$ X) S9 [1 w: r5 h% Q
- USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断. clear掉即可,不用关掉TCIE
0 n5 D* q& T: X- Q k - else4 i( y v' ~+ D3 e8 Y
- USART_SendData(USART1, *pDataByte++ ); m" d( K! F5 M# T% e
- }9 m2 u% k& X' Z
- + E0 I! m- Q0 ^2 z, T i
- }
复制代码 ( @8 e( Y/ x. a6 q
其中u8 *pDataByte;是一个外部指针变量 & G) ^5 i$ [* d, ~1 H6 t& r4 a A7 l
0 e5 |; g1 b P( D
在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。
' { y) a( t; ~ x7 t1 c. H串口初始化函数如下 & L/ A+ K" P" z( D& _
; e/ `( D' q s; l ?/ ~! J
- /*********
3 `; f j4 {8 \) m4 U& D - 名称: USART_Config) Z9 c* G/ D3 s5 l0 R
- 功能: 设置串口参数 y) b X0 V6 s8 u Y! }
- 输入: 无- O! m; V* }* v: l$ o9 y8 L5 |
- 输出: 无& V v, V) i3 Q! k1 J# B: [
- 返回: 无
A. t! U3 E' s( b - **********/1 _ F* d7 e3 {7 J1 u0 y/ t
- void USART_Config()$ ?6 e" j6 n# Y8 [; y9 E
- {' ?" u7 e% ?& F2 K9 `- P
- USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体
* [+ Q: l7 m2 d - 7 ~' S! S2 C$ H4 \! y; I9 K
- USART_InitStructure.USART_BaudRate = 9600; //波特率9600
2 N* r* y) j- C/ [( Q7 f; ]% ` - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
5 Z6 h4 |+ D5 I - USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位* f. n9 m0 N0 Q. I, [0 q. W6 Y. f
- USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
: i8 u S1 N; S; X* J' @2 X - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制% ^) a7 G# Z% u+ A
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
5 A4 A5 Y3 ?$ C# ]8 ]$ r - USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭# R5 p2 M( r2 l9 |
- USART_InitStructure.USART_CPOL = USART_CPOL_Low;+ d/ R4 c/ e' Y' B" Q0 u
- USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
. y& v' L% p3 O9 p2 x - USART_InitStructure.USART_LastBit = USART_LastBit_Disable;0 K: ~( l# _8 H% w
- USART_Init(USART1, &USART_InitStructure);//设置到USART1/ Y2 z5 A. q' ]
- % q; _' I/ W8 S ~% _4 q
- USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节 N7 K5 `2 u V
% k; H9 ~9 g' r6 x, r- USART_Cmd(USART1, ENABLE); //使能USART1
+ ^! k4 Y( h$ W - }
复制代码 # _9 y8 |, Y: A5 C, t
这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么?? + Q X( D3 Q) Y7 p# z4 M
这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供. 9 X! h ]0 y% s0 Q
( w* p+ r4 Y: T& k3 U: y
再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。
7 y( x% f' ^! P6 J+ L$ l: K( {; P/ M) x2 ]
发送函数如下:
/ t. e* n4 ~$ n/ w* d1 Q- /*******" R" D9 I5 G+ k3 x
- 功能:中断方式发送字符串.采用判断TC的方式.即 判断 发送后中断 位.
+ m$ T3 Z: I( w" _ - 输入:字符串的首地址
& V6 m b9 \' `3 B - 输出:无
X; q! N9 z3 f - *******/
% r0 F4 }) T+ E$ f' S+ E# e - void USART_SendDataString( u8 *pData )
0 \8 i; z9 w# ~ w2 h4 z5 f, R - {
7 O1 B4 c! q/ ~5 E$ D - pDataByte = pData;0 E5 c: K, |0 e2 c0 G3 V* F) ]
- USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。 7 F1 Z2 |- X0 v. }- v
- # O# C6 U/ |. _2 K& g6 q
- }6 y; z6 H3 j$ B. x
复制代码
5 ?, t" R* ]9 u' T2 L3 x, A. _8 g/ K. h中断处理函数如下:6 k$ c- p, n9 P* c6 ]* ]
) j1 J9 H3 n8 M# \- /********% e. ^% C7 G6 x. h
- * Function Name : USART1_IRQHandler1 u2 T, u- p& Z0 e6 y) n+ Y
- * Description : This function handles USART1 global interrupt request.
7 I D3 ]9 j2 Q/ ?+ n7 S - * Input : None
3 D `( D- ]& | - * Output : None8 `: w* D7 Y$ _& [3 Q* o
- * Return : None
/ `2 Z- u& y* J; q - ********/- q) R7 |, T# ~. b0 N
- void USART1_IRQHandler(void). b% J0 d7 y0 Y# J+ E9 x
- {
; X6 k- D+ h0 i% F - if( USART_GetITStatus(USART1, USART_IT_TXE) == SET )
) i0 H. V1 Q$ M( L1 f- w - {
6 n5 x6 [! N7 F' I+ F8 m - if( *pDataByte == '\0' )//待发送的字节发到末尾NULL了7 F+ b* r# H8 Z/ l" D, I, A0 Y
- USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断
7 w! ^8 ~, k# t - else
7 e k# ]. A+ q8 X - USART_SendData(USART1, *pDataByte++ );6 d6 ~( I5 Z1 N) n7 R
- }
/ \- {0 p$ ^* v# O& k
6 o$ f7 F$ t. o F6 [7 L- }
复制代码 ( q- c2 r I' |' O# ?7 S3 i: T# e
在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:3 h1 a3 n% h% x4 U1 L, i5 T9 q- l
- /************
( M# t6 m2 o. a2 @# W - 名称: USART_Config
/ n2 r3 C, @6 \. w/ k& Z6 a, Y+ Y - 功能: 设置串口参数
7 B4 ~7 P' m$ T* q - 输入: 无
7 L: P8 c6 F1 p L' J& h - 输出: 无& _7 b) X: S! n# u [3 e) D G
- 返回: 无, {" T1 v9 L' M4 X
- ************/
9 l3 Z4 a! s5 i - void USART_Config()1 D1 |$ B& j! u, l! X
- {) L4 X3 s U! N; c8 ]& J, Y
- USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体( D; }# l+ {+ w- T
- 4 a! y( ~* t- M- S0 D
- USART_InitStructure.USART_BaudRate = 9600; //波特率9600* I Q! _! g# p. ]5 e) n; u
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位# I, j- K6 }9 p# N6 R( C' O
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
( S& {; ]: W; z* w. U9 u4 j9 T - USART_InitStructure.USART_Parity = USART_Parity_No;//无校验
4 U3 d' `& Q8 Z$ O3 L4 B - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
9 B* j! O$ I8 b) s* q - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式
& ?( A0 v( C# R7 O: f- n5 A - USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭
$ ? D5 Q9 R$ d" H - USART_InitStructure.USART_CPOL = USART_CPOL_Low;
. x/ B2 B* Q9 `! Z! X6 K - USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;' k& g/ B% Q& _0 M& E- T6 a1 J
- USART_InitStructure.USART_LastBit = USART_LastBit_Disable;; R( ?& a# A8 P# v9 j
- . z. t$ f! ?' X) k/ U
- USART_Init(USART1, &USART_InitStructure);//设置到USART1% n! @& a: d& s5 ?% F; }
- : Q/ M# u+ Q) J* J$ S
- USART_Cmd(USART1, ENABLE); //使能USART1
5 n7 j8 v: F0 f. X( p - <div align="left">}</div>
复制代码 9 Y2 F3 M* X# {- H. K
SECTION 3 9 b5 N6 f9 w9 {# R. o, [9 @3 S' z
6 W/ H. F, f! o+ K
在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。
4 K# @- X7 C7 `7 l+ b' M
2 _# ~' z, j7 J0 M$ ?对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。8 Z/ [; D9 }6 F4 @
% t) A+ Z6 _8 m3 }' z( u# C另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。" S) {% Z2 m3 Z2 y
; O) \8 ?' ~1 M* w" E3 b
TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
% H0 x$ Y3 W m9 E
! |9 C) }1 ?( ~0 R0 \至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。
" ~% z8 N, G. a) jSECTION 4 总的来说,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。在发送完成后立即将其关闭,以免引起不必要的麻烦。 对于发送,需要注意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清零。 对于发送单个字符可以考虑不用中断,直接以查询方式完成。 对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用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是索引值: - <div align="left">extern __IO uint8_t TxCounter1;
@ C2 |6 d: s1 O' c - extern uint8_t *TXS;) \5 ^# p7 l1 @3 @& w; d m
- extern __IO uint8_t TxLen; </div><div align="left">void USART1_IRQHandler(void). r3 x. v8 y+ k% \# j( S; t
- {
& V. p1 s7 g! n- { - if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)# |% O- {; s. U' l* e y
- { # b- q, m' X; f4 i9 v/ y
- if(TXS[TxCounter1]) //如果是可显示字符$ d6 m, g+ V9 t- |6 r% m
- { USART_SendData(USART1,TXS[TxCounter1++]);}3 ] n# c5 W3 D1 b- z5 e$ X
- else //发送完成后关闭TXE中断,/ F7 r. L9 c- @- A
- { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}
% t; x1 X9 Z6 g& i6 Q" c - } : U3 u) T \ a
- }</div><div align="left"> 对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:</div><div align="left">void USART1_IRQHandler(void)
( @3 q) P6 c: [% |6 ^7 R - {
" u. [! j* u; M% \$ C* G$ T - if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。) V# c, Q/ L% z/ U( [
- { 9 X- v0 \. [3 A6 A' @# W2 F; j
- if(TxCounter1<TxLen)
G6 A4 b! x/ E& Z - { USART_SendData(USART1,TXS[TxCounter1++]);}
/ c# B( ?, ~+ e) i. ^- o8 V$ c - else //发送完成后关闭TXE中断& _! \# x1 g) u! x" Z% w
- { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);} ' l' O1 u# I% p
- }
4 k2 B1 }0 O, P - }</div>
复制代码 2 y+ S% [/ n+ Q1 H7 e$ l8 F, Y8 }
事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号! 在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例: - <div align="left">uint32_t *TXS;
+ K/ v, g; T' D4 R9 J, Q9 m2 Z - uint8_t TxBuffer1[]="0123456789ABCDEF";" b& S' n1 W2 T3 v& Z1 [" ~
- uint8_t DST2[]="ASDFGHJKL";
+ P1 K* W1 C, z5 w& a- w a; E - __IO uint8_t TxLen = 0x00;</div><div align="left"> TxLen=8; //发送8个字符,最终发送的是01234567
8 ]: E2 ~$ b) x - TXS=(uint32_t *)TxBuffer1; //将TXS指向字符串TxBuffer1+ }# S. p. U- v, n* l+ D6 }
- TxCounter1=0; //复位索引值
7 M) o0 t( e' X" t2 S0 z - USART_ITConfig(USART1, USART_IT_TXE,ENABLE); //启用TXE中断,即开始发送过程
8 t) H. h: D, F& B4 E - while(TxCounter1!=TxLen); //等待发送完成
) G* x) n9 r0 W& W; i# \+ C
! t# M# a$ `/ l0 ^" j- TXS=(uint32_t *)TxBuffer2; //同上,最终发送的是ASDFGHJK
4 `9 t5 x, |5 O5 Q$ t - TxCounter1=0;
/ {: Y& B5 S) e2 b8 a - USART_ITConfig(USART1, USART_IT_TXE,ENABLE);
9 ]6 z* r, I1 _. a' v - while(TxCounter1!=TxLen);</div>
复制代码 9 q( N% ^$ P. Z& l6 c J7 T
以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。 + l5 W H, h9 N$ a
1 i0 f( I3 S! ^7 p' y' c5 \
|