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

STM32 DMA经常遇到的几个问题

[复制链接]
yumeii 发布时间:2019-12-27 13:30
1、概念上的误解6 o) a  t* k) t7 }( P& s

! B) F; V: ?  ?6 ^  e

: D5 r- h6 o2 u2 c( }: J9 S我们知道DMA传输是在DMA请求下,将数据从源端传输到目的端。( l3 N/ t  V! D1 P

0 A$ ?! M" u9 [! b( a0 Q常有人将DMA请求跟DMA的源端或目的端混为一谈。这里,我们可以将DMA传输类比成收发快递,发件方即DMA源端,收件方即DMA目的端,而DMA请求端就是呼叫快递的人。这个呼叫快递的人可能是发件方、也可能是收件方,还可能是另外第三方。比方你要发个快递,叫快递的人可能是公司的前台美眉。
8 T" q+ M% n2 Z. e4 ]: |
4 L$ v( m+ s: v) M1 d$ S

/ e0 q& M* @, C# P) K. x/ _9 W' \具体到我们STM32应用,比方通过DMA将内存数据传输给UART DR寄存器发送出去,源端是存储相关待发送数据的内存区域,目的端是UART DR数据寄存器。至于DMA请求,可以是UART发送空事件【TXE】,也可以是定时器的某个周期性触发事件等。
3 L1 j- m  D! t7 t! T# q* U7 T$ q4 }$ g
在STM32各个系列的参考手册的DMA章节部分,都有类似如下的DMA请求映射表。表格里填写的都只是针对各个DMA传输流的DMA请求事件,并非一定是源端或目的端。当然,不排除作为源端或目的端同时又担当DMA请求角色的可能。
  b/ o4 l3 F# G# z" E8 b9 e7 l& e1 o7 ~( d7 i
. r9 o3 ?+ r' v& W2 [) w' o$ b* O
1.png . z# L, B5 I# w
: J: N; T: \# A; K  v2 I) N
! Y$ r. X4 s# I: N5 c
2、配置上容易忽视的问题
  n$ V1 ?  h/ k; t' C/ L' Q0 b+ ^* X
# h- M5 U0 m0 d% _" ]
+ F4 O3 {6 ^$ m( U5 p
我们在做DMA配置时,比较容易忽视两个小问题。第一个就是源端和目的端的数据宽度的定义问题,除了考虑配置满足实际需要的数据宽度外,还要注意将源端、目的端二者数据访问宽度配置一致。不然的话,往往会导致些奇怪的问题。
  o- x/ `4 d5 j. T7 _# Y! f( }3 ]( g) Y5 F" Y6 m8 N
0 t# ^. w1 a1 C  z2 }3 b
2.png # }3 K' m6 o; L' E

5 d7 q- k$ @3 a6 C
" ]# y3 r! f( }8 [! }* {9 @5 g
还有,就是要注意DMA的传输方向别弄错了,到底是PERIPHERIAL到MEMORY还是MEMORY到PERIPHERIAL或者说是Memory到Memory要配置正确。尤其是在用CubeMx配置时,这里有个默认配置是PERIPHERIAL到MEMORY。如果说你的真实意图根本不是从PERIPHERIAL到MEMORY,而你无意中使用了这个默认配置,结果可想而知,DMA传输根本没法正常运行。我前两天就被这个地方折腾过,用定时器触发DMA输出任意周期性的PWM波,白白多耗了近2个小时。此时,在调试环境里想强制使能相关DMA Stream的传输都不行,整个就不工作。1 T+ t! D6 _" |, i# ]/ T: y
2 o5 P8 [4 J8 F

& l3 H: q' a5 ?5 D0 _ 3.png
1 H4 v1 }5 B8 d6 _& M" o( L% N$ \/ k( P5 U( l4 [) M/ ]8 ?: y9 P

* t% G4 K0 {6 l% S, O类似配置方面的小细节要多加注意,忽略了那些往往会累死人。" p! ?) C# w+ B- C1 q

* ], R$ _1 v- N& _  a3、DMA传输作用范围问题. l/ S+ ?" x8 M% Q

$ I- |/ I# `! ~0 `4 k  q4 P

/ G6 m9 v8 e* S4 _- V6 C: ]前面将DMA传输类比成发快递,发快递时,快递公司一般也没法无处不到,那DMA传输也有同样的问题,各个DMA模块往往有各自的服务范围。比方以下图STM32F4的一个框图为例。DMA1可以轻松访问右边黄色标注出来的外设,DMA2可以访问左边分数标注出来的各个外设。如果我们在程序里的DMA配置部分,将DMA1的源端或目的端安排为左边的粉色标注出来的外设、或者将DMA2的源端/目的端安排为右边的黄色标注出来的外设,结果一定会让你失望。因为你期望它访问它到达不了的地方。( q6 y9 b4 G7 Y. y3 k3 K) T; J' D& r: i

' [  F$ m9 f* f* V
* _8 b$ k. Z- h( x
4.png
7 u, a. Y7 X2 H; Y/ F4 f7 Q' v3 C7 P8 W9 \1 s! a% n
* S+ o' Q0 n5 H- B# V- V
这个地方比较隐蔽,因为我们在代码里只是根据DMA请求自行指定源端和目的端,有时会忽视所用DMA的服务范围。前面提过,各个STM32系列参考手册里DMA章节部分都有明确的基于各路DMA传输流的DMA请求事件源的描述和展示,但并未指定基于各个DMA请求的源端或目的端。所以我们在基于某个DMA请求来自指定源端或目的端时,一定注意你安排的源/目的端是不是该DMA可以到达的地方,具体要结合手册中功能框图和总线访问架构图。7 H$ U: C5 b; [& I/ _

& L* p' q% z/ f. z  @: y

0 ?# h) d3 T- W( T- K) }9 s: T比方说,下图是STM32F7系列一个总线访问框架图。不难看出,DMA1是访问不了AHB1外设和AHB2外设的。当然,DMA2访问AHB1外设和AHB2外设又没有问题。$ ~  I7 c- c5 U6 Z& Z( X% [

. V. e0 Z; [8 L, j
: L3 [" D) u/ [" Z7 {4 l9 \
5.png : K/ X/ |$ ~% ]( M2 [4 C* d

, w  U/ h! m$ j2 }
% `+ W& l! d* Y
再比方,下图是STM23H7系列一个总线访问框架图,其中BDMA是没法访问D1域或D2域的外设及内存的。D2域的DMA1/DMA2没法访问D1域中的DTCM/ITCM。D1域中的MDMA没法访问D2域中的AHB2外设。
5 Z' O" Q( p( Y0 F
3 I! u+ F6 |3 \9 V0 M8 J

" F) v, w8 m& c+ ^  ~; G 6.png 9 K! I8 y4 \! U. }: l) Z1 ?+ i

# u! {9 r- Y; i. z$ k6 }

3 F8 G) z' Q- c; F0 Y6 l关于这些总线框架性的东西,在我们的STM32应用中也要多加关注。比方有时在做通信数据传输时发现,使用中断没问题,用DMA就失败。这时不妨查看下DMA访问的外设或内存区域到底是不是它所能访问得到地方,如果不是就需要适当调整下。5 Z( z2 R: n  ^, N7 ]3 B7 Z: b" p7 b

; J9 {4 }0 c; V4 L: t

; I, W: ?" U) `5 L4、跟DCache有关的问题: G7 N2 ~8 m; {6 A( m1 R

, x3 j6 s7 R: R  d5 h1 Y! \

6 `1 h0 n$ U' V! X该问题往往跟我们使用带cache的M7内核的STM32F7或STMH7系列芯片有关,使用DMA传输时有时会遇到DMA访问到的数据不是实时的正确数据。这往往可能是因为DMA要访问的内存区域,跟CPU是共享的,同时又开启了相关区域的D-Cache属性,即CPU访问该内存区域数据时使用D-Cache,将内存数据拷贝到D-Cache。之后,CPU访问相关数据时往往只在Cache里进行。" t1 s. }' E( {2 h' I+ y
; i! s. s6 z  ^  ?: A. S
比如下面的一个基于STM32F7芯片的经典示例。首先CPU从flash里拷贝128字节常量数据到片内SRAM,然后通过DMA将SRAM里的这128字节数据拷贝进DTCM内存区。最后通过CPU将DTCM里的数据跟FLASH里的原始数据进行比较,这时会发现比较结果是二者内容根本不一致。
+ N0 l0 G1 C+ B8 j  F  T3 H' I  i
5 J& j" M2 H/ f# G5 ?

3 l$ K* @* V! e6 J这是因为开启了SRAM的回写的Cache属性,第一次将数据从flash拷贝进SRAM过程中,数据还没有真正写进SRAM,还只是放在了Cache。当我们通过DMA将SRAM相关区域的数据拷贝进DTCM内存区时,并没有将真正的来自FLASH区的数据拷贝过去,导致最后的数据比较失败。) Z: Z, A1 S1 v8 m0 q

/ h; U8 |* E- c! @& b
# g; E0 R1 L3 U7 _
7.png 0 D2 D" @8 w( l. J6 P9 h6 K
  \7 ~' R9 W% E
+ o: K5 t7 I4 W8 f
像这种情况下,我们可以有几种方案来解决这个问题:
! b( L% V+ C7 u; R. {  ?: Z: K# L! q) I9 S/ ?& H
1 K( _6 u  W' W
1、在做将数据从SRAM拷贝到DTCM区之前,先做个D-Cache的清除操作【SCB_CleanDCache()】,将D-Cache里的数据写回到实际存储区SRAM里。
) f# ~# o- n& n; a9 }
& {5 M" }9 @* Q2、我们可以通过MPU设置SRAM区域的MPU属性,将其回写的Cache属性【writeback】调整为透写的Cache属性【writethrough】。
9 ]. K) t8 M3 L# s. w) S/ A- O! q, }& H
6 g1 W0 {. }' q4 a+ |3、配置SRAM的MPU属性为shareable共享属性,令CPU访问它时不使用D-Cache.) W1 C8 W7 k0 ?( n$ Q( g

- D3 }" n: [. J) S5 h. G& e6 D# }4、将所有涉及到具有Cacheable可缓存属性的存储区域,都使用透写策略。这点可以通过配置M7内核相关控制寄存器位实现。( A% u3 X; U8 L
) f+ @! f9 t& O
再举个实例,使用STM32F7芯片做UART的通信数据接收,UART接收到数据后触发DMA,DMA将数据从UART_DR寄存器拷贝到片内SRAM,CPU再从相应的SRAM区取走数据送到某LCD显示设备显示输出。这时,你很可能会发现一个奇怪的现象,显示设备输出的数据永远都时第一次接收到的数据,不管UART的发送方任何改变发送数据,显示出来的数据就是不变,只是跟第一次发送出来的数据相比是正确的。这是怎么回事呢?  S- @4 f% C6 g9 t# @

2 m8 a3 M0 u% B! Y* e1 s6 F
# ]- Y% |9 V- ?
8.png
: r. l4 ~- Z1 _$ H' w- z5 O" z  N* A+ L* J

& F2 r8 X9 k3 a% |原因是因为开启了SRAM的D-Cache属性,CPU第一次从SRAM读取数据后,同时又将数据放到D-Cache里,后面再来读取SRAM相应地址数据时并没有前往SRAM,而是直接区D-Cache里提取数据,从而导致每次显示出来的数据总是第一次接收到的数据,尽管UART那边后续接收的数据在不停变化,但并没有对D-Cache里的数据做同步更新。1 Q7 @& M$ [( ~# M& Y( J3 C0 P' D
( L- T9 B0 N% v9 v; h7 W$ @& {
这时我们可以在CPU读取SRAM数据前做个D-cache的清除操作,让实际存储器数据与D-Cache里数据同步更新,或者做D-Cache的失效操作,让CPU无视D-cache直接从SRAM区读取数据,或者说通过MPU配置禁用该SRAM区的Cache属性。当然,最终你选用哪种策略,得结合你的实际应用来定。: J; A) x. n5 w/ B! Z9 ^* k! |5 V7 h8 Z

! W) V. {! p) w. p
2 收藏 2 评论3 发布时间:2019-12-27 13:30

举报

3个回答
wolfgang 回答时间:2019-12-28 08:14:42
研究得深入啊~~5 w% W9 N+ X, u; n/ I' s+ j
赞一个
yumeii 回答时间:2019-12-31 15:33:14
wolfgang2015 发表于 2019-12-28 08:14
5 M2 l$ O' A% L$ l研究得深入啊~~& k" I6 F7 r2 t% J/ I, p
赞一个

0 N$ L* H" z7 [+ {2 s! `谢谢支持
伊森亨特 回答时间:2024-1-5 10:32:47

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

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版