前言
/ s- w7 ~# V V1 DDMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接' Z& a8 M4 n( P0 z
控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备
) b* E' A M' S* c# @; }& o. H! B开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
, f0 o+ p5 m( U O9 O0 J. z5 N9 ^# Q( n5 F2 L
一、配置步骤/ P0 ~/ _, ~7 W
1)使能 DMA 时钟- U4 f: j4 B1 R
- U: @( h l% J) w0 \0 A- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟
复制代码 5 Z1 W( ^& [' W6 F1 j
4 i: u2 h$ _1 `
2)初始化 DMA 通道 4 参数( S8 G2 I( @7 r, s+ x
I& b* s! I2 L, C5 ?9 G
- void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
复制代码- DMA_InitTypeDef DMA_InitStructure;
' s. C0 D" q- z/ ^8 e - DMA_InitStructure.DMA_PeripheralBaseAddr = &USART1->DR; //DMA 外设 ADC 基地址
, J, a" n- g9 L- z, | - DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址
1 x6 X9 w: C7 v' L# j+ k! b% I4 f) ] - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //从内存读取发送到外设7 |# A2 H( L8 P9 S4 c
- DMA_InitStructure.DMA_BufferSize = 64; //DMA 通道的 DMA 缓存的大小2 ?0 Q! B l# f$ z/ \0 {( L
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变
; S: n! J3 x I! w - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增$ ^# @, n9 C6 {. u$ @
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8 位1 x8 \+ [' j, a" H0 s
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8 位& _. @7 j* u6 @& X+ r
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
" S/ T* t) s2 ? y+ f: d - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道 x 拥有中优先级% ~. U5 J7 i \: f* E6 ]* C
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输
5 x* j! s" ]( M# E& V) T - DMA_Init(DMA_CHx, &DMA_InitStructure); //根据指定的参数初始化
复制代码
) `; c; g0 ?& u9 X& e( _8 C3)使能串口 DMA 发送- USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
复制代码 * u& R" O- ` j; p
4)使能 DMA1 通道 4,启动传输
, m: X& B/ P+ u. `9 P& ]0 L" U# B8 E
; H- I1 B" E: G% S2 ], X l5 p- DMA_Cmd(DMA_CHx, ENABLE);
复制代码
! P- X) S" m# C& ~+ f5)查询 DMA 传输状态
+ [# ~7 a/ @0 o, g* D
: F; t' z- {+ _+ F6 I% d在 DMA 传输过程中,我们要查询 DMA 传输通道的状态:
3 p) q2 |( \& R( z, Z2 C/ f; M
6 Y2 g& U4 C- L6 ?FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
" E$ |* p5 j5 R( h6 l: a5 m* X, {; K$ l/ r% i4 s8 j
比如我们要查询 DMA 通道 4 传输是否完成:, _- D* ^6 r4 t
H' ]) E* Q* \
DMA_GetFlagStatus(DMA2_FLAG_TC4);
! i ^# E: ^. B7 N, }# Z1 ~
7 q* u; R5 B0 v5 C% l1 J( q/ L获取当前剩余数据量大小的函数:- uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
复制代码
7 x e& c5 I; I$ M" R! a$ d比如我们要获取 DMA 通道 4 还有多少个数据没有传输:- DMA_GetCurrDataCounter(DMA1_Channel4);
复制代码
: V8 n; n6 h0 D0 M8 J9 l$ i二、代码实例
/ y3 z J; I# s) A# K我们需要创建dma.c 文件和其头文件 dma.h,9 h* {* l: W5 o4 f0 w
同时我们要引入 DMA 相关的库函数文件 stm32f10x_dma.c 和 stm32f10x_dma.h。8 Z; x5 l2 o2 y1 L8 R5 Q
0 D+ j& G$ M( e8 R/ Y/ g" [2 T: A3 t/ p——dma.c 文件
; Y% W* N6 S( C3 \) ~ i, l* `" D% A% T# |
- #include "dma.h"
8 Y9 Q1 T( J, X0 p' O3 i# ]1 v - DMA_InitTypeDef DMA_InitStructure;
8 g5 U) l2 g9 K( g - u16 DMA1_MEM_LEN;//保存 DMA 每次数据传送的长度
2 c/ C2 |, R/ \8 r) r. t9 q( `0 K - //DMA1 的各通道配置
' U2 ^$ k7 z3 r$ [; y - //这里的传输形式是固定的,这点要根据不同的情况来修改
% }% d' c' S/ ?# t& ~, M5 Y - //从存储器->外设模式/8 位数据宽度/存储器增量模式" M6 E% P6 ?# p$ D
- //DMA_CHx:DMA 通道 CHx$ [$ l4 w5 [5 G& c$ s7 i
- //cpar:外设地址# B2 z# T+ i6 y. y; N( S
- //cmar:存储器地址8 q' w, T9 _' X2 L) z3 ^( S5 E
- //cndtr:数据传输量
7 f5 Q* W: p' c0 I9 j- z8 o - void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
% j' k. } {5 p3 l5 ~: n l - {
$ m# s/ f/ @ U+ G - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟
6 ?$ y7 X3 Z* Q+ r) j7 V& l - DMA_DeInit(DMA_CHx); //将 DMA 的通道 1 寄存器重设为缺省值! n% y) u9 j/ a' \8 P
- DMA1_MEM_LEN=cndtr;% l+ {. l. F8 y6 w% n. c
- DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA 外设 ADC 基地址
. x% }5 g. H/ ` - DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址
; k5 Y) Z* F0 ^8 J - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向内存到外设
; n- z6 |- s5 C. _* |( g; m0 f - DMA_InitStructure.DMA_BufferSize = cndtr; //DMA 通道的 DMA 缓存的大小
( N( l$ U5 c( |$ D7 A! |# b2 k7 R. D - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变# t0 F) e) M* \# T& e6 Q
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增
, n+ i, \; e/ Y8 v& b. Q - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
7 @1 P* p, O. t9 |( ^( J; k* x4 ` - //数据宽度为 8 位% g$ B6 |$ j! \) e
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度( H+ W& D" x# w' J5 S2 R5 ?) u
- //为 8 位
; y+ n0 n1 ?3 i. ^6 w* [, e - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
. t8 _# Z6 l! c1 v0 W - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DM 通道拥有中优先级
3 b2 G. r$ c+ k( P - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输; C# F! L6 U4 ^: N8 V. s' V
- DMA_Init(DMA_CHx, &DMA_InitStructure); //初始化 DMA 的通道
# E0 l& z( O: B. e9 A - } * N/ y/ N" j# }- I
- //开启一次 DMA 传输3 a' w; Y# w! ~0 }: H/ g$ q& g5 P, W
- void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)7 e# G. K" ?5 }6 r& y
- { : P' ?+ o: M- q3 B/ l# K/ k
- DMA_Cmd(DMA_CHx, DISABLE ); //关闭 USART1 TX DMA1 所指示的通道 5 g. G4 H+ g; y* j& k! @6 B" k
- DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//设置 DMA 缓存的大小1 ^: e# b, z# g( F7 Y! V; u0 A ]- a
- DMA_Cmd(DMA_CHx, ENABLE); //使能 USART1 TX DMA1 所指示的通道
. v- k6 p7 C1 G7 v0 P q$ n: z - }
% W8 n8 K3 t* \& b# t
复制代码 7 a) L/ y! d. @' `
|