一、为什么要用DMA?2 B% }2 z @- {- h4 T; H% Z
' U+ w! \( q) q: x- V! WDMA 全称:Direct MemoryAccess 就是可以直接内存存取;8 y9 ]5 |; o, p2 |
' Q _; l! C" _5 }' W5 C; b. p
正是它可以直接操作内存所以具备以下优点:; }4 r) Y ^8 X9 r0 T
C4 [6 {* P4 N r, X. ~
而无需经过CPU去操作内存的存取,这样可以解放CPU出来干其他的事情;
. G8 |0 Y8 a# ~% ]* ^7 X0 j; C" P) z
因为他可以进行存储器时间的数据传输,而不需经过cpu,所以大大加快了数据传输速度—是一种高速的数据传输;
2 [3 Y/ U9 p( L9 p, k9 \$ Q/ }! m8 \# } d" p
- \2 _/ V0 H) T. T
& s: ?) ~ B7 m7 x
二.DMA有几种传输数据方式:
6 [0 s. j2 ~ F* u. N, Q, ~# P! h. F# \: m
(1)内存到 内存之间的;即:SRAMßà SRAM
& ~( o% `9 w6 ~: {
4 M: \' O( u4 d/ ] y+ i* m' ^( P(2)内存到 外设之间的; (例如:串口收到的数据 从数据寄存器 à 内存)% o- h" V. h& \4 V4 c
$ X9 Y1 E3 W) d1 V1 _5 F- n(3) 外设到内存之间的;$ R B7 V) W7 Z
. @+ L, \3 z% t3 v+ K: j
3 S& s% X4 T" Q5 }. W
) [7 f: d0 c3 c/ l4 R' ~( g三.传输的数据宽度是怎样的,数据是什么样的形式传输?DMA 能传输多大的数据量?
D- o S. c0 y0 L# T" X3 v" w% @% J S& I" y3 Y7 A
A. 数据源地址到数据目的地址 传输宽度或者说传输数据的形式,有几种:1)字节;2)半字;3)全字 [1字节=8bit 1半字=2字节=16位 1全字=2半字=4字节=32位]: ?5 v5 A1 F. w4 D3 D6 D& ?
& Z4 @/ ]' b& J" j8 e
B.传输的最大数据量是655369 T, }4 i8 G! @( E' `. M5 T
, T" g7 E5 E+ j. p" z
我们来看一下stm32f103ve的数据手册第九章DMA 大概的了解一下DMA的特性:
, D& |4 [- g0 V! I5 I% E3 R
3 l, Y; [/ w1 D
. l" v7 \: Y t- J# B3 h
6 u; K4 B0 T' W% N+ @
8 I* p3 p2 X& I1 |. M/ `
% N) }, e5 q! \/ f( [- V( l四、怎样配置软件来使用DMA?* d9 N* U3 Z3 \5 Q& s0 \* M
4 w7 G" @5 K5 a
(1)配置dma' s+ |- \( x) q* V) U; P7 I, b+ q
* B" a. e5 w; [4 q* p- /***************************************************************** ; p$ f' B! R9 N
- *函数名称: Dma_Init 6 C- n" _9 I* C t( G8 S) z! j3 p
- *功能描述: 利用DMA 把内存的数据 传输到flash 达到高速传输的目的0 T7 [+ o3 ^' Z/ J( ?/ E- P7 {! V
- * ' R7 o# E, F! ?' q4 ~
- *输入参数:无 , I' e r/ _- y: e" v1 o, U
- *返回值 :无+ M# G, g/ a0 ]3 E$ |
- *其他说明:无
6 F1 L( Y; M8 j+ o% F! ^& q! g - *当前版本:v1.0 7 y) {& U/ T0 Z: k
- *作者 :尹宣 $ {4 B% S/ O& }3 W3 B/ n( E
- *完成时间:2013年12月1日 " j2 C5 ^: H y
- *修改日期 版本号 修改人 修改内容
) M* D- h' o# E# U3 n; V - *----------------------------------------------------------------- ; m- T& F5 \ g2 ?% o* I2 m! L
- *
1 A, k1 U7 m' N4 X, L: M - ******************************************************************/
1 X! k; c# S; {9 }3 D) | - void Dma_Init(void): n" t8 R( q1 R) z$ h+ X
- {
0 e# H0 O4 a N3 U/ [% V# W" p - DMA_InitTypeDef DMA_InitStructure;, ~: H5 y U/ @9 R: h
-
8 N( ], V9 y# z8 ~0 S2 U& Q - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);3 q; B& B z; h) P! i$ r4 L
- /* DMA channel6 configuration */# m. v) C2 Q' y8 U: E6 H! @
- DMA_DeInit(DMA1_Channel1);4 J+ a& Q! R6 w$ ?9 T
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SRC_Const_Buffer; //外设地址
$ j* p3 S) @1 C+ | - DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DST_Buffer; //内存地址
: K- J+ k; T' k6 o - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为DMA的源端 DMA_DIR_PeripheralDST; //外设作为目的地址
; G0 b0 X+ E/ ~( _ - DMA_InitStructure.DMA_BufferSize = BufferSize; //传输大小
7 V: s1 u0 O7 O0 Q9 U4 Z# ~ - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //外设地址增加$ O" r/ q! l2 t; P( U" M
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增使能
( @: g) O/ E" J. K4 D - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //内存存储方式:字节 DMA_MemoryDataSize_Word;//字(32位). X) f- x3 k9 \ F' M0 x
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
6 A$ m* @# h; o! ~& Y - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA_Mode_Normal 正常模式,只传送一次; DMA_Mode_Circular:循环模式,不停的传送;! ^, l6 n7 T$ |2 `4 [6 ^2 H
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;5 u0 s* |( a, {' N. `. H. B. C
- DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
4 p; [2 O1 ]' [3 `; V. W o - DMA_Init(DMA1_Channel1, &DMA_InitStructure);0 ^" l* t) L2 p% [
/ j! l; t" r; Y/ s6 O2 P/ Z" f- /* Enable DMA Channel1 Transfer Complete interrupt */! S" _* R* E* c; [& M* {6 z
- DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
: j% m7 L- a& d, K8 S% W -
" ]. `7 \5 W4 m+ S - /* Get Current Data Counter value before transfer begins *// o G& w4 Z* e
- CurrDataCounter= DMA_GetCurrDataCounter(DMA1_Channel1);
/ {; K3 X/ M( K) I5 p - 7 J( x" {6 N3 ^/ C: m8 A* l
- /* Enable DMA Channel6 transfer */" G: N. o4 _7 u5 ^! r) X6 `
- DMA_Cmd(DMA1_Channel1, ENABLE);7 U- B0 @- F A4 O% r3 q: _
-
! Q' R' t! [) B @. U - }
复制代码 & x' W* s# z! m: Y
2.设置DMA优先级 W9 r V! P% D4 I
" r' K, S% m, j2 W8 f- /*****************************************************************
9 u- J3 ~/ @' y8 h, _2 c9 J - *函数名称: NVIC_Config . b1 }2 c h4 d# A
- *功能描述: 配置DMA的中断优先级
5 W3 t1 _; {% F2 d6 @& f7 a! `; s: \ - *
1 v8 J/ Y0 X. @! d* ^" t$ M8 k" k - *输入参数:无 & q' Z" }- P; P$ g# ^8 x
- *返回值 :无8 ?: `( _, G$ V9 g
- *其他说明:无
+ }; k; P9 X! l- G9 Z - *当前版本:v1.0
7 t8 S$ o- c$ ~: L! U - *作者 :尹宣 0 F& d$ v2 `& M4 J3 A! F) c
- *完成时间:2013年12月1日 ) E8 i, Q" C4 k. b
- *修改日期 版本号 修改人 修改内容 ' P, y8 t; L8 y$ }2 E" X- H' L2 l% m
- *-----------------------------------------------------------------
' [8 p# `; I% R# e* ^! | - * * M H: W5 j0 \ }1 p8 ?! v5 L$ D
- ******************************************************************/
' K9 `% C; `. j1 |9 J/ h) o - void NVIC_Config(void)2 |) q2 N; Z/ p* V7 x9 e
- {
! y$ C, R) G0 s. W# |1 K - NVIC_InitTypeDef NVIC_InitStructure;
( ~2 j4 g8 u) p8 Z* K& q9 {1 K - " t' |, R; k: s8 e. l ~7 b
- /* Configure one bit for preemption priority -------------------------------- */
0 ?2 N! @4 Y5 q0 i - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);6 H7 G! b& D6 ?; ]0 r+ n
; ~! a1 A/ n, I- /* Enable DMA channel1 IRQ Channel */ D9 O: C u4 e
- NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
7 b, { E1 m7 E2 k8 O3 F - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
" Q- m4 G8 Q# X* {; G6 x4 ^ - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;2 L P, K, Q9 o, R5 x' K" E9 R
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
$ D! Q5 w' w2 @5 }, ^ - NVIC_Init(&NVIC_InitStructure);" _: x, F. [/ `. R$ p0 B* S1 t# f9 g3 r0 a
- }
复制代码 # {. m7 d: H# T7 h7 J) w
3.在stm3210x_it.c 文件中添加 DMA中断处理函数5 _/ F. R( ~5 o0 W! Z, _+ T
) C2 V3 r( Q; ^+ D( o# a3 y
- /*******************************************************************************
( L& V: g3 }# U3 w3 q) w: | - * Function Name : DMAChannel1_IRQHandler6 f* }" f1 z7 }5 B5 Z' A, X
- * Description : This function handles DMA Stream 1 interrupt request.
4 ~- q& B- ]7 I# O - * Input : None
, v* A) G$ [. s9 F% G/ f - * Output : None1 N. w; C( K# a' k& ?
- * Return : None5 n y+ y* N! o* s4 G# o
- *******************************************************************************/! f) t; J7 O: u/ {- D
- 1 h; X+ B1 l/ @# z B z0 `
- void DMA1_Channel1_IRQHandler(void), e8 p. Q$ T' L- a' z
- { ) Z( B# L& J; ^: d
- /* Test on DMA Channel1 Transfer Complete interrupt */
& W$ a1 w5 C0 y- M7 S7 ^' I! ^7 I: X' W - if(DMA_GetITStatus(DMA1_IT_TC1)) //DMA1_IT_TC1:通道1传输完成中断5 h7 M# M+ d. D2 R3 I
- {1 M8 u3 z! B, k3 L, `
- /* Get Current Data Counter value after complete transfer */
& @+ h5 z0 Q6 Z1 S2 W, F - CurrDataCounter= DMA_GetCurrDataCounter(DMA1_Channel1); //返回当前DMA通道1 剩余的待传输的数据数目" |' V4 |% G+ U4 H+ j6 T0 o
- /* Clear DMA Channel1 Half Transfer, Transfer Complete and Global interrupt pending bits */7 d! R5 D7 u; J4 U/ V
- DMA_ClearITPendingBit(DMA1_IT_GL1); //清中断1全局中断
. w, ]+ u, w$ f/ f - }, Y) N S& F9 j( N8 d ~+ }
- 8 V( m, L' Y: w1 e
- }
复制代码 4 [4 W A! v' B/ G1 `* c
4主函数部分:
6 G! d$ a Z8 N S% v. L7 m1 k% O2 m4 D$ ~6 i
- int main(void)
! O0 J% B/ k; ~+ Q' {6 Y$ d - {
7 \ q$ _8 G/ k+ t/ }1 J - // int count;+ g% Q3 ]8 \5 M( c- \6 P3 d
- uint32 judge;
0 p% A# \) G. D8 F - / a! `7 |7 N/ i5 g, e
- NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x00); // NVIC_VectTab_FLASH=0x08000000
# B/ c3 v ?8 \7 p* Z7 D) Z - // RCC_Config();9 O6 b, H+ z; k+ {# P
- SysTick_Init();& l! n" ^6 {- K& m" q: M; m
- GPIO_Config();0 M# L# F0 r& \
- USART1_Init(19200); , K, F a, o9 @# e/ V' C ]
- ) t2 d9 u6 X+ u' Y
- NVIC_Config();& w# `: ]5 i/ f( c2 m V
- $ J6 _0 f6 L1 f0 q
- FLASH_SetLatency(FLASH_Latency_1); //延时2个时钟周期+ H" z; l& F' R# L
- /* Enable Prefetch Buffer --使能预取指缓存*/
: y+ W+ i5 z" E+ s; D - FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);5 f+ D3 ~/ `6 C3 t- r+ N- R
- Dma_Init();* @7 X; j+ s" I( h# T, |# ]
- /* Get Current Data Counter value before transfer begins */
; b) C* `& q d5 p1 D - CurrDataCounter = DMA_GetCurrDataCounter(DMA1_Channel1); //返回当前DMA通道x, 剩余待传输数据数目
3 K% A. X: Q% N' C -
1 }' e2 O* G7 b; s% g. P - while( CurrDataCounter!=0) ; X' l" G# l$ z- y
- judge = memcmp(SRC_Const_Buffer,DST_Buffer,BufferSize);
* i% [) j& d; u* e& g, y - ; `9 L$ v# p. ?' ?! [- N
- if(0==judge)' F3 n0 z* |5 S
- {( ^( I- G* {4 p% l$ M4 d" N" i! w
- USART1_SendData(" Same !!!\r\n",sizeof(" Same !!!\r\n"));3 F3 I7 k2 @ B' D3 h
- }5 c0 f2 s/ K, v4 b z
- else7 `& l- ~5 E3 s; A+ j2 W- Z2 ~
- {" J, a, H4 c- @4 C
- USART1_SendData(" different !!!\r\n",sizeof(" different !!!\r\n"));: e+ o5 G. M s" [
- }
4 r% D: }! T: g, s0 J+ {0 K. b
A ?1 ~5 ?2 E7 a- ?- Delay_ms(1);
6 ^. Y$ k* I# T4 D, B" w: M -
4 u3 K8 @8 e4 B8 M, U - } u8 D5 Z% M, Y5 U, w
复制代码
, F- s# Y, D% u* Q! u) B1 X五、测试验证* K( d$ r" r3 P; P! |" ]1 t& k
5 j) H' o4 N5 P' f2 x9 J1.我们先看看看下图4 o2 B. ^/ c$ x8 M! Y% s5 ~1 v
0 K: }5 h# V5 m8 m. V% m" P7 k
) H" t: y! ]- [3 `: V5 n
3 Y* v- \4 a: q- W看点0:
- j5 \) I9 `: I, d1 ~. Z问1:4 k3 v1 }3 [: v/ S; [% [8 O
+ b- ^' n0 u4 u |. w! o 使用内存窗口观测SRC_Const_Buffer和DST_Buffer所在的位置,可以发现SRC_Const_Buffer
c/ F& \7 o9 D+ q( e: O, e8 z+ N# p5 J2 j
地址为0x08001B24,,即Flash中;DST_Buffer地址为0x2000002c,即RAM中;2 t4 F+ Z. r$ U' j: L7 _1 M
( l7 Y9 H: N; @$ ?) G
. R5 H+ s* w$ }% ?2 T( [7 t
6 F/ ]+ {4 `; N: ]% b% `7 ~5 c+ M n答1:2 K9 Q# Y- S! a6 O( k0 h, S) @- E
; [+ W" y& m8 M( B5 [/ ]: q不能改变其值得变量(包括全局和局部)都是存储在FLASH中的,能改变的都储存在SRAM中
! a" J9 r. E# F3 u' R! d3 O; r
N! X0 X" L; Q- ^SRC_Const_Buffer 的定义:uc32 SRC_Const_Buffert+ ?( K. T9 u7 A
$ y7 \! H6 e% Y+ o8 _搜索了一下uc32的出处----4 H1 ?+ U! H. B8 E0 [4 M
1 ~' I0 E, B5 Atypedef const uint32_tuc32; /*!< Read Only */
, e- }& |) o0 w3 H; R5 I6 A, p; C `, k; T: i7 q
% \9 {$ L. \6 I8 l! w- Q
8 k' i, k' ?$ K" Y& a( w9 x( vDST_Buffer 的定义:u32DST_Buffer[BufferSize];, d& i3 q* c/ Y( U; ^& _: I0 B
& Q K4 ?8 [ q# ^2 X8 P/ U搜索了一下u32的出处----% y, B' Z0 s1 [ F8 s4 y, ]4 u; i
, n5 `% _' A9 D }2 U
Typedef uint32_t u32; 6 a& r X9 `. x
! ?, Q/ N3 X: q
2 v( U/ t, t g6 q- B$ p! M1 d
看点1:断点设置开始DMA传输前,
3 k1 H: n! ]7 M2 h1 F/ V( x' n
) y: k0 l: t1 p! B( dCurrDataCounter = DMA_GetCurrDataCounter(DMA1_Channel1);可以读出待传输的数据长度为0x20=32 跟我我们定义的待传输的数据长度是一样的;9 l9 U% z2 z' g1 T
7 I+ o+ ?; }' C* h& _: }看点2:开始传输前,目的数组DST_Buffer里面的全部为空即为:0x00;/ }* ?+ M" E, M" a/ a e
$ e9 e0 e6 x0 c8 Y: Q. r* _0 V3 E, C! t2 ]+ E8 H1 _+ Z. a% C! q
2.传输完成时,CurrDataCounter 值已经为0,即待传输数据为0;同时目的数组 DST_Buffer 已经有数据,我们用对比了一下源数组 和目的数组 相同则返回0; 证实了是相同的;# j* `0 ~5 l/ k7 P
! ~' k& |. ?9 E& \0 Q
8 E% R" T! I' f" P" z+ o# k5 S
3 q! l; S9 M# Q/ D4 G
3 j- I! |, l. G$ u$ Y4 ?+ B9 A6 G4 k( \3 |1 Z
' a# _& y1 [5 i |