前言: T0 n; M, N8 a
DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接
# Y) V; R! p1 h$ G/ n0 @* q# Y控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备
# {1 t2 S5 E1 F开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。' \9 C& C* C( E9 q" g. [' [
2 U$ t; \6 W1 G, @% m! g6 d一、配置步骤) [/ p4 \$ X& ?' j' Q/ `" v/ \) o
1)使能 DMA 时钟
) `+ U7 g, E6 V& U
. f% W y% t& |7 {% q8 U- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟
复制代码 3 N5 A3 G* Q; S* C( z" J" e+ o
5 Z; d2 K/ u1 c
2)初始化 DMA 通道 4 参数( _6 I# z1 D/ D4 [& }5 {
+ G) g) [; [. H- void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
复制代码- DMA_InitTypeDef DMA_InitStructure;6 X( N$ ]$ o6 k o
- DMA_InitStructure.DMA_PeripheralBaseAddr = &USART1->DR; //DMA 外设 ADC 基地址: Z0 M! @$ e" z2 k) ~$ n% E
- DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址
3 m; g( V2 w( r2 {2 S) Z9 t, S% x! v - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //从内存读取发送到外设
4 N4 n: V. ?1 a' Z: n' O - DMA_InitStructure.DMA_BufferSize = 64; //DMA 通道的 DMA 缓存的大小
( v3 ^3 A( a7 J- e- L/ H" S - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变% Z7 c5 V" V5 z/ V& ^
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增' Y+ x* n# v2 a# l; B' F2 Z9 E7 A
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8 位# m: t* I6 n7 J% M- Z
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8 位
4 q8 b/ h. p1 @; c# q$ a/ h - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
' u' j: s+ O* x1 Q/ I z: k - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道 x 拥有中优先级
. {4 Y7 U* K K - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输
7 m W8 s/ O4 u! w$ G8 \0 x - DMA_Init(DMA_CHx, &DMA_InitStructure); //根据指定的参数初始化
复制代码 3 F1 f# [4 t! n' z
3)使能串口 DMA 发送- USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
复制代码
/ f4 i, n# R8 c4)使能 DMA1 通道 4,启动传输# X8 y- s% G2 h) C: ]$ q6 {
5 q5 v8 B- S& }% U$ l. L- DMA_Cmd(DMA_CHx, ENABLE);
复制代码
9 k2 w) o* K. M: b) g. v9 E5)查询 DMA 传输状态
% D2 h+ S$ Q) _5 k3 N. {2 V5 J2 t: A; d8 ~% B" f! S/ T% u
在 DMA 传输过程中,我们要查询 DMA 传输通道的状态:9 m0 C/ c/ J9 B; B" {& w+ _7 [
8 z- K- D& w& D. TFlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)# P" }8 _0 D. j
1 P+ i; f. [1 q4 K, x X
比如我们要查询 DMA 通道 4 传输是否完成:% Q% O' R$ m C& Q
- Y" N/ e' ?! hDMA_GetFlagStatus(DMA2_FLAG_TC4);
4 X5 H! H- ]. {/ R- I9 Z& `5 l' r7 O' u3 E( }
获取当前剩余数据量大小的函数:- uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
复制代码 4 G% R& x. A# ?- c
比如我们要获取 DMA 通道 4 还有多少个数据没有传输:- DMA_GetCurrDataCounter(DMA1_Channel4);
复制代码
2 z6 t3 p5 b4 }4 R二、代码实例
. m+ O/ N& q/ F6 U我们需要创建dma.c 文件和其头文件 dma.h,- g7 w- {3 b: Y- P/ D% R0 L+ b
同时我们要引入 DMA 相关的库函数文件 stm32f10x_dma.c 和 stm32f10x_dma.h。
5 l! o$ U- P! v' M0 R
3 c# P, o. \ G6 b2 d' R9 j! p——dma.c 文件
9 Y+ i- T: N- B5 J2 y2 P) Z
- |+ Z) Q# t+ [# c4 E) ^- #include "dma.h", W. r; I5 M7 c2 M
- DMA_InitTypeDef DMA_InitStructure;& l% _- A5 w& E) }; d% u+ @
- u16 DMA1_MEM_LEN;//保存 DMA 每次数据传送的长度 - Y! W6 g/ P# S3 e0 I5 P* B, @$ ]
- //DMA1 的各通道配置3 D5 S9 _9 i) }( N9 s3 _" P
- //这里的传输形式是固定的,这点要根据不同的情况来修改* n! l) P% I0 \% `; _% X
- //从存储器->外设模式/8 位数据宽度/存储器增量模式) Q7 Y+ w$ U' h' m/ B
- //DMA_CHx:DMA 通道 CHx8 e, X6 ~& x# R$ a6 q% S
- //cpar:外设地址/ i7 g- U- n7 C0 H p
- //cmar:存储器地址 Z/ m' o2 n& F2 C! x' U0 n
- //cndtr:数据传输量
1 ]( j: |% R: A, Y) `- j - void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
( d" H* b5 ]9 b) f) Y - {9 F: T( k, U9 E* ?* S& q: |. A
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟0 |( ]* H5 h" t2 @3 [2 @4 N
- DMA_DeInit(DMA_CHx); //将 DMA 的通道 1 寄存器重设为缺省值. ]; y8 Y) l9 d& ?
- DMA1_MEM_LEN=cndtr;' G- }) N& Q1 J: o- Y% u
- DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA 外设 ADC 基地址: V, P; c! Y7 q+ n2 e
- DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址
6 R/ |* k2 D5 Q+ y7 B% o - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向内存到外设
! g; Q8 [, _% T* t+ C. v - DMA_InitStructure.DMA_BufferSize = cndtr; //DMA 通道的 DMA 缓存的大小" J5 }7 q7 {. K8 R3 W! p
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变
0 Y9 N7 Z: l; n8 p( ?' ^ - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增
; B2 v$ D2 _3 S$ \: e5 ~ - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
* W" @( Y+ v5 h2 r7 f - //数据宽度为 8 位' r, D1 B- D7 t j6 w* l
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度
0 M: a# e/ D3 w4 @+ ~8 n7 j: R - //为 8 位
, s. ^2 E! {- V8 Z, l - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
+ I8 v) Y$ E1 c [9 E- O4 { - DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DM 通道拥有中优先级
: y7 f" z* V/ r - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输4 {! W4 [# E. ^, v* d( j
- DMA_Init(DMA_CHx, &DMA_InitStructure); //初始化 DMA 的通道- M; O# A. \: ~
- }
/ Q4 N/ a$ V5 @9 x- O - //开启一次 DMA 传输0 \2 x$ ^8 b" C. l/ X2 H
- void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
, \# ~2 J( |! f* y, D, |' | - { 3 q w6 H$ S+ k7 ]: r& E
- DMA_Cmd(DMA_CHx, DISABLE ); //关闭 USART1 TX DMA1 所指示的通道 & R) s; J# P0 g
- DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//设置 DMA 缓存的大小
; b, c: v5 }% M1 U) {- H" A2 q - DMA_Cmd(DMA_CHx, ENABLE); //使能 USART1 TX DMA1 所指示的通道$ _% J/ ~3 W3 k, b ]% S1 n H
- }+ F# d1 F2 Y' L7 w6 A
复制代码
: t1 h( T0 e# }2 u |