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

【经验分享】串口工作在 DMA 模式下有时接收异常

[复制链接]
STMCU小助手 发布时间:2022-2-14 21:13
前言
5 N- N0 e( B2 k/ d0 z) B客户反馈在使用 STM32F205 的串口工作在 DMA 模式时,有时能够接收数据,有时完全没有数据,但如果换成中断模式来接
# v; X2 q' p  A) o/ Q  u收又能 100%正常收到数据。, S0 R; a$ r: q8 @( s: J; g( U
一 复现现象+ `7 e) T- E0 J3 K+ g1 a4 G
问题背景
* r2 H; T; E# `; O- N$ X- j3 n与客户沟通,客户使用的是 STM32F2 标准库 V1.1.0,串口波特率为 1.408Mbps,不经过串口 RS232,直接连接主 CPU 和从 MCU(STM32F205)的串口发送和接收引脚,如下图所示:2 ]. }5 o6 G7 I5 i4 k1 o0 j

2 p  L6 I, l7 Y! G L~F9~}F}TMRGAGZ(8)`)LX8.png
) e% T( ^8 X6 w% W; p  |9 o7 R2 {. p4 Z% M' Z* \5 V' o: p5 R
$ E# N9 B- _' B* B2 F$ g) M, Q
尝试重现问题
0 J& e/ [. f! ?# c由于客户使用的是主从架构,实验采用两块 STM3220G-EVAL 评估板来重现现象。一块用来不间断发送串口数据,另一块采用串口 DMA 进行接收,直接通过杜邦线连接串口 PIN 脚并共地,不使用评估板上的 RS232 收发器。接收端使用STM32F2xx_StdPeriph_Examples\ USART\USART_TwoBoards 的示例代码。代码片段如下:
, h( A9 f  r( J" d1 C* r) \  o
  1. int main(void), n9 a0 ?  p  r7 r/ k
  2. {
    $ V0 G( u8 c/ Q. [6 g8 _. }
  3. ...
    : I) f9 }2 t+ a. A4 ?
  4. USART_Config();' J% d% l6 S) ]  V+ {. }3 z
  5. .../ |& ^% F, N; A& t* J. q7 c
  6. while (1)
    2 k1 j! }# e' @! F% ]% i
  7. {! w* O7 q7 z  b) F+ P
  8. /* Clear Buffers */
    , T6 t- b0 @) w' L% {; Z
  9. Fill_Buffer(RxBuffer, TXBUFFERSIZE);6 ?  V' ^. s* ]4 p* s; a" b8 ~2 }2 n! S
  10. Fill_Buffer(CmdBuffer, 2);( G& [& Q7 M2 W# ]( ]) P% p
  11. $ k9 O4 W0 Z1 |# m( b* ~1 J
  12. DMA_DeInit(USARTx_RX_DMA_STREAM);
    4 x* M; h/ G, y( ]) u% G; Q$ v& u
  13. DMA_InitStructure.DMA_Channel = USARTx_RX_DMA_CHANNEL;
    6 ?9 b, _+ ?; N
  14. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    9 C% c  z* n( s) `/ p
  15. /************* USART will receive the the transaction data ****************/
    2 b! L4 B) ?% C/ [( C" z3 ^& X# @
  16. /* Transaction data (length defined by CmdBuffer[1] variable) */) N% L; W/ t0 k, U" r+ w- K5 ~
  17. DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RxBuffer;
    0 F2 h% P% O1 [1 M0 w2 a' y
  18. DMA_InitStructure.DMA_BufferSize =10;// (uint16_t)CmdBuffer[1];
    ( J  F2 ~, ~! B
  19. DMA_InitStructure.DMA_Mode =DMA_Mode_Normal;//DMA_Mode_Circular;
    , i) P! d. H1 R! J; i
  20. DMA_Init(USARTx_RX_DMA_STREAM, &DMA_InitStructure);' Q5 Q$ m6 I$ Z
  21. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn;. b2 k' W, N) x; e: `' ?# J& X
  22. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;) W$ }7 _- A9 `  z$ b
  23. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;) B1 Y6 q9 C! W! c/ ~0 z
  24. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    0 k& s, T5 @; Z& I
  25. NVIC_Init(&NVIC_InitStructure);) g+ p. f# ~$ \9 z! u: X8 {
  26. /* Enable DMA Stream Transfer Complete interrupt */
    / J" C; s( \6 E- P+ N8 v$ F6 w% `$ h
  27. DMA_ITConfig(USARTx_RX_DMA_STREAM, DMA_IT_TE|DMA_IT_DME|DMA_IT_FE, ENABLE);1 Y, x, G1 [1 P( ~
  28. /* Enable the DMA Stream */. }6 |' ]. `" X2 p! [" l) b
  29. DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
    ! A1 E" v" G6 N' z
  30. /* Enable the USART Rx DMA requests */
    ' w% R, n) ~/ V  a- [
  31. USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);# ?0 _# u/ z$ t# F% }
  32. // USART_Cmd(USARTx, ENABLE);
    2 n8 W! k1 L* a; M3 P, R
  33. // while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
    ! [9 Y5 Q' e$ f, s3 C4 T1 H
  34. // {
    7 o4 L. G# ?! ~# @4 R
  35. // Tmp =USART_ReceiveData(USARTx);
    : G# ?- Q5 L/ h( C% R1 @
  36. // }2 Y" }3 l% B% A. [. `. ^
  37. while ((DMA_GetFlagStatus(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_TCIF) ==
    9 a! y. L& K* {4 T- o
  38. RESET)  P6 X9 w* q% q# J( X
  39. {% ~  o7 X9 J2 `8 M3 w
  40. }) W) C; V) }7 P, G# g
  41. /* Clear all DMA Streams flags */2 y; Q( ~5 U, A$ N1 L2 O
  42. DMA_ClearFlag(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_HTIF |. g5 A0 {) i) h; K& O4 E
  43. USARTx_RX_DMA_FLAG_TCIF);
    # T) A+ X7 |2 Q
  44. ; e1 H7 p) B2 A, o$ ?6 B
  45. /* Disable the DMA Stream */
    " G5 K/ R( e6 s% p
  46. DMA_Cmd(USARTx_RX_DMA_STREAM, DISABLE);% I% N0 o' Y# c/ ^1 {8 h

  47. & A" ?  u) W# u6 w
  48. /* Disable the USART Rx DMA requests */% S7 N( _; S9 [+ @9 w* ]) ^
  49. USART_DMACmd(USARTx, USART_DMAReq_Rx, DISABLE);1 q9 D) s- C, W+ {  r
  50. 4 K* l  M4 \3 @3 ^# b- X$ k
  51. //handle the RxBuffer data.... P' f5 P$ F( l6 R  p9 @8 _; q
  52. //...
    " N+ M! K0 c, T) B* e$ s6 J* ]
  53. }0 c) u" j: D* A* T+ H' W0 J8 L
  54. }5 n- L  G/ H- Y5 V
复制代码
USART_Config()函数如下:9 s9 c6 r4 k8 M4 S! f+ y+ n4 Q
  1. static void USART_Config(void)* Y% m' T* J$ A( E: G
  2. {
    + _! J: X$ m$ j) E7 H6 r
  3. USART_InitTypeDef USART_InitStructure;( ~5 j5 e1 g# U* p6 m& l' ^
  4. GPIO_InitTypeDef GPIO_InitStructure;
    . [% B+ R( w1 z1 `2 F  U
  5. /* Peripheral Clock Enable -------------------------------------------------*/3 r, l1 H( V; J9 F* @% W
  6. /* Enable GPIO clock */( c+ i% u( `; w7 U. u
  7. RCC_AHB1PeriphClockCmd(USARTx_TX_GPIO_CLK | USARTx_RX_GPIO_CLK, ENABLE);
    * q$ K% p: B% l9 i9 [  }
  8. /* Enable USART clock */
    5 _% w! _! [! E# E3 P! y) ]! Z
  9. USARTx_CLK_INIT(USARTx_CLK, ENABLE);
    9 d  T5 C1 |# k  J6 Z

  10. 0 K3 G9 D4 e, Y" f0 {
  11. /* Enable the DMA clock */
    ) y! H$ J/ E7 G! }9 l
  12. RCC_AHB1PeriphClockCmd(USARTx_DMAx_CLK, ENABLE);
    5 e- ]! _8 J2 ^7 ^

  13. # ~# Q0 f/ v  r5 |* l
  14. /* USARTx GPIO configuration -----------------------------------------------*/# D+ q" B" |3 ~  k, ]& f
  15. /* Connect USART pins to AF7 */
    & z2 t  a1 H( K
  16. GPIO_PinAFConfig(USARTx_TX_GPIO_PORT, USARTx_TX_SOURCE, USARTx_TX_AF);
    6 z/ Q; A% e- L0 V
  17. GPIO_PinAFConfig(USARTx_RX_GPIO_PORT, USARTx_RX_SOURCE, USARTx_RX_AF);
    % @3 R  {2 o, ~- `8 D

  18. # d6 F- T9 Q) a# i- T' `
  19. /* Configure USART Tx and Rx as alternate function push-pull */
    / K3 g, m! v1 N/ l% ~" Q8 G9 T0 a
  20. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;' O: f1 P) B1 N5 a
  21. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;9 O- g3 s, k1 T, U
  22. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;* F: |( R" [/ U
  23. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    , h% i) E4 d/ g6 J& y6 k

  24. - p1 U2 c: @, {  E; Q& J
  25. GPIO_InitStructure.GPIO_Pin = USARTx_TX_PIN;
    1 g2 K; P, c* e
  26. GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStructure);  A& U4 j# G% H4 _' O; G+ B
  27. ' J  R" X. m" Y. ?
  28. GPIO_InitStructure.GPIO_Pin = USARTx_RX_PIN;6 j4 B; ?; H, X; k9 f
  29. GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStructure);
    1 Z5 j" \) U* S. d
  30. /* USARTx configuration ----------------------------------------------------*/
    % j5 i7 Z( \. T& J0 d/ t
  31. /* Enable the USART OverSampling by 8 */
    * m# c' m2 M. q2 y3 n. H
  32. USART_OverSampling8Cmd(USARTx, ENABLE);
      i0 G& X) g1 U( y( k0 B: J
  33. + {+ ~% C. U% u/ q3 ?' P: c0 `
  34. USART_InitStructure.USART_BaudRate = 1408000;//3750000;; P. l' C! g( p( ?, C; v  m
  35. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    : R1 U7 {  H+ x  I% z! k
  36. USART_InitStructure.USART_StopBits = USART_StopBits_1;" X3 }0 `* f$ j5 g% A
  37. /* When using Parity the word length must be configured to 9 bits */
    , R3 X1 x6 ]0 L9 E0 f9 V
  38. USART_InitStructure.USART_Parity = USART_Parity_No;
    & i8 J% u2 r) X3 `1 i) y
  39. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;1 `6 c9 W6 ~+ `: H6 ~8 B  }5 y
  40. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;: X7 b% d5 t! R) H
  41. USART_Init(USARTx, &USART_InitStructure);
    4 {9 r1 w- i0 Q# I! f# T
  42. /* Configure DMA controller to manage USART TX and RX DMA request ----------*/$ b  C1 }! j8 l1 ~7 U
  43. DMA_InitStructure.DMA_PeripheralBaseAddr = USARTx_DR_ADDRESS;
    8 `2 u  H( i6 F; Y+ [
  44. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    * G; i+ a- H* `0 R  T. S' \
  45. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;! @6 a0 @- u7 n
  46. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    ' \4 m( n+ Y1 u+ ^9 j
  47. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;4 _1 o( c% U( k& L$ O! f
  48. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;" `  o2 X& j$ G6 `; z& u
  49. DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    & R; Z# h. o* E" G* R6 S
  50. DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;+ e0 f" i! b( f% m9 T+ e
  51. DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    ) W" F. Z) z7 L9 a, a
  52. DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    4 d  r# g, ?; Y3 R% O
  53. DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    , F9 z5 y( k. b3 e& V, g
  54. /* Here only the unchanged parameters of the DMA initialization structure are6 F3 e+ Y* O- l
  55. configured. During the program operation, the DMA will be configured with
    + z  p( U3 x% K7 ^
  56. different parameters according to the operation phase */
    # O( y5 z$ Y/ ^( k# C
  57. 6 K: f# {3 T7 E3 @1 Z+ h/ H
  58. /* Enable USART */3 k* _% Q6 C$ N2 R6 U6 q" f. @
  59. USART_Cmd(USARTx, ENABLE);9 `4 Q$ o6 e4 ?' l+ e
  60. }
复制代码
5 C2 F; f7 _: ~0 O2 C+ R" w
按如上代码,有如下现象:
# ]4 g+ L2 I+ r( o. y. ^$ T1 代码不做修改,若先启动接收端 MCU 再启动发送端 MCU,接收端 MCU 的串口能正常接收。3 {- V( s! o+ g
2 代码不做修改,若先启动发送端 MCU 再启动接收端 MCU,接收端 MCU 的串口 100%接收异常。$ D& X6 P/ \! ]1 t1 V
3 修改发送端代码,改为发送端 MCU 串口每 1 秒间隔发送一次,则无论启动顺序如何,接收端 MCU 的串口都能正常。/ L& F7 U6 z1 H  g5 G. w7 e

, |% W  f; j3 U2 F二 程序分析
  K3 l8 a4 _3 y* V
由上述代码可知,程序是先在 USART_Config()函数函数内初始化串口并使能,然后再在接下来的 main 函数的 while 循环内初始化 DMA 并使能。这个是标准库内附带的示例代码,咋一看没什么问题,但仔细一想,针对用户的使用场景,这里就会产生一个问题:由于用户的主 CPU 有可能在从 MCU 启动之前就已经有可能启动,那么在这种情况下,在初始化完串口并使能后,到 DMA 使能之前这段时间内,若主 CPU 向从 MCU 发送串口数据,从 MCU 是否能正确接收?
1 N+ N6 }/ O9 a  Y5 I8 j, w  q3 V9 t" {# R9 P( ~6 m5 c
从上述测试代码的结果 2 可以得出,若在串口初始化并使能后到 DMA 使能之前有数据来,MCU 是不能接收的,经进一步调试,发现此时数据寄存器 USART_DR 存在一个数据,且在状态寄存器 USART_SR 中 ORE 值 1,由此可知,串口的接收寄
! j2 P& g- L, ~$ x  {# s存器中已经接收到一个数据,但是后面的数据又来了,由于数据寄存器中的数据没有及时转移走(此时 DMA 还没有开启),从而导致后面的数据无法存入,所以产生了上溢错误(ORE),而一旦产生上溢错误后,就无法再触发 DAM 请求,及时之后再启动 DMA 也不行,无法触发 DMA 请求就无法将数据寄存器内的数据及时转移走,如此陷入死锁,这就是串口无法正常接收的原因。这时反观一下代码的结果 3,这又将做如何解释?' R6 h) s  Q7 J; y$ @# R

' z  o) f1 R. t, o2 a1 `) n# R8 x/ L8 @9 F仔细查看测试结果 3,发现这个发送端每 1 秒间隔发送一次,那么就会存在这个一个概率,这个发送的时间点是否刚好在接收端 MCU 的串口初始化并使能和 DMA 使能之间还是之后,这个时间窗口非常关键,如果刚好在时间窗,那么串口接收就不正常,如果在这个时间窗之后,串口接收就能正常。由于测试代码采用的是 1 秒间隔,对于 MCU 来说这个是非常大的时间长度,还是很小概率能碰中这个时间窗的,因此,测试结果看起来是都能正常,实际严格来说,还是存在刚好碰中的可能。如果间隔时间缩短,那个碰中的几率就增大。由此看来,这也就能解释测试结果 3 了,也能解释客户提到的有时正常有时不正常的现象了。) J! j3 X* ?& ?) g$ J8 f$ \

3 |, [4 ^5 H4 t1 `" N三 问题处理
5 x! n& z+ ^5 y& J* i处理有两种方法,第一种方法是在使能 DMA 后,及时将数据寄存器 DR 中的数据清除掉,如下代码所示:/ w9 M7 o# Y1 `( n# v9 V
  1. ...
    6 m& l5 Y  u5 P2 |0 R" \+ r
  2. /* Enable the DMA Stream */
      g8 ~' ?5 F5 x7 w+ `
  3. DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);; L+ @) x  w9 o+ i) o: e( ]' u& h; Y
  4. /* Enable the USART Rx DMA requests */
    ! ~! F6 l# C! W$ R5 I  C
  5. USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);- j* o6 X" X% D; H
  6. while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
    ! H1 i; O% ~2 N5 T; e* B2 |
  7. {. G3 q3 b5 _2 a- [5 ~  R3 N6 Q% @
  8. Tmp =USART_ReceiveData(USARTx);
    # `' a) g/ A# R4 X4 s9 @1 p
  9. }# h# F, E8 |9 M# D; X
  10. ...$ H: T( h* `$ J* H9 e+ y
复制代码
5 i& ^: u$ _, \; z' y9 I$ P! B
8 ~; f- @  }, x" m5 n9 L5 h+ J
这里是使用读 DR 的方法来清除的,从参考手册中也提到使用这种方法来清除 ORE 标志:
- U# K: |8 a7 w  w6 s
' d* G: R: U: t! h$ x8 S0 ~0 _ YNFNU51MS8FI%{Z{XOKKZRA.png
$ s" }; L1 s& N) c: t! U& q
- X0 b! o. z6 A9 c第一种方法类似于一种纠错措施,下面介绍另一种推荐的方法,如下代码所示:( u! f+ u' h; s7 ]8 x
  1. ...
    + W& x3 T$ f, J8 z- |) i
  2. /* Enable the DMA Stream */
    8 O* I1 I  O4 M0 L  t; L4 R
  3. DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);) K  l4 y9 T. E: t3 E
  4. /* Enable the USART Rx DMA requests */* x0 U: d1 R, T* ?
  5. USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);1 f) \- H+ L0 d9 g! Y* J
  6. USART_Cmd(USARTx, ENABLE);
    " ~* ~! F" {  B
  7. ...
复制代码
' _; W& u9 B" E# M; e# |
如上所示,可以先使能 DMA 再使能串口,这样就彻底不存在那个时间窗了,不管数据何时过来能能被 DAM 及时转走。这个是推荐的解决方法。
# U+ |7 ?! x5 j7 S9 j  t9 @' C# Y& S( m# {
四 结论
- e# P$ _  f3 G" v
标准库中的示例代码一般来说只供参考,对于大部分情况来说都是能正常工作的,但偶尔也会出现不适用的情况,此时更需要我们针对问题进行思考分析,进一步找到原因才能解决问题。对于串口使用 DMA 来接收的情况,这里建议一定要先使能. R/ {" o3 u) r$ ~1 k5 I( e* A
DMA,最后使能串口,这样就能避免类似问题出现了。
+ C6 m4 d5 Y/ W3 P* t" L0 C, U, ^5 i# x
收藏 评论0 发布时间:2022-2-14 21:13

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版