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

STM32 DMA经常遇到的几个问题

[复制链接]
yumeii 发布时间:2019-12-27 13:30
1、概念上的误解
3 A/ K" A1 B" m7 ^' h
5 i0 \; `8 K. z$ w+ C. r' v
: P1 w: A# W  i; [& N9 r
我们知道DMA传输是在DMA请求下,将数据从源端传输到目的端。* u! h$ s# G- ~
- E( F7 q. e2 R% y8 [/ d1 A
常有人将DMA请求跟DMA的源端或目的端混为一谈。这里,我们可以将DMA传输类比成收发快递,发件方即DMA源端,收件方即DMA目的端,而DMA请求端就是呼叫快递的人。这个呼叫快递的人可能是发件方、也可能是收件方,还可能是另外第三方。比方你要发个快递,叫快递的人可能是公司的前台美眉。
$ |" W" o( ~) m' m, ^0 G
% ~$ I$ L! r$ f

6 k# X) v2 ?5 T, ~: B. x9 g5 g具体到我们STM32应用,比方通过DMA将内存数据传输给UART DR寄存器发送出去,源端是存储相关待发送数据的内存区域,目的端是UART DR数据寄存器。至于DMA请求,可以是UART发送空事件【TXE】,也可以是定时器的某个周期性触发事件等。( F) ~3 X& u% a% V

5 X2 w2 T9 h+ g$ M/ c, Z* d在STM32各个系列的参考手册的DMA章节部分,都有类似如下的DMA请求映射表。表格里填写的都只是针对各个DMA传输流的DMA请求事件,并非一定是源端或目的端。当然,不排除作为源端或目的端同时又担当DMA请求角色的可能。
" R; X, m- m& d$ k. q8 D- z( L9 {1 Q) n% D
. ~' z5 Q2 ^4 W2 k4 u
1.png
  b( F4 G; v5 v: w4 F" l
% @' y2 H3 U1 t
# U  k' ^1 G) v+ A9 H% X% I4 u
2、配置上容易忽视的问题6 X4 ^; j9 w! P, ?$ t
' R, M7 W( W7 a% H, I
/ v* F) M+ A& @$ T, O4 e, z; |- O
我们在做DMA配置时,比较容易忽视两个小问题。第一个就是源端和目的端的数据宽度的定义问题,除了考虑配置满足实际需要的数据宽度外,还要注意将源端、目的端二者数据访问宽度配置一致。不然的话,往往会导致些奇怪的问题。$ ^' e$ {4 X. }
0 h$ T9 o; }4 i: i$ z$ o

/ {& n3 q: ^' t+ o2 N6 O 2.png 4 j' l. p, R) t3 i
5 g! o$ V; U) M; E* J( T$ S
( i3 r2 ^' I4 G8 |7 y( P6 ]* c% I. J
还有,就是要注意DMA的传输方向别弄错了,到底是PERIPHERIAL到MEMORY还是MEMORY到PERIPHERIAL或者说是Memory到Memory要配置正确。尤其是在用CubeMx配置时,这里有个默认配置是PERIPHERIAL到MEMORY。如果说你的真实意图根本不是从PERIPHERIAL到MEMORY,而你无意中使用了这个默认配置,结果可想而知,DMA传输根本没法正常运行。我前两天就被这个地方折腾过,用定时器触发DMA输出任意周期性的PWM波,白白多耗了近2个小时。此时,在调试环境里想强制使能相关DMA Stream的传输都不行,整个就不工作。
- H8 b3 w8 V' ]1 k
+ }& K) g4 ^* Z/ J' F  R7 o# L

( ^1 ~0 \. O' d. O1 }" h6 [+ h 3.png 0 X( c) y, }% m4 W: U

) K. P% ]6 Z/ }$ J: A9 K, c: y( s
5 M3 I" r& f  I2 p) @) ~
类似配置方面的小细节要多加注意,忽略了那些往往会累死人。
2 o! o+ }# G) f7 F2 y3 L( k8 `3 q$ |- r/ @" L3 }4 b- h+ P8 Y
3、DMA传输作用范围问题2 s5 w. D( t5 {
6 A0 L8 g, i! w  G9 F1 {' o" B' }
$ l1 a# G7 x5 {: ]# s. x! R+ z' u8 \
前面将DMA传输类比成发快递,发快递时,快递公司一般也没法无处不到,那DMA传输也有同样的问题,各个DMA模块往往有各自的服务范围。比方以下图STM32F4的一个框图为例。DMA1可以轻松访问右边黄色标注出来的外设,DMA2可以访问左边分数标注出来的各个外设。如果我们在程序里的DMA配置部分,将DMA1的源端或目的端安排为左边的粉色标注出来的外设、或者将DMA2的源端/目的端安排为右边的黄色标注出来的外设,结果一定会让你失望。因为你期望它访问它到达不了的地方。
- u+ x" C" B) r% M% A; h  Z9 F3 |& E& M, o2 @

+ s) u! g+ r) f, W: y' e5 U 4.png
$ a3 @. n8 E1 k
% W3 Q" x/ u1 y+ s7 B% E

