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

【经验分享】STM32F2 中 DMA 的 FIFO 模式

[复制链接]
STMCU小助手 发布时间:2022-2-27 16:07
问题:, P+ P2 I- v# \! h, a. E/ x$ u
该问题由某客户提出,发生在 STM32F205VE STM32F205VET6 器件上。据其工程师讲述:使用 STM32F205 的 DMA 对USART 的接收进行处理的时候,发现如下现象:如果发送端发送 10 个字节,程序可以正常接收到数据,通过DMA_GetCurrDataCounter(USARTx_RX_DMA_STREAM)获取的数据长度以及程序中数据接收缓冲区中的数据均是正常的;但是如果发送端只发送 9 个字节,程序就无法正常接收到数据,通过DMA_GetCurrDataCounter(USARTx_RX_DMA_STREAM)获取的数据长度是正确的,但是在程序中数据接收缓冲区却没有数据,全为 0x00。不解,所以提出帮忙分析。
: t( n! _8 @1 x/ @
5 R& q$ \: }2 F8 |2 \6 |调研:5 [. U5 K( F+ O4 ^: k
检查客户的 DMA 配置程序,配置如下:
" u' P' ^1 }% z+ H
  1. DMA_InitStructure.DMA_Channel = USARTx_RX_DMA_CHANNEL;
      N! j% l: x, I; P5 J
  2. DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RcvBuffer;
    . m0 R5 ?0 V6 |! t6 l2 ~4 @  }
  3. DMA_InitStructure.DMA_PeripheralBaseAddr = USARTx_DR_ADDRESS;! ]: a: Z9 ?8 [6 U, t$ }$ k
  4. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;2 x. B  f1 C! K; r( h1 @
  5. DMA_InitStructure.DMA_BufferSize = BUFFERSIZE;
      R; p# h& y. w3 Q% N7 u
  6. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;% t# Q1 d+ h0 p0 b3 c2 y
  7. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    ! y' P9 Z, f* v) `
  8. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;- `% ^2 V: o3 I* O# p2 F
  9. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;' `0 U8 e) U; ]7 S& B+ U
  10. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;, K! F3 r, A* v8 [3 w
  11. DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    , ?4 }7 k  |5 C; ]" A+ W: `& W! K
  12. DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
    / ?! F; ?; l6 B( M4 s. F! t
  13. DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    . S7 ?& R# |8 Q# R4 B; P6 |# _1 [
  14. DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    & g5 p4 k# |) g, ^0 V
  15. DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
复制代码

+ ?0 J0 c0 E& S& H其中,BUFFERSIZE = 10。客户的目的就是使用 DMA 从 USARTx 的接收数据寄存器中取值,放入RcvBuffer 这个数组中,这个数组共有 10 个字节。- w' }6 \3 e4 o! T& l( J  P
' I* G, H. K! C, D! z0 W# V
uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx) 函数用来返回当前DMA 数据流剩下的数据单元个数。用户通过使用 BUFFERSIZE 减去 DMA_GetCurrDataCounter 返回的值来判断已经发送的数据单元个数。
* X( i' U2 A6 g
1 X1 J' e9 U) `$ X& a( R, v运行程序,验证一下结果,现象如客户所述:如果发送端发送 10 个字节,程序可以正常接收到数据,通过 DMA_GetCurrDataCounter(USARTx_RX_DMA_STREAM)获取的数据长度以及程序中数据接收缓冲区中的数据均是正常的;但是如果发送端只发送 9 个字节,程序就无法正常接收到数据,通过DMA_GetCurrDataCounter(USARTx_RX_DMA_STREAM)获取的数据长度是正确的,但是在程序中数据接收缓冲区却没有数据,全为 0x00。
$ r& c$ \& d" Q, l1 `& K
7 X$ H: W7 q) t仔细观察 DMA 的配置,可以看到这两条配置:: O/ L8 E9 T) p: F8 O
  1. DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;' C- a4 G1 p5 K4 g3 H5 C+ D3 A
  2. DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
