一、概述

本例程是用STOP休眠模式,用RTC的周期性自动唤醒功能来唤醒芯片。

根据手册的说明:这里有个可编程的可自动重装的向下计数器,按照相应的时钟频率赋予适当的值,每当向下计数到0时便产生一个唤醒标志,如果此时使能了相应的定时唤醒中断,它就可以把MCU从低功耗模式唤醒。需做如下四项基本的准备工作。

1、确定RTC时钟,即RTCCLK.可以是LSE、LSI、HSE/32其中一个。下面的例程中选用LSI.

2、为自动唤醒定时计数器选择合适的时钟源。

cubemx rtc闹钟多次 rtc闹钟唤醒_RTC


3、RTC时钟进来分频之后达到1秒(1Hz),每一次时间更新RTC时钟寄存器(RTC_TR、RTC_DR),我们读取的数字就会更改。如果配置了中断,相应事件的时候,中断也会响应。如果配置了闹钟,同样达到了闹钟设定的值也会响应闹钟。

默认设置

AsynchPrediv = 127;

SynchPrediv = 255;

4、做好RTC周期性定时唤醒的中断配置,即NVIC配置。RTC唤醒事件是连接到EXTI 17/19/20号线。

二、详细步骤

2-1 stm32MxCube 操作

cubemx rtc闹钟多次 rtc闹钟唤醒_STM32CubeM_02

cubemx rtc闹钟多次 rtc闹钟唤醒_闹钟_03

cubemx rtc闹钟多次 rtc闹钟唤醒_STM32CubeM_04

注意配置NVIC(中断控制器),RTC中断的条件选择。
生成工程文件,即可。

2-2 代码解析 (以下部分同时为直接的代码移植做参考,因为stm32cubeMx前期代码生成后,再重新生成会很麻烦,只能直接通过代码操作了)重点内容
2-2-1 相关文件j及配置条件

stm32f0xx_hal_conf.h 
 #define HAL_RTC_MODULE_ENABLED //打开宏定义 
 驱动文件:低功耗相关 stm32f0xx_hal_pwr.c 
 RTC相关 stm32f0xx_hal_rtc.c stm32f0xx_hal_rtc_ex.c 
 MCU初始化 stm32f0xx_hal_msp.c 
2-2-2 详细说明 
 如何进入睡眠模式通过执行 WFI(等待中断)或WFE(等待事件)指令进入睡眠状态。代码实现: 
 * @param Regulator: Specifies the regulator state in STOP mode. // 调压器模式选择,功耗会存在差异 
 * This parameter can be one of the following values: 
 * @arg PWR_MAINREGULATOR_ON: STOP mode with regulator ON 
 * @arg PWR_LOWPOWERREGULATOR_ON: STOP mode with low power regulator ON 
 * @param STOPEntry: specifies if STOP mode in entered with WFI or WFE instruction. // 进入睡眠状态进入机制,同时也表明了唤醒的选择方式 
 * This parameter can be one of the following values: 
 * @arg PWR_STOPENTRY_WFI:Enter STOP mode with WFI instruction 
 * @arg PWR_STOPENTRY_WFE: Enter STOP mode with WFE instruction
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI );

其他各种不同的模式可以自己研究。

如何退出睡眠模式

