keyi
(引自开发者指南)
DMA简介
DMA,全称为: Direct Memory Access,即直接存储器访问。 DMA 传输方式无需 CPU 直接
控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备
开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。
STM32F4 最多有 2 个 DMA 控制器(DMA1 和 DMA2), 共 16 个数据流(每个控制器 8 个), 每一个 DMA 控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达 8
个通道(或称请求)。每个数据流通道都有一个仲裁器,用于处理 DMA 请求间的优先级。
一些特性
- 双 AHB 主总线架构,一个用于存储器访问,另一个用于外设访问
- 仅支持 32 位访问的 AHB 从编程接口
- 每个 DMA 控制器有 8 个数据流,每个数据流有多达 8 个通道(或称请求) 也就是说一共两个DMA,每个DMA有八个数据流,每个数据流有8个通道。
- 每个数据流有单独的四级 32 位先进先出存储器缓冲区(FIFO),可用于 FIFO 模式或直
接模式。 - 通过硬件可以将每个数据流配置为:
1,支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道
2,支持在存储器方双缓冲的双缓冲区通道 - ......
示意图
存储器到存储器需要外设接口可以访问存储器,而仅 DMA2 的外设接口可以访问存储器,所以仅 DMA2 控制器支持存储器到存储器的传输, DMA1 不支持。
DMA_SxCR 控制数据流到底使用哪一个通道,每次只能选择其中一个通道进行 DMA 传输。
DMA2各数据流通道映射表,例如要实现串口 1的 DMA 发送,即 USART1_TX,就必须选择 DMA2 的数据流 7,通道 4,来进行 DMA 传输。
寄存器和代码对应关系
DMA 中断状态寄存器
该寄存器总共有 2 个: DMA_LISR 和 DMA_HISR,每个寄存器管理 4 数据流(总共 8 个), DMA_LISR 寄存器用于管理数据流 0~3,而 DMA_HISR 用于管理数据流 4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。
此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除
常用的是 TCIFx位,即数据流 x 的 DMA 传输完成与否标志。
DMA 中断标志清除寄存器
该寄存器同样有 2 个: DMA_LIFCR 和 DMA_HIFCR,同样是每个寄存器控制 4 个数据流, DMA_LIFCR 寄存器用于管理数据流 0~3,而 DMA_ HIFCR 用于管理数据流 4~7。这两个寄存器各位描述都完全一模一样,只是管理的数据流不一样。
DMA_LIFCR 的各位就是用来清除 DMA_LISR 的对应位的,通过写 1 清除
DMA 数据流 x 配置寄存器(DMA_SxCR)(x=0~7)
DMA_ SxCR 是 DMA 传输的核心控制寄存器,该寄存器控制着 DMA 的很多相关信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。见《STM32F4xx 中文参考手册》第 223 页 9.5.5 一节
等待 DMA 可配置,也就是等待 DMA_SxCR 寄存器最低位为 0 的方法为:
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待 DMA 可配置
位 0 EN:数据流使能/读作低电平时数据流就绪标志 (Stream enable / flag stream ready when read low)此位由软件置 1 和清零。
0:禁止数据流
1:使能数据流
此位读作 0 时,软件可以对配置和 FIFO 位寄存器编程。 EN 位读作 1 时,禁止向这些寄存
器执行写操作。
DMA 数据流 x 数据项数寄存器(DMA_SxNDTR)
这个寄存器控制 DMA 数据流 x 的每次传输所要传输的数据量。其设置范围为 0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为 0 的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前 DMA 传输的进度。 特别注意,这里是数据项数目,而不是指的字节数。
DMA 数据流 x 的外设地址寄存器(DMA_SxPAR)
寄存器用来存储 STM32F4 外设的地址
DMA 数据流 x 的存储器地址寄存器(DMA_SxM0AR 和 DMA_SxM1AR)
DMA_SxM1AR 仅在双缓冲模式下,才有效。
代码编写顺序与解读
1)使能 DMAx时钟,并等待数据流可配置。
2) 初始化 DMAx数据流 x,包括配置通道,外设地址,存储器地址,传输数据量等,可以看到入口参数第一个为选择数据流,第二个入口参数为结构体,配置不同寄存器,内容在代码注释里。
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
typedef struct
{
uint32_t DMA_Channel;//通道选择,对应DMA_SxCR 位 27:25 CHSEL[2:0]
uint32_t DMA_PeripheralBaseAddr;//用来设置 DMA 传输的外设基地址,对应DMA_SxPAR
uint32_t DMA_Memory0BaseAddr;//内存基地址,也就是存放 DMA 传输数据的内存地址,对应DMA_SxM0AR
uint32_t DMA_DIR;/设置数据传输方向,对应DMA_SxCR 位 7:6 DIR[1:0]
uint32_t DMA_BufferSize;//设置一次传输数据量的大小,对应DMA_SxNDTR?
uint32_t DMA_PeripheralInc;//设置传输数据的时候外设地址是否递增,对应DMA_SxCR位 15 PINCOS?
uint32_t DMA_MemoryInc;//设置传输数据时候内存地址是否递增,对应DMA_SxCR位 10 MINC,置1
uint32_t DMA_PeripheralDataSize;
uint32_t DMA_MemoryDataSize;
uint32_t DMA_Mode;//用来设置 DMA 模式是否循环采集,对应DMA_SxCR位 8 CIRC
uint32_t DMA_Priority;//用来设置 DMA 通道的优先级,对应DMA_SxCR位 17:16 PL[1:0]
uint32_t DMA_FIFOMode;//用来设置是否开启 FIFO 模式,DMA 数据流 x FIFO 控制寄存器DMA_SxFCR
uint32_t DMA_FIFOThreshold;//选择 FIFO 阈值,对应DMA_SxFCR 位1:0 FTH
uint32_t DMA_MemoryBurst;//配置存储器突发传输配置,对应DMA_SxCR位 24:23
uint32_t DMA_PeripheralBurst;//配置外设突发传输配置,对应DMA_SxCR位 22:21
}DMA_InitTypeDef;
3)使能串口 1 的 DMA 发送
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA 发送
4)使能 DMA2 数据流 7,启动传输。
使能 DMA 数据流的函数为:
void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState)
使能 DMA2_Stream7,启动传输的方法为:
DMA_Cmd (DMA2_Stream7, ENABLE) ;
5)查询 DMA 传输状态(选)
在 DMA 传输过程中,我们要查询 DMA 传输通道的状态,使用的函数是:
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
获取当前剩余数据量大小的函数:
uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);