news 2026/4/16 15:41:16

DMA技术入门:高效数据传输实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DMA技术入门:高效数据传输实战

目录

一、DMA 基础入门

二、经典应用案例

1. 串口 (UART) DMA 收发

2. ADC DMA 数据采集

三、常见问题答疑

1. 配置问题

2. 中断与性能问题

3. 实战疑难

四、初学者建议

总结

一、DMA 基础入门

DMA(Direct Memory Access)是一种无需 CPU 参与就能完成数据传输的硬件机制,特别适合高速、批量数据处理场景,可显著降低 CPU 负载,提高系统效率。

HC32L130 DMA 特性

  • 2 个独立 DMA 控制器 (共 2 个通道)
  • 支持外设→内存、内存→外设、内存→内存三种传输方向
  • 支持单次 / 连续传输模式,可配置地址递增 / 固定
  • 传输完成 / 错误中断功能,便于异步处理

二、经典应用案例

1. 串口 (UART) DMA 收发

应用场景

  • 高速数据通信 (如传感器数据采集)
  • 无需 CPU 干预的后台数据传输
  • 实现不定长数据包接收

配置步骤

// 1. 使能时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA, ENABLE); // DMA时钟 RCC_APBPeriphClockCmd(RCC_APBPeriph_UART0, ENABLE); // UART时钟 // 2. 配置GPIO复用 GPIO_SetFunc(GPIO_PORTB, GPIO_PIN_6, GPIO_FUNC_2); // UART0 TX GPIO_SetFunc(GPIO_PORTB, GPIO_PIN_7, GPIO_FUNC_2); // UART0 RX // 3. 初始化UART UART_InitTypeDef UART_InitStructure; UART_StructInit(&UART_InitStructure); UART_InitStructure.BaudRate = 115200; UART_Init(UART0, &UART_InitStructure); // 4. 配置DMA DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA_CH0); // 复位通道0 // 配置DMA接收(外设→内存) DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&UART0->DR; // UART数据寄存器 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)uart_rx_buf; // 接收缓冲区 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 方向:外设→内存 DMA_InitStructure.DMA_BufferSize = 100; // 传输长度 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 8位数据 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8位数据 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 单次传输模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 高优先级 DMA_Init(DMA_CH0, &DMA_InitStructure); // 5. 关联DMA与UART UART_DMACmd(UART0, UART_DMA_Rx, ENABLE); // 使能UART接收DMA // 6. 启动DMA传输 DMA_Cmd(DMA_CH0, ENABLE); // 7. 传输完成中断处理(可选) DMA_ITConfig(DMA_CH0, DMA_IT_TC, ENABLE); // 使能传输完成中断 NVIC_EnableIRQ(DMA_IRQn); // 使能DMA中断

进阶应用:串口 DMA + 定时器实现空闲超时接收

在实际通信中,我们常需要处理不定长数据包,可结合高级定时器实现空闲超时检测:

// 1. 初始化定时器6(高级定时器) Timer_InitTypeDef Timer_InitStructure; Timer_StructInit(&Timer_InitStructure); Timer_InitStructure.Prescaler = 71; // 预分频系数(72MHz/72=1MHz计数时钟) Timer_InitStructure.CounterMode = Timer_CounterMode_Up; Timer_InitStructure.AutoReload = 1000; // 1ms溢出 Timer_Init(TIMER6, &Timer_InitStructure); // 2. 使能定时器中断 Timer_ITConfig(TIMER6, Timer_IT_Update, ENABLE); NVIC_EnableIRQ(TIMER6_IRQn); // 3. 串口DMA接收配置(同前) // 4. 在UART接收中断/回调中重启定时器 void UART0_IRQHandler(void) { if (UART_GetITStatus(UART0, UART_IT_RXNE) != RESET) { Timer_Cmd(TIMER6, ENABLE); // 接收到数据,重启定时器 UART_ClearITPendingBit(UART0, UART_IT_RXNE); } } // 5. 定时器中断处理 void TIMER6_IRQHandler(void) { if (Timer_GetITStatus(TIMER6, Timer_IT_Update) != RESET) { Timer_Cmd(TIMER6, DISABLE); // 关闭定时器 Timer_ClearITPendingBit(TIMER6, Timer_IT_Update); // 处理已接收数据 process_received_data(); // 重新启动DMA接收新数据 DMA_SetCurrDataCounter(DMA_CH0, 100); // 重置传输长度 DMA_Cmd(DMA_CH0, ENABLE); // 重新启动DMA } }

