DMA
Direct Memory Access 直接存储器存取
DMA 可以提供外设和存储器或者存储器和存储器之间的高速数据传输
协助 CPU实现数据转运(无须 CPU 干预,节省了 CPU 的资源)
STM32 可拥有 12 个独立可配置的通道
每个通道都支持软件触发和特定的硬件触发
- DMA1(7 个通道)
- DMA2(5 个通道)
注意
STM32F103C8T6 仅有 DMA1 资源
两种触发方式的选择:
存储器到存储器一般选用软件触发
外设到存储器一般选用硬件触发,不同的外设有不同的硬件触发源
框图
总线矩阵的左端为主动单元,拥有存储器的访问权
左端为被动单元,只能被主动单元读写
内核 CPU
- DCode 专门访问 Flash
- 系统总线访问其他外设
DMA 转运数据,也为主动单元
各个通道可以设置转运数据的源地址和目的地址
由于 DMA 总线只有一条
所有通道只能分时复用 DMA 总线
如果产生冲突,由仲裁器 Arbiter 根据优先级分配启用顺序
AHB 从设备,作为 DMA 的被动单元部分,也在 AHB 总线上
CPU 通过此来对 DMA 进行配置
外设通过DMA 请求,硬件触发 DMA 进行数据转运
基本结构
起始地址
- 外设起始地址
- 存储器起始地址
数据宽度 - 字节 Byte 8
- 半字 HalfWord 16
- 字 Word 32
如果数据宽度不匹配,会自动进行数据对齐
低位到高位,缺失的高位部分会自动写 0
高位到低位,多出来的高位部分会被舍弃
地址是否自增
如果自增,每次转运后自动移到下一个地址
一般存储器设置自增,外设寄存器不设置自增
如果想要实现存储器到存储器数据的转运,
可以让存储器先放到外设,再从外设运到存储器
传输计数器
实质上为一个自减计数器,可以设置转运数据的次数
计数器记为 0 时,自增的地址会回到起始地址,方便新一轮的转运
自动重装器
给传输计数器重新赋值
M2M Memory to Memory
设置转运的触发方式
1
软件触发
适合存储器到存储器的转运
实质上是连续触发,尽快将传输计数器清零,无需等待硬件响应
不能和循环模式(自动重装器)一起使用2
硬件触发
与外设有关的转运
开关控制 ENABLE 使能时,传输计数器的值一定要大于零,且有触发转换信号,才能使得 DMA 开始转换
注意在开关控制 DISABLE 时要对传输计数器写值,再打开
不能在 DMA 开启的时候对传输计数器写值
DMA 使用
配合 ADC扫描模式
- 外设地址不自增
- 存储器地址自增
配置流程
RCC 开启时钟
DMA 初始化
开关控制使能
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //给传输计数器写入数据
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);//DMA获取当前数据寄存器,返回传输计数器的值
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//获取标志位状态
void DMA_ClearFlag(uint32_t DMAy_FLAG);//清除标志位
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);//获取中断状态
void DMA_ClearITPendingBit(uint32_t DMAy_IT);//清除中断挂起位