DMA
DMA简介
- DMA(Direct Memory Access)直接存储器存取,直接访问STM32内部的存储器(包括运行内存SRAM,程序存储器、Flash和寄存器等等),拥有权限访问存储器。
- DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无需CPU干预,节约CPU的资源。其中外设指外设的数据存储器、ADC的数据寄存器、串口的数据寄存器等等。
- 12个独立可配置的通道:DMA1(7个通道)、DMA2(5个通道)。
- 每个通道都支持软件触发和特定的硬件触发,Flash中数据转运到SRAM中一般使用软件触发,如果是外设和存储器,一般是硬件触发。
- STM32F103 DMA资源:DMA1(7个通道)无DMA2。
存储器映像
- 计算机系统的5大组成部分:运算器、控制器、存储器、输入设备和输出设备,其中运算器和控制器一般会合在一起,叫做CPU,计算机的1核心关键部分是CPU和存储器。
- bootloader程序是芯片出厂家自动写入的,一般也不允许我们修改选项字节,主要是对Flash的读保护以及写保护,内核外设寄存器各人外设的配置参数(NVIC、Systick),内核外设和其他外设是不同厂家生产设计的,所以地址分开。
- Flash存储代码、SRAM存储变量、选项字节有读写保护
DMA框图

DMA基本结构
- ADC使用扫描模式,用DMA进行数据转运。不能再DMA Enable时改传输计数器。
- 传输计数器是一个大于0,递减的计数器,转送n此,转运为0,之前递增的地址也会恢复到起始地址,而传输计数器是否恢复到原来的值,是看转运的模式来决定
- DMA以最快的速度将传输计数器清0,(与ADC、外部中断不一样),软件触发和循环模式不能同时使用。
DMA请求

DMA配置
数据宽度与对齐

总结:高位舍弃、空出来的补0
代码
- RCC开启DMA的时钟
- 直接调用DMA_Init,初始化这里的各个参数(存储器的起始地址、数据宽度、地址是否自增、方向、传输计算器、是否需要自动重装、选择触发源、通道优先级-仲裁器)
- DMA_Cmd开启DMA请求
Notice:在对应的外设调用一下XXX_DMACmd,开启触发信号的输出
DMA中断:DMA_ITConfig,再再NVIC里,配置相应的中断通道,然后写中断函数就行了
在转运的过程中,如果转运完成,传输计数器清0
先Disable DMA再写传输计数器,再Enable DMA
Request: 1. 传输计数器大于0
2. 触发源由触发信号
3. DMA 使能
4. 条件满足则DMA 转运,否则停止转运
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t bufferSize)
{
BufferSize = bufferSize;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitTypeDefStructure;
DMA_InitTypeDefStructure.DMA_BufferSize = BufferSize; // 传输计数器的值
DMA_InitTypeDefStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitTypeDefStructure.DMA_M2M = DMA_M2M_Enable;
DMA_InitTypeDefStructure.DMA_MemoryBaseAddr = AddrB;
DMA_InitTypeDefStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitTypeDefStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitTypeDefStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitTypeDefStructure.DMA_PeripheralBaseAddr = AddrA;
DMA_InitTypeDefStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitTypeDefStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitTypeDefStructure.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &DMA_InitTypeDefStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
void MyDMA_Transfer()
{
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1, BufferSize);
DMA_Cmd(DMA1_Channel1, ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
DMA_ClearFlag(DMA1_FLAG_TC1);
}