3、DMA基础与STM32H7 DMA控制器:DMA传输原理、通道配置、循环模式与中断机制

在STM32H7系列中,ADC连续采样产生的高速数据流如果全部依赖CPU搬运,不仅会大量占用中断处理时间,还会因Cache一致性维护和总线仲裁带来不可控的延迟抖动。DMA(Direct Memory Access)正是为解决这一矛盾而设计的硬件搬运引擎。本章将深入剖析STM32H7 DMA控制器的传输原理、通道选择策略、循环模式配置以及中断同步机制,为后续ADC与AI推理之间的Buffer同步打下坚实的底层基础。

外设侧 DMA控制器 存储器侧 ADC1 DR数据寄存器 TIM1_TRGO 触发源 通道选择 DMA1/DMA2 FIFO/突发 数据宽度/突发 循环模式 + 半传输/传输完成中断 NDTR递减 / 重载 ADC_Buffer SRAM/DTCM AI_Input 对齐后数据 外设请求 存储器写 中断: 半传输 / 传输完成 / 错误 循环模式: NDTR重载,地址回绕 关键配置链路 1. 选择DMA控制器与通道(DMA1_Stream0/1/2/3 或 DMA2_Stream0/1/2/3) 2. 配置外设地址(ADC1->DR)与存储器地址(Buffer首地址) 3. 设置数据宽度(Byte/HalfWord/Word)、方向(外设到内存)、突发模式 4. 使能循环模式(Circular)与中断(HT/TC/Error) 5. 启动DMA并触发ADC转换(软件触发或定时器触发) 6. 在中断回调中切换Buffer指针,保证AI推理与DMA写入互不干扰

3.1 DMA传输核心原理:从外设到存储器的零拷贝搬运

DMA的本质是替代CPU完成外设与存储器之间的数据复制。在STM32H7中,DMA控制器作为AHB总线上的主设备,可以发起总线传输请求。当ADC完成一次转换并将结果写入其数据寄存器(ADCx_DR)时,会产生一个DMA请求信号。DMA控制器收到该请求后,根据预先配置的源地址、目的地址、传输宽度和传输计数,自动完成一次数据搬运,整个过程无需CPU干预。

STM32H7的DMA控制器分为DMA1和DMA2两个实例,每个实例包含8个独立的可配置数据流(Stream)。每个数据流可以关联多个外设请求,但同一时刻只能服务一个外设。与F1/F4系列不同,H7的DMA采用双AHB端口设计,可以同时访问不同存储器区域,这为ADC采样与AI推理数据并行处理提供了硬件基础。

关键参数理解:
  • NDTR(Number of Data Transfer Register):指定剩余传输次数,每次传输后自动递减,当减至0时触发传输完成事件。
  • 数据宽度(PSIZE/MSIZE):外设侧与存储器侧的数据宽度可独立配置,支持Byte(8位)、HalfWord(16位)、Word(32位)。若两侧宽度不一致,DMA会自动进行数据拼接或拆分。
  • 突发模式(Burst):支持单次传输或4/8/16次突发传输,突发模式可提高总线利用率,但要求存储器地址对齐。

3.2 通道配置与选择策略:匹配ADC采样需求

STM32H7的DMA通道映射并非固定不变,而是通过DMA_SxCR寄存器中的CHSEL位段进行动态选择。以ADC1为例,其DMA请求可以映射到DMA1的Stream0/1/2/3或DMA2的Stream0/1/2/3,具体取决于系统设计中的总线负载均衡需求。

DMA实例 可用Stream ADC1映射选项 适用场景
DMA1 Stream0~7 Stream0/1/2/3 轻负载场景,ADC采样率较低(< 1Msps)
DMA2 Stream0~7 Stream0/1/2/3 高采样率或同时使用多个外设DMA的场景

配置DMA通道时,需要遵循以下操作流程:

  1. 确定DMA实例与Stream:查看参考手册的DMA请求映射表,选择未被占用的Stream。
  2. 配置外设地址:将外设地址寄存器设置为ADC1->DR的地址(0x40022040),并设置地址递增模式为固定(外设地址不递增)。
  3. 配置存储器地址:指向ADC采样Buffer的首地址,使能存储器地址递增模式。
  4. 设置传输方向:选择外设到存储器(Peripheral-to-Memory)。
  5. 配置数据宽度:若ADC分辨率为12位或16位,建议外设侧与存储器侧均使用HalfWord(16位),避免数据错位。
  6. 选择优先级:高优先级Stream会优先获得总线访问权,但需注意避免低优先级Stream饿死。
