# 引言 STM32F4 系列凭借高性能 Cortex-M4 内核和丰富外设,广泛用于工业与消费电子。但在电池供电场景下,低功耗模式(Sleep、Stop、Standby)是延长续航的关键。其中 Stop 模式功耗极低(典型值 ~20µA),但默认情况下,DMA 外设的时钟被关闭,导致无法在低功耗下持续搬运数据(如 ADC 采样、UART 接收)。本文针对这一问题,探讨 DMA 外设的保持策略,并通过实测电流对比,给出工程实践建议。 # 低功耗模式与 DMA 的冲突 ## 1. STM32F4 低功耗模式概览 - **Sleep 模式**:CPU 停止,外设时钟保持,功耗 ~10mA(取决于外设)。 - **Stop 模式**:所有时钟停止(除 RTC 和备份域),SRAM 内容保留,功耗 ~20µA。 - **Standby 模式**:仅备份域供电,SRAM 丢失,功耗 ~2µA。 在 Stop 模式下,DMA 控制器(属于 AHB1 外设)的时钟被关闭,因此 DMA 无法工作。若需在低功耗下保持 DMA 功能,必须采取特殊策略。 ## 2. DMA 外设保持的难点 - **时钟域**:DMA 挂在 AHB1 总线上,Stop 模式下 AHB1 时钟关闭。 - **唤醒源**:DMA 本身不能作为唤醒源,需借助外部中断或 LPTIM。 - **数据完整性**:若 DMA 在进入 Stop 前未完成传输,数据可能丢失。 # 三种 DMA 保持策略 ## 策略一:禁止 DMA,进入 Stop 前手动搬运 **原理**:在进入 Stop 前,关闭 DMA 中断,将关键数据从外设寄存器拷贝到 SRAM,然后进入 Stop。唤醒后重新初始化 DMA。 **优点**:实现简单,功耗最低(无额外外设时钟)。 **缺点**:无法在低功耗期间持续采集数据,适用于间歇性工作场景。 ## 策略二:保持 DMA 时钟(不推荐) **原理**:在进入 Stop 前,通过设置 RCC 的 AHB1 时钟使能位,强制保持 DMA 时钟。但 STM32F4 的 Stop 模式设计会强制关闭所有时钟,此方法通常无效,除非使用特殊寄存器(如 RCC->AHB1ENR 的位操作),但实测会破坏低功耗特性。 **实测**:在 Stop 模式下,即使强制使能 DMA 时钟,电流会从 20µA 升至 1.2mA,且 DMA 仍无法正常工作(因为总线时钟已停)。因此不推荐。 ## 策略三:使用 LPTIM + DMA 在 Sleep 模式(推荐) **原理**:将系统进入 Sleep 模式(而非 Stop),此时 CPU 停止,但外设时钟(包括 DMA)保持。通过 LPTIM 定时触发 DMA 搬运数据,CPU 唤醒后处理。Sleep 模式功耗约 5mA(取决于外设),但可保持 DMA 功能。 **改进**:若需更低功耗,可结合 RTC 唤醒,在数据采集间隙进入 Stop,但需设计状态机。 # 实测电流对比 测试环境:STM32F407VET6 开发板,VCC=3.3V,使用高精度万用表(精度 0.1µA)测量系统总电流(外设全部关闭,仅保留必要部分)。 | 策略 | 模式 | 电流(µA) | DMA 功能 | 备注 | |------|------|------------|----------|------| | 策略一 | Stop | 18.5 | 无 | 进入前手动搬运 | | 策略二 | Stop(强制时钟) | 1250 | 异常 | 功耗剧增 | | 策略三 | Sleep | 5200 | 正常 | 外设时钟全开 | | 策略三优化 | Sleep + 外设时钟门控 | 2100 | 正常 | 仅开启 DMA 和 LPTIM | 从数据看,策略一功耗最低,但牺牲功能;策略三优化版在功耗与功能间取得平衡,适合需要持续采样的场景。 # 代码实现:策略三优化版 以下代码演示如何在 Sleep 模式下使用 LPTIM 触发 DMA 搬运 ADC 数据,并保持低功耗。 ```c // 初始化 LPTIM(使用 LSI 时钟,约 32kHz) void LPTIM_Init(void) { RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN; RCC->CSR |= RCC_CSR_LSION; // 使能 LSI while (!(RCC->CSR & RCC_CSR_LSIRDY)); LPTIM1->CR = 0; // 关闭 LPTIM LPTIM1->CFGR = LPTIM_CFGR_PRESC_1; // 分频 1 LPTIM1->ARR = 32000; // 1 秒定时 LPTIM1->IER = LPTIM_IER_ARRMIE; // 使能自动重载中断 NVIC_EnableIRQ(LPTIM1_IRQn); LPTIM1->CR = LPTIM_CR_ENABLE; // 使能 LPTIM } // DMA 配置(ADC1 -> SRAM) void DMA_Init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; DMA2_Stream0->CR = 0; // 复位 DMA2_Stream0->PAR = (uint32_t)&ADC1->DR; DMA2_Stream0->M0AR = (uint32_t)adc_buffer; DMA2_Stream0->NDTR = 128; DMA2_Stream0->CR = DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_TCIE | DMA_SxCR_EN; NVIC_EnableIRQ(DMA2_Stream0_IRQn); } // 进入 Sleep 模式 void Enter_Sleep(void) { __WFI(); // 等待中断 } // LPTIM 中断回调:启动 DMA 搬运 void LPTIM1_IRQHandler(void) { if (LPTIM1->ISR & LPTIM_ISR_ARRM) { LPTIM1->ICR = LPTIM_ICR_ARRMCF; // 清除标志 DMA2_Stream0->CR |= DMA_SxCR_EN; // 使能 DMA } } // 主循环 int main(void) { SystemClock_Config(); ADC_Init(); LPTIM_Init(); DMA_Init(); while (1) { Enter_Sleep(); // 唤醒后处理数据 Process_Data(); } } ``` **注意事项**: - 进入 Sleep 前,需关闭不必要的外设时钟(通过 RCC->AHB1ENR 和 APB1ENR 门控),以降低功耗。 - LPTIM 使用 LSI 时钟,精度较低,但适合定时唤醒。 - DMA 传输完成中断需在唤醒后处理,避免在中断中长时间运行。 # 总结 STM32F4 在低功耗模式下保持 DMA 功能需权衡功耗与复杂度。若允许间歇性工作,优先选择 Stop 模式 + 手动搬运;若需持续采集,推荐 Sleep 模式 + LPTIM 触发 DMA,并通过时钟门控优化功耗。实测数据表明,策略三优化版可将电流控制在 2.1mA 左右,满足多数低功耗应用。开发者应根据具体需求选择合适策略,并注意外设时钟管理。