由于我们是采用指令WFI进入睡眠模式,那么任意一个被嵌套向量中断控制器NVIC响应的外设中断都能将系统从睡眠模式唤醒。并且该模式唤醒所需的时间最短,因为没有时间损失在中断的进入或退出上。
在RTX系统上,主要是周期性执行的系统滴答定时器中断会将系统从睡眠态唤醒,我们这里用的闹钟 Alarm A 来唤醒。

   2-2-3 时钟初始化
   void SystemClock_Config(void)
   {
      ....// 省略
             PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1|RCC_PERIPHCLK_RTC;
             PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;*//cube时钟配置部分默认的时钟选择*

      ....// 省略
   }

   RTC初始化           static void MX_RTC_Init(void);
   中断向量初始化    static void MX_NVIC_Init(void);

     static void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime; 
 RTC_DateTypeDef sDate; 
 RTC_AlarmTypeDef sAlarm;/**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(); 
 }/**Initialize RTC and set the Time and Date
*/sTime.Hours = 10; 
 sTime.Minutes = 12; 
 sTime.Seconds = 34; 
 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_FRIDAY; 
 sDate.Month = RTC_MONTH_MAY; 
 sDate.Date = 19; 
 sDate.Year = 17;if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) 
 { 
 Error_Handler(); 
 }/**Enable the Alarm A        //允许RTC报警中断
*/sAlarm.AlarmTime.Hours = 0; 
 sAlarm.AlarmTime.Minutes = 0; 
 sAlarm.AlarmTime.Seconds = 10; 
 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_A; 
 if (HAL_RTC_SetAlarm(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK) 
 { 
 Error_Handler(); 
 }} 
 static void MX_NVIC_Init(void) 
 {/* RTC_IRQn interrupt configuration */ 
 HAL_NVIC_SetPriority(RTC_IRQn, 0, 0); 
 HAL_NVIC_EnableIRQ(RTC_IRQn); 
 }void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)  // 可以重新定义 闹钟中断回调函数,添加自己的代码辅助调试
{
        /* NOTE : This function Should not be modified, when the callback is needed,
        the HAL_RTC_AlarmAEventCallback could be implemented in the user file
        */
        DBS("EventCallback");
}
 读取时钟: 
                HAL_RTC_GetTime(&hrtc, &thisTime, RTC_FORMAT_BIN);
                HAL_RTC_GetDate(&hrtc, &thisData, RTC_FORMAT_BIN);
                DBSTRLONG("_",thisData.Year);
                DBSTRLONG("_",thisData.Month);
                DBSTRLONG("_",thisData.WeekDay);       
                DBSTRLONG("_",thisTime.Hours);
                DBSTRLONG("_",thisTime.Minutes);
                DBSTRLONG("_",thisTime.Seconds);   

                RTC_AlarmTypeDef sAlarm;
                HAL_RTC_GetAlarm(&hrtc,&sAlarm,RTC_ALARM_A,RTC_FORMAT_BIN);  // 获取闹钟定时
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)  // 可以重新定义 闹钟中断回调函数,添加自己的代码辅助调试
{
        /* NOTE : This function Should not be modified, when the callback is needed,
        the HAL_RTC_AlarmAEventCallback could be implemented in the user file
        */
        DBS("EventCallback");
}
 读取时钟: 
                HAL_RTC_GetTime(&hrtc, &thisTime, RTC_FORMAT_BIN);
                HAL_RTC_GetDate(&hrtc, &thisData, RTC_FORMAT_BIN);
                DBSTRLONG("_",thisData.Year);
                DBSTRLONG("_",thisData.Month);
                DBSTRLONG("_",thisData.WeekDay);       
                DBSTRLONG("_",thisTime.Hours);
                DBSTRLONG("_",thisTime.Minutes);
                DBSTRLONG("_",thisTime.Seconds);   

                RTC_AlarmTypeDef sAlarm;
                HAL_RTC_GetAlarm(&hrtc,&sAlarm,RTC_ALARM_A,RTC_FORMAT_BIN);  // 获取闹钟定时

三、问题总结 【本次实验碰到的问题】

3-1 
 STM32F030用LSI作时钟源走时不准,40Khz配置,总会跳秒,要想准时,还是用外部LSE时钟,32768hz,会比较准。 
 3-2 sAlarm.AlarmMask =RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS |RTC_ALARMMASK_MINUTES ;

//注意屏蔽的对象,设置的日期,星期,时分是无效的,这时RTC闹钟 的秒匹配后触发闹钟中断。如果设置RTC_AlarmMask=RTC_AlarmMask_None;则为精确匹配,即闹钟不仅要求时分秒匹配还要匹配日期和星期,都匹配后触发闹钟中断)

起初因为没有注意到这一点,我设置10s 闹钟中断,当时只做了RTC_ALARMMASK_DATEWEEKDAY屏蔽 ,以至于耗费了好几天而不得结果。

3-3 测试低功耗时,最好通过任务,或者条件控制进入低功耗,否则,开机几秒就进入,那么下来,再烧写程序就麻烦了。