2. ADC DMA 数据采集

应用场景

  • 多通道模拟信号连续采集 (如传感器阵列)
  • 高速数据记录 (如波形采集)
  • ADC 转换结果自动存储处理

配置步骤

// 1. 使能时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA, ENABLE); // DMA时钟 RCC_APBPeriphClockCmd(RCC_APBPeriph_ADC0, ENABLE); // ADC时钟 // 2. 配置GPIO为模拟输入 GPIO_SetFunc(GPIO_PORTA, GPIO_PIN_0, GPIO_FUNC_0); // ADC通道0 // 3. 初始化ADC ADC_InitTypeDef ADC_InitStructure; ADC_StructInit(&ADC_InitStructure); ADC_InitStructure.Resolution = ADC_Resolution_12b; // 12位分辨率 ADC_InitStructure.ScanMode = DISABLE; // 单通道模式 ADC_InitStructure.ContinuousConvMode = ENABLE; // 连续转换 ADC_Init(ADC0, &ADC_InitStructure); // 4. 配置通道 ADC_ChannelConfig(ADC0, ADC_Channel_0, ADC_SampleTime_55_5Cycles); // 通道0,采样时间 // 5. 配置DMA DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA_CH1); // 复位通道1 // ADC→内存传输配置 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC0->DR; // ADC数据寄存器 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_buf; // 存储缓冲区 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 外设→内存 DMA_InitStructure.DMA_BufferSize = 50; // 传输50个数据 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_Init(DMA_CH1, &DMA_InitStructure); // 6. 关联DMA与ADC ADC_DMACmd(ADC0, ENABLE); // 使能ADC DMA // 7. 启动ADC与DMA ADC_Cmd(ADC0, ENABLE); ADC_SoftwareStartConvCmd(ADC0, ENABLE); // 软件触发转换 DMA_Cmd(DMA_CH1, ENABLE); // 启动DMA

三、常见问题答疑

1. 配置问题

Q1:DMA 传输没有启动 / 没有反应?

A1:

  • 检查时钟:确保 DMA 控制器和相关外设时钟已启用
  • 检查通道映射:确认 DMA 通道与外设正确关联 (不同 UART 可能使用不同通道 / 映射)
  • 检查 DMA 使能:调用DMA_Cmd(DMA_CHx, ENABLE)启动传输
  • 检查外设 DMA 使能:如 UART 需调用UART_DMACmd(UARTx, UART_DMA_Rx/Tx, ENABLE)

Q2:DMA 传输数据错误或乱码?

A2:

  • 检查数据宽度配置:确保 DMA 与外设数据宽度一致 (字节 / 半字 / 字)
  • 检查地址对齐:某些情况下需确保内存地址为 4 字节对齐 (可用__align(4)修饰数组)
  • 检查传输方向:确认 DMA_DIR 设置正确 (外设→内存或内存→外设)
  • 检查缓冲区越界:确保传输长度不超过目标缓冲区大小

2. 中断与性能问题

Q3:DMA 传输完成后没有触发中断?

A3:

  • 检查中断使能:调用DMA_ITConfig(DMA_CHx, DMA_IT_TC/DMA_IT_ERR, ENABLE)使能中断
  • 检查 NVIC 配置:确保对应 DMA 通道的中断在 NVIC 中已启用
  • 检查中断优先级:确保 DMA 中断优先级足够高,不会被其他中断屏蔽
  • 检查中断标志:必要时手动清除中断标志位

Q4:DMA 传输影响系统性能 / 导致其他功能异常?

A4:

  • 检查 DMA 优先级:合理分配通道优先级,避免高优先级 DMA 长时间占用总线
  • 检查总线竞争:多个 DMA 通道同时工作时可能产生竞争,可错开传输时间或调整优先级
  • 考虑使用循环模式:在连续数据采集场景下,使用循环模式可减少重新配置开销

3. 实战疑难

Q5:串口 DMA 接收时,数据丢失或不完整?

A5:

  • 检查缓冲区大小:确保缓冲区足够大,能容纳最大数据包
  • 使用空闲超时机制:结合定时器检测数据包结束,避免缓冲区溢出
  • 启用传输完成中断:在中断中处理接收到的数据,及时重启 DMA 接收新数据