& L3 V) p- }) `9 t$ u这个地方比较隐蔽,因为我们在代码里只是根据DMA请求自行指定源端和目的端,有时会忽视所用DMA的服务范围。前面提过,各个STM32系列参考手册里DMA章节部分都有明确的基于各路DMA传输流的DMA请求事件源的描述和展示,但并未指定基于各个DMA请求的源端或目的端。所以我们在基于某个DMA请求来自指定源端或目的端时,一定注意你安排的源/目的端是不是该DMA可以到达的地方,具体要结合手册中功能框图和总线访问架构图。
  x+ w; A3 m4 e  S# w! q4 _1 W; m+ f3 X+ n* I. _4 n4 ~

7 m8 K$ R6 x  J! G7 U比方说,下图是STM32F7系列一个总线访问框架图。不难看出,DMA1是访问不了AHB1外设和AHB2外设的。当然,DMA2访问AHB1外设和AHB2外设又没有问题。0 u% f5 U% z( w& Y- C
2 S  ?' P* C2 p5 v
0 z2 P; v" k. O% E, o
5.png
7 w/ V( Y( [: ]& ^, o) {- R
5 r( N/ A5 B6 ~- f
+ z3 Q* C/ n9 g
再比方,下图是STM23H7系列一个总线访问框架图,其中BDMA是没法访问D1域或D2域的外设及内存的。D2域的DMA1/DMA2没法访问D1域中的DTCM/ITCM。D1域中的MDMA没法访问D2域中的AHB2外设。; ~% j$ g3 F1 x: e$ e0 b
3 E' {# ~/ f+ z# g: D4 x9 ~7 {

5 Q7 c( r1 {$ i1 u2 V* M2 l 6.png
: v0 F. A9 ~7 K7 e. x" z
+ X" W, X' c5 b/ r* Q5 [+ a
+ `4 _* q% m9 @/ C; M# ]' x
关于这些总线框架性的东西,在我们的STM32应用中也要多加关注。比方有时在做通信数据传输时发现,使用中断没问题,用DMA就失败。这时不妨查看下DMA访问的外设或内存区域到底是不是它所能访问得到地方,如果不是就需要适当调整下。' t1 m8 F% n1 Y) V0 P+ d! T7 \' y

' i+ V, m7 L' z# [1 C

  \) m$ [$ T: u. z4、跟DCache有关的问题
. m7 R* f3 Q( U& g8 a, f! ~8 X4 n7 f6 l

$ p7 r# K& [: }& _/ j2 ^" F该问题往往跟我们使用带cache的M7内核的STM32F7或STMH7系列芯片有关,使用DMA传输时有时会遇到DMA访问到的数据不是实时的正确数据。这往往可能是因为DMA要访问的内存区域,跟CPU是共享的,同时又开启了相关区域的D-Cache属性,即CPU访问该内存区域数据时使用D-Cache,将内存数据拷贝到D-Cache。之后,CPU访问相关数据时往往只在Cache里进行。
6 y1 p* ]) d$ p) l& i  m) \( B- z6 w0 m) j8 }
比如下面的一个基于STM32F7芯片的经典示例。首先CPU从flash里拷贝128字节常量数据到片内SRAM,然后通过DMA将SRAM里的这128字节数据拷贝进DTCM内存区。最后通过CPU将DTCM里的数据跟FLASH里的原始数据进行比较,这时会发现比较结果是二者内容根本不一致。: }- \' G, c5 f  n

; v/ o; J* ]! Z! a# E

