# 引言 在嵌入式系统设计中,低功耗与数据完整性往往相互制约。STM32F4 系列凭借其丰富的外设和灵活的电源管理,成为许多便携式设备的首选。STOP 模式是 STM32F4 低功耗策略中的关键一环,它关闭大部分时钟,但保留 SRAM 和寄存器内容,功耗可低至 10µA 左右。然而,要在 STOP 模式下保持 RTC 计时并让 DMA 持续采集传感器数据,需要精心的时钟配置和外设协作。本文将带你一步步实现这一方案。 # 1. 原理剖析:STOP 模式下的外设行为 ## 1.1 STOP 模式概述 STOP 模式基于 Cortex-M4 内核的 SLEEPDEEP 机制。进入 STOP 后,所有时钟(包括 HSE、HSI、PLL)均被关闭,但以下模块保持供电: - **SRAM 和寄存器**:内容不丢失。 - **RTC**:若使用 LSI 或 LSE 作为时钟源,可继续运行。 - **备份域**:包括备份寄存器、RTC 等。 - **部分 GPIO**:可配置为唤醒源。 关键点:DMA 在 STOP 模式下默认停止,因为其时钟(AHB/APB)被关闭。但我们可以通过**外部事件**或**RTC 唤醒**来短暂唤醒系统,执行 DMA 传输,再重新进入 STOP,实现“间歇性采集”。 ## 1.2 RTC 与 DMA 的协作策略 - **RTC 唤醒**:配置 RTC 闹钟或唤醒定时器,周期性产生唤醒事件(如每 1 秒)。 - **DMA 采集**:唤醒后,系统时钟恢复,DMA 可快速将 ADC 或传感器数据搬运到内存,然后再次进入 STOP。 - **数据缓存**:使用双缓冲或环形缓冲区,避免数据覆盖。 # 2. 硬件准备 - 开发板:STM32F407 Discovery 或类似板卡 - 传感器:如 MPU6050(I2C 接口)或内部 ADC 采样 - 工具:STM32CubeIDE 或 Keil MDK # 3. 配置步骤 ## 3.1 时钟配置 1. **使能 LSE 或 LSI**:RTC 需要独立时钟源。推荐使用 LSE(32.768kHz),精度更高。 ```c RCC_BackupResetCmd(ENABLE); RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); ``` 2. **配置系统时钟**:正常运行时使用 HSE+PLL,进入 STOP 前无需切换,因为 STOP 会关闭它们。 ## 3.2 RTC 配置 1. **使能 PWR 时钟**:访问电源控制寄存器。 ```c RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); ``` 2. **配置 RTC 唤醒定时器**: ```c RTC_WakeUpCmd(DISABLE); RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); // 1Hz 计数 RTC_SetWakeUpCounter(1); // 1 秒唤醒 RTC_WakeUpCmd(ENABLE); RTC_ITConfig(RTC_IT_WUT, ENABLE); ``` 3. **使能 RTC 中断**:在 NVIC 中配置。 ## 3.3 DMA 配置(以 ADC 为例) 1. **初始化 ADC**:配置为连续转换或外部触发。 2. **配置 DMA**:将 ADC 数据寄存器映射到内存缓冲区。 ```c DMA_InitTypeDef DMA_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); DMA_DeInit(DMA2_Stream0); DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)adc_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_Init(DMA2_Stream0, &DMA_InitStructure); DMA_Cmd(DMA2_Stream0, ENABLE); ``` ## 3.4 进入 STOP 模式 在进入 STOP 前,确保 DMA 已停止(或暂停),然后执行: ```c PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); ``` 注意:进入 STOP 后,DMA 时钟关闭,传输会暂停。唤醒后需重新使能。 ## 3.5 唤醒处理 在 RTC 唤醒中断中: 1. 清除中断标志。 2. 重新配置系统时钟(如果使用 HSE/PLL,需重新初始化)。 3. 重新使能 DMA 并启动传输。 4. 处理数据(如存储到 Flash 或发送)。 5. 再次进入 STOP。 # 4. 完整代码示例 以下代码演示了核心逻辑(基于标准外设库): ```c // 全局变量 uint16_t adc_buffer[128]; volatile uint8_t wakeup_flag = 0; void RTC_WKUP_IRQHandler(void) { if (RTC_GetITStatus(RTC_IT_WUT) != RESET) { RTC_ClearITPendingBit(RTC_IT_WUT); wakeup_flag = 1; } } void SystemClock_Config(void) { // 配置 HSE、PLL,使能 FLASH 预取等(略) } void ADC_DMA_Config(void) { // 初始化 ADC1 和 DMA2_Stream0(见上文) } void Enter_StopMode(void) { // 停止 DMA 传输 DMA_Cmd(DMA2_Stream0, DISABLE); // 进入 STOP 模式 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 唤醒后重新配置系统时钟 SystemClock_Config(); // 重新使能 DMA DMA_Cmd(DMA2_Stream0, ENABLE); } int main(void) { // 初始化时钟、RTC、ADC、DMA、NVIC SystemClock_Config(); RTC_Config(); ADC_DMA_Config(); NVIC_Config(); while (1) { Enter_StopMode(); if (wakeup_flag) { wakeup_flag = 0; // 处理采集到的数据(此时 DMA 已重新使能,但需等待传输完成) // 例如:存储到外部 Flash 或通过 UART 发送 } } } ``` # 5. 注意事项 - **时钟恢复**:从 STOP 唤醒后,系统时钟默认使用 HSI,若需高性能,必须重新配置 PLL。 - **DMA 缓冲管理**:使用双缓冲时,注意切换时机,避免数据覆盖。 - **RTC 时钟源**:LSE 在低功耗下更稳定,但 LSI 无需外部晶振,适合成本敏感设计。 - **唤醒源**:除了 RTC,还可配置 EXTI 外部引脚唤醒,但需注意 GPIO 状态。 - **调试**:在 STOP 模式下,SWD 调试可能失效,建议使用串口打印调试信息。 - **功耗测量**:实际功耗受 GPIO 漏电流影响,未使用的引脚应配置为模拟输入或下拉。 # 6. 总结 通过合理配置 RTC 唤醒和 DMA 传输,STM32F4 可以在 STOP 模式下实现低功耗与数据采集的平衡。本文提供的方案适用于周期性数据记录场景,如环境监测、可穿戴设备等。开发者可根据实际需求调整唤醒周期和采样率,进一步优化功耗。记住,低功耗设计是一个系统工程,需从硬件到软件全面考虑。