STM32CubeMX低功耗模式——待机模式(standby)RTC唤醒

STM32CubeMX低功耗模式——待机模式(standby)RTC唤醒


在ST的单片机中,一般低功耗模式都有stop(停机)模式和standby(待机)模式两种,这篇博客主要是分享一下standby模式,并且通过RTC实时时钟的闹钟将单片机从低功耗模式中唤醒的方法。为了方便演示,实验流程是,通过串口命令来设置单片机进入低功耗模式,再通过RTC的闹钟将单片机从低功耗状态唤醒,进入正常模式。

使用CubeMX生成工程

1.根据使用的单片机来配置生成工程,我使用的是NUCLEO-F303RE来做实验
2.配置需要使用到的外设
在这里插入图片描述
首先要设置单片机的时钟源,在这里我是用的是内部高速时钟源和外部的低速时钟源。因为RTC需要使用一个低速时钟源。
在这里插入图片描述激活RTC功能,并设置RTC的闹钟
在这里插入图片描述
第一步:配置数据格式为二进制格式(Binary data format),便于后期处理。
第二步:自动异步分频使能,这里可以自动将RTC的时钟源分频成1HZ
第三步:选择使能闹钟屏蔽标识,例如图中的参数设置,表示在秒相同的时候触发,也就是一分钟触发一次闹钟。同理,可以设置一秒触发,一天触发一次或者一周触发一次。
在这里插入图片描述
在这里我还设置了串口,用于在实验中显示输出。
在这里插入图片描述
最后使能相关的外设的中断。
3.设置好工程名称和保存位置,选择自己用的开发工具和版本,然后生成工程代码

代码处理

首先引用和定义一些必要的头文件和参数

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include "usart_user.h" 
#include "string.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

RTC_DateTypeDef gDateType;  //获取RTC日期
RTC_TimeTypeDef gTimeType;  //获取RTC时间

RTC_DateTypeDef sDateType;  //设置RTC日期
RTC_TimeTypeDef sTimeType;  //设置RTC时间

static char cmd_setTime[] = "AT+SETTIME";   //设置RTC时间命令
static char cmd_standby[] = "AT+STANDBY";   //进入standby模式命令

/* USER CODE END PTD */

main函数循环中用于处理命令的部分

 /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

      if(USART_RX_Finish)   //判断串口接收完成
      {
         USART_RX_Finish = 0;   //清除串口接收完成标识
         if(strncmp(USART_RX_BUF,cmd_setTime,strlen(cmd_setTime)) == 0) //判断命令,处理RTC时钟设置
         {
            sTimeType.Hours = (USART_RX_BUF[(strlen(cmd_setTime) + 1)]-'0')*10 +(USART_RX_BUF[(strlen(cmd_setTime) + 2)]-'0');
            sTimeType.Minutes = (USART_RX_BUF[(strlen(cmd_setTime) + 4)]-'0')*10 +(USART_RX_BUF[(strlen(cmd_setTime) + 5)]-'0');
            sTimeType.Seconds = (USART_RX_BUF[(strlen(cmd_setTime) + 7)]-'0')*10 +(USART_RX_BUF[(strlen(cmd_setTime) + 8)]-'0');
            printf("设置RTC时间为 %02d:%02d:%02d \r\n",sTimeType.Hours,sTimeType.Minutes,sTimeType.Seconds);
            if(HAL_RTC_SetTime(&hrtc,&sTimeType,RTC_FORMAT_BIN)!=HAL_OK)    //设置RTC时间
            {
                printf("HAL_RTC_SetTime ERR \r\n");
            }
            printf("设置RTC时间成功!\r\n");
         }
        
         if(strncmp(USART_RX_BUF,cmd_standby,strlen(cmd_standby)) == 0) //判断命令,进入standby模式
         {
             printf("Executing test (standby) \r\n");
             
             GPIO_AnalogState_Config(); //设置IO口为模拟输入状态
             
             __HAL_RCC_PWR_CLK_ENABLE();
             
             HAL_PWR_EnterSTANDBYMode();    //进入standby模式
             
             printf("进入待机模式失败\r\n");
             
         }
       }
       if((PWR->CSR & PWR_CSR_WUF )== PWR_CSR_WUF)  //清除闹钟中断的标识
       {
          PWR->CR |= PWR_CR_CWUF;
       }
       HAL_Delay(200);
       HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin);   //翻转LD2,显示单片机处于正常工作状态
  }
  /* USER CODE END 3 */

