STM32H7 零 CPU 干预多通道 ADC 采样:MDMA 与 LPTIM 联动实战
为什么需要 MDMA + LPTIM 联动?
传统 ADC 采样通常依赖 CPU 处理中断或 DMA 传输完成回调,但高频采样时中断频繁,CPU 占用率飙升,影响系统实时性。STM32H7 系列内置 MDMA(Master DMA)和 LPTIM(低功耗定时器),可构建一条完全自主的数据通路:
- LPTIM 产生精确的定时触发信号,启动 ADC 转换,无需 CPU 参与。
- ADC 按序列自动扫描多个通道,并将结果存入数据寄存器。
- MDMA 在 ADC 转换完成后自动将数据搬运至内存缓冲区,支持双缓冲或环形缓冲,搬运完成后可触发中断(可选)。
整个流程 CPU 零干预,仅在需要时处理数据,极大提升系统效率。
硬件与软件准备
- 硬件:STM32H743 开发板(或任意 H7 系列)、信号源(如电位器或函数发生器)。
- 软件:STM32CubeIDE(或 Keil MDK)、STM32CubeMX(用于初始化配置)。
- 知识基础:熟悉 STM32 基础外设(ADC、DMA、定时器)及中断概念。
工作原理详解
1. LPTIM 触发 ADC
LPTIM 可配置为 PWM 输出模式,产生周期性脉冲,连接到 ADC 的触发源(如 ADCx_EXTTRIG)。每个脉冲启动一次 ADC 转换,转换频率由 LPTIM 的时钟和预分频决定。
2. ADC 多通道扫描
ADC 配置为扫描模式,序列包含多个通道(如 IN0、IN1、IN2)。每次触发后,ADC 按序列顺序转换所有通道,并将结果依次存入数据寄存器(或 DMA 请求)。
3. MDMA 搬运数据
MDMA 支持内存到内存、外设到内存的传输,且具有更高的带宽和更灵活的描述符配置。此处将 ADC 的数据寄存器(外设地址)作为源,内存缓冲区作为目标,每次 ADC 转换完成后产生 DMA 请求,MDMA 自动搬运。
4. 零 CPU 干预的实现
通过配置 MDMA 的循环模式,数据持续填充缓冲区,当缓冲区满时可产生中断(可选),CPU 仅在需要时处理数据,其余时间可进入低功耗模式或执行其他任务。
配置步骤(基于 STM32CubeMX)
1. 时钟配置
- 将系统时钟配置为 400MHz(H7 最高主频)。
- 确保 LPTIM 时钟源选择(如 LSI 或 APB 时钟),并计算所需触发频率。
2. LPTIM 配置
- 选择 LPTIM1,时钟源设为 LSI(典型 32kHz)或 APB 时钟。
- 配置为 PWM 模式,设置周期和脉冲宽度,例如周期 1ms(1kHz 触发频率)。
- 使能输出引脚(如 PB5),并连接到 ADC 的外部触发引脚(需硬件连接)。
3. ADC 配置
- 选择 ADC1,启用扫描模式,添加多个通道(如 IN0、IN1、IN2)。
- 设置采样时间(如 32.5 周期),确保转换精度。
- 触发源选择外部触发,触发边沿选择上升沿(对应 LPTIM 脉冲)。
- 使能 DMA 请求(但使用 MDMA 时,需将 DMA 请求映射到 MDMA)。
4. MDMA 配置
- 在 MDMA 配置界面,添加一个通道,源地址设为 ADC1 的数据寄存器地址(如
ADC1->DR),目标地址设为内存缓冲区。 - 设置传输方向为外设到内存,数据宽度为半字(16 位,ADC 结果)。
- 启用循环模式,缓冲区大小设为通道数 × 采样次数(例如 3 通道 × 100 次 = 300 半字)。
- 使能传输完成中断(可选),用于通知 CPU 处理数据。
5. 生成代码并整合
生成初始化代码后,在 main.c 中启动 LPTIM、ADC 和 MDMA,并实现中断回调(如有)。
完整代码示例(核心部分)
// main.c 中初始化后启动
// 定义 ADC 缓冲区(3 通道,100 次采样)
#define ADC_CHANNELS 3
#define SAMPLE_COUNT 100
uint16_t adc_buffer[ADC_CHANNELS * SAMPLE_COUNT];
// 启动 LPTIM、ADC 和 MDMA
void start_acquisition(void) {
// 启动 LPTIM PWM 输出
HAL_LPTIM_PWM_Start(&hlptim1, LPTIM_PERIOD, LPTIM_PULSE);
// 启动 ADC 的 DMA 请求(实际由 MDMA 处理)
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_buffer, ADC_CHANNELS * SAMPLE_COUNT);
// 启动 MDMA 传输(源为 ADC 数据寄存器,目标为缓冲区)
HAL_MDMA_Start_IT(&hmdma_mdma_ch0, (uint32_t)&hadc1.Instance->DR, (uint32_t)adc_buffer, ADC_CHANNELS * SAMPLE_COUNT, 0);
}
// MDMA 传输完成回调(可选)
void HAL_MDMA_IRQHandler(void) {
HAL_MDMA_IRQHandler(&hmdma_mdma_ch0);
}
void HAL_MDMA_TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) {
// 数据已就绪,可处理 adc_buffer
process_adc_data(adc_buffer, ADC_CHANNELS * SAMPLE_COUNT);
}
注意事项
- 硬件连接:LPTIM 输出引脚必须与 ADC 的外部触发引脚相连(如通过跳线或 PCB 走线),否则无法触发。
- 时钟同步:确保 LPTIM 和 ADC 的时钟域匹配,避免触发抖动。
- 缓冲区大小:MDMA 循环模式下,缓冲区大小需为通道数的整数倍,否则数据错位。
- 中断优先级:若启用 MDMA 中断,建议设置较高优先级,避免数据覆盖。
- 功耗优化:在无采样需求时,可停止 LPTIM 和 ADC,进入低功耗模式,进一步节能。
- 调试技巧:使用逻辑分析仪观察 LPTIM 输出和 ADC 触发信号,验证时序正确性。
总结
通过 MDMA 与 LPTIM 联动,STM32H7 实现了真正的零 CPU 干预多通道 ADC 采样,大幅降低 CPU 负载,适用于高速数据采集、传感器融合等场景。本文提供的配置和代码可直接应用于实际项目,开发者可根据需求调整采样频率、通道数和缓冲区大小。掌握此技术,你将能构建更高效、更实时的嵌入式系统。