完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
在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时钟需要继续按照设定的时间计时,需要通过后备寄存器来判断单片机是否是第一次启动。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1780 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1081 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1679 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
596浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
556浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 16:57 , Processed in 0.783412 second(s), Total 46, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号