复制代码
6 A2 N" Z4 t% r; O  g* P, U
它的意思是打开 DMA 的 FIFO 模式,FIFO 的门限大小为 16 个字节。: k* q+ M9 j6 Q" q, X* i
3 Q7 v7 h) v( n# T# ~2 s
( ]$ D+ I9 o  n( A
修改上述 DMA 配置,如下:
( \5 |1 c2 i3 h" [5 o
  1. DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    # Y6 m, X8 ~4 ]
  2. DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
复制代码

2 g7 z: X7 Z4 {3 P重新运行程序进行测试,现象正常如下:如果发送端发送 10 个字节,程序可以正常接收到数据,通过DMA_GetCurrDataCounter(USARTx_RX_DMA_STREAM)获取的数据长度以及程序中数据接收缓冲区中的数据均是正常的;如果发送端只发送 9 个字节,程序也可以正常接收到数据,通过DMA_GetCurrDataCounter(USARTx_RX_DMA_STREAM)获取的数据长度是正确的,在程序中数据接收缓冲区的数据也是正确的,前面为接收的 9 个字节的数据,最后一个字节为 0x00。6 ]' O( x) M9 E: t

$ }. i5 f4 c8 W8 V/ c2 O
8 n) {3 b3 ]' ?2 m7 p结论:
2 r* q$ H3 U# v) y& m2 t由于客户在使用 DMA 的时候打开了 DMA 的 FIFO 模式,并将 FIFO 的门限设置为 16 个字节。当发送端发送 10 个字节时,由于字节数虽然没有达到门限,但是已经达到 DMA 设置的 Buffer Size,DMA 传输完成,FIFO 中的数据被传输到 RcvBuffer;而当发送端发送 9 个字节时,由于这个数值既没有达到 FIFO的门限,也没有达到 DMA 设置的 Buffer Size,导致 9 个字节的数据仍然停留在 DMA 的 FIFO 中,并没有传输到 RcvBuffer,所以在 RcvBuffer 中就看不到任何接收到的数据。而DMA_GetCurrDataCounter(USARTx_RX_DMA_STREAM)函数返回的是 USARTx 接收数据流剩下的数据单元个数,因为 9 个字节均已经接收到,自然这个数值就为 1,只不过数据还在 FIFO 中,没有送到RcvBuffer 罢了。
% Q" Q' x; R9 j, l7 t, l6 \
) `4 w$ _* X$ k- R! w0 _0 s3 ?) W# Q$ h( u4 D1 @5 x
处理:, ~0 `' L: r) O* O3 u3 H( s- M& k
不使用 FIFO 模式,直接使用 Direct Mode,问题解决。3 f" X" |- |& I( J3 @. K

; h  I  S: o: N* M, z: m, m6 e# p
! ]* K3 Y& T* g建议:4 F4 \' U( j* _! V" _! f+ b, M
除了循环模式和双缓冲模式,以及流控,STM32F2 系列的 DMA 与 STM32F1 的 DMA 相比,还新增了 FIFO模式功能。因为 STM32F1 的 DMA 是“Lite”DMA,只支持直接模式,并且未对带宽使用作任何优化。在STM32F2 上,首先实现双 AHB 主接口,更好地利用总线矩阵和并行传输。另外,为 DMA stream 增加各自的 FIFO,以弥补外设没有 FIFO。每个 stream 拥有各自的 4*32 位的 FIFO。
2 G9 S; ?2 l$ W9 w! J% `# L' O( ]4 z. E; I: y6 e* n
FIFO 的大小是 4*4 字节,传输的过程为:源地址→AHB 主端口 x→FIFO→AHB 主端口 y→目的地址。是否使用 FIFO 门限,可以区别 FIFO 模式和 Direct 模式。FIFO 常用于 DMA 控制器和 Memories 之间的缓冲。特别是 Memory-to-Memory,是必须使用 FIFO 模式的。
  H+ T6 A$ B8 C1 W
6 Y, n4 [2 w2 Q% @& u3 i5 s6 _对于 FIFO 模式和 Direct 模式的区别:FIFO 模式——从源地址来的数据先放在 FIFO 中,达到门限后,再根据目的地址的数据宽度送出;Direct 模式——数据放在 FIFO,有 DMA 请求就送走,和门限值无关。默认 DMA 工作在 Direct 模式下,Direct 模式常用于在每次 DMA 请求后都有立即和 Memory 之间单次传输的应用场合。它的缺点是源地址和目的地址的数据宽度必须一致,不能支持突发传输,也不支持 Memory-to-Memory 的传输。而 FIFO 模式中,源地址和目的地址的数据宽度可以不同,分别由 PSIZE和 MSIZE 指定。当宽度不同时,在 FIFO 中将进行数据的 pack/unpack。另外,可支持突发传输。; \% \0 \: x% p+ a& {: `: T' _

( D8 m3 L6 G- G- R  u, f+ d. d使用 FIFO 模式时,会存在剩余“尾巴”数据的问题。什么情况下 FIFO 中会有“尾巴数据”呢?当NDTR 还没有自减到 0 的情况下,不再有数据进入 FIFO;并且现有数据还未达到 FIFO 门限的时候,将会有“尾巴数据”留在 FIFO 中。此次的问题正是这样的情况。那么尾巴数据怎么处理呢?我们可以通过 FIFO 刷新来解决这一问题,步骤如下:
& O2 E4 h- k: \9 Q- g# }4 |9 n+ L9 `1. 通过复位 DMA_SxCR 寄存器中的 EN 位来禁止数据流,可以刷新 FIFO。这个时候,DMA 控制器会将剩余的数据继续传输到目的地址中,即使已经有效禁止了数据流。
% l) N4 ^! k% B  c1 M7 {) M2. 传送完成后,硬件置位 TCIFx(传输完成标志)。3 p; U6 Q+ z" {9 c1 ^
3. DMA_SxNDTR 中还保持刷新 FIFO 之前的值。$ U& j5 Y) S; i. @/ k
a. 如果剩余数据少于 MSIZE,DMA 会以 MSIZE 的数量来传送数据。所以,Memory 中会被多写入不需要的数据,因此需要参考 DMA_SxNDTR 来决定哪些才是真正需要的数据。4 }( V% z9 P" B) W' e0 G( l# V
b. 如果剩余的数据不足一次 Memory Burst 传输的数据量时,可以使用 Single 传输来完成 “尾巴数据”到目的地址的数据传输。) \, T, t' z0 T

  O9 T# Z( i. b) @1 e0 n, W% _  H0 F0 h: `. z* L
收藏 1 评论1 发布时间:2022-2-27 16:07

举报

1个回答
伊森亨特 回答时间:2023-10-10 13:27:27

什么级别的客户才有你们的支持啊

所属标签

相似技术帖

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