硬汉嵌入式论坛

 找回密码
 立即注册
查看: 446|回复: 9
收起左侧

[有问必答] STM32F405芯片低功耗——停止模式的进入与退出

[复制链接]

8

主题

57

回帖

81

积分

初级会员

积分
81
发表于 2025-7-8 16:28:08 | 显示全部楼层 |阅读模式
目前遇到一个低功耗的问题,使用STM32F405芯片配置低功耗停止模式,现阶段想法是通过一个IO:PB6,将其配置成如下触发模式:

[C] 纯文本查看 复制代码
void bsp_InitKeyEXTI(void) {
  EXTI_InitTypeDef EXTI_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  /* 使能SYSCFG时钟 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

  /* 连接 EXTI Line6 到 PB6 引脚 */
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource6);

  EXTI_InitStructure.EXTI_Line = EXTI_Line6;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);

  /* 设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

  /* 中断优先级配置 最低优先级
   * 这里一定要分开的设置中断,不能够合并到一个里面设置 */
  NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void EXTI9_5_IRQHandler(void) {
  static uint8_t cnt = 0;
  if (EXTI_GetITStatus(EXTI_Line6) != RESET) {
    // EXTI->IMR &= ~(1 << 6);             /* 关闭中断       */
    EXTI_ClearITPendingBit(EXTI_Line6); /* 清除中断标志位 */
    g_ucKey1IRQ = 6;
  }
}

//进入低功耗逻辑

if (close_flag) {
    // /* 在这里关闭嘀嗒定时器 因滴答中断会唤醒睡眠 */
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
    SystemInit();
    // /* 在这里开启嘀嗒定时器 */
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
  }

想通过PB6这个引脚,当程序处于正常运行状态时,触发PB6引脚,产生中断,延时30s后进入——停止模式,并且在低功耗模式下,再次通过PB6引脚触发产生中断,再次退出低功耗模式。按照这种方式设计,根据电流大小,确实有进入低功耗模式,但是却无法正常进入正常运行状态。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2025-7-8 17:27:06 | 显示全部楼层
意思是无法正常唤醒停机模式?
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2025-7-9 08:43:07 | 显示全部楼层
eric2013 发表于 2025-7-8 17:27
意思是无法正常唤醒停机模式?

是的,我目前使用的是进入停机模式跟退出停机模式用的同一个IO,不知道有没有问题,唤醒停机模式,程序没有正常运行,重新复位mcu才正常工作
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2025-7-10 08:59:22 | 显示全部楼层
Ainit 发表于 2025-7-9 08:43
是的,我目前使用的是进入停机模式跟退出停机模式用的同一个IO,不知道有没有问题,唤醒停机模式,程序没 ...

我们V5或者V6开发板的标准库网盘做了很多停机模式的例子,检索关键词停机即可。

估计是停机唤醒方式有问题。
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2025-7-11 10:40:13 | 显示全部楼层
eric2013 发表于 2025-7-10 08:59
我们V5或者V6开发板的标准库网盘做了很多停机模式的例子,检索关键词停机即可。

估计是停机唤醒方式有 ...

