STM32一般具有三种电源管理模式

  1. sleep mode

  • CPU 停止运行,但所有外设(包括定时器、USART、SPI、I2C、RTC 等)仍然保持工作状态

  • 可以由任意可用的中断源(例如外部中断、RTC、USART 接收等)唤醒

  1. stop mode

  • CPU 和大部分外设停止运行,但 SRAM 和寄存器保持内容。

  • PLL 和 HSI/HSE 关闭,但 LSE(低速外部晶振)或 LSI(低速内部 RC 振荡器)可用于 RTC

  • 唤醒后,时钟需要重新配置

  1. stanby mode

  • 所有时钟(包括 HSI、HSE、PLL、LSE)都关闭

  • SRAM 和寄存器内容丢失(仅 RTC 和备份寄存器保持)

  • 功耗最低(几乎是断电状态),仅供电部分电路保持工作

  • 唤醒后相当于 硬件复位(复位 MCU)

对于STOP mode,在实践中发现,在使用Cube生成HAL库代码的情况下,systick会唤醒STOP mode——这与数据手册上systick依赖于HCLK相冲突,因为在STOP mode下会关闭HCLK。

这可能是HAL库的一个BUG,或许是stm32有些特性不明确,但在使用休眠模式下,尽量关闭systick

这里使用到的是SLEEP mode,因为CPU可以即使准备工作而且系统不需要重启,同时可以通过串口中断唤醒

而STOP mode下只能通过外部中断或RTC唤醒,一般非低功耗系列的32单片机不能通过串口唤醒,而L系列的可以。

但如果搭配上看门狗,SLEEP mode下没有外设喂狗,势必会发生狗咬人重启。

这里我创建了一个auto_sleep函数,用于没有操作延迟自动进入休眠

void auto_sleep(){
	if(g_sleep){
		my_printf("SLEEP Zzzzzzz...\r\n");
		HAL_Delay(1);

		// Pause sys Tick
		HAL_SuspendTick();		// 发现了sys tick会唤醒stop mode
		__HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();	// clear flag

		// 使能Sleep-On-Exit模式
		HAL_PWR_EnableSleepOnExit();

		HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

		// 如果进入stop mode后,不能通过串口来唤醒,所以不再使用stop mode休眠
//		if(g_stop){
//			HAL_ResumeTick();
//			my_printf("DEEP SLEEP Zzzz...\r\n");
//			HAL_Delay(1);
//			HAL_SuspendTick();
//			HAL_PWR_EnableSleepOnExit();
//			// 进入 Stop Mode(主调节器模式)
//			HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
//			// 唤醒后恢复时钟
//			SystemClock_Config();
//		}

		HAL_ResumeTick();
		my_printf("WAKEUP Vvvvvvv...\r\n");

	}
}

这个函数中有几个注意的点

  1. 首先关闭systick,清楚外部中断唤醒标识位

  2. 使能sleep-on-exit模式,该模式能够在中断唤醒之后立即再次进入休眠,而不用调用WFI(),比较适合通过RTC中断喂狗的情况

  3. 进入SLEEP mode

  4. 退出SLEEP mode 后恢复systick

对于喂狗的情况,通过RTC内部中断进行

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc){
	HAL_IWDG_Refresh(&hiwdg);	//feed dog

	// sleep delay time: rtc interrupt interval times $(g_sleepDelayCounter)
	// 750 * 10 = 7500ms
	if(!g_sleep && g_sleep_enable){
		g_sleepDelayCounter++;
	}else if(g_sleep && !g_stop){
		g_sleepDelayCounter++;
	}
	else{
		g_sleepDelayCounter = 0;
	}

	if(g_sleepDelayCounter >= SLEEPDELAYTIME && g_sleepDelayCounter < STOPDELAYTIME){
		g_sleep = 1;
	}
//	else if(g_sleepDelayCounter >= STOPDELAYTIME){
//		g_stop = 1;
//		HAL_PWR_DisableSleepOnExit();
//	}
    
    // sleep status led 
	if(g_sleep){
		HAL_GPIO_TogglePin(USR_LED_GPIO_Port, USR_LED_Pin);
	}else{
		HAL_GPIO_WritePin(USR_LED_GPIO_Port, USR_LED_Pin, SET);
	}
}

通过设置SLEEPDELAYTIME 来限制休眠延时时间

山和山不相遇,人与人要相逢