获取RTC时间和日期的函数,以及RTC的闹钟中断回调函数

/* USER CODE BEGIN 4 */

/*获取RTC的时钟和日期*/
void get_date (RTC_DateTypeDef *sDateStruc,RTC_TimeTypeDef *sTimeStruc)
{
    
    if(HAL_RTC_GetTime(&hrtc,sTimeStruc,RTC_FORMAT_BIN) != HAL_OK)
    {
        printf("HAL_RTC_GetTime ERR \r\n");
    }
    
    if(HAL_RTC_GetDate(&hrtc,sDateStruc,RTC_FORMAT_BIN) != HAL_OK)
    {
        printf("HAL_RTC_GetDate ERR \r\n");
    }
    
}

/*RTC闹钟的回调函数*/
void HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc)
{
    
    get_date(&gDateType,&gTimeType);
    printf("当前RTC时间为 %02d:%02d:%02d \r\n",gTimeType.Hours,gTimeType.Minutes,gTimeType.Seconds);
    
}

/* USER CODE END 4 */

设置IO口为模拟输入状态的函数,这个函数主要是为了在低功耗状态下,尽量控制IO上不必要的漏电流产生。

/* USER CODE BEGIN 2 */

void GPIO_AnalogState_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    
    /*Set all GPIO in analog state to reduce power consumption*/
    
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Pin = GPIO_PIN_All;
    
    HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
    HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);
    HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
    
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    __HAL_RCC_GPIOC_CLK_DISABLE();
}

/* USER CODE END 2 */

到这里基本就已经实现了单片机的standby模式了,并且可以通过RTC的闹钟实现休眠一分钟后唤醒。但是每次唤醒后都会让RTC时钟的时间重置,所以最后我通过判断后备寄存器的方式,来保证RTC的时间参数在唤醒时不被重置。具体代码如下:

void MX_RTC_Init(void)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};
  RTC_AlarmTypeDef sAlarm = {0};

  /** Initialize RTC Only 
  */
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN Check_RTC_BKUP */
  
  if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1) != 0xA5A5) //判断后备寄存器是否被赋值
  {
      /* USER CODE END Check_RTC_BKUP */

      /** Initialize RTC and set the Time and Date 
      */
      sTime.Hours = 0;
      sTime.Minutes = 0;
      sTime.Seconds = 0;
      sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
      sTime.StoreOperation = RTC_STOREOPERATION_RESET;
      if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
      {
        Error_Handler();
      }
      sDate.WeekDay = RTC_WEEKDAY_MONDAY;
      sDate.Month = RTC_MONTH_JANUARY;
      sDate.Date = 1;
      sDate.Year = 0;

      if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
      {
        Error_Handler();
      }

      HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1, 0xA5A5); //为后备寄存器写入值
  }

  /** Enable the Alarm B 
  */
  sAlarm.AlarmTime.Hours = 0;
  sAlarm.AlarmTime.Minutes = 0;
  sAlarm.AlarmTime.Seconds = 0;
  sAlarm.AlarmTime.SubSeconds = 0;
  sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
  sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS
                              |RTC_ALARMMASK_MINUTES;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
  sAlarm.AlarmDateWeekDay = 1;
  sAlarm.Alarm = RTC_ALARM_B;
  if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
}

到这里,待机模式(standby)下RTC唤醒就已经实现了。

实验结果

在这里插入图片描述
在电路上,程序复位后,LD2会周期闪烁,然后再发送“AT+STANDBY”命令后,LD2熄灭,然后等待大概1分钟(时间和你发送进入待机模式的时间有关系),LD2重新开始周期闪烁。

经过万用表测量,最后单片机的待机功耗为5.8uA
在这里插入图片描述

总结

在利用RTC闹钟唤醒standby模式中,需要关注的问题有:
1.关于功耗控制,需要对不使用的IO口进行漏电流的控制,因此,最好将没有使用的IO口都配置为模拟输入状态。
2.利用RTC闹钟进行唤醒时,需要注意闹钟中断触发后,中断标识是否被清除,否则,standby模式很可能在进入后立马退出。
3.如果要在退出standby模式后,RTC时钟需要继续按照设定的时间计时,需要通过后备寄存器来判断单片机是否是第一次启动。

  • 11
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值