关于stm32单片机低功耗的实现和唤醒

时间:2024-02-20 08:25:56

最近做的项目中要求低功耗,在单片机完成了手头上的工作之后,就进入低功耗模式,项目的需求是单片机进入低功耗模式的时候系统时钟从HSE切到HSI,但是在未进入低功耗模式之前引脚是什么状态,进入低功耗模式之后也不应该改变。可以通过RTC定时唤醒,或者接收到串口数据就唤醒,处理完数据后再进入stop模式。查询了一些资料之后,发现stop模式最符合项目需求。下面讲述一下什么是低功耗:

一、stm32支持三种低功耗模式,可以在低功耗,短启动,多种唤醒模式下寻找平衡。

1、SleepMode睡眠模式,只有CPU停止运行,所有的外设处于工作状态并且可以在发生中断/事件时唤醒CPU。

2、StopMode停机模式,保持SRAM和寄存器内容不丢失,达到最低的电能消耗。停止所有内部1.8V部分的供电, PLL、 HSI的RC振荡器和HSE晶体振荡器被关闭,调压器可以被置于普通模式或低功耗模式。可以通过任一EXTI从停机模式中唤醒, EXTI信号可以是16个外部I/O口之一、 PVD的输出、 RTC闹钟或USB的唤醒信号。

3、StandbyMode待机模式。最低的电能消耗。内部1.8V部分的供电被切断; PLL、 HSI的RC振荡器和HSE晶体振荡器关闭;SRAM和寄存器的内容将消失,但后备寄存器的内容仍然保留,待机电路仍工作。从待机模式退出的条件是: NRST上的外部复位信号、 IWDG复位、 WKUP引脚上的一个上升边沿或RTC的闹钟到时。

二、低功耗模式基于HAL库的常用API

2.1睡眠模式SleepMode

  • __HAL_RCC_PWR_CLK_ENABLE();//电源管理使能
  • HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);//进入睡眠模式

2.2停机模式StopMode

  • __HAL_RCC_PWR_CLK_ENABLE();//电源管理使能
  • HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_STOPENTRY_WFI);//进入停机模式

2.3 待机模式StandbyMode

  • __HAL_RCC_PWR_CLK_ENABLE();//电源管理使能
  • PWR_Check_Standby();//检查是否是待机模式
  • __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);//清除唤醒标志
  • __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);//清除待机标志
  • __HAL_PWR_GET_FLAG(PWR_FLAG_SB);//获得待机模式标志
  • HAL_PWR_EnterSTANDBYMode();//进入待机模式

以上相关描述参考https://blog.csdn.net/u010058695/article/details/100008613

三、Stop模式中遇到的一些问题及解决方法

1、没有配置好唤醒机制,导致进入stop模式后没能唤醒,导致无法识别到st-link,无法重新烧录程序。即使重新上电也不能识别到st-link。

我在这个问题上栽了跟头,因为单片机重新上电后依然不能识别到st-link,所以以为是单片机坏了,重新换了单片机再烧录同样的程序,结果问题还是一样,我开始意识到问题不是那么简单,上网找了一下资料,才知道是进入stop模式后不能唤醒,具体的做法有几种:

①  可以将单片机的NRST引脚引出来,外接复位键。(关于引脚查看可用ST官方软件STM32cubeMX,很方便)如图:把NRST引出来通过一个按键连接到GND,当进入STOP模式不能烧录程序的时候只需要在按下按键后,点击keil的,然后松手即可。

 

 

 

②  通过ISP的方式下载程序,必须将boot0接高电平,boot1(PB2)接低电平,然后必须断电后重启,此时单片机会进入ISP模式,就可以通过串口1(一般单片机都是只有串口1才可下载程序)再用flymcu等软件烧入hex文件就可以了。

关于boot0和boot1,在每个stm32单片机都会有这两个引脚,这两个引脚在复位时的电平状态决定了芯片在复位后从哪个区域开始执行程序。

  boot1=x boot0=0从用户闪存启动,这是正常的工作模式。

  boot1=0 boot0=1从系统存储器启动(进入ISP模式)。

  boot1=1 boot0=1从内置SRAM启动,这种模式可以用于调试。

  如此一来,单片机无法进入正常的工作模式,就无法进行休眠,就可以通过ISP来下载程序。

③  使用STlink烧写程序,STlink至少要接4根线(SWDIO、SWCLK、GND、SW-RST),一般的下载程序只需要前三根就可以了,但是以防出现休眠无法唤醒的情况,这时有复位键就可以将SWDIO和SWCLK重置,如果板子设计没有与SW-RST相对应的,此时需要把单片机上的NRST引出来接到SW-RST上。下载程序之前boot0接高电平,boot1不用管(也需要断电后重启)。

下载配置这里按如下配置,RESET那里HW RESET和SYSRESETREQ任选一个。

配置好之后load一个空程序即可,不要再烧一个休眠的程序了,之后就可以正常烧程序了。

以上解决方法参考https://blog.csdn.net/geekjin/article/details/79232405,总结就是复位NRST这个引脚是最简单的方法,以后做项目还是要提醒硬件工程师把这个引脚引出来。

2、项目需要单片机在进入stop 模式后过5秒钟就唤醒,实际上RTC闹钟中断也是挂在EXTI中断上的。我是用CubeMX配置的RTC,但是一直没有唤醒,后面终于找到问题所在了:

 

 红色框框里的前三个应该配置为Enable,第四个应该配置为Disable,前三个是闹钟屏蔽星期,闹钟屏蔽小时,闹钟屏蔽分钟,如果不是这样配置的话,只能等到时间达到了设定好的某天某时某分某秒才能发生中断,那么只能一周唤醒一次了。如果是这样设置的话,就是一分钟唤醒一次,那么怎么才能5秒钟唤醒一次呢?我的做法是每次进入stop模式前就调用一次MX_RTC_Init()函数一次,因为我的时间是设置为x年x月x日星期x x时x分0秒,所以相当于每次进入stop模式前初始化一次RTC,5秒钟后就会发生闹钟中断。这种办法可能是比较笨的方法,但是好歹实现了这个功能哈哈

3、为了方便调试,我还配置了LED,每隔200ms闪烁一次,但是我发现从stop模式唤醒后灯的闪烁明显变得很慢,而且串口数据的接收和发送也不能完成,这是为什么呢?

  原来进入stop模式后系统时钟默认切换到内部8M时钟,所以频率就会变慢了

 

 

  我的解决方法是重新配置系统时钟:

/**************************************
*函数名称:void SYSCLKConfig_STOP(void)
*函数功能:选择HSE作为时钟源
*函数形参:无
*函数返回值:无
****************************************/
void SYSCLKConfig_STOP(void)
{
	__HAL_RCC_HSE_CONFIG(RCC_HSE_ON);
	
	while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
	{
	}
	
	 /* 使能 PLL */
  __HAL_RCC_PLL_ENABLE();

  /* 等待 PLL 准备就绪 */
  while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
  {
  }

  /* 选择PLL作为系统时钟源 */
  __HAL_RCC_SYSCLK_CONFIG(RCC_SYSCLKSOURCE_PLLCLK);

  /* 等待PLL被选择为系统时钟源 */
  while(__HAL_RCC_GET_SYSCLK_SOURCE() != 0x08)
  {
  }
}

4、接收到串口数据后怎么唤醒stop模式?

   其实RTC闹钟中断唤醒stop模式也是因为闹钟事件是连接着外部中断线的,但是串口接收中断并不能唤醒stop模式啊,怎么办?

 

 解决方法有两种,一种是在RX引脚上接上光耦连接到其他IO口上,把该IO口配置为外部中断,但是这种方案比较麻烦,所以可以采取第二种方案,在进入stop模式之前把RX引脚配置为外部中断模式,这样的话,RX引脚上有电平变化的时候就会发生外部中断,在服务函数中记得把RX引脚再配置为复用为UART的接收引脚。并且重新配置系统时钟,选择HSE为时钟源。这样就可以实现唤醒并接收数据了。但是因为第一次接收到串口数据的时候只能唤醒stop模式,并不能接收到数据,所以应该跟发送方约定好,一帧数据要发送两次。而且中间要有一定间隔,我的间隔是500ms。这样确实比较麻烦,但是暂时没有更好的办法。

5、串口接收引脚配置为外部中断引脚后还是没有正常接收到数据。在唤醒后延时1秒钟就可以接收到数据。

  这是因为stop模式唤醒后,因为没有延时,很快又进入stop模式,这个时候第二帧数据还没到来,等到第二帧数据到来的时候处于stop模式中,这帧数据只能起到唤醒的作用,很快又进入了stop模式,变成一个恶性循环。

 

以上就是我对stop模式和唤醒的总结,不正确的地方请大家斧正!