STM32 DMA(直接内存访问)详解
目录
1.简介
DMA(Direct Memory Access)直接存储器存取
DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
每个通道都支持软件触发和特定的硬件触发
STM32F103C8T6 DMA资源:DMA1(7个通道)
2.存储器映射
类型 | 起始地址 | 存储器 | 用途 |
---|---|---|---|
ROM | 0x0800 0000 | 程序存储器Flash | 存储C语言编译后的程序代码 |
0x1FFF F000 | 系统存储器 | 存储BootLoader,用于串口下载 | |
0x1FFF F800 | 选项字节 | 存储一些独立于程序代码的配置参数 | |
ROM | 0x2000 0000 | 运行内存SRAM | 存储运行过程中的临时变量 |
0x4000 0000 | 外设寄存器 | 存储各个外设的配置参数 | |
0xE000 0000 | 内核外设寄存器 | 存储内核各个外设的配置参数 |
对应到数据手册的的
3.DMA框图
1. Cortex-M3 内核
核心组件:
2. 总线矩阵
总线矩阵(Bus Matrix)是 STM32 的通信枢纽,用于连接内核、外设、DMA 和存储资源。
主要功能:协调系统中各模块之间的数据传输。
连接模块:
3. DMA 控制器
DMA(Direct Memory Access)用于实现 外设与存储器 或 存储器之间 的数据直接传输,无需 CPU 干预,从而提高效率。
(1) DMA 控制器分类
图中显示了两个独立的 DMA 控制器:
(2) DMA 控制器内部结构
通道(Channel):
仲裁器(Arbiter):
(3) DMA 请求映射
DMA 控制器通过请求线与外设连接,用于启动传输。每个通道对应不同的外设请求源:
DMA1 请求:
DMA2 请求:
(4) AHB 从设备
DMA 控制器通过 AHB(Advanced High-performance Bus) 从设备接口连接内存和外设。
4. 外设桥(APB1 和 APB2)
APB(Advanced Peripheral Bus)是挂载外设的低速总线,分为 APB1 和 APB2 两部分:
APB1(低速外设):
APB2(高速外设):
DMA 控制器可以从 APB1 或 APB2 请求数据传输。例如:
5. Flash 和 SRAM
Flash:
SRAM:
6. RCC(复位与时钟控制)
作用:控制系统时钟信号,为 DMA、外设、总线等模块提供工作时钟。
与 DMA 关系:
4.DMA基本结构图
\1. 外设与存储器的配置
(1) 外设
配置参数:
(2) 存储器
配置参数:
\2. 数据传输方向
DMA 的数据传输有三种模式:
外设到存储器:
存储器到外设:
存储器到存储器(M2M):
方向的设置由 DMA 的控制寄存器决定,通常是用户根据传输需求手动配置。
\3. DMA 的触发方式
DMA 传输需要一个触发信号,触发方式有两种:
硬件触发:
软件触发:
触发信号进入 DMA 后,DMA 控制器根据配置启动数据传输。
\4. DMA 的核心模块
(1) 外设寄存器
(2) 传输计数器
作用:记录需要传输的数据量。
工作过程:
配置方式:用户需要设置总的数据传输数量。
(3) 自动重装器
作用:支持循环模式。
用途:常用于持续采样的场景,例如 ADC 连续采样的数据传输。
\5. 数据传输路径
DMA 的数据传输涉及以下几个路径:
在传输过程中,数据宽度和地址的自增与否由配置决定:
如果地址自增:
如果地址固定:
\6. 开关控制
DMA 传输的启停由用户手动控制或由硬件自动控制。
开关控制寄存器的主要作用:
\7. 传输状态与中断
DMA 工作中可能会产生以下状态信号:
中断的使用可以让 DMA 实现完全自动化的数据传输,CPU 只需响应完成中断即可处理数据。
\8. 工作流程总结
- 配置 DMA 通道:
- 启动 DMA:
- 数据传输:
- 后续处理:
5.DMA请求
STM32F103C8T6 DMA资源:DMA1(7个通道)
不同的通道开始请求转运的所对应的引脚有所不同,如果硬件请求,各个通道之间是不同的。如果是软件请求,则不受通道几的局限。
主要是对应基本结构图的这一部分:
6.数据宽度与对齐
当外设和存储器需要转运的数据的宽度决定了需要传输的操作,也就是如何去转运。大概就是如果大的数据转进小的,高位的数据就会舍去;如果小的数据转进大的,高位就会补0。
7.如何工作的
7.1 数据转运+DMA
目的就是将DataA的数据转运到DataB。
那么外设的起始地址就是填DataA的首地址,存储器的起始地址就是填DataB的首地址。
可以看出这里是存储器到存储器的转运,那么触发方式就是软件触发,是不需要硬件时机。
这种属于是复制转运,转运后DataA的数据并不会消失。
7.2 ADC扫描模式+DMA
左边是ADC转换,每一个通道转换完成后的数据会放进ADC_DR寄存器中,这时候就要通过DMA将其转运到存储器,防止下一个AD通道转后的编码数据将ADC_DR寄存器的上一个通道的数据给覆盖掉,因为对于常规的通道,是16个通道对应到一个寄存器的,只能临时存放一个通道的数据。
8.结构体和相关api
8.1 结构体
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Channelx. */
uint32_t DMA_MemoryBaseAddr; /*!< Specifies the memory base address for DMAy Channelx. */
uint32_t DMA_DIR; /*!< Specifies if the peripheral is the source or destination.
This parameter can be a value of @ref DMA_data_transfer_direction */
uint32_t DMA_BufferSize; /*!< Specifies the buffer size, in data unit, of the specified Channel.
The data unit is equal to the configuration set in DMA_PeripheralDataSize
or DMA_MemoryDataSize members depending in the transfer direction. */
uint32_t DMA_PeripheralInc; /*!< Specifies whether the Peripheral address register is incremented or not.
This parameter can be a value of @ref DMA_peripheral_incremented_mode */
uint32_t DMA_MemoryInc; /*!< Specifies whether the memory address register is incremented or not.
This parameter can be a value of @ref DMA_memory_incremented_mode */
uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width.
This parameter can be a value of @ref DMA_peripheral_data_size */
uint32_t DMA_MemoryDataSize; /*!< Specifies the Memory data width.
This parameter can be a value of @ref DMA_memory_data_size */
uint32_t DMA_Mode; /*!< Specifies the operation mode of the DMAy Channelx.
This parameter can be a value of @ref DMA_circular_normal_mode.
@note: The circular buffer mode cannot be used if the memory-to-memory
data transfer is configured on the selected Channel */
uint32_t DMA_Priority; /*!< Specifies the software priority for the DMAy Channelx.
This parameter can be a value of @ref DMA_priority_level */
uint32_t DMA_M2M; /*!< Specifies if the DMAy Channelx will be used in memory-to-memory transfer.
This parameter can be a value of @ref DMA_memory_to_memory */
}DMA_InitTypeDef;
\1. DMA_PeripheralBaseAddr
作用:设置外设的基地址,用于指定 DMA 数据传输的外设端地址。
典型值:
ADC1_BASE
或 USART1_BASE + DR_OFFSET
。\2. DMA_MemoryBaseAddr
作用:设置存储器的基地址,用于指定 DMA 数据传输的存储器端地址。
典型值:
(uint32_t)&buffer
,buffer
是存储数据的缓冲区。\3. DMA_DIR
作用:指定数据传输方向,是从外设到存储器还是从存储器到外设。
可选值:
DMA_DIR_PeripheralDST
:数据从存储器传输到外设。DMA_DIR_PeripheralSRC
:数据从外设传输到存储器。\4. DMA_BufferSize
作用:指定数据传输的大小,以数据单元(Data Unit)为单位。
说明:
DMA_PeripheralDataSize
或 DMA_MemoryDataSize
确定。\5. DMA_PeripheralInc
作用:指定外设地址在每次数据传输后是否自动递增。
可选值:
DMA_PeripheralInc_Enable
:外设地址递增。DMA_PeripheralInc_Disable
:外设地址固定不变。应用场景:
DMA_PeripheralInc_Disable
。\6. DMA_MemoryInc
作用:指定存储器地址在每次数据传输后是否自动递增。
可选值:
DMA_MemoryInc_Enable
:存储器地址递增。DMA_MemoryInc_Disable
:存储器地址固定不变。应用场景:
Enable
。Disable
。\7. DMA_PeripheralDataSize
作用:设置外设数据宽度。
可选值:
DMA_PeripheralDataSize_Byte
:8 位。DMA_PeripheralDataSize_HalfWord
:16 位。DMA_PeripheralDataSize_Word
:32 位。说明:需根据外设的寄存器宽度进行设置。例如,USART 数据寄存器为 8 位时,应选择 Byte
。
\8. DMA_MemoryDataSize
作用:设置存储器数据宽度。
可选值:
DMA_MemoryDataSize_Byte
:8 位。DMA_MemoryDataSize_HalfWord
:16 位。DMA_MemoryDataSize_Word
:32 位。说明:与 DMA_PeripheralDataSize
配合使用,确保数据传输正常。
\9. DMA_Mode
作用:设置 DMA 的操作模式(普通模式或循环模式)。
可选值:
DMA_Mode_Normal
:普通模式,传输完成后停止。DMA_Mode_Circular
:循环模式,传输完成后自动重新开始。说明:
\10. DMA_Priority
作用:设置 DMA 通道的优先级。
可选值:
DMA_Priority_Low
:低优先级。DMA_Priority_Medium
:中优先级。DMA_Priority_High
:高优先级。DMA_Priority_VeryHigh
:非常高优先级。说明:当多个 DMA 通道请求同时发生时,优先级高的通道会优先传输。
\11. DMA_M2M
作用:指定是否启用存储器到存储器(M2M)传输模式。
可选值:
DMA_M2M_Enable
:启用 M2M 传输模式。DMA_M2M_Disable
:禁用 M2M 传输模式。说明:
以下是一个配置 DMA 用于 ADC 连续采样的示例:
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // ADC 数据寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Buffer; // 存储采样数据的缓冲区地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 数据从外设到存储器
DMA_InitStructure.DMA_BufferSize = 100; // 传输 100 个数据单元
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;// 外设地址不递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 存储器地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 外设数据宽度为 16 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 存储器数据宽度为 16 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁用内存到内存模式
DMA_Init(DMA1_Channel1, &DMA_InitStructure); // 初始化 DMA 通道 1
DMA_Cmd(DMA1_Channel1, ENABLE); // 启用 DMA
8.2 api
1. void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx)
作用:
参数:
DMAy_Channelx
:指定需要重置的 DMA 通道,DMA1_Channel1
~ DMA1_Channel7
,或 DMA2_Channel1
~ DMA2_Channel5
。使用场景:
代码示例:
DMA_DeInit(DMA1_Channel1); // 重置 DMA1 通道 1
2. void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
作用:
DMA_InitTypeDef
结构体配置指定的 DMA 通道。参数:
DMAy_Channelx
:指定 DMA 通道(同上)。DMA_InitStruct
:指向包含 DMA 配置信息的初始化结构体。使用场景:
注意事项:
DMA_DeInit
复位通道后再初始化。DMA_InitTypeDef
中的所有参数。代码示例:
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Buffer; // 存储器基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 数据方向
DMA_InitStructure.DMA_BufferSize = 100; // 数据大小
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式
DMA_Init(DMA1_Channel1, &DMA_InitStructure); // 初始化 DMA 通道
3. void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct)
作用:
DMA_InitTypeDef
初始化为默认值。参数:
DMA_InitStruct
:指向 DMA_InitTypeDef
结构体的指针。默认值:
DMA_DIR_PeripheralDST
0
Byte
Low
Normal
使用场景:
DMA_Init
之前,调用此函数初始化结构体,再根据需求修改。代码示例:
DMA_InitTypeDef DMA_InitStructure;
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // 修改部分配置
4. void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState)
作用:
参数:
DMAy_Channelx
:指定 DMA 通道。NewState
:DMA 通道状态,ENABLE
(启用)或 DISABLE
(禁用)。使用场景:
代码示例:
DMA_Cmd(DMA1_Channel1, ENABLE); // 启用 DMA 通道 1
5. void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState)
作用:
参数:
DMAy_Channelx
:指定 DMA 通道。DMA_IT
:要配置的中断源,可以是以下值:DMA_IT_TC
:传输完成中断。DMA_IT_HT
:半传输完成中断。DMA_IT_TE
:传输错误中断。NewState
:中断状态,ENABLE
(启用)或 DISABLE
(禁用)。使用场景:
代码示例:
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); // 启用传输完成中断
6. void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber)
作用:
参数:
DMAy_Channelx
:指定 DMA 通道。DataNumber
:传输的数据单元数量。使用场景:
代码示例:
DMA_SetCurrDataCounter(DMA1_Channel1, 50); // 设置传输 50 个数据单元
7. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
作用:
参数:
DMAy_Channelx
:指定 DMA 通道。返回值:
使用场景:
代码示例:
uint16_t remaining = DMA_GetCurrDataCounter(DMA1_Channel1);
8. FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
作用:
参数:
DMAy_FLAG
:要检查的标志位,可以是以下值:DMA1_FLAG_TC1
:通道 1 传输完成标志。DMA1_FLAG_HT1
:通道 1 半传输标志。DMA1_FLAG_TE1
:通道 1 传输错误标志。返回值:
SET
或 RESET
。代码示例:
if (DMA_GetFlagStatus(DMA1_FLAG_TC1) == SET) {
// 传输完成
}
9. void DMA_ClearFlag(uint32_t DMAy_FLAG)
作用:
参数:
DMAy_FLAG
:要清除的标志位。代码示例:
DMA_ClearFlag(DMA1_FLAG_TC1); // 清除通道 1 的传输完成标志
10. ITStatus DMA_GetITStatus(uint32_t DMAy_IT)
作用:
参数:
DMAy_IT
:要检查的中断标志,可以是以下值:DMA1_IT_TC1
:通道 1 传输完成中断。DMA1_IT_HT1
:通道 1 半传输中断。DMA1_IT_TE1
:通道 1 传输错误中断。返回值:
SET
或 RESET
。代码示例:
if (DMA_GetITStatus(DMA1_IT_TC1) == SET) {
// 处理传输完成中断
}
11. void DMA_ClearITPendingBit(uint32_t DMAy_IT)
作用:
参数:
DMAy_IT
:要清除的中断标志。代码示例:
DMA_ClearITPendingBit(DMA1_IT_TC1); // 清除传输完成中断挂起位
这些函数为 DMA 的初始化、启动、状态检查和中断处理提供了全面的支持。在使用时,需按顺序完成以下步骤:
- 调用
DMA_DeInit
清除通道配置。 - 调用
DMA_StructInit
初始化结构体,并配置相关参数。 - 调用
DMA_Init
配置 DMA 通道。 - 调用
DMA_Cmd
启用 DMA。 - 在传输完成或中断触发时,使用状态检查和标志清除函数。
9.实验
9.1 DMA转运
📎8-1 DMA数据转运.zip
User:
Hardware:
System:
9.2 DMA+AD多通道
📎8-2 DMA+AD多通道.zip
作者:憧憬一下