Q6:ADC+DMA 采集的数据总是 0 或固定值?

A6:

  • 检查 ADC 通道配置:确认使用的通道和引脚正确,且已设置为模拟功能
  • 检查参考电压:确保 VDDA 和参考电压连接正确,电压范围符合要求
  • 检查 DMA 数据宽度:ADC 输出为 12 位,应设置为半字 (16 位) 传输,高位补零不影响精度

Q7:使用 DMA 时系统死机,但无错误标志?

A7:

  • 最常见原因:DMA 与其他高速外设同时工作时,总线访问冲突导致系统锁死
  • 解决方案
    1. 降低采样率 / 传输速率
    2. 增加 DMA 传输间隔
    3. 确保关键代码段使用DMA_Cmd(DMA_CHx, DISABLE)暂时关闭 DMA
    4. 为 DMA 传输分配专用通道并设置适当优先级

四、初学者建议

  1. 从简单案例入手:先掌握内存→内存的数据搬运,理解 DMA 基本工作流程

    // 内存到内存传输示例 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory; // 内存→内存 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)src_buf; // 源地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)dst_buf; // 目标地址
  2. 善用 HAL 库函数:HC32L130 提供完善的 HAL 库,可大幅简化 DMA 配置,减少寄存器操作错误

  3. 调试技巧

    • 使用断点观察 DMA 寄存器状态 (DMA_DTCTLx、DMA_CNDTR 等)
    • 使能 DMA 中断,在中断处理函数中打印调试信息
    • 利用示波器 / 逻辑分析仪观察外设与总线时序
  4. 资源规划

    • HC32L130 只有 2 个 DMA 通道,合理分配很重要
    • 高优先级任务 (如实时通信) 分配高优先级通道
    • 避免多个任务同时使用同一通道造成冲突

总结

DMA 是 HC32L130 等现代 MCU 的重要功能,能显著提升系统性能和实时性。通过本文介绍的串口和 ADC 应用案例,你已掌握 DMA 最常见的使用场景。建议先在开发板上实践基础例程,再尝试结合定时器等外设实现更复杂的功能,逐步建立对 DMA 技术的深入理解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 12:09:01

探索 BMS 动力电池管理系统仿真:从模型到实战

bms动力电池管理系统仿真 Battery Simulink电池平衡控制策略模型 动力电池管理系统仿真 BMS Battery Simulink 控制策略模型, 动力电池物理模型,需求说明文档。 BMS算法模型包含状态切换模型、SOC估计模型(提供算法说明文档)、电池平衡模型、功率限制模…

作者头像 李华
网站建设 2026/4/16 10:45:08

常用Git命令清单

1. 新建代码库# 在当前目录新建一个Git代码库 $ git init ​ # 新建一个目录,将其初始化为Git代码库 $ git init [project-name] ​ # 下载一个项目和它的整个代码历史 $ git clone [url]2. 配置Git的设置文件为.gitconfig,它可以在用户主目录下&#xf…

作者头像 李华
网站建设 2026/4/13 0:06:39

Agentic RAG 新手指南

大语言模型 (LLMs) 几乎完全改变了我们获取和理解信息的方式。这些先进的 AI 系统经过大量数据的训练,对于识别语言的模式和意义不在话下。借助 LLM,人们不论是探索新想法、学习新事物,还是快速高效地找到答案都变得比以前更容易。 早期的传统…

作者头像 李华
网站建设 2026/4/16 9:09:45

构建高效学习推荐系统(仅限TOP10%机构掌握的核心方法论)

第一章:教育 AI Agent 学习推荐系统的演进与核心价值随着人工智能技术的快速发展,教育领域正经历一场由AI驱动的深刻变革。AI Agent在学习推荐系统中的应用,已从早期基于规则的简单推送,逐步演进为融合深度学习、知识图谱与个性化…

作者头像 李华
网站建设 2026/4/15 8:10:17

这是一份大模型入门手册!(附学习文档)

今年秋招,大模型相关的岗位开出的薪资都是比较高的,而且现在大模型尚未成熟落地,有很多机会 如果你想要从事算法相关的工作,大模型算法及应用无疑是一个比较好的选择 很多同学学习大模型的过程中,可能会比较迷茫&#…

作者头像 李华