# j0 e+ m: U3 n0 l1 |7 t这是因为开启了SRAM的回写的Cache属性,第一次将数据从flash拷贝进SRAM过程中,数据还没有真正写进SRAM,还只是放在了Cache。当我们通过DMA将SRAM相关区域的数据拷贝进DTCM内存区时,并没有将真正的来自FLASH区的数据拷贝过去,导致最后的数据比较失败。% a- Y3 O! }: q
: A3 T3 {. s  \0 @& Q. u  X

7 M: y/ u5 Z- i7 @/ S 7.png 7 F- L& z, h* e0 J3 L& D
3 p9 x6 P- X) q; }

3 e2 L) t) I# }7 _+ W像这种情况下,我们可以有几种方案来解决这个问题:
( L" G3 E9 y1 H  ]8 g3 l' y9 }% E

# O) }" T6 h+ Z1、在做将数据从SRAM拷贝到DTCM区之前,先做个D-Cache的清除操作【SCB_CleanDCache()】,将D-Cache里的数据写回到实际存储区SRAM里。
' S( Y# M* {; e& K, N
$ i7 o/ `8 f, u2、我们可以通过MPU设置SRAM区域的MPU属性,将其回写的Cache属性【writeback】调整为透写的Cache属性【writethrough】。
* K! O8 I- R- Z9 f9 d1 V5 _3 w/ m! s0 ?+ S7 K2 ?9 v
3、配置SRAM的MPU属性为shareable共享属性,令CPU访问它时不使用D-Cache., c# s. R: Y6 H- w- o: ?9 T: W0 ]9 b. i: }

$ n; D+ d5 H/ g1 @+ O; X& ~4、将所有涉及到具有Cacheable可缓存属性的存储区域,都使用透写策略。这点可以通过配置M7内核相关控制寄存器位实现。4 A8 E6 K; C* F, ~: F4 Z0 `

. w) r$ l" k+ s7 b* G" j1 @再举个实例,使用STM32F7芯片做UART的通信数据接收,UART接收到数据后触发DMA,DMA将数据从UART_DR寄存器拷贝到片内SRAM,CPU再从相应的SRAM区取走数据送到某LCD显示设备显示输出。这时,你很可能会发现一个奇怪的现象,显示设备输出的数据永远都时第一次接收到的数据,不管UART的发送方任何改变发送数据,显示出来的数据就是不变,只是跟第一次发送出来的数据相比是正确的。这是怎么回事呢?
5 g# P8 @+ i/ }9 l7 P! D; D9 e/ U& g' m" }% J7 q
$ w1 d# M; [* W4 \) \
8.png
5 e& d3 B  _8 [0 W9 p+ X% t, A, E4 {( ~# j
2 p* q2 ]3 b3 ^' j7 K
原因是因为开启了SRAM的D-Cache属性,CPU第一次从SRAM读取数据后,同时又将数据放到D-Cache里,后面再来读取SRAM相应地址数据时并没有前往SRAM,而是直接区D-Cache里提取数据,从而导致每次显示出来的数据总是第一次接收到的数据,尽管UART那边后续接收的数据在不停变化,但并没有对D-Cache里的数据做同步更新。' e& z/ {" Z. \$ L- w+ |4 }' L
3 q) T1 Z8 Y0 A! d  t+ z5 f
这时我们可以在CPU读取SRAM数据前做个D-cache的清除操作,让实际存储器数据与D-Cache里数据同步更新,或者做D-Cache的失效操作,让CPU无视D-cache直接从SRAM区读取数据,或者说通过MPU配置禁用该SRAM区的Cache属性。当然,最终你选用哪种策略,得结合你的实际应用来定。
5 _6 \6 A. Y- K* T# H6 t
4 p6 F# t* G4 n
赞 2 收藏 2 评论4 发布时间:2019-12-27 13:30

举报

4个回答
wolfgang 回答时间:2019-12-28 08:14:42
研究得深入啊~~8 @, R1 G: ~* G* s- E
赞一个
yumeii 回答时间:2019-12-31 15:33:14
wolfgang2015 发表于 2019-12-28 08:14
8 }5 {4 A4 I5 D" f: y- s研究得深入啊~~6 N6 Q+ T! @! K# m* L4 e" f# `
赞一个

9 I- M5 T  J1 A& O9 _; b) y谢谢支持
伊森亨特 回答时间:2024-1-5 10:32:47

第2点,宽度可以不一样啊,需要使用FIFO,

YK1 回答时间:2024-9-23 21:12:33

请问一下,使用DMA双缓冲区进行数据的发送的时候,出现了重复发送数据的情况,请问该怎么解决呢?感觉主要是剩余文件数量达不到BUFFER_SIZE要求,带来的重复发送数据。

所属标签

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