01. DMA简介( s- u8 s! _0 D' n$ s/ W# [2 D1 h
DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。0 b9 P5 @- K( X8 T" m+ x
& @1 `- N4 {( K1 @1 FSTM32F4 最多有 2 个 DMA 控制器(DMA1 和 DMA2),共 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达 8个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理 DMA 请求间的优先级。
, ^1 A/ B5 f5 @0 U+ m$ y
9 L. Y7 i6 w0 R H02. 功能描述1 G3 W, Y/ `% w! ?# e
利用 STM32F4 的 DMA 来实现串口数据传送,并在 TFTLCD 模块上显示当前的传送进度。
% I% l) i( x& b" _; N+ V; N1 i, r6 s, f X+ k7 L
03. 硬件设计9 m2 v( O U) F
硬件资源有:) B4 [8 D! Z3 t
, J2 I0 o8 h, b( Z, M" l1) 指示灯 DS0
+ L' E, ^$ I) ?- G" d
$ F* G5 Y- X( Q2) KEY0 按键
2 V; _% }) S0 Z2 Q, r3 C9 G9 w5 ?4 |8 B0 j* Z
3) 串口% ~) w+ W3 a) h3 B, Y- m
4 V3 C) o [& K D/ Q
4) TFTLCD 模块2 i9 K+ l0 d) j
& m( ?: {" U* k* f
5) DMA
, Q( Y) Z0 c- O) {) a6 U' N* D, [9 G/ X
04. 程序设计
2 a: w- b6 m/ G! V! t5 w% ?, @- pdma.h7 O: E l: D7 C% }: s& w
5 ^" F, }+ r9 {5 F- #ifndef __DMA_H__; E2 A$ F7 J$ ^6 g
- #define __DMA_H__
3 e' G" s( X: p
" F8 i1 I+ l) y& F- #include "sys.h"% e# X6 r) `( F5 Y0 Y: X; e
. h- W& O4 Y4 e$ o; l- //DMA初始化
! `% N' @, ^ a8 G: h. s# j1 ^ - void MYDMA_Init(DMA_Stream_TypeDef* DMA_Streamx, u32 chx, u32 par, u32 mar, u16 ndtr);. \$ x. K# o$ d/ S* l/ B
- u# ], c& l' `1 m' Z" S; N1 V$ y$ w
- //DMA使能
5 {) ?* s; c: ]* I J& | - void MYDMA_Enable(DMA_Stream_TypeDef* DMA_Streamx, u16 ndtr);
- j! I( z) p( _, g8 N
; c1 ]& V" i; ]" a3 e6 P6 y- 5 t2 [* N' m1 n
- #endif /*__DMA_H__*/8 f3 `3 S& K8 |5 Y# j* w
复制代码
. c5 z' X4 b" Fdma.c- e5 D* o8 Z% T# F) X" P$ j
4 {2 e; N$ `4 C) O k- [; N- #include "dma.h"
4 F, Y& B; H- B+ i/ D& k) G( z - % T0 D+ ?$ f7 }( j7 T
/ i( \3 o9 h. {- //DMA初始化
3 @. W) f+ n e - void MYDMA_Init(DMA_Stream_TypeDef* DMA_Streamx, u32 chx, u32 par, u32 mar, u16 ndtr)
5 _- J$ O3 C$ l2 ~; @% \ - {
) u! c7 `" A: M: u/ B - DMA_InitTypeDef DMA_InitStructure;
6 ]+ w8 O' \: v. U - % m, r$ D, J) k* c3 k* `
- //判断是否为DMA1或者DMA2
0 [$ x( E3 v7 @3 s4 | k - if ((u32)DMA_Streamx > (u32)DMA2)3 n, c) i0 _: ~5 m2 m$ f n
- {$ a: I+ P9 I: o& v/ E
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);+ o* m7 ]# R* w% ] ^$ f
- }: k/ T1 N" e0 m7 U
- else3 |; M' L/ w2 {. A% b9 k
- {7 \- @. @# v& k1 v
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
2 r& L" v* A9 r6 s1 h - }
* E- a7 o& W! m' K - + | T9 m+ i$ O- Y( \
- //反初始化% d. o' O% u' r6 p7 P
- DMA_DeInit(DMA_Streamx);
. K3 z# m1 u0 v2 `; ?! t3 Z -
% `0 I D: }0 T! p; z# Z7 r4 @ - //等待DMA可以配置
7 q3 t0 d+ t e0 u - while(DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
; E9 n' W7 z3 Q& k" K' P1 J - {
/ s* i, ]) ^8 _) j - }' Z" U1 R% _$ ^' J; j1 o/ N
- / g8 m; L' _3 }/ z/ A
- //配置DMA Stream% M) g1 f- J9 p& U! D6 I
- ; |! H( k1 i N8 _' }8 m
- ' u6 ]! }, U. j$ _ T" _# ^
- //通道选择
" P! ~ S+ `+ p& _2 v4 x+ a - DMA_InitStructure.DMA_Channel = chx;
+ _& `. q4 S7 k5 n( H - //DMA外设地址
1 G; H; H" p0 l$ g6 w; b' C7 p! { - DMA_InitStructure.DMA_PeripheralBaseAddr = par;& g) j% ~ s! I2 w- _, P: Q
- //DMA存储器0地址, C/ n$ p# M4 V" d- V
- DMA_InitStructure.DMA_Memory0BaseAddr = mar; l* Y- p+ z# d3 f
- //存储器到外设模式4 }4 D- j3 D/ r4 p* E6 K
- DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
; _8 M6 L7 D5 p$ g, c5 } - //数据传输量. p% b% e$ p+ S" y& ^
- DMA_InitStructure.DMA_BufferSize = ndtr;
6 Z+ B5 v. M; _" o2 h" J+ t, R - //外设非增量模式
9 i- F* n+ Y U# B5 V - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
# D8 g! l; T1 R - //存储器增量模式% ?( `1 g6 V' |" G: O- s4 j: u
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;2 g( x) t) D" U; G4 |$ p6 |( I2 t
- //外设数据长度8位
( W$ X: \/ [6 T; h6 I, H" | - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;( D5 E9 i) d+ Q2 V6 B; M; X
- //存储器数据长度8位 l2 W- a( m. d1 q' j
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;; N1 U% D3 v6 q6 X) a
- //使用普通模式5 s- t+ [/ n# ~. I% b5 B T
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
7 }" M) G3 z8 ~ - //中等优先级
- @% k3 n D- f: V - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;3 o$ r) |* A" s6 P
- //关闭FIFO模式+ `& o% J/ O) b6 Z# E5 f$ `% _
- DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;5 @" k: M1 A/ |' K/ z% i
- //选择FIFO阈值 没有启动FIFO 随便选择
) P8 B8 J# D% d- q- g. c - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
5 f6 N7 X; L w! ^# i! u+ x - //存储器突发单次传输
" S$ q/ ?: F: o - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;! R# _# L" w* v3 g0 x
- //外设突发单次传输# B3 z" k A; r( h- z" L+ }1 ~
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;8 X, }9 _7 Z) b) `. {0 T
- //初始化4 k& g8 r: K/ B, D
- DMA_Init(DMA_Streamx, &DMA_InitStructure);2 X: x# n5 S; _
- }" V( {& B/ X) D4 T; r; O5 j+ Q
6 q/ ~/ h+ ?1 g9 c( k! M- //DMA使能# N! k, x# I2 o3 r" d2 k# W
- void MYDMA_Enable(DMA_Stream_TypeDef* DMA_Streamx, u16 ndtr)
& T8 {( B% z4 O; I - {
. X& R" }% P5 f4 n. N) k( X3 | - //关闭DMA传输
3 {& _% `% U' _ - DMA_Cmd(DMA_Streamx, DISABLE);
8 r; m. W2 X0 v' T- ]7 h - ( c" [$ g8 h C; g- @
- //保证DMA可以被设置
! E3 A; `2 h) y: y# g. p2 h) f - while(DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
, f7 \* q7 ^ r- n& J6 V - {- E+ z0 Z9 x5 i
- }
) S) B5 n- I1 D+ O
) \) ?8 S f7 H U! A- //设置数据传输量
& d0 j3 Z6 m$ f: i% d7 U j4 y, i - DMA_SetCurrDataCounter(DMA_Streamx, ndtr);, Z( Z* p& G$ R: k* B- }) z
-
( ]1 |6 j' A& Y6 H" L6 A - //开启DMA传输
& ^& | V' C. W6 `7 _: ?. j% Z - DMA_Cmd(DMA_Streamx, ENABLE);7 V7 F5 q5 m& D1 G o
- }
复制代码 / ^% a% P- S9 ]8 Y. j) o k
main.c; [$ T% X. I2 L
5 w: W) J" Q6 P- #include "sys.h"
9 X: I# [! S/ d) y - #include "delay.h"7 @0 L/ `$ Z+ y3 G) s8 z; ^
- #include "usart.h"5 N+ j9 Z9 U- B8 x( Y8 H
- #include "led.h"" Z9 Y5 l' p# E w. \' w- ^, V6 [3 Q$ Y
- #include "beep.h"
% K; {3 Y8 Y' e4 @1 \0 h- J% F3 W - #include "key.h"
1 X& u$ p7 r) Q) p/ ]/ |+ v4 R* Y* @ - #include "usmart.h". a* v& i* S# Q! i2 Z/ c
- #include "lcd.h"
) S- g }- x- { - #include "rtc.h", s6 S5 E4 f' o
- #include "rng.h"
7 g, D- s! s. s. F8 e. c# j - #include "key.h"5 \) h E6 ?5 R1 Z
- #include "wkup.h"
# U6 i E6 F4 `3 f$ u - #include "adc.h"
2 T' Z2 ~. R# g# f; ~5 B8 T& s - #include "dma.h"5 k! u3 {+ v) E9 ~% n5 e
- 1 x- s5 ^8 y, j7 K# ~; E/ E" }+ E
- & U2 o7 k+ W( [1 |: n3 o4 y
- #define SEND_BUF_SIZE 8200 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.* ^" u8 F3 m% U- {' }( ]: @6 E- M2 b
- 2 ~ h( u# {7 K$ a) \
- u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区7 Z! I2 n4 y! O
- const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"};
& O9 ^4 S+ f: D - 2 y P& f. L9 H: n6 l# b
# |5 E& X+ ?5 N2 N1 i5 l. d/ r+ S) l7 t- 8 h+ G$ C$ \/ [4 z/ M2 ^' p1 r" U
- int main(void)
7 f! r# |+ `! a. n* r - { c5 ?- {/ } f
- u16 i = 0;
1 N' U# M; @+ _) I: ` - u8 t = 0;' F$ E# |! o' Q# X) i! h4 u
- u8 j, mask = 0;
8 N- E; p1 z- k, a1 G4 n# n1 B! a - //进度6 x) S/ G# l! l) H9 |
- float pro = 0;; _! D# q/ b, l) e- w6 q
- & c7 x4 {. I5 l" `
9 ^) t6 l: J0 a R! ~1 P( a- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组21 O! @" r# y0 q2 q& Y& K! _
-
; v) t' w% N5 h3 [ R - delay_init(168);* A" L3 ?, g0 X, o# F2 w5 c
-
1 A/ Q. l6 J& X; H* |, A - uart_init(115200);
; I- f7 C' P3 G% g - ' @: f! V1 K1 s: {5 e
- LED_Init();
; _ V q4 U' G; r t8 M* s - KEY_Init();
0 T$ N' p% E2 l9 Q - LCD_Init();& J1 ]/ a+ m- \2 _
- Q* U2 X/ N/ `9 U
- MYDMA_Init(DMA2_Stream7, DMA_Channel_4,(u32)&USART1->DR, (u32)SendBuff, SEND_BUF_SIZE);
0 B) \3 o) \0 O - % ?& l3 _8 l5 k: p: y2 B* c, j
-
: q& U3 S+ I" p1 P - POINT_COLOR = RED;2 D2 Q0 B4 `( ~, g1 e4 v
-
# d% [$ O+ c! \: h( o - LCD_ShowString(30,50,200,16,16,"Explorer STM32F4"); " K8 ~+ l( \7 Y! m8 H" T
- LCD_ShowString(30,70,200,16,16,"DMA TEST");
U$ d8 g H$ p: r% x1 j4 m m - LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");9 v4 f; M1 x) D8 e. K
- LCD_ShowString(30,110,200,16,16,"2014/5/6");
; g1 z0 o: h$ ^ - LCD_ShowString(30,130,200,16,16,"KEY0:Start");
% R2 g4 q2 h/ B& e$ n6 n: K4 _( U - POINT_COLOR=BLUE;//设置字体为蓝色 ; A/ w& \% e; Z2 C
- 4 i: g+ U9 l% Y' N' \! ?
-
6 K6 ^ R9 q$ ?6 m+ X/ V+ m1 N3 v) q - //显示提示信息 4 s5 E+ U }5 B
- j = sizeof(TEXT_TO_SEND);
5 Y: c" {* G0 V5 D5 F - for(i = 0;i < SEND_BUF_SIZE; i++)//填充ASCII字符集数据
3 w/ k2 i+ |/ c7 r - {
' k$ X1 D U& R% }( C3 w4 A. }4 ~ - if(t >= j)//加入换行符
1 w/ R6 O% Z' l9 G# o - {
5 i5 v1 G e0 H) [ - if(mask) {: y% J- l3 `" c# a1 g
- {/ T( w( ?, k* ]9 c+ u
- SendBuff<i> = 0x0a;' O& z3 w( B$ k& {. ?
- t = 0;8 f1 n g8 `& _# [% J* }
- }else
. y8 N7 h5 S$ x% ^+ m$ J( _ - {
) _4 K3 {+ X% f7 m. X3 P - SendBuff<i> = 0x0d;8 x+ _( M1 D# e( b
- mask++;
7 ^/ { [5 {/ s0 C# @+ _ - } 2 F6 q+ ~- I7 x2 d) K
- }
: v7 p- Q. f7 m) u6 R0 l - else//复制TEXT_TO_SEND语句
4 z. [ K6 D5 ]* \ - {
& _3 [4 F5 G# _4 I1 m' D: v, Z - mask = 0; d4 \0 g/ [7 |8 o3 n
- SendBuff<i> = TEXT_TO_SEND[t];) t! V' d1 t% f) g5 E
- t++;
$ i, V# @+ o% \9 D- O - }
1 k- l) C, r$ B8 u. a" [ - } & X }9 q N) ?+ f1 Z
- - O6 k/ r5 B$ Z9 `. o9 s, g
- POINT_COLOR = BLUE;//设置字体为蓝色
7 Z; h# \. N; H -
1 O( z. L+ Y9 }' ^7 b2 E, M9 y7 H - i=0;5 z! k, F- u5 |8 T
-
2 A- f9 ^7 l8 \! A - while(1)
( @9 d3 V! {" v$ c - {
% K' k1 l0 m/ f( q b" E6 x; G - t=Key_Scan();+ l8 j: K& X$ m1 L+ c# D
- if(t==KEY0_PRESS) //KEY0按下. Z- Z9 @/ R2 T6 _. G' H
- {
! o3 x }' ]; c - printf("\r\nDMA DATA:\r\n"); - E, X- P+ e; c9 x
- LCD_ShowString(30,150,200,16,16,"Start Transimit....");! P' h5 ~" A! @2 Y6 z
- LCD_ShowString(30,170,200,16,16," %") ; //显示百分号 : |4 \9 ]" L0 z" J7 z
- USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送
! }1 V7 w+ |- x5 n2 V, G+ D - MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE); //开始一次DMA传输!
, ~1 }* B4 ]: m$ Z$ k* ? -
4 _, w3 }6 A6 P7 J - //等待DMA传输完成,此时我们来做另外一些事,点灯7 W* E0 N4 ]- V N# i
- //实际应用中,传输数据期间,可以执行另外的任务
% g! |$ h1 `# l9 ~ - while(1)! o! V, \/ M4 N% \$ d4 K H9 i7 o
- {
: C/ p# [3 }8 @. b" o - if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成- A; I2 y0 j7 ?( V H
- { + Q. U8 T7 k" \1 x) N
- DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清除DMA2_Steam7传输完成标志* |4 k z& H5 i# q2 v) C
- break; % a( Z+ D+ i2 Q( n& ]
- }% A! V4 m2 N7 }$ A S( s* ?
- pro=DMA_GetCurrDataCounter(DMA2_Stream7);//得到当前还剩余多少个数据
' ?9 m7 p6 M- P - pro=1-pro/SEND_BUF_SIZE;//得到百分比
! K4 l- Y0 Q) J7 b9 w6 b# { - pro*=100; //扩大100倍) V( o1 f4 q! _9 w5 g p" B
- LCD_ShowNum(30,170,pro,3,16);
! s, t* t( v! c& L; F - } 7 } |2 w+ F7 o7 |
- LCD_ShowNum(30,170,100,3,16);//显示100%
7 Y$ a5 r7 p2 y - LCD_ShowString(30,150,200,16,16,"Transimit Finished!");//提示传送完成( o- v8 [7 R$ O
- }" Z V) M1 A$ D2 k4 \4 a8 F" c3 m
- i++;% `) i4 }7 o. d2 v$ X% R/ \* g
- delay_ms(10);
2 f& K& k' @* T A+ t - if(i==20)
4 c; i# u+ N! }* }! Z8 }4 f - {$ p' R$ h$ Z$ s8 v3 c% k; }- ^4 _2 i
- LED1=!LED1;//提示系统正在运行
! B3 w: }" |3 H* d' r% A - i=0;: e# h& w" g, t1 M# S5 ~
- } - n; d: _* G c, A
- }
8 w+ c" Z- K. E' v" n- Z, t - }</i></i></i>
复制代码 ; Q. z; d9 ]* f+ s# W
05. 结果验证& T$ T% m( l5 _: `9 H; I* p% I8 s& c
DS0 的不停闪烁,提示程序在运行。我们打开串口调试助手,然后按 KEY0,可以看到串口显示的数据。
& m4 B" y" Y4 ~% r% K% z% |; U; r. }, j
7 }$ A4 Q5 u V
|