常见误区:很多开发者直接将DMA2的Stream0用于ADC,却忽略了DMA2同时服务以太网MAC或SDMMC等高速外设。当多个高带宽外设同时请求DMA时,总线仲裁延迟会导致ADC采样数据丢失。建议在系统设计阶段就规划好DMA资源分配,优先将实时性要求高的ADC采样放在独立且负载较低的Stream上。

3.3 循环模式与中断机制:实现无间隙采样

在AI推理场景中,通常需要连续采集一段时间的ADC数据(例如256点或512点FFT窗口)。如果使用单次传输模式,每次传输完成后需要CPU重新配置NDTR并启动下一次传输,这会引入不可忽略的间隙。循环模式(Circular Mode)正是为解决这一问题而设计:当NDTR递减至0时,硬件自动将NDTR重载为初始值,并将存储器地址指针回绕到Buffer起始地址,从而形成无限循环的数据流。

循环模式配合中断机制,可以实现双缓冲(Ping-Pong)策略:

  • 半传输中断(HT):当NDTR递减到初始值的一半时触发,此时Buffer的前半部分已填满,CPU可以处理前半部分数据。
  • 传输完成中断(TC):当NDTR递减至0时触发,此时Buffer的后半部分已填满,CPU可以处理后半部分数据。

通过交替使用HT和TC中断,CPU可以在DMA持续写入Buffer的同时,处理已填满的半区数据,实现采样与计算的重叠执行。这种机制对于AI推理至关重要——当推理引擎处理当前数据块时,DMA已经在填充下一块数据,从而最大化系统吞吐量。

工程经验:在STM32H7上,建议将ADC采样Buffer放置在DTCM RAM或SRAM中,并确保Buffer地址按32字节对齐。H7的Cache行大小为32字节,若Buffer未对齐,DMA写入的数据可能部分在Cache中,部分在RAM中,导致CPU读取到不一致的数据。使用__ALIGNED(32)属性声明Buffer即可避免该问题。

3.4 实战案例:双缓冲ADC采样与AI推理数据同步

假设我们需要设计一个基于STM32H743的振动信号分析系统,ADC以1Msps采样率连续采集加速度传感器信号,AI模型需要每256个采样点执行一次推理。系统约束如下:

  • ADC1配置为12位分辨率,由TIM1_TRGO事件触发采样,采样率1Msps。
  • DMA2_Stream0配置为循环模式,外设地址为ADC1->DR,存储器地址为adc_buffer[512](双缓冲,每半区256点)。
  • AI推理引擎运行在Cortex-M7内核,需要从adc_buffer中读取数据并进行FFT和分类。

初始现象:直接使用单缓冲模式时,CPU在TC中断中处理数据,但处理期间DMA停止采样,导致采样率下降至约500Ksps,且数据出现周期性丢失。

分析过程:通过逻辑分析仪观察DMA请求信号,发现TC中断服务函数执行时间约30μs(包含FFT计算),而DMA在中断期间无法继续传输,导致采样间隙。进一步检查发现,CPU在中断中访问adc_buffer时,由于Cache未失效,读取到的是旧数据。

解决方案:

  1. 将adc_buffer声明为__ALIGNED(32) uint16_t adc_buffer[512],并放置在DTCM RAM区域(地址0x20000000),该区域不经过Cache,避免一致性问题。
  2. 启用DMA2_Stream0的半传输中断和传输完成中断,在HT中断中处理adc_buffer[0]~[255],在TC中断中处理adc_buffer[256]~[511]。
  3. 将AI推理任务放在主循环中执行,通过标志位判断当前可用的数据半区,避免在中断中执行耗时计算。

结果对比:优化后,采样率稳定在1Msps,无数据丢失。CPU在HT中断中仅需复制数据指针并置位标志位(约2μs),AI推理在主循环中完成,整体系统吞吐量提升约40%。

风险提示:双缓冲方案中,必须确保CPU处理数据的速度快于DMA填充半区数据的速度。若AI推理耗时超过半区填充时间(本例中为256μs),则会出现数据覆盖。此时需要增大Buffer容量或降低采样率,否则推理结果将基于不完整数据。

3.5 常见误区与总结复盘

误区 后果 正确做法
未配置存储器地址递增 所有数据写入同一地址,Buffer内容被覆盖 使能MSIZE递增,确保数据顺序写入
外设与存储器数据宽度不一致 数据错位,AI推理结果异常 统一使用HalfWord或Word宽度
忽略Cache一致性 CPU读取到陈旧数据 使用DTCM或执行Cache清理/失效操作
中断服务函数执行过长 DMA传输被阻塞,采样丢失 中断中仅做标志位操作,重计算移至主循环