前言
9 v) o$ r$ v @/ k6 w5 U! KDMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接* ^" n( `. O9 w4 ~, N0 K# F
控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备; b0 @' y. j1 z, h/ g
开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
3 ]/ R) ^# B! S/ e
2 m. z M8 H: R: v% o5 d一、配置步骤
8 J6 N; c9 M1 b' o" ~' Q1)使能 DMA 时钟
3 A3 h6 E/ m: ?
' {7 f( W6 O8 M& U: H* A- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟
复制代码 / p! w: e3 N* K6 n: \: {
5 d' q, F/ i, q& Y) m2)初始化 DMA 通道 4 参数
3 J7 y6 M7 S6 b/ e1 Y1 |( M1 d, m* n' d$ ]
- void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
复制代码- DMA_InitTypeDef DMA_InitStructure;8 v7 H& R* F% _
- DMA_InitStructure.DMA_PeripheralBaseAddr = &USART1->DR; //DMA 外设 ADC 基地址
% g0 P9 f, t y9 t7 a6 H - DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址
" t' n2 X( f \7 w - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //从内存读取发送到外设
& }9 j4 v* O3 m - DMA_InitStructure.DMA_BufferSize = 64; //DMA 通道的 DMA 缓存的大小
* Y0 N0 Z8 t& ?4 P7 K9 ] S - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变& ^3 J, N( ^) l$ t( B& O
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增) O; v2 ? t; }5 N
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8 位# x, H$ V* V' a
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8 位; C& }7 L- X+ ?8 J
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式2 M! w" B! M, w4 W+ U1 V
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道 x 拥有中优先级4 f( j( g; R1 B0 ?$ x* d
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输
/ w( N+ a7 a% b4 w - DMA_Init(DMA_CHx, &DMA_InitStructure); //根据指定的参数初始化
复制代码
0 ~4 b" u) g/ M7 J2 x6 N7 f; M3)使能串口 DMA 发送- USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
复制代码
% a ^; O! s# B2 E( [7 {4)使能 DMA1 通道 4,启动传输7 |( g- E: `- Q2 a5 e
2 o: Z1 N! G( a2 Z
- DMA_Cmd(DMA_CHx, ENABLE);
复制代码 3 _4 K9 T, s) K% n" Q" Q
5)查询 DMA 传输状态8 J' E x' R) Y# `: F$ M; R
/ O6 ^7 u3 P q" e! G& }; o在 DMA 传输过程中,我们要查询 DMA 传输通道的状态:& f+ k( S) O2 \- Q" u* s4 o
$ }& f( f/ Q. m4 q; g: S: x
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)& l+ j+ p3 Q: `' N
" `, y2 v6 q# F+ k: S( M# K; N6 `( g) i
比如我们要查询 DMA 通道 4 传输是否完成:
2 ?: M. M* F Q9 S# I7 Z/ w- R4 k$ n$ l5 X; J2 M- k
DMA_GetFlagStatus(DMA2_FLAG_TC4);! p+ m, @7 m7 \3 ]+ x6 @
" z( R3 z% m# }; R" v5 {* @
获取当前剩余数据量大小的函数:- uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
复制代码
7 A* d' o$ z* @ [9 W比如我们要获取 DMA 通道 4 还有多少个数据没有传输:- DMA_GetCurrDataCounter(DMA1_Channel4);
复制代码
; x/ w, q9 B# X; c+ H* u二、代码实例+ x/ t! ]! J: G
我们需要创建dma.c 文件和其头文件 dma.h,8 j% y8 T7 r4 d# J% `
同时我们要引入 DMA 相关的库函数文件 stm32f10x_dma.c 和 stm32f10x_dma.h。( _) ` o, f, J: N s, X
" U3 o# q" ^+ R5 H; M# h+ |9 K6 u
——dma.c 文件# q7 j# k8 y: v L( D
9 K9 z% x' b4 Y3 l1 Q- #include "dma.h"8 e/ U9 i. k4 |5 `. ^# x& v, t
- DMA_InitTypeDef DMA_InitStructure;8 u+ h1 T* C; k3 x
- u16 DMA1_MEM_LEN;//保存 DMA 每次数据传送的长度
5 S! A. K8 e! e/ r( a& W - //DMA1 的各通道配置7 h% @+ H' D- H/ t4 w
- //这里的传输形式是固定的,这点要根据不同的情况来修改
4 L+ C# z0 r- M - //从存储器->外设模式/8 位数据宽度/存储器增量模式
' d6 n& L+ V* ^) O& y - //DMA_CHx:DMA 通道 CHx
) S% [2 t2 U% Z& U - //cpar:外设地址
6 H+ k. E! V3 M. I) \ - //cmar:存储器地址
+ ~& s$ E, |* W* D - //cndtr:数据传输量' A K) i M8 d& j$ H) R0 m
- void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)( H; f; u' k9 E4 K
- {' S& L+ h9 j+ d
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟/ t: X, D. P# _" g7 z, M U
- DMA_DeInit(DMA_CHx); //将 DMA 的通道 1 寄存器重设为缺省值
8 o8 y( {6 A1 t5 x! ? ? - DMA1_MEM_LEN=cndtr;. g% |, g8 q! c& g* g4 B
- DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA 外设 ADC 基地址
- P, @9 E8 i0 o" _) C7 M - DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址
/ r; d! S9 O- T! ?& l7 M0 m" T - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向内存到外设. L3 { i1 A7 @' A) O
- DMA_InitStructure.DMA_BufferSize = cndtr; //DMA 通道的 DMA 缓存的大小' t$ u( n. P. L, j) w
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变
! ?# `1 Y6 n+ v8 d, `' A; t- y( s - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增( Q5 b# `1 P) v8 j
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; $ w7 L6 Q/ d4 N- p( F- A
- //数据宽度为 8 位
: G8 ]9 n2 v; u - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度
) H4 j' i8 c" K/ O$ r* S - //为 8 位
& r% v" a: ~% k1 n; ?2 J3 ~$ w - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式 T4 B7 Q1 T7 C% W
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DM 通道拥有中优先级& D, U' w0 T' X# C
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输) U: ]% t3 J9 V; W& a" h/ N9 S
- DMA_Init(DMA_CHx, &DMA_InitStructure); //初始化 DMA 的通道* k7 U& j8 L5 {2 p* r0 x! X
- } M( P1 g7 Z+ V
- //开启一次 DMA 传输
4 w9 f8 S, k! F2 y% B1 ] - void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
* R9 }% Z$ [- P, t1 x# { - { R& [" c& Y3 _5 v5 {1 x
- DMA_Cmd(DMA_CHx, DISABLE ); //关闭 USART1 TX DMA1 所指示的通道 5 U! M! ~* ?- ^/ C+ M5 Z( t" s
- DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//设置 DMA 缓存的大小 A z8 k% N; b4 R" A8 C4 w
- DMA_Cmd(DMA_CHx, ENABLE); //使能 USART1 TX DMA1 所指示的通道
5 {, ^# @4 [& F+ I$ o, ]+ t - }3 v |/ c; q/ u; M
复制代码 ( ?6 o" w5 B) N7 H" t' }
|