01. DMA简介
9 n) y q( G& J. ] lDMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。# y) |5 @# }5 R8 v' N ?9 Q* E
/ _$ z6 P1 Q" Q) r5 y! b7 p
STM32F4 最多有 2 个 DMA 控制器(DMA1 和 DMA2),共 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达 8个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理 DMA 请求间的优先级。
4 |# N9 ?9 T! e9 H- [! {8 j0 P1 O$ |4 {# M2 d- H2 d+ j
02. 功能描述
8 p; g- T* f- m* u2 d8 r4 u$ V% C利用 STM32F4 的 DMA 来实现串口数据传送,并在 TFTLCD 模块上显示当前的传送进度。! F! _2 I, D2 s$ n
5 E' ]# y) ] v- q# @4 Q* H03. 硬件设计
- |( g: X6 i& r硬件资源有:7 q$ m3 Z2 @' w2 A. s1 x$ A
% G6 _8 Q! f5 P
1) 指示灯 DS02 E) h! F& U: r1 |2 {5 G: U6 Z# f
! S3 [% k# c$ Z2 e4 A* h2) KEY0 按键
' L+ X$ ]+ e) b8 c# k6 _: N$ R' m; m4 O' r# o
3) 串口6 l: y1 j( w L7 Z! O! ] t. p- B/ K
( Z' d% c. j6 p4 N$ T4) TFTLCD 模块
5 B6 H/ y. a! B7 o4 G( }; C( c$ p8 E4 Y. ?: K8 d9 Y
5) DMA
, g5 h7 Z; K/ S+ j
* w* v6 Z( L* h9 v! Y5 h0 p04. 程序设计
3 [2 M+ Z: Y6 e& h2 {0 _$ [% ydma.h7 o3 Z- n& G; `3 c! {0 k
/ [2 c5 `) D2 }5 t7 X# A- P$ _6 u
- #ifndef __DMA_H__
, q4 `9 [+ [+ m4 Q6 ~0 P! R& s9 g - #define __DMA_H__
* z" t" g5 y# y1 y% f( a
/ U7 P, _0 Z, Y/ P- #include "sys.h"& j* m" P5 x0 w
" w" ^! X5 R4 _( E- //DMA初始化
+ @* M; p1 V' O5 C - void MYDMA_Init(DMA_Stream_TypeDef* DMA_Streamx, u32 chx, u32 par, u32 mar, u16 ndtr);
% K* l$ |: j; `$ l* Z* i' n5 K - , g. U* T( D' y" e' { X
- //DMA使能# L& R8 y+ Y6 w
- void MYDMA_Enable(DMA_Stream_TypeDef* DMA_Streamx, u16 ndtr);
9 B0 ^: e% h; M, A" ^: ?4 h. h
8 ]& ]' _* @" I+ t1 K- " G4 D ~) o7 k* M: i2 y! h' e
- #endif /*__DMA_H__*/% n( j5 i' D/ a8 A; P% U, \# m% D- G
复制代码
3 D% R3 q) I% w5 q0 q: @+ mdma.c
' y9 M$ \: \0 E$ @2 x' _ w6 l( n6 `
- #include "dma.h" M/ L* }" O7 \7 f
- : K6 J' T2 q M2 G6 I4 b) g
/ \4 P: M1 a9 P4 X" j- //DMA初始化# m/ }( W9 m# a* A
- void MYDMA_Init(DMA_Stream_TypeDef* DMA_Streamx, u32 chx, u32 par, u32 mar, u16 ndtr)" |/ u c0 H {5 I
- {' I1 Q+ S! ^' p1 ]2 |/ w# h
- DMA_InitTypeDef DMA_InitStructure;
$ p* O6 G) `+ K- g5 ] - 9 }5 Z1 E( F# \- N2 D" L* d, _0 Q
- //判断是否为DMA1或者DMA25 L& j7 `# M' T* `
- if ((u32)DMA_Streamx > (u32)DMA2)
. J+ |2 F3 q& L8 H/ | - {, Q* N0 c/ K* j+ R5 X2 u0 B8 g5 i3 c
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);) e. f& L- T# X1 t# D& Z, P: U
- }) A) \( {8 F2 Q8 H1 H
- else
( p8 M9 R+ ^; ` - {' c: t+ ~- S; b* B0 w% F0 U
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
2 p& Y( m+ N! c& h6 A7 A - }" W' b0 m+ L1 V
-
+ g+ i4 a; h+ R - //反初始化* d2 o# k9 U; n! u' Y; t c* j) M
- DMA_DeInit(DMA_Streamx);+ \. v4 ] |! s! x1 X a9 I
- 9 g7 J: f a( K1 o$ J+ N
- //等待DMA可以配置+ T8 g+ V% U& J1 P
- while(DMA_GetCmdStatus(DMA_Streamx) != DISABLE)! F, y0 {# P8 I% C \+ g
- {
5 Z# R* `- U# `" @ - }6 W# D' P- D! V5 o" u5 m
-
; A) Z7 p* u( t1 @* a - //配置DMA Stream c5 U% G5 f" o, f
-
}9 k. q9 i1 f, j - + b, b9 Y7 V& l, F2 Q U$ v d
- //通道选择
8 L6 d1 v! P! H4 U3 t* s - DMA_InitStructure.DMA_Channel = chx;
* t, p' t4 e. ]; w d) K - //DMA外设地址' J. \6 w, D$ x5 z; n6 `. r1 j' m" J
- DMA_InitStructure.DMA_PeripheralBaseAddr = par;- _/ w1 t- |* M6 v: h+ d4 ^9 R
- //DMA存储器0地址
/ C0 v* P0 Q# N) R5 d4 c, ~ - DMA_InitStructure.DMA_Memory0BaseAddr = mar;. ~- @8 t4 {! r0 \4 E, o' @
- //存储器到外设模式7 A) n" w. b3 g+ e2 E: t# D" }1 z
- DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;- l' e, V6 f, W4 o: Z/ v
- //数据传输量& D; l2 l) K. |; K
- DMA_InitStructure.DMA_BufferSize = ndtr;8 x" x8 |- a( X$ p2 J( D# e
- //外设非增量模式) |& t& |2 h( H4 B+ g% Z; g! e
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;4 g: w+ [& S& ~* o- e
- //存储器增量模式
( ~7 V6 G8 g; B+ q; N( V, G5 o - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;1 M+ u/ Q. j) o4 o# K
- //外设数据长度8位* `- \- Y4 k* o& z; u8 b! i" Y0 n* q7 _
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
* s+ W8 I8 F9 y- g - //存储器数据长度8位
$ W" i% Y' U* z# n# L! V. ^ - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
: @/ J7 O4 `8 i3 |/ j# e% | - //使用普通模式
4 g6 i) T$ t1 B9 s - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;5 U1 t3 G6 F: V9 S% o& h
- //中等优先级: G) C" Z j8 b0 d
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
6 E& z* L) L! T1 T, ^1 j9 k - //关闭FIFO模式
0 M* C! N( f1 l1 d, R* z - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;) D' d. W. R* R
- //选择FIFO阈值 没有启动FIFO 随便选择
! d1 i! s9 h& e. c - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;; n4 \% s3 x% h, Q! r- ~$ s
- //存储器突发单次传输
& V* C! W$ r: H% ~) s3 A+ J - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
7 A* r5 Y' P& | R# U/ N0 | - //外设突发单次传输4 P( e8 u! y# g5 A- u7 {
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
. |" Y4 M7 A* m1 x& a, C" Y - //初始化( T' X8 z) J# R# W8 ]4 d3 R
- DMA_Init(DMA_Streamx, &DMA_InitStructure);
! W) W3 [- G* T5 o - }1 V5 { A5 n# H
- 2 j. @# N9 _6 i) v* k( v3 c' Z
- //DMA使能6 V7 `$ J$ T' w& Q. I
- void MYDMA_Enable(DMA_Stream_TypeDef* DMA_Streamx, u16 ndtr)& @$ c. X8 [; P
- {
# s4 o: u' V5 g7 S" \6 N1 o* D - //关闭DMA传输( s5 w$ K' t( d) _3 l1 B" F
- DMA_Cmd(DMA_Streamx, DISABLE);
8 t0 T: F4 F# q6 } -
* @$ ^$ G% X' B9 c% d& o - //保证DMA可以被设置
3 d& k% L* S5 t% G: E6 T- \% k - while(DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
$ {- B. d4 Z- ?- R1 i6 W$ j4 ` - {+ z& d$ ^9 {4 f8 A
- }
) L9 w4 I' m6 @( h8 `; q
! l/ ~. X* R) j2 b) M- L& U- //设置数据传输量3 p9 j. w1 j u4 U4 _
- DMA_SetCurrDataCounter(DMA_Streamx, ndtr);
) t- d# @5 d& D6 F. m3 A! N0 q' n -
6 @, {. k3 y4 q8 v - //开启DMA传输+ K% V, I3 w( u
- DMA_Cmd(DMA_Streamx, ENABLE);+ W( ~' |" |9 @, R7 K. F
- }
& Z8 D! o: t, e: F( n
复制代码 4 W# c8 M$ I2 l+ o0 h" t x
main.c
. X a! k' ~3 r9 A; j! v l) U! f$ W0 o5 I: o
- #include "sys.h"+ e9 I, \( S, M, B
- #include "delay.h"
! {. O$ S3 f2 Y2 K' `& ] - #include "usart.h"/ e1 t( Y5 U% o- \: U! a* R; Y
- #include "led.h"" a2 o' v# Z5 M \
- #include "beep.h"1 d1 Y" Q1 q' g6 u
- #include "key.h"& {' K) r! z* h
- #include "usmart.h" I& ?& e: b/ F# s; _
- #include "lcd.h"
5 x) I4 i+ s: ` - #include "rtc.h"
+ R* q) H2 B z7 D - #include "rng.h"
& A1 n* H7 s# m8 i' S - #include "key.h"
, }% i# y: k1 m- T2 T! R9 c# F - #include "wkup.h"5 F3 s" r3 I% ^! a. }; l* S+ }7 o6 u" r
- #include "adc.h"3 i* V7 Q" e* g
- #include "dma.h"
- b( {3 O9 Y" d/ L9 h# \, Y5 ]
5 O/ b- x1 a( W$ [- 9 e, z. p( R; I. `) S
- #define SEND_BUF_SIZE 8200 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
: }0 @7 I0 H9 A' I- A - 6 X8 t% D, r* p9 V4 [
- u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区% q; d3 V0 H( U# Y/ \$ n3 u5 j
- const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"}; 3 O% n8 V* q. m
- 1 K V5 e* ^& D$ p2 w
2 [4 l( u% O+ b; n- * J9 N" `, `8 z9 z
- int main(void)
! \; A6 s" w( J1 H: g - { 8 X$ l4 s; n5 t( \) Q6 M
- u16 i = 0;% ]* i9 a" H+ i# }9 n$ F
- u8 t = 0;9 |' ?5 p- v) x5 K) [, D
- u8 j, mask = 0;1 F& p% ^6 ~4 h& G5 H
- //进度
+ ^) u0 y2 N5 k# v2 D2 V( H, D - float pro = 0;
$ g1 f0 {0 I9 n5 o/ r
# K B e: d% h7 q% j( b. @
! O. i, H% K/ z$ O+ K$ |- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2% h% U+ X6 v1 e3 ?) G, b
-
0 y7 i. g6 B3 \, ` - delay_init(168);
" W7 H* o, P* b3 d8 V - 5 C' s) ~% `) B8 x$ o
- uart_init(115200);
' s" z' ]3 r) ?+ r - - E4 s$ O: `' D( P6 U
- LED_Init();
" s+ F7 M, c) D2 s% q - KEY_Init();
1 }; n1 l+ e8 O! @0 U! } - LCD_Init();
2 \! g+ m: {( g0 _$ H: Z - 0 g5 W7 Z+ |- r, H' G, i# Q# m
- MYDMA_Init(DMA2_Stream7, DMA_Channel_4,(u32)&USART1->DR, (u32)SendBuff, SEND_BUF_SIZE);, F6 {9 t# C! v8 B) _' ?/ O: H
- x; f6 T& }; q( Y* G5 ~& Q2 d+ ]1 q- * s$ l! v6 E K- D$ \
- POINT_COLOR = RED;
9 Z4 B" D2 D/ U+ A% c; }) K - # ~) C$ a- o7 Q; x& c
- LCD_ShowString(30,50,200,16,16,"Explorer STM32F4"); . ]/ _( W# @3 s" {8 c
- LCD_ShowString(30,70,200,16,16,"DMA TEST");
: T4 V8 Z! @& M; V7 P - LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");/ ]2 t1 V/ w' l; @+ m, o
- LCD_ShowString(30,110,200,16,16,"2014/5/6");
$ [0 Y: ^( }( ?, A: u' v; U - LCD_ShowString(30,130,200,16,16,"KEY0:Start");
6 Y3 c! |% I3 [5 S% d. y - POINT_COLOR=BLUE;//设置字体为蓝色
, ]$ d' _8 D* ~, z1 x - ; ^; @" ]9 r( i! j- e
- 6 ^0 K) Y [9 N: c
- //显示提示信息 0 Z6 V3 e5 i8 w# L
- j = sizeof(TEXT_TO_SEND);
8 Q& J4 O0 A4 W7 T z/ ^3 d S - for(i = 0;i < SEND_BUF_SIZE; i++)//填充ASCII字符集数据& _; h6 S5 X) P+ K# [
- {1 a; h B' ]; @2 Q/ Y5 J
- if(t >= j)//加入换行符 p0 C2 l2 m. O; u( r0 v6 r
- {
( o& h" N" x! w7 s G' m - if(mask)
/ W3 x+ o8 h/ v/ k& l& v - {
8 t. n+ \5 y3 Z8 O% T - SendBuff = 0x0a;
7 Y2 G9 T: O, s1 n - t = 0;8 u: n1 K/ y! J& h: _" j
- }else
4 Z' C1 _3 m+ b( c% i# ^ - {
# y3 s# W% N1 @$ [, h' o( K - SendBuff = 0x0d;" C& D: x$ ?2 z
- mask++;' O: v* q+ ^! h4 @' U2 V* L" B- T* q
- } 8 T! Q6 w9 j# [5 h" N
- }2 q6 {' C. ^' C1 U, V7 T4 r
- else//复制TEXT_TO_SEND语句
) ^" E/ q6 ?- P3 F( ~ - {+ v! o, P5 z% f* i& R. a
- mask = 0;/ R4 q. Z, s7 |8 N l$ a; P
- SendBuff = TEXT_TO_SEND[t];
( j7 I* l H1 L& b7 U - t++;
+ w3 F4 @ ?$ w) {- v c" W - }
% `$ T. ` r# y8 ?0 h6 U - } 4 P' \( r1 D' J' F) G# I/ p* @
- 6 t- G* r8 k! q8 H0 X
- POINT_COLOR = BLUE;//设置字体为蓝色 , J, G! B7 h3 M8 u& E
-
3 @% d) m% H5 d% ]- k* w+ m* i - i=0;
0 I9 X* u- l2 i' d -
( |; g. H. ] u& D: _3 W2 ~3 P- O - while(1)
1 Q' V8 ^! @7 f0 } a# d- v - {) i/ w! z7 k5 W& J% ^' l, D
- t=Key_Scan();
/ D. Y6 g( `- Y. S0 T9 d# m# ], z - if(t==KEY0_PRESS) //KEY0按下 T P- W+ U0 R2 ]& }7 s
- {
! Z; W1 e& \& w: {3 O/ m6 K! g, v - printf("\r\nDMA DATA:\r\n");
* f; h7 C. i4 q" ` - LCD_ShowString(30,150,200,16,16,"Start Transimit....");
, L6 {2 i- B! H6 D! m - LCD_ShowString(30,170,200,16,16," %") ; //显示百分号
8 X# i6 X% |. h4 p) N - USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送
: g; p6 v, U. d/ T* @& k - MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE); //开始一次DMA传输!
$ f, a; j8 z5 V. m& _% c; U -
. y2 K& ^8 h/ C" }" ]+ X; X - //等待DMA传输完成,此时我们来做另外一些事,点灯6 A" B" l8 K1 _- G2 v
- //实际应用中,传输数据期间,可以执行另外的任务
- {6 g @" z/ D ]- K - while(1)* [ u; A- f. ^' \4 g
- {
2 F* D' z# f5 Q# u - if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
9 l% U! k3 h8 n; ] - {
$ w7 b; g& ^' ~5 z- b' h2 J9 x) J - DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清除DMA2_Steam7传输完成标志
2 t/ q7 ^) q; ^5 y# Y6 B1 m - break;
- W5 R, }) N7 l2 l' s: U - }7 N! y; I- v) S# c) D
- pro=DMA_GetCurrDataCounter(DMA2_Stream7);//得到当前还剩余多少个数据
9 v7 l5 J. ~% }$ f/ O3 a! ] - pro=1-pro/SEND_BUF_SIZE;//得到百分比
6 j F: L5 ?5 w+ p7 n! q( G - pro*=100; //扩大100倍4 T. t. d4 S& u; o
- LCD_ShowNum(30,170,pro,3,16);
% N7 c8 W$ C. i5 i - }
& R8 z6 e3 u6 y& \) U6 z, w - LCD_ShowNum(30,170,100,3,16);//显示100% 0 ^0 H! w8 b8 U- U. y
- LCD_ShowString(30,150,200,16,16,"Transimit Finished!");//提示传送完成
6 w" e, U% J1 g5 b& _$ r% c - }
9 o- U, C$ b* d4 |' Z. E - i++;. I- v6 Q r- T. K8 F; @
- delay_ms(10);2 J' w+ h( k/ v* ]* _
- if(i==20)
a4 [) O1 A! h( i2 o - {. f" X6 N7 c# Y$ R3 Z
- LED1=!LED1;//提示系统正在运行
w6 S# X; J! n0 P( M - i=0;
+ @ t. B$ ~; O# | - } 2 @4 _% B+ Z' E; d
- } & ~0 i8 [+ d' L! B: U( w
- }' r; z0 V5 f/ [; K+ u
复制代码 " {7 ~/ j6 d$ [. A8 j& e: `
05. 结果验证
4 h* S: G2 I- w1 k3 @* K0 V0 YDS0 的不停闪烁,提示程序在运行。我们打开串口调试助手,然后按 KEY0,可以看到串口显示的数据。
. f2 P9 v9 _2 j1 Z7 Z, T7 @. t$ J* }
& O- e- [5 {( ^) F r
|