您好,参考了您给的例子,目前能够进入停止模式,进入之前只关闭了滴答定时器,然后直接调用系统
[C] 纯文本查看 复制代码
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
,这种情况下,系统电流大概30mA,电流参考值与参考手册上的差距很大,想请教一下您,这种情况下是什么原因导致的?另外在退出停止模式后,无论先初始化系统时钟还是先打开滴答定时器,程序都会有出现滴答定时器定时不准,或者退出后程序没有正常启动。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2025-7-12 09:24:27 | 显示全部楼层
Ainit 发表于 2025-7-11 10:40
您好,参考了您给的例子,目前能够进入停止模式,进入之前只关闭了滴答定时器,然后直接调用系统[mw_shl_ ...

1、板载外设功耗还在。
2、这个有必要在个简单的hello world程序上测试下,如果正常,可能与你的程序有关。
回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2025-7-16 14:26:03 | 显示全部楼层
eric2013 发表于 2025-7-12 09:24
1、板载外设功耗还在。
2、这个有必要在个简单的hello world程序上测试下,如果正常,可能与你的程序有 ...

根据您所说的测试验证后,结合现有的硬件,功耗很难再降低了,目前低功耗满足现阶段使用,就不纠结了。另外滴答定时器不准和程序不再启动,根据现有硬件,我直接做了一个触发退出低功耗模式后,我自己将单片机复位。暂时只能做到这一步了。
回复

使用道具 举报

5

主题

212

回帖

227

积分

高级会员

积分
227
发表于 2025-7-16 16:10:29 | 显示全部楼层
本帖最后由 honami520 于 2025-7-16 16:13 编辑

最近这1个月刚刚做STM32F407的低功耗。
1、STM32F407进入STOP模式前,需要关闭所有已经启用的资源(定时器、ADC,串口,SPI等等)
2、未使用的IO口都设置为模拟输入,输入的IO也设置为模拟输入。输出的IO口,如果外部有上下拉电阻,也可以设置为模拟输入,这个最省电。如果不设置的话,IO口这块就会增加4-5ma的电流都有可能。
3、板子上面有的LDO静态电流是很大的,好比常用的ASM1117-3.3,这个静态电流就接近10ma了。做低功耗的话不能用这种LDO
4、有些水货STM32F407,本身质量有问题,会比正常芯片电流大几ma或者10+ma,我就遇到了。这种无论你程序怎么处理,都没用的。
我的产品,进入STOP模式后电流是300+ua(3.3V)

下面是一些外部中断、还有RTC(使用LSI)的代码,可以参考。因为产品低功耗后需要10秒钟闪一次灯,然后喂狗。所以用的内部LSI。然后按键中断唤醒

[C] 纯文本查看 复制代码
// 配置PB3-PB8为外部中断唤醒源
void Configure_Wakeup_EXTI(void)
{
    EXTI_InitTypeDef EXTI_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    //打开SYSCFG时钟,引脚重映射、EXTI 配置、存储器保护
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    
    // 连接PB3-PB8到EXTI线
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource3);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource4);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource5);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource6);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource7);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource8);
    
    // 配置EXTI中断(下降沿触发)
    EXTI_InitStruct.EXTI_Line = EXTI_Line3 | EXTI_Line4 | EXTI_Line5 | 
                                EXTI_Line6 | EXTI_Line7 | EXTI_Line8;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStruct);
    
    // 配置NVIC中断优先级
    NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

// 配置RTC周期唤醒(LSI时钟,1秒间隔)
void Configure_RTC_Wakeup(uint16_t rtc_second)
{
    //打开SYSCFG时钟,引脚重映射、EXTI 配置、存储器保护
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    //使能时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    //启用备份域时钟(RTC需要使用)
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
    //启用备份域访问
    PWR_BackupAccessCmd(ENABLE);

    //复位备份域(低功耗模式下必须,否则可能会由于电压残留、导致下一次RTC不起作用)
    RCC_ClearFlag();
    RCC_BackupResetCmd(ENABLE);
    RCC_BackupResetCmd(DISABLE);
    
    // 启用LSI时钟
    RCC_LSICmd(ENABLE);
    while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
    
    // 配置RTC时钟源为LSI
    RCC_RTCCLKCmd(DISABLE);
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
    RCC_RTCCLKCmd(ENABLE);

    // 等待RTC的时间和日期寄存器数据同步完成
    RTC_WaitForSynchro();

    // 4. 配置RTC预分频器(LSI=32kHz → 1Hz)
    //RTC_InitTypeDef RTC_InitStruct;
    //RTC_InitStruct.RTC_AsynchPrediv = 0x7F;   // 32kHz / (127+1) = 250Hz
    //RTC_InitStruct.RTC_SynchPrediv  = 0xF9;   // 250Hz / (249+1) = 1Hz
    //RTC_Init(&RTC_InitStruct);
    
    // 设置RTC唤醒周期(LSI=32kHz,1秒间隔)
    RTC_WakeUpCmd(DISABLE);
    RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits);
    RTC_SetWakeUpCounter(rtc_second - 1);  // 60秒
    RTC_WakeUpCmd(ENABLE);
    RTC_ITConfig(RTC_IT_WUT, ENABLE);

    // 清除唤醒标志
    PWR_ClearFlag(PWR_FLAG_WU);

    /* EXTI configuration EXTI 线 22 连接到 RTC 唤醒事件 */
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_ClearITPendingBit(EXTI_Line22);
    EXTI_InitStructure.EXTI_Line = EXTI_Line22;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    // 启用RTC唤醒中断
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = RTC_WKUP_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

回复

使用道具 举报

8

主题

57

回帖

81

积分

初级会员

积分
81
 楼主| 发表于 2025-7-16 17:16:10 | 显示全部楼层
honami520 发表于 2025-7-16 16:10
最近这1个月刚刚做STM32F407的低功耗。
1、STM32F407进入STOP模式前,需要关闭所有已经启用的资源(定时器 ...

是的,根据您的解释,做低功耗的设备,前提就是硬件上要能支持低功耗,目前我是用的这版硬件,原来没有考虑过低功耗的,所以临时搞一个也只能跟原来对比,有比较明显效果就可以。另外我这个设备比较单一,只能用指定的一个外部输入IO引脚做退出低功耗模式,其他的唤醒方式也没有去做。另外我目前还有疑问的是退出低功耗时,我重新初始化系统时钟和滴答定时器等,有可能程序会无法启动或者滴答定时器不再是1ms的中断。不确定这个问题出在哪里,如果您有更好的唤醒低功耗的处理方式,可以为我解答一下,谢谢
回复

使用道具 举报

5

主题

212

回帖

227

积分

高级会员

积分
227
发表于 2025-7-19 13:56:18 | 显示全部楼层
Ainit 发表于 2025-7-16 17:16
是的,根据您的解释,做低功耗的设备,前提就是硬件上要能支持低功耗,目前我是用的这版硬件,原来没有考 ...

指定的IO口没关系,我也是指定的IO口+内部LSI做时钟源的RTC定时唤醒。

至于你说的,唤醒后滴答不正常,或者时钟不正常,都是代码的问题,我给你我这边唤醒后做的事情
1、STOP休眠,唤醒后是从下一条命令开始执行,这个时候时钟是内部的HSI。

[C] 纯文本查看 复制代码
//禁止按键中断、禁止RTC中断
    Exit_STOP_Mode();
    
    // 重置系统时钟(HSI默认)
    SystemInit();
    //重新初始化资源
    SysTick_Config(SystemCoreClock / 1000); // 重新启用SysTick
//重新初始化你自己的其他资源

//退出STOP模式
void Exit_STOP_Mode(void)
{
    // 关闭EXTI中断
    EXTI_InitTypeDef EXTI_InitStruct;
    EXTI_InitStruct.EXTI_Line = EXTI_Line3 | EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line7 | EXTI_Line8;
    EXTI_InitStruct.EXTI_LineCmd = DISABLE;
    EXTI_Init(&EXTI_InitStruct);
    
    // 关闭NVIC中断
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&NVIC_InitStruct);

    // 关闭RTC唤醒定时器
    RTC_WakeUpCmd(DISABLE);
    RTC_ITConfig(RTC_IT_WUT, DISABLE);
    RCC_RTCCLKCmd(DISABLE);

    EXTI_InitStruct.EXTI_Line = EXTI_Line22;
    EXTI_InitStruct.EXTI_LineCmd = DISABLE;
    EXTI_Init(&EXTI_InitStruct);
    
    // 关闭NVIC中断
    NVIC_InitStruct.NVIC_IRQChannel = RTC_WKUP_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&NVIC_InitStruct);
}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-11 20:56 , Processed in 0.052837 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表