DMA(Direct Memory Access)直接存储器存储
DMA可以提供外设和存储器或存储器和存储器之间的高速数据传输,无需CPU干预,节省了CPU的资源(外设:数据寄存器等 存储器:Flash、Ram等)
c8t6DMA资源:DMA1(7个通道)
存储器映像
DMA框图
试着理解上图:当我们想要进行DMA转运数据时,我们先要对DMA寄存器(AHB)进行配置。程序写在RAM里,因此CPU先从Flash里读取程序,然后通过系统进入总线矩阵,再进入AHB从设备对DMA寄存器进行配置。因此,DMA配置完成,数据可以凭借DMA进行转运。
DMA编程基本结构
配置DMA时,需要外设和存储器的三个参数:起始地址、数据宽度、地址是否自增。
传输寄存器的作用是转运次数,自动重装器的作用是当传输寄存器位0时,对传输寄存器进行重装。
M2M(Memory to Memory)控制DMA触发是软件触发还是硬件触发。这里应注意软件触发时自动重装值为0,因为软件触发的目的是立刻完成DMA的转运从而完成接下来的任务,不应接着传输。
写传输寄存器时,必须要先关闭DMA,再进行。不能在DMA开启时写传输寄存器,这是手册里的规定。
DMA请求映像
EN使能端,EN为1时DMA工作,EN为0时DMA不工作。
软件触发为可以理解为编程基本结构图里面的M2M位,为0时硬件触发,为1时软件触发。
这里应注意的是,外设请求信号与DMA的通道是一一对应的,如ADC1的外设请求必须对应DMA的通道1,但是软件触发的话可随意指定通道。
代码解释
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
MyDMA_Size = Size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;//外设站点的基地址,32位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//地址是否自增
DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;//存储器站点的基地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//数据宽度
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//地址是否自增
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设站点是源端还是目的地
DMA_InitStructure.DMA_BufferSize = Size;//传输寄存器,传输多少次
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//是否自动重装
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//是否软件触发
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, DISABLE);
}
void MyDMA_Transfer(void)
{
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);//设置传输次数
DMA_Cmd(DMA1_Channel1, ENABLE);
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//转运完成标志位
DMA_ClearFlag(DMA1_FLAG_TC1);//清除标志位
}
上面为软件触发DMA,MyDMA_Transfer(void)函数的作用为每次手动设置传输次数。防止软件触发开启循环模式使得DMA一直循环下去。