首页 > 编程知识 正文

stm32低功耗外部中断唤醒,stm32 待机模式

时间:2023-05-03 16:36:18 阅读:188982 作者:1241

STM32CubeMX低功耗模式——待机模式(standby)RTC唤醒 使用CubeMX生成工程代码处理实验结果总结
在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 rn",sTimeType.Hours,sTimeType.Minutes,sTimeType.Seconds); if(HAL_RTC_SetTime(&hrtc,&sTimeType,RTC_FORMAT_BIN)!=HAL_OK) //设置RTC时间 { printf("HAL_RTC_SetTime ERR rn"); } printf("设置RTC时间成功!rn"); } if(strncmp(USART_RX_BUF,cmd_standby,strlen(cmd_standby)) == 0) //判断命令,进入standby模式 { printf("Executing test (standby) rn"); GPIO_AnalogState_Config(); //设置IO口为模拟输入状态 __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWR_EnterSTANDBYMode(); //进入standby模式 printf("进入待机模式失败rn"); } } 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 rn"); } if(HAL_RTC_GetDate(&hrtc,sDateStruc,RTC_FORMAT_BIN) != HAL_OK) { printf("HAL_RTC_GetDate ERR rn"); } }/*RTC闹钟的回调函数*/void HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc){ get_date(&gDateType,&gTimeType); printf("当前RTC时间为 %02d:%02d:%02d rn",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时钟需要继续按照设定的时间计时,需要通过后备寄存器来判断单片机是否是第一次启动。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。