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

【经验分享】STM32串口中断的一些资料

[复制链接]
STMCU小助手 发布时间:2022-1-23 22:20
在研究STM32串口接收发送中断的时候找到不少不错的资料,现在备份在这里。以供自己查阅,以及方便其他人。
TC ====TXE
顺便预告下最近会写个有关串口处理数据的帖子,从查询和中断方面以及数据处理的方式,从队列以及FIFO方面写起。
/ f/ s* n2 {( u/ ?- P
SECTION 1
  1. /*
    1 m9 q  w  N' a1 S2 x( F# q
  2. 调试STM32串口过程中发现一个奇怪的问题,初始化串口1口,使能串口发送完成中断后,立刻就进入了发送完成中断。* _$ G( J/ n) C6 v: q5 O" h
  3. 仔细的查阅了STM32手册中的串口部分的介绍:
    & d0 u: X+ m# ]; [+ W; S2 B
  4.           ) \' m8 V! V: ?: O7 E
  5. 以下是字符发送的配置过程,注意第6点,在设置USART_CR1中的TE位时,会发送一个空闲帧作为第一次数据发送,所以即便你执行了USART_ClearFlag(USART1, USART_FLAG_TC); (这个函数肯定在空闲帧数据发送完成前执行),所以当空闲帧发送完后,就进入发送完成中断。- d9 A4 @! u7 b
  6.           6 y+ V1 S; l* D
  7. 配置步骤:; g& u' `0 C! H4 f6 y8 i( A. s
  8. 1.  通过在USART_CR1寄存器上置位UE位来激活USART! P# ]) q, t- X. w9 [
  9. 2.  编程USART_CR1的M位来定义字长。
    / F! T9 `! t1 N/ O
  10. 3.  在USART_CR2中编程停止位的位数。( d+ [  _1 s) F$ F1 U( @
  11. 4.  如果采用多缓冲器通信,配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中5 G( [# R& ^7 t" e
  12. 的描述配置DMA寄存器。; l' _# j! Y' N; h- ^0 M" c/ B
  13. 5.  利用USART_BRR寄存器选择要求的波特率。% F4 P$ ^0 ~0 W$ E" Y: N
  14. 6.  设置USART_CR1中的TE位,发送一个空闲帧作为第一次数据发送。
    3 A2 h* U! z. g
  15. 7.  把要发送的数据写进USART_DR寄存器(此动作清除TXE位)。在只有一个缓冲器的情况& @' j2 s  i- b  [+ d
  16. 下,对每个待发送的数据重复步骤7。" x! r. Y* R- U+ r7 j) E
  17. 8.  在USART_DR寄存器中写入最后一个数据字后,要等待TC=1,它表示最后一个数据帧的$ U# [) J  q  I5 M- ~
  18. 传输结束。当需要关闭USART或需要进入停机模式之前,需要确认传输结束,避免破坏7 Q' _# ]4 c0 Z9 Z3 r
  19. 最后一次传输。! `  A* Q5 }, N+ C! h
  20. */
    ! Q' l$ N" x9 U* c
  21. //解决的办法:
      P: ~0 f. ?$ p4 u# B' V
  22. //方法一  u# x; H* |* [  g% k% U
  23. //在执行
      g5 K  y0 Z/ R; ~- Y" r5 g/ w# H
  24. USART_ITConfig(USART1, USART_IT_TC, ENABLE); . L) `1 f" a; g/ f
  25. //之前,先延时一段时间,基本上比一个字符发送的时间长一点就可以了,然后再执行2 Q3 ~5 k! {7 v7 k+ M5 _; t/ v
  26. USART_ClearFlag(USART1, USART_FLAG_TC);
    ( r0 ?" i+ ~, r' t( Z! R* z
  27.           9 R7 |4 z3 B2 t3 i0 O  z
  28. //方法二:
    , t5 V8 t# y1 ]! x
  29. //在执行
    & i6 l) M$ @# V% E
  30. USART_ITConfig(USART1, USART_IT_TC, ENABLE);) Z- N$ t' X6 M% t: j
  31. while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET)
    5 a, I" ?1 j7 x
  32. {
    * j; T! j+ A8 A; P- h4 @
  33.         ; //等待空闲帧发送完成后  再清零发送标志
    6 S: ?  F& I# T5 ^0 \1 V
  34. }2 \' J+ G* K/ V/ a! Y7 I
  35. USART_ClearFlag(USART1,USART_FLAG_TC);
复制代码
) W& {& a& y  f, S  L2 I

; M0 X* a. U  n5 c5 Y4 M
SECTION 2
) E2 _1 }6 c  n! d0 `% G0 x' U; X  H
先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下
1 k9 X, y" U2 a! D/ w5 b; u" X! {5 t. d5 j& a2 y4 t& W2 y! T
  1. /*******
    ( g. h( f! L1 i' `9 T& i
  2. 功能:中断方式发送字符串.采用判断TC的方式.即 判断 发送后中断 位.
    * V+ j. G" z* V+ L- z; a
  3. 输入:字符串的首地址
    * V: e" D- e' a+ z. p
  4. 输出:无
    8 e! R& ?7 B" @
  5. *******/) N' `" E- U5 b* @. p$ u
  6. void USART_SendDataString( u8 *pData )
    / O: V. T8 q/ _5 Z" O
  7. {; O, O  A+ x# m( t- t
  8.     pDataByte = pData;
    ' f; L  s8 B' B2 F- n/ W
  9.   ! I. L7 M7 s$ `5 z% a
  10.     USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
    5 D' X6 A% n! N9 Z
  11.     , J$ F+ I' U: f) w* B
  12.     USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次/ v, V% @- V' L; l' J; P/ s
  13. }
复制代码

( W7 z" N* l7 n6 b* E: c) @4 _中断处理函数如下
3 F, q, o7 G& F& B  [; t0 `/ {
  1. /********* k) C* h: I, s+ w
  2. * Function Name  : USART1_IRQHandler
    ' K7 N5 r$ z; Q6 v$ e
  3. * Description    : This function handles USART1 global interrupt request.4 q" O$ L* T' r4 I8 X3 S; t
  4. * Input          : None6 b$ x0 _0 Y7 }8 c1 V
  5. * Output         : None
    ' j( c9 G2 H4 o( g% c. B
  6. * Return         : None& o, T, o; q$ Y# H0 C9 O
  7. *********/
      n* ?% @* P5 t6 t. F  d/ L
  8. void USART1_IRQHandler(void)
    ! x, F( }- w/ c0 D8 \% h" L- Q
  9. {
    7 r6 m/ _( P* M; o1 J: C4 t& O  O
  10.     if( USART_GetITStatus(USART1, USART_IT_TC) == SET  )6 a8 O/ ~8 I9 m5 C. `8 F' `! ?# L* A
  11.     {
    " v- o' t4 z) }" _; h
  12.         if( *pDataByte == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\0'的时候用个if判断关掉
      e6 L& ?. B( H8 J, W, r  z; E
  13.             USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断. clear掉即可,不用关掉TCIE
    0 ^' r! ~) D& @% `$ {
  14.         else% ?) T5 G5 X1 D" ^  j' Z! ?( m& ~
  15.             USART_SendData(USART1, *pDataByte++ );
    / P5 w3 |' z" n! K  _5 G
  16.     }
    4 g6 m( F! C- d7 L

  17. : m" [' |2 R. _+ n+ \9 M
  18. }
复制代码

3 M: b' H" o/ s4 \其中u8 *pDataByte;是一个外部指针变量
1 Z6 C0 K' i6 F7 V3 ?- Z) N$ t3 f4 |4 M6 q6 z2 G- S4 y
在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。
* o6 q7 n- X, w8 |3 L* \3 L7 m
串口初始化函数如下
7 S- d7 r' B6 W; u+ V
" H" @; P8 Q/ E/ X- g
  1. /*********: Y. c6 E1 L) J# N# g2 ^+ N8 S! {/ e
  2. 名称:  USART_Config) _9 }1 V  f8 U4 p" R* G3 X" Z! W
  3. 功能:  设置串口参数
    + ?6 E  M  _/ d# e7 n( B
  4. 输入:  无+ @8 S8 R% q. P- z  s
  5. 输出:  无4 R& j$ w% r9 |) Z( b! l' u+ g
  6. 返回:  无
      D2 M( l' _  o. E" }/ C
  7. **********/8 O7 J  c1 M8 C9 x+ Q
  8. void USART_Config()# y. F* _( k! {! U: v. e. E7 `
  9. {
    , l1 q: W! R/ L7 ?2 C& s* u
  10.   USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体' v1 b+ i/ o: d/ ~3 b: _
  11.   + M  j" A- m2 ?+ |7 x: K
  12.   USART_InitStructure.USART_BaudRate = 9600; //波特率9600
    " |$ S' a2 i* V* N
  13.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位) Y0 i. N% T6 x' K! Q4 \8 P
  14.   USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位
    ' U) Q0 f: s2 P( |6 N9 i; C5 P
  15.   USART_InitStructure.USART_Parity = USART_Parity_No;//无校验  E8 |. t* _5 ~; k% d5 w" v+ {
  16.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
    3 l. P/ V3 C, C0 M" l
  17.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式/ Z$ V( w% K# \/ h% r
  18.   USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭
    ! \% {, u8 M& o- }# G5 F9 n9 Q$ H
  19.   USART_InitStructure.USART_CPOL = USART_CPOL_Low;( l2 B( Y6 A$ C8 s9 S3 ^
  20.   USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
    * y# A. h* t. l! D
  21.   USART_InitStructure.USART_LastBit = USART_LastBit_Disable;+ b8 p) Q  u, [9 U
  22.   USART_Init(USART1, &USART_InitStructure);//设置到USART1" H8 [! M3 u5 H" G3 _9 S* O
  23.   
    ; `! K# w( ~: }) \) R7 }! X
  24.   USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节
    8 ?& l: y9 s0 \, h  q6 N+ I6 k
  25. ! N( T1 F7 t3 Z
  26.   USART_Cmd(USART1, ENABLE); //使能USART1
    . g+ b) i6 m: M# U
  27. }
复制代码

# a3 N* K8 e3 N# Q7 Q7 Q这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么??
' v0 A1 n* z$ e+ E; |
这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
4 L7 \. y7 w# C

) }1 ?6 @; `8 ~) w再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。- Q4 _# Z* J+ x8 |

- @) h" k; X: g9 H9 Q# p发送函数如下:
3 U4 h7 Z0 X) r9 a4 [
  1. /*******
    9 O% q# ]* p% x9 O# N6 m# p. a
  2. 功能:中断方式发送字符串.采用判断TC的方式.即 判断 发送后中断 位.$ M. ^* l/ `. ^& m. w7 ?  t
  3. 输入:字符串的首地址
    7 L# `: E6 N$ B1 s
  4. 输出:无
    : P6 C) u; ]* d
  5. *******/
    2 |* F0 F2 E: q9 H' J& {- f1 t. E4 c
  6. void USART_SendDataString( u8 *pData )
    1 ^0 J- V" E" f' Z6 R* Z
  7. {& b3 Q2 \% k8 y
  8.     pDataByte = pData;8 Q( q8 O8 c/ J
  9.     USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。
    * H0 B# m7 v  a9 c
  10.    
    ; A6 V4 g# S* d4 g+ w+ E% t3 N
  11. }
    ) G! M5 Q1 p1 A( O- ?
复制代码

. x5 o5 k0 |( \# q3 W7 W中断处理函数如下:
8 J& ^* W3 j/ ]  f: i, e8 a8 I, x
( X+ G1 t! }& R7 m. b, ]* @
  1. /********- E5 k; ?$ m% z: E1 h
  2. * Function Name  : USART1_IRQHandler3 r) S8 B# l9 j; A8 Y+ p
  3. * Description    : This function handles USART1 global interrupt request.
    ) _  f7 P# _3 n: n
  4. * Input          : None
    + Y) {+ Y; l" f* R
  5. * Output         : None4 H/ `5 O' [& k/ z3 L: d- z
  6. * Return         : None
    * a5 k7 d. ]. A! x  Z  ~4 L4 W3 A/ y
  7. ********/
    % t' L  N/ Y4 ?5 W
  8. void USART1_IRQHandler(void)
    # w! S1 K6 U. C- H0 C( r
  9. {" b  V5 Z& a# q) t* o
  10.     if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )
    ' V3 G* M, r% u: ?1 g, G: L& }- R! p
  11.     {6 l: y& W7 j& q0 W8 W7 G: S! @" Z
  12.         if( *pDataByte == '\0' )//待发送的字节发到末尾NULL了6 [, n. I7 @+ p, |9 i
  13.             USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断  A, Y' z: c& o, u2 V* B) N
  14.         else
    ; p; h1 F0 _0 s
  15.             USART_SendData(USART1, *pDataByte++ );
    6 N  k  ?6 o# p5 y5 w( P* o
  16.     }  f/ G$ \; I4 E, i7 O
  17. ! p! b# w% i1 c7 m, u- T- W1 f
  18. }
复制代码

, s' x' D- A5 n. D% }' |6 p% s在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:
& t$ y$ D+ X/ H1 m
  1. /************6 k6 r8 l4 h" v4 A7 E* A. O# I
  2. 名称:  USART_Config
    0 {  G! U: n; e) L: I- G; r
  3. 功能:  设置串口参数6 h5 M# G: B& Y9 b$ X1 D4 n/ n* C/ ^: [
  4. 输入:  无% B* w5 k7 O( @  X
  5. 输出:  无
    ; U! d/ \4 X. S  w
  6. 返回:  无) d+ U1 j9 X+ z7 ^+ H
  7. ************/6 S! D( w" J2 {: }% O
  8. void USART_Config()
    ) n$ `% T0 \( E- u
  9. {
    & I' X" D7 z6 M5 S4 Y5 K
  10.   USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体
    % V: J; p1 B/ @; b! y6 I
  11.   $ \- ^! Z2 p% ^, o+ T
  12.   USART_InitStructure.USART_BaudRate = 9600; //波特率9600: d  C$ r) _; S# I$ |
  13.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
    : P5 u" J5 X2 \4 B) U) e/ i6 W
  14.   USART_InitStructure.USART_StopBits = USART_StopBits_1;//1位停止位$ c: S, o9 N9 P
  15.   USART_InitStructure.USART_Parity = USART_Parity_No;//无校验. [) Y& J+ a  K; T# e4 E0 ~# m
  16.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
    7 q! p9 h' l1 }* k. _2 O6 A
  17.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//输入加输出模式' N, U0 m; y" s, R
  18.   USART_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭
      E3 ~9 L, X$ f; Q
  19.   USART_InitStructure.USART_CPOL = USART_CPOL_Low;1 x3 I2 B& h, A3 {
  20.   USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;, t, O7 B2 ?' S* U: a1 O
  21.   USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
    # r; A% l7 Z( w* d# X
  22. 0 E( B9 [8 I& S: A4 d# V
  23.   USART_Init(USART1, &USART_InitStructure);//设置到USART1- o7 b' X3 D& G' X8 f+ `3 K
  24.   * M0 D# L& f& {4 i: a
  25.   USART_Cmd(USART1, ENABLE); //使能USART1
    / U  Y" @3 }% B+ [8 v
  26. <div align="left">}</div>
复制代码
" A# ^& {  H; X+ @
SECTION 3
( s3 X7 T6 ^7 G6 y+ h) \. ?
2 Z, V  [: f$ A3 W8 B: ?' O
在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。4 p7 p7 F, P6 K* d0 C) X
' t; ~% ~6 d, _
对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。
6 T2 b+ @/ l3 \$ ~- R4 q& K0 u
- J! g& Q' P& M4 |: ?" K另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。
8 c/ c/ A. C8 I# ~' a) {1 z
5 h" z( S9 H2 K3 Z) Q4 e, kTXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。
' Q, j% w' k$ _2 f; }
7 a  c' ?" X( w, X9 `; S至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

+ h9 R& N1 n! h3 Q% W" W9 m3 `' S0 P
SECTION 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是索引值:
  1. <div align="left">extern __IO uint8_t TxCounter1;
    4 c% Y) |: t# K, \+ U
  2. extern uint8_t *TXS;' _7 j- F. u5 D
  3. extern __IO uint8_t TxLen; </div><div align="left">void USART1_IRQHandler(void)- g6 ]$ {8 F( l
  4.     {0 C: Z! U. a9 r$ L( L
  5.         if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
    5 ?1 T5 B  t0 w& n( C3 a8 M
  6.             {                                               
    $ f# a7 h3 [/ j- _
  7.                 if(TXS[TxCounter1]) //如果是可显示字符
    4 s& y4 a" n! G/ _
  8.                     { USART_SendData(USART1,TXS[TxCounter1++]);}3 ~7 W. c5 _" U$ f) I1 d
  9.                 else   //发送完成后关闭TXE中断,
    5 h6 [1 A5 Y1 l+ ~7 n9 S
  10.                     { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                        
    6 C7 g; s, p: S  I# P# T
  11.             }                   ; s$ R" `9 g/ X) @
  12.     }</div><div align="left">        对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:</div><div align="left">void USART1_IRQHandler(void)# U$ y7 P1 x1 U
  13.     {" P1 T. e9 r9 U7 R) ]
  14.         if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。  n" r! y( }8 S
  15.             {                                              + p4 D& f  C% r/ A0 [9 e- n
  16.                 if(TxCounter1<TxLen)1 A  |* g3 ?" s6 r' Z5 }* j
  17.                     { USART_SendData(USART1,TXS[TxCounter1++]);}6 x  e- z* c. A( s- L
  18.                 else   //发送完成后关闭TXE中断
    9 ]- }" `" e  x$ ~
  19.                     { USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}                                                         0 U" T! S! t8 w
  20.             }                    ) T# R2 h6 q5 q# A1 e5 m" n
  21.     }</div>
复制代码
" o5 H# b; V* e& k
        事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!
        在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:
  1. <div align="left">uint32_t *TXS;
    / X: Q/ \, I, @: a
  2. uint8_t TxBuffer1[]="0123456789ABCDEF";9 \5 n* S6 b6 `5 W4 a& h- ^
  3. uint8_t DST2[]="ASDFGHJKL";& P5 \# h6 V* j6 O# {
  4. __IO uint8_t TxLen = 0x00;</div><div align="left">     TxLen=8;                               //发送8个字符,最终发送的是01234567
    / }# T0 v/ r4 b" S2 q9 L
  5.     TXS=(uint32_t *)TxBuffer1;   //将TXS指向字符串TxBuffer1  @7 J. G5 {; ~$ J+ v
  6.     TxCounter1=0;                     //复位索引值
    : @* v4 r( E5 B8 i! A
  7.     USART_ITConfig(USART1, USART_IT_TXE,ENABLE);   //启用TXE中断,即开始发送过程9 v  b- T0 Y, A6 G
  8.     while(TxCounter1!=TxLen);   //等待发送完成7 \9 r" R9 U. f# h% L9 ?6 m+ \+ S& s
  9. 2 \- N% c* |% A1 M2 f- _
  10.     TXS=(uint32_t *)TxBuffer2;   //同上,最终发送的是ASDFGHJK
      r8 A& O4 m; Y
  11.     TxCounter1=0;
    0 S; U6 s+ u5 b; H/ g3 v7 k
  12.     USART_ITConfig(USART1, USART_IT_TXE,ENABLE);+ u3 E' y0 ^3 m# d! H& X8 v7 e$ R. {% J  ~
  13.     while(TxCounter1!=TxLen);</div>
复制代码
3 P2 a' `; l  [) r1 |
        以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。
5 U0 J% Q$ S- L4 V" U
) ?8 ^2 y) j8 t% U3 \
收藏 评论0 发布时间:2022-1-23 22:20

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版