基于STM32_HAL库的DMA应用
文章目录
- 基于STM32_HAL库的DMA应用
-
- 一、DMA介绍
- 二、DMA控制器相关API函数
-
- 2.1 初始化指定的 DMA 通道函数HAL_DMA_Init()原型及功能
- 2.2 停止|重置DMA通道函数HAL_DMA_DeInit()原型及功能
- 2.3 启动 DMA 传输(非中断方式)函数HAL_DMA_Start()原型及功能
- 2.4 启动 DMA 并启用中断函数HAL_DMA_Start_IT()原型及功能
- 2.5 立即中止正在进行的 DMA 传输函数HAL_DMA_Abort()原型及功能
- 2.6 以中断方式中止 DMA函数HAL_DMA_Abort_IT()原型及功能
- 2.7 阻塞式等待 DMA 传输完成函数HAL_DMA_PollForTransfer()原型及功能
- 2.8 判断 DMA 中断标志是否被置位宏函数__HAL_DMA_GET_FLAG()原型及功能
- 2.9 DMA方式串口发送数据函数HAL_UART_Transmit_DMA()原型及功能
- 2.10 DMA方式串口接收数据函数HAL_UART_Receive_DMA()原型及功能
- 2.11 启用某个 UART 中断功能函数__HAL_UART_ENABLE_IT()原型及功能
- 2.12 检查指定的 UART 状态标志位是否被置位函数__HAL_UART_GET_FLAG()原型及功能
- 2.13 清除 UART 的 IDLE(空闲)标志位函数__HAL_UART_CLEAR_IDLEFLAG()原型及功能
- 2.14 停止通过 DMA 进行的 UART 发送或接收操作函数HAL_UART_DMAStop()原型及功能
- 2.15 获取当前 DMA 传输还剩下多少个数据单位尚未完成函数__HAL_DMA_GET_COUNTER()原型及功能
- 三、内存👉内存搬运
- 四、内存👉外设搬运
- 五、外设👉内存搬运
一、DMA介绍
STM32 的 DMA(Direct Memory Access,直接存储器访问)是一种硬件模块,用于在 外设(如ADC、USART、SPI、I2C等)与 内存(RAM) 之间,或在 内存与内存之间,实现 无需CPU干预 的数据传输。
1.1 DMA的核心作用
不通过CPU,自动搬运数据,减少CPU负担,提高效率。例如:
- 从ADC采集数据直接写入内存
- 接收串口数据后直接存入缓冲区
- 内存中大块数据搬运
1.2 DMA数据传输方向
在STM32F103C8T6的DMA控制器中,三种数据传输方向决定了数据流动的路径,是DMA配置的核心要素之一。以下是清晰的分类和说明:
1. 外设到内存(Peripheral-to-Memory)
特点
- 数据源:外设寄存器(如ADC、USART的DR寄存器)。
- 数据目标:内存(如数组、变量)。
- 典型应用:
- ADC采集数据存入内存数组。
- 串口接收数据到缓冲区。
2. 内存到外设(Memory-to-Peripheral)
特点
- 数据源:内存(如待发送的数据数组)。
- 数据目标:外设寄存器(如SPI/I2C的数据寄存器)。
- 典型应用:
- 通过USART发送字符串。
- SPI驱动显示屏批量写入数据。
3. 内存到内存(Memory-to-Memory)
特点
- 数据源和目标:均为内存地址(如数组间复制)。
- 仅DMA1支持:需手动触发,无硬件外设请求。
- 典型应用:
- 快速初始化大块内存(如清零RAM)。
- 数据备份或格式转换。
1.3 DMA数据传输方式
在STM32F103C8T6的DMA控制器中,数据搬运模式主要分为两种核心模式,它们决定了DMA传输的连续性和工作方式。以下是详细的介绍:
1. 正常模式(Normal Mode)
特点
- 单次触发,单次传输:
每次使能DMA后,仅完成一次设定的数据传输(DMA_BufferSize),之后自动关闭DMA通道。 - 需手动重启:
传输完成后需重新配置并启动DMA(或由外设再次触发),适合非连续数据场景。 - 中断标志:
传输完成会置位TC(Transfer Complete)标志,可触发中断。
典型应用
- 单次ADC采样数据搬运。
- 非周期性的串口数据发送(如按键触发发送)。
2. 循环模式(Circular Mode)
特点
- 自动循环,无需干预:
传输完成后自动重置地址和计数器,无限循环传输数据,形成闭环。 - 双缓冲技巧:
结合传输完成(TC)和半传输完成(HT)中断,可实现双缓冲(Ping-Pong Buffer),提升实时性。 - 外设持续触发:
适合外设连续产生数据(如ADC连续采样、串口接收数据流)。
典型应用
- 实时音频信号采集(ADC+DMA循环模式)。
- 高速传感器数据流(如陀螺仪SPI通信)。
- 串口接收不定长数据(配合IDLE中断)。
1.4 DMA控制器通道
STM32F103 有 2 个 DMA 控制器,DMA1 有 7 个通道,DMA 2 有 5 个通道。一个通道每次只能搬运一个外设的数据!! 如果同时有多个外设的 DMA 请求,则按照优先级进行响应。STM32F103C8T6 只有 DMA1 !
-
DMA1有7个通道

-
DMA2有5个通道

1.5 DMA控制器的优先级
在STM32F103系列单片机中,DMA控制器的优先级管理是一个关键功能,它决定了当多个DMA请求同时发生时,哪个请求会被优先处理。以下是关于DMA优先级的详细介绍:
1. 优先级分类
STM32F103的DMA控制器支持两种优先级管理机制:
(1) 软件可编程优先级
每个DMA通道可以独立配置以下4个优先级等级(通过DMA_InitStruct.DMA_Priority设置):
- 最高优先级(DMA_Priority_VeryHigh)
- 高优先级(DMA_Priority_High)
- 中优先级(DMA_Priority_Medium)
- 低优先级(DMA_Priority_Low)
(2) 硬件固定优先级(自然优先级)
当多个通道的软件优先级相同时,硬件根据通道编号自动仲裁:
- 通道编号越小,优先级越高(通道1 > 通道2 > … > 通道7)。
2. 优先级仲裁规则
- 软件优先级优先:
不同优先级的请求中,更高软件优先级的通道总是先被处理。
例如:通道3(高优先级)会抢占通道1(低优先级),即使通道1编号更小。 - 硬件优先级补充:
若多个通道的软件优先级相同,则按通道编号决定顺序。
例如:通道2和通道4均为中优先级时,通道2优先。 - 传输中的抢占:
- 低优先级传输可被高优先级请求打断(当前传输完成后切换)。
- 同优先级请求需等待当前传输完成。
二、DMA控制器相关API函数
2.1 初始化指定的 DMA 通道函数HAL_DMA_Init()原型及功能
📌 函数原型:
HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma);
✅ 参数:
| 参数 | 类型 | 说明 |
|---|---|---|
hdma |
DMA_HandleTypeDef * |
指向 DMA 通道句柄的指针,包含初始化结构体配置 |
🔁 返回值:
HAL_OK: 成功HAL_ERROR: 错误(通常为参数错误)HAL_BUSY: 控制器忙
🎯 功能:
初始化指定的 DMA 通道,并配置:
- 数据方向
- 数据宽度
- 地址增量
- 优先级
- 模式(普通、循环)
2.2 停止|重置DMA通道函数HAL_DMA_DeInit()原型及功能
📌 函数原型:
HAL_StatusTypeDef HAL_DMA_DeInit(DMA_HandleTypeDef *hdma);
✅ 参数:
| 参数 | 类型 | 说明 |
|---|---|---|
hdma |
DMA_HandleTypeDef * |
指定要释放的 DMA 通道句柄 |
🔁 返回值:
HAL_OK: 成功HAL_ERROR: 错误(通常为参数错误)HAL_BUSY: 控制器忙
🎯 功能:
- 停止 DMA 通道
- 重置为默认状态
- 清除中断标志
2.3 启动 DMA 传输(非中断方式)函数HAL_DMA_Start()原型及功能
📌 函数原型:
HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);
✅ 参数:
| 参数 | 含义 |
|---|---|
hdma |
DMA 通道句柄 |
SrcAddress |
源地址 |
DstAddress |
目的地址 |
DataLength |
数据项数量(以数据宽度计) |
🔁 返回值:
HAL_OK: 成功HAL_ERROR: 错误(通常为参数错误)HAL_BUSY: 控制器忙
🎯 功能:
- 启动 DMA 传输(非中断方式)
- 手动启动一次传输
2.4 启动 DMA 并启用中断函数HAL_DMA_Start_IT()原型及功能
📌 函数原型:
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);
✅ 参数:
| 参数 | 含义 |
|---|---|
hdma |
DMA 通道句柄 |
SrcAddress |
源地址 |
DstAddress |
目的地址 |
DataLength |
数据项数量(以数据宽度计) |
🔁 返回值:
HAL_OK: 成功HAL_ERROR: 错误(通常为参数错误)HAL_BUSY: 控制器忙
🎯 功能:
- 启动 DMA 并启用中断
- 完成时调用中断回调(非阻塞)
2.5 立即中止正在进行的 DMA 传输函数HAL_DMA_Abort()原型及功能
📌 函数原型:
HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma);
✅ 参数:
| 参数 | 类型 | 说明 |
|---|---|---|
hdma |
DMA_HandleTypeDef * |
指定要中止的 DMA 通道句柄 |
🔁 返回值:
HAL_OK: 成功中止HAL_ERROR: 中止失败
🎯 功能:
- 立即中止正在进行的 DMA 传输
- 清除使能位
2.6 以中断方式中止 DMA函数HAL_DMA_Abort_IT()原型及功能
📌 函数原型:
HAL_StatusTypeDef HAL_DMA_Abort_IT(DMA_HandleTypeDef *hdma);
✅ 参数:
| 参数 | 类型 | 说明 |
|---|---|---|
hdma |
DMA_HandleTypeDef * |
指定要中止的 DMA 通道句柄 |
🔁 返回值:
HAL_OK: 成功中止HAL_ERROR: 中止失败
🎯 功能:
- 以中断方式中止 DMA
- 中止后触发
HAL_DMA_AbortCpltCallback
2.7 阻塞式等待 DMA 传输完成函数HAL_DMA_PollForTransfer()原型及功能
📌 函数原型:
HAL_StatusTypeDef HAL_DMA_PollForTransfer(DMA_HandleTypeDef *hdma, HAL_DMA_LevelTypeDef CompleteLevel, uint32_t Timeout);
✅ 参数:
| 参数 | 含义 |
|---|---|
hdma |
DMA 句柄 |
CompleteLevel |
HAL_DMA_FULL_TRANSFER 或 HAL_DMA_HALF_TRANSFER |
Timeout |
等待超时(ms) |
🔁 返回值:
HAL_OK,HAL_TIMEOUT
🎯 功能:
- 阻塞式等待 DMA 传输完成
- 适合不使用中断场景
2.8 判断 DMA 中断标志是否被置位宏函数__HAL_DMA_GET_FLAG()原型及功能
✅ 1. 宏原型:
#define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->ISR & (__FLAG__)) != 0U)
💡 这是一个 宏定义(不是函数),用于读取 DMA 状态寄存器中的某个标志位是否被置位。
✅ 2. 参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
__HANDLE__ |
DMA_HandleTypeDef * |
指向 DMA 通道的句柄(通常是 &hdma_xxx) |
__FLAG__ |
uint32_t |
要检查的标志位宏定义(如下表) |
常见标志位如下(适用于 DMA1 的各个通道):
| 宏定义 | 含义 |
|---|---|
DMA_FLAG_TCx |
通道 x 的传输完成标志(Transfer Complete) |
DMA_FLAG_HTx |
通道 x 的半传输完成标志(Half Transfer) |
DMA_FLAG_TEx |
通道 x 的传输错误标志(Transfer Error) |
DMA_FLAG_GIx |
通道 x 的全局中断标志(全局中断 = TC + HT + TE) 例如 DMA_FLAG_TC1、DMA_FLAG_HT3、DMA_FLAG_TE2 |
💡 其中
x是通道号(17,F1 系列为 DMA1_Channel17)
🔁 3. 返回值:
| 返回类型 | 说明 |
|---|---|
uint32_t(0 或 1) |
如果指定标志位被置位 → 返回 1,否则返回 0 |
🎯 4. 功能说明:
- 用于判断 DMA 中断标志是否被置位。
- 可在中断服务函数或轮询中使用。
- 一般配合
__HAL_DMA_CLEAR_FLAG()使用来手动清除中断标志位(如果 HAL 没有自动清除)。
🧪 示例用法:
if (__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1))
{
// 通道1的传输完成
__HAL_DMA_CLEAR_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1);
// 执行接下来的操作...
}
2.9 DMA方式串口发送数据函数HAL_UART_Transmit_DMA()原型及功能
📌 1. 函数原型:
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
✅ 2. 参数说明:
| 参数名 | 类型 | 说明 |
|---|---|---|
huart |
UART_HandleTypeDef * |
指向 UART 句柄的指针(例如:&huart1) |
pData |
uint8_t * |
指向要发送的数据缓冲区 |
Size |
uint16_t |
要发送的数据长度(单位:字节) |
🔁 3. 返回值:
| 返回值 | 含义 |
|---|---|
HAL_OK |
操作成功 |
HAL_BUSY |
UART 当前正在忙于另一项传输 |
HAL_ERROR |
参数错误或初始化失败 |
HAL_TIMEOUT |
(不适用于本函数,一般不会返回) |
🎯 4. 功能说明:
- 启动一个 UART 数据发送过程,由 DMA 自动完成传输。
- 使用 DMA 可以大幅减轻 CPU 负担,适用于高速或大批量数据发送。
- 在传输完成后,HAL 会调用回调函数
HAL_UART_TxCpltCallback()(需要用户自己实现)。 - 非阻塞方式,函数调用后立即返回,不等待传输完成。
🧪 5. 使用示例:
示例 1:简单发送字符串
uint8_t message[] = "Hello DMA UART!\r\n";
HAL_UART_Transmit_DMA(&huart1, message, sizeof(message) - 1);
💡 提示:
- 如果串口未配置 DMA,函数会失败。
- 如果正在发送其他数据,函数会返回
HAL_BUSY。
🔁 6. 配合中断回调使用(推荐):
你可以定义 HAL_UART_TxCpltCallback() 来处理传输完成后的任务:
void HAL_UART_TxCpltCallback
3645




被折叠的 条评论
为什么被折叠?



