本篇总结
使用 DMA,实现内存到内存的数据搬运。
🔑 KEIL MDK、UART调试、DMA
W55MH32L 内置 12 通道 DMA 控制器,支持外设到内存、内存到外设和内存到内存三种传输模式。本示例演示内存到内存的数据搬运,32 个 32 位数据通过 DMA 自动传输。
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口收发测试工具:点击下载
硬件连接
本例程为内存到内存传输,无需外部硬件连接。USART1 用于输出传输结果。
| 引脚 | 功能 | 方向 | 模式 |
| PA9 | USART1_TX | 开发板 → PC | 复用推挽输出 (AF_PP) |
| PA10 | USART1_RX | PC → 开发板 | 浮空输入 (IN_FLOATING) |
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\DMA\,核心文件如下:
DMA/
├── USER/
│ ├── main.c ← DMA配置 + 内存到内存传输 + 数据验证
│ ├── w55mh32_conf.h ← 外设头文件集中包含
│ ├── w55mh32_it.c/h ← 中断服务函数(DMA传输完成中断)
│ ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│ └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
└── delay/
└── delay.c/h ← SysTick 延时函数
步骤1:DMA 配置与传输完成中断
函数:DMA_Configuration() — 配置 DMA1 Channel1 为内存到内存传输,32 个 32 位数据,传输完成后触发中断验证数据。
#define BUFFER_SIZE 32
uint32_t SRC_Buffer[BUFFER_SIZE] = {
0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10,
0x11121314, 0x15161718, 0x191A1B1C, 0x1D1E1F20,
0x21222324, 0x25262728, 0x292A2B2C, 0x2D2E2F30,
0x31323334, 0x35363738, 0x393A3B3C, 0x3D3E3F40,
0x41424344, 0x45464748, 0x494A4B4C, 0x4D4E4F50,
0x51525354, 0x55565758, 0x595A5B5C, 0x5D5E5F60,
0x61626364, 0x65666768, 0x696A6B6C, 0x6D6E6F70,
0x71727374, 0x75767778, 0x797A7B7C, 0x7D7E7F80
};
uint32_t DST_Buffer[BUFFER_SIZE];
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1);
/* 内存到内存传输 */
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* 传输完成中断 */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
void DMA1_Channel1_IRQHandler(void)
{
if (DMA_GetITStatus(DMA1_IT_TC1) != RESET)
{
DMA_ClearITPendingBit(DMA1_IT_TC1);
printf("DMA Transfer Complete!\n");
/* 验证数据 */
uint8_t i, match = 1;
for (i = 0; i < BUFFER_SIZE; i++)
{
if (DST_Buffer[i] != SRC_Buffer[i])
{
match = 0;
break;
}
}
printf("Data %s\n", match ? "Match!" : "Mismatch!");
}
}
关键配置:DMA_M2M_Enable 使能内存到内存模式(此时外设地址实际为源内存地址),DMA_Mode_Normal 单次传输模式(传输一次后停止),源地址和目标地址均自增,数据宽度为 32 位字。传输完成中断中逐字对比源数据和目标数据。
如果你想自定义
- 改传输数据量 → 修改
BUFFER_SIZE 和源数据数组
- 改为循环传输 → 将
DMA_Mode_Normal 改为 DMA_Mode_Circular
- 改数据宽度 → 选择
Byte(8位)、HalfWord(16位) 或 Word(32位)
编译与下载
Keil MDK 中打开 DMA.uvprojx,F7 编译,确认 0 Error(s)。连接 DEBUG USB 口到 PC,F8 下载。
运行验证
打开 WIZ UartTool V1.0,波特率 115200 / 8N1。下载固件并按 RESET 后,串口打印 "DMA Transfer Complete!" 和 "Data Match!",表示 32 个 32 位数据已成功从源地址搬运到目标地址且数据一致。
WIZ UartTool V1.0 显示 DMA 传输完成和数据验证结果
常见问题
Q: DMA 传输完成后数据不匹配
检查源地址和目标地址是否正确设置,数据宽度是否一致(Word/HalfWord/Byte)。确认源地址和目标地址都使能了自增(DMA_PeripheralInc_Enable 和 DMA_MemoryInc_Enable)。
Q: DMA 传输完成中断不触发
确认 DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE) 已调用。检查 NVIC 中断是否使能,中断服务函数名是否正确(DMA1_Channel1_IRQHandler)。
Q: 内存到内存模式不工作
内存到内存模式必须设置 DMA_M2M_Enable。注意 M2M 模式下不能使用循环模式与外设请求,传输由软件启动(DMA_Cmd 使能后立即开始)。
总结
本文基于 W55MH32L 的 DMA1 控制器实现了内存到内存的数据搬运:
- DMA1 Channel1 配置为内存到内存模式,32 个 32 位数据自动搬运
- 单次传输模式(Normal),传输一次后自动停止
- 传输完成中断中逐字对比源数据和目标数据,验证数据完整性
- W55MH32L 内置 12 通道 DMA 控制器,支持外设到内存、内存到外设和内存到内存三种模式