一、为什么要用DMA?: v) C7 ?. Y9 k. P, b, P7 H: Q
9 ^) h( x1 R' k! L: \( j- PDMA 全称:Direct MemoryAccess 就是可以直接内存存取;
0 P. {# x, H; _1 y
" M% g& `, U( j正是它可以直接操作内存所以具备以下优点:
; i# F" k2 F2 Y5 Z( e
2 s4 V' q1 }4 [) U6 d% J而无需经过CPU去操作内存的存取,这样可以解放CPU出来干其他的事情;! t2 c Y6 S! K# J- J
+ f3 p. Y9 V6 m+ G& o6 \' ^
因为他可以进行存储器时间的数据传输,而不需经过cpu,所以大大加快了数据传输速度—是一种高速的数据传输;
2 c) L) @4 k! m% ~1 _& E. Y
8 {2 _/ J- J+ E, b8 e: S; O' m- e. C0 R3 k
C6 h5 ?( j1 B1 M
二.DMA有几种传输数据方式:
7 f% O# \/ ?. t( S; Q/ K' u
% A- H1 g; A2 s: v; {9 z# y(1)内存到 内存之间的;即:SRAMßà SRAM# L% n! w/ G+ h6 Q7 E% G* G* q
: z( \5 s' f' u% W8 v(2)内存到 外设之间的; (例如:串口收到的数据 从数据寄存器 à 内存)
/ J# p1 d* N) t. g9 }* L0 U* Z9 c: @( D: m. g/ D
(3) 外设到内存之间的;
3 R" ^& |+ S3 @ F& M5 X& a& l8 ~1 S+ K
" c- m% @0 N2 M! Y$ Z% }
+ F' w* a, y, w* W' U- y$ M三.传输的数据宽度是怎样的,数据是什么样的形式传输?DMA 能传输多大的数据量?" I/ t- _ K4 g: l7 |
4 U6 S# V' d$ E+ b' {0 g, r
A. 数据源地址到数据目的地址 传输宽度或者说传输数据的形式,有几种:1)字节;2)半字;3)全字 [1字节=8bit 1半字=2字节=16位 1全字=2半字=4字节=32位]. X/ A' m: @% t1 y7 Q' W5 d s
6 a% U) P5 d# k2 p3 W, d
B.传输的最大数据量是655363 ~/ y' O8 {( A
6 N$ D c) F! B1 X
我们来看一下stm32f103ve的数据手册第九章DMA 大概的了解一下DMA的特性:
1 E ^4 I- d! I. y5 b
* X$ g2 t# \# i) I
) t- O9 W* z; u) r( h# [
' |5 p& F9 b" M m7 L
- q' A6 O* T+ @& v- T$ I w* x8 R# p/ j) P$ l3 |
四、怎样配置软件来使用DMA?
# K' v& a) d; q: I8 K3 L
0 s4 g& x& s7 }' m. q(1)配置dma' n' H: z, N7 l( ^2 ?
, E' j$ E4 `3 S5 z4 c4 `7 h/ v
- /*****************************************************************
& M; w* B7 l- g& M9 G5 C/ I - *函数名称: Dma_Init % P! e' M+ \7 R2 `: x" c: E
- *功能描述: 利用DMA 把内存的数据 传输到flash 达到高速传输的目的/ z9 z7 Y9 I% Q+ V# ]) m4 _" h
- * 6 t7 P& v6 |5 _+ L% j
- *输入参数:无 " G3 \* H' S. q$ g. A
- *返回值 :无
3 {( c! l* k/ g( |$ h X8 X2 L - *其他说明:无
1 H+ `* f% ] B. X3 c - *当前版本:v1.0 6 F% B% E6 P" C- X. h+ K2 `
- *作者 :尹宣 + I/ x" O/ J2 {, e- ]/ p4 `
- *完成时间:2013年12月1日 2 c% {. t8 \' t
- *修改日期 版本号 修改人 修改内容 ! t# O3 _1 g% T' d/ y, Q0 Y
- *-----------------------------------------------------------------
1 `9 F. {+ ? C" | w+ h - * 4 X* m- U; W' u$ m' o
- ******************************************************************/ # d" q$ j& d3 m" [( K
- void Dma_Init(void)' O7 ^' v" f) a0 I" E0 h4 l
- {4 l- l+ y( J/ s, i. L$ j. h
- DMA_InitTypeDef DMA_InitStructure;
5 N1 K- B0 K: W+ B7 c$ | -
1 x- m+ b& `0 m) W- r2 y4 l - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);* ]9 I1 z% t, t- ?( a6 c
- /* DMA channel6 configuration */& Z$ l( ^* `" y7 {
- DMA_DeInit(DMA1_Channel1);! C+ q4 z2 G9 _- b5 z) s
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SRC_Const_Buffer; //外设地址2 M, X; i* t, b" `/ ]
- DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DST_Buffer; //内存地址
8 x& o/ A" A* Z! i/ A' E7 x( c - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为DMA的源端 DMA_DIR_PeripheralDST; //外设作为目的地址 . W+ a2 P& y) E) V( r
- DMA_InitStructure.DMA_BufferSize = BufferSize; //传输大小
4 H# R" I4 n: r1 k - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //外设地址增加2 Q5 M7 }/ V0 a" J+ `5 y% i
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增使能
, V- s5 K# m! M0 P - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //内存存储方式:字节 DMA_MemoryDataSize_Word;//字(32位)
, J+ q' k: f6 p g - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;: F v2 T, F: ^% H/ A* y# u
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA_Mode_Normal 正常模式,只传送一次; DMA_Mode_Circular:循环模式,不停的传送; @- E( `) u# M1 _7 Y
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;
" k* K# Q: C8 T6 ?; \ - DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
8 a$ O4 x7 s& C, j - DMA_Init(DMA1_Channel1, &DMA_InitStructure);% _& c# c) V0 n8 D, i
3 V# t2 c9 X$ |, G" Y, c- /* Enable DMA Channel1 Transfer Complete interrupt */
3 [+ `/ s$ d& J4 {1 y# L8 @3 q. _ d - DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
+ P2 U% L4 z$ k3 c$ P - 3 x1 h- Y/ k$ q* V/ u! i
- /* Get Current Data Counter value before transfer begins */ {- J6 j: @- ~( G
- CurrDataCounter= DMA_GetCurrDataCounter(DMA1_Channel1);, q3 F3 p2 `, u) ]+ q. [ J
-
( [3 a% m% L- g" g! d- z/ \ - /* Enable DMA Channel6 transfer */
7 J" S- O3 D% y, `0 R - DMA_Cmd(DMA1_Channel1, ENABLE);
% W. k2 D# H( y- {! C. d -
0 h) F. n( t+ ^ G2 Z% h - }
复制代码 . L9 g( |! _4 p. x1 p
2.设置DMA优先级, W& `% u1 T1 Y4 f8 c# v* `
0 S {( @: C3 ?' \- /***************************************************************** : v7 [+ i G; d) D& i2 H
- *函数名称: NVIC_Config 1 B0 F5 H4 p4 J' Y2 Q0 a3 J' E
- *功能描述: 配置DMA的中断优先级9 I: \' ~4 M u
- *
9 j4 W6 @9 N- E% E7 L - *输入参数:无 . K9 m% L! {% H( K& Q
- *返回值 :无3 }" T9 X5 H+ S
- *其他说明:无0 T% i( s' [& U3 s# T3 D" I: C
- *当前版本:v1.0 8 M4 Z* ^( H. q1 ]
- *作者 :尹宣
1 M# c( T( d4 R, S. Q9 A4 V: y - *完成时间:2013年12月1日 . |& \ k; z1 _4 \/ O$ ]
- *修改日期 版本号 修改人 修改内容
/ f4 J; [6 k9 d5 f8 L' _ - *----------------------------------------------------------------- 1 p% a' y: B# C6 n. z( y
- * 5 t5 w' T2 W; [. q. p
- ******************************************************************/
0 S; Z6 N/ P! x/ @; a - void NVIC_Config(void)
- b* V7 F# t: U" z9 U( q2 Z - {2 c8 |* O5 p7 a4 \! h
- NVIC_InitTypeDef NVIC_InitStructure;
" P; Q- e! S- g% L -
9 q+ Q; k7 Z9 d6 H; Z9 H - /* Configure one bit for preemption priority -------------------------------- */* S' m3 [" \: L- \$ F
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
( \2 }5 q0 n$ M6 _
; q- @5 R! }9 R/ x, s1 q- /* Enable DMA channel1 IRQ Channel */
7 e% w% k2 y) | - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
# ]( R6 [5 o q& r - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
' R% J1 ]% g; O3 I - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
9 R% i+ d3 z& U - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
) o) I( ^* y0 U, p - NVIC_Init(&NVIC_InitStructure);# w3 F2 e5 ~; F1 x; F0 \
- }
复制代码 . p+ R) q1 Y9 m' A. I- m5 C R
3.在stm3210x_it.c 文件中添加 DMA中断处理函数
/ Y% {5 Q0 ?' ?! [1 I2 v0 V6 |% T/ Z! X) ?/ N
- /*******************************************************************************
" U. y0 H' G j, d; I - * Function Name : DMAChannel1_IRQHandler
7 B- |- _& L+ N/ Z; m - * Description : This function handles DMA Stream 1 interrupt request.( j+ K; S7 ^0 Q2 n
- * Input : None
9 h% u5 f+ Q: B; z+ H" J) b - * Output : None
8 k8 V' N0 t# U4 H6 i8 t9 s - * Return : None
* [8 ~; V- q9 N5 n& b; [ - *******************************************************************************/
6 Y& \1 r0 l' J! c# z -
5 L0 [; o- ^4 p; I - void DMA1_Channel1_IRQHandler(void)
( `9 P8 x$ a4 S- |9 e2 e - {
3 @& O/ f7 \+ @4 ^! l6 L i - /* Test on DMA Channel1 Transfer Complete interrupt */
# w: W: ?7 [8 ]; I( { - if(DMA_GetITStatus(DMA1_IT_TC1)) //DMA1_IT_TC1:通道1传输完成中断
1 O' D& T p% U+ q' W1 e - {
2 v6 C8 o% E, h" A9 Q - /* Get Current Data Counter value after complete transfer */
: Q, B- ]9 f" K+ Z( `& v- l# w- L - CurrDataCounter= DMA_GetCurrDataCounter(DMA1_Channel1); //返回当前DMA通道1 剩余的待传输的数据数目
' o( q9 W8 u8 x& t - /* Clear DMA Channel1 Half Transfer, Transfer Complete and Global interrupt pending bits */
7 p$ k! K6 d7 v; a3 \ - DMA_ClearITPendingBit(DMA1_IT_GL1); //清中断1全局中断
# h- m! D' d! L - }1 _/ Y/ ]1 Q( W9 u; p
-
0 o( O5 P$ ~8 N( O, K - }
复制代码
l0 R- j7 e& A# o1 W( B5 ]7 w4主函数部分:# Z# ?0 H( F. `% [8 e! g4 ^; G' |
9 p( P/ u/ `$ `* E- int main(void)2 l6 Y7 F f1 t5 [
- { % X7 P8 Z6 D, d9 D
- // int count;
( i6 f4 n/ u- |' C - uint32 judge;
& D3 @! F: {# f0 R7 O - 8 Y& K! K! v" g. A; G1 A
- NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x00); // NVIC_VectTab_FLASH=0x08000000+ l+ T' [7 t) n% m1 f: H
- // RCC_Config();3 ^( ~0 }6 D* D, Q: S
- SysTick_Init();
6 t) P& A0 U6 J) A" _" X - GPIO_Config();4 [+ r1 ^# l% B
- USART1_Init(19200);
" g6 c* G1 A- O - + k. @5 \# A- D" d5 R4 {
- NVIC_Config();
; @: u. r1 `. j; P8 F/ Y3 s - & U6 C7 a { M. D1 {7 h
- FLASH_SetLatency(FLASH_Latency_1); //延时2个时钟周期2 e+ Y% @ u; n1 o0 d2 T) w
- /* Enable Prefetch Buffer --使能预取指缓存*/6 }) d7 }5 K- W0 H6 ]/ J1 J0 g$ i
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); z% @. _) o C- T. h L- O% E
- Dma_Init();
6 |' O7 X( s6 L - /* Get Current Data Counter value before transfer begins */: v' R5 m. o* c1 X8 O. O3 M
- CurrDataCounter = DMA_GetCurrDataCounter(DMA1_Channel1); //返回当前DMA通道x, 剩余待传输数据数目7 R5 D- Q7 _, [2 |
- " b8 O( `4 r/ k* ?5 F
- while( CurrDataCounter!=0) ;( K2 z2 g6 X& q2 r5 b$ F- E; X
- judge = memcmp(SRC_Const_Buffer,DST_Buffer,BufferSize);
& @3 T3 X, p, @5 e - - \5 e1 z. T+ @, [
- if(0==judge)( G& @- C0 d3 c' U' h1 h
- {
/ R0 Y4 s. p! y - USART1_SendData(" Same !!!\r\n",sizeof(" Same !!!\r\n"));$ y; ?8 N+ v$ b! N5 f
- }
' j" W9 H! n8 ^: c - else
, u7 m1 p) |! W& N - {
7 x; n$ |# n; |9 U" U - USART1_SendData(" different !!!\r\n",sizeof(" different !!!\r\n"));3 z. X6 [) v9 X
- }& X9 _/ `3 e3 n& W% P
, k d: A4 G. h Q- Delay_ms(1);
4 |; C% }4 x# k+ p# h7 T* E4 [ - + o8 C6 X4 H, v3 ?0 ?) }
- }" f. k* V' ?2 x7 B" Z& p+ K
复制代码
4 W+ X+ G7 n i五、测试验证
# Y( C7 Y. E L/ X( Z- Z9 n- d) N( M, l& ?& o( R# s# t
1.我们先看看看下图
8 x$ O4 }; N5 |: u: K1 m# S: [
0 b+ p% Y- F6 P) Q5 j$ ~; z
$ _! V* H3 m+ ^: }) E看点0:+ P9 G% A3 F! [8 {( J. Q3 K7 e* \
问1:
0 ~# c. i5 w9 A: q" H! z. L2 e" M- M; |
使用内存窗口观测SRC_Const_Buffer和DST_Buffer所在的位置,可以发现SRC_Const_Buffer# K1 e4 F. y X
; P5 @* t0 }- S6 Y& I
地址为0x08001B24,,即Flash中;DST_Buffer地址为0x2000002c,即RAM中;" B5 P. g1 [' G
6 _- A* U4 |+ F( L( g4 [
- q8 a( I2 u% _' R3 E" w4 L
: F# a" |& x( S* I. y; ?答1:
! k9 U3 W/ R+ d/ ?8 X* d, n4 ~
* O; k- X& U% `2 K不能改变其值得变量(包括全局和局部)都是存储在FLASH中的,能改变的都储存在SRAM中2 X/ o- Y% O6 N
2 X# o! ]# P8 \) W
SRC_Const_Buffer 的定义:uc32 SRC_Const_Buffert
# M( E9 w& [* n9 K, W) I; ~; k( F6 b% w2 z/ F6 D5 T& \
搜索了一下uc32的出处----& { {, i) _6 j# s+ _4 x# D
( G9 I- d2 ?( Q* [$ G
typedef const uint32_tuc32; /*!< Read Only */
1 o7 v ]7 l) J6 W5 Z
5 ~- f, b5 O! {+ t. R7 R& ^2 _
4 V8 |8 |9 b ^6 W7 m& c* b# A
# |" P2 W9 N9 s+ W- |1 C. gDST_Buffer 的定义:u32DST_Buffer[BufferSize];
% i: M1 e* \5 c8 ]/ e7 r. }6 M" b$ R. W
搜索了一下u32的出处----6 h& E! ^! E- M8 M
2 W$ \0 d# [! {
Typedef uint32_t u32; 9 t& k! Q( p6 U1 q# [
3 e3 E7 A/ @, \/ I
( L: y2 i/ g: |: R& h看点1:断点设置开始DMA传输前,+ b8 G' q9 V( m+ A' l
- G7 Z$ z' X9 W! P: _9 s" F
CurrDataCounter = DMA_GetCurrDataCounter(DMA1_Channel1);可以读出待传输的数据长度为0x20=32 跟我我们定义的待传输的数据长度是一样的;" y( V2 s: n) h0 c" ]. w' n3 i( e! D
/ @/ \9 C$ v' F$ h4 {+ J, c
看点2:开始传输前,目的数组DST_Buffer里面的全部为空即为:0x00;8 p1 V2 E# a9 q6 w. C) f. d- i2 A
/ m% A' n+ F3 B0 n T p
! R! |' l, ~. ~! s4 m2.传输完成时,CurrDataCounter 值已经为0,即待传输数据为0;同时目的数组 DST_Buffer 已经有数据,我们用对比了一下源数组 和目的数组 相同则返回0; 证实了是相同的;
- U* H( `' u! d9 Q3 Z' W* ^8 _0 y+ s9 @1 f! i
" f/ I' Y( Q7 p( S5 e0 h( x* i: o# S$ _, t9 V( {& s
( S' T4 `6 z8 H) a) _6 m, w4 U+ C- s2 ^ D
; [8 o: B* r( i, I+ H# E |