DMA循环传输
时间: 2025-07-30 12:04:46 浏览: 1
DMA(Direct Memory Access)循环传输是一种常用于嵌入式系统中的数据传输机制,它允许外设与内存之间在不经过 CPU 的情况下进行高效数据交换。**循环传输(Circular Mode)** 是一种特殊模式,它在传输完成后自动从缓冲区的起始地址重新开始传输,非常适合需要连续发送或接收数据的应用场景,例如音频播放、串口数据流、ADC 采样等。
---
## ✅ 一、DMA 循环传输的基本原理
在循环传输模式下:
- DMA 控制器在传输完指定长度的数据后,不会停止;
- 而是自动将源地址或目标地址重置为初始地址,重新开始传输;
- 这样可以实现数据的**连续循环传输**;
- 常用于**双缓冲区机制(Ping-Pong Buffer)** 或 **音频流、串口流等连续数据流**。
---
## ✅ 二、实现方式(以 STM32 为例)
以下代码演示如何在 STM32 上使用 **HAL 库** 配置 DMA 循环发送模式,将数据从内存发送到 UART。
### 📌 1. 定义两个缓冲区
```c
#define BUFFER_SIZE 64
uint8_t bufferA[BUFFER_SIZE] = "Hello from buffer A\n";
uint8_t bufferB[BUFFER_SIZE] = "Hello from buffer B\n";
uint8_t *currentBuffer = bufferA;
```
### 📌 2. 初始化 UART 和 DMA(使用 CubeMX 配置)
确保 UART TX 使用 DMA,并设置为 **Circular 模式**。
### 📌 3. 启动 DMA 循环发送(使用 HAL_UART_Transmit_DMA)
```c
// 启动 DMA 发送(循环模式)
HAL_UART_Transmit_DMA(&huart2, bufferA, BUFFER_SIZE);
```
---
## ✅ 三、DMA 循环传输 + 动态内容更新(乒乓缓冲)
如果你想在每次传输结束后**更新发送内容**,可以使用中断机制来实现。
### 📌 1. 在 `main()` 中启动 DMA:
```c
// 启动第一次 DMA 发送
HAL_UART_Transmit_DMA(&huart2, bufferA, BUFFER_SIZE);
```
### 📌 2. 在 `stm32f4xx_it.c` 中定义中断回调函数:
```c
void DMA1_Stream6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_tx);
}
```
### 📌 3. 在 `main.c` 中实现 `HAL_UART_TxCpltCallback()`:
```c
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2) {
// 切换缓冲区
if (currentBuffer == bufferA) {
currentBuffer = bufferB;
memcpy(currentBuffer, "This is buffer B\n", BUFFER_SIZE);
} else {
currentBuffer = bufferA;
memcpy(currentBuffer, "This is buffer A\n", BUFFER_SIZE);
}
// 重新启动 DMA 发送
HAL_UART_Transmit_DMA(&huart2, currentBuffer, BUFFER_SIZE);
}
}
```
---
## ✅ 四、配置 DMA 为循环模式(CubeMX 设置)
在 STM32CubeMX 中配置 UART TX 的 DMA 通道时,将以下选项设置为:
- **Mode** = `Circular`
- **Peripheral Increment** = `Disabled`
- **Memory Increment** = `Enabled`
- **Data Alignment** = `Byte`
---
## ✅ 五、代码解释
| 代码片段 | 说明 |
|----------|------|
| `HAL_UART_Transmit_DMA()` | 启动一次 DMA 发送 |
| `HAL_UART_TxCpltCallback()` | 每次发送完成后被调用,用于切换缓冲区 |
| `memcpy()` | 更新缓冲区内容 |
| `Circular Mode` | 自动循环传输,不需要手动重新启动 |
---
## ✅ 六、TI EDMA 实现循环传输(非 STM32)
如果你使用的是 TI 的 DSP(如 C6748、AM335x 等),可以通过设置 `linkAddr` 和 `bCntReload` 来实现循环传输:
```c
paramSet.linkAddr = (unsigned short)((unsigned int)¶mSet >> 16);
paramSet.bCntReload = paramSet.bCnt;
```
这样每次传输完后,DMA 会自动从头开始传输,实现循环。
---
## ✅ 七、关键技巧
- 使用 **双缓冲机制(Ping-Pong Buffer)** 提高数据处理效率;
- 在中断中更新缓冲区内容,实现动态发送;
- 如果使用 **HAL 库 + STM32**,只需设置 `Circular` 模式即可;
- 如果使用 **裸寄存器或 EDMA**,需要手动配置 `linkAddr` 和 `bCntReload`。
---
##
阅读全文
相关推荐



















