DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,DMA 控制器独立于内核,属于一个单独的外设。
数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是 DMA2 只存在于大容量产品和互联型产品中。
如果外设要想通过 DMA 来传输数据,必须先给 DMA 控制器发送 DMA 请求,DMA 收到请求信号之后,控制器会给外设一个应答信号,当外设应答后且 DMA 控制器收到应答信号之后,就会启动 DMA 的传输,直到传输完毕
虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个
多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,分为两个阶段。
第一阶段属于软件阶段,可以在 DMA_CCRx 寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。
第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高。0>1.
以 ADC 采集为例。DMA 外设寄存器的地址对应的就是 ADC数据寄存器的地址,DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址。
以串口向电脑端发送数据为例。DMA 外设寄存器的地址对应的就是串口数据寄存器的地址,DMA 存储器的地址就是我们自定义的变**量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)**的地址。方向我们设置外设为目标地址
以内部 FLASH 向内部 SRAM 复制数据为例。DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址,DMA存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。跟上面两个不一样的是,这里需要把DMA_CCR 位 14:MEM2MEM:存储器到存储器模式配置为 1,启动 M2M 模式。
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; // 外设地址
uint32_t DMA_MemoryBaseAddr; // 存储器地址
uint32_t DMA_DIR; // 传输方向
uint32_t DMA_BufferSize; // 传输数目
uint32_t DMA_PeripheralInc; // 外设地址增量模式
uint32_t DMA_MemoryInc; // 存储器地址增量模式
uint32_t DMA_PeripheralDataSize; // 外设数据宽度
uint32_t DMA_MemoryDataSize; // 存储器数据宽度
uint32_t DMA_Mode; // 模式选择
uint32_t DMA_Priority; // 通道优先级
uint32_t DMA_M2M; // 存储器到存储器模式
} DMA_InitTypeDef;
结构体成员 | 功能选项 |
---|---|
DMA_PeripheralBaseAddr | 设定 DMA_CPAR 寄存器的值 |
DMA_MemoryBaseAddr | 设定 DMA_CMAR 寄存器值 |
DMA_DIR | 传输方向选择,可选外设到存储器、存储器到外设。它设定 DMA_CCR 寄存器的DIR[1:0] 位的值。 |
DMA_BufferSize | 设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值 |
DMA_PeripheralInc | 如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值 |
DMA_MemoryInc | 如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它 设定 DMA_CCR 寄存器的 MINC 位的值 |
DMA_PeripheralDataSize | 外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位)DMA_CCR 寄存器的 PSIZE[1:0] 位的值。 |
DMA_MemoryDataSize | 存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),DMA_CCR 寄存器的 MSIZE[1:0] 位的值。 |
DMA_Mode | DMA 传输模式选择,可选一次传输或者循环传输,它设定 DMA_CCR 寄存器的CIRC 位的值。 |
DMA_Priority | 软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,DMA_CCR 寄存器的 PL[1:0] 位的值。 |
DMA_M2M | 存储器到存储器模式,使用存储器到存储器时用到设定 DMA_CCR 的位 14MEN2MEN 即可启动存储器到存储器模式。 |
void USARTx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
// 启用 DMA1 时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 设置 DMA_InitStructure 结构体的各项参数
DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS; // USART 数据寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; // 发送缓冲区的内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 数据传输方向为从外设到存储器
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE; // 发送缓冲区大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 禁止外设地址增量
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 允许存储器地址增量
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据大小为字节
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 存储器数据大小为字节
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 设置 DMA 优先级为高
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA 模式为正常模式
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁止存储器到存储器传输
// 使用配置好的 DMA_InitStructure 结构体初始化 USART 的 DMA 通道
DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);
// 启动 USART 的 DMA 传输
DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE);
}