STM32CubeMX(stm32F030C8T6) 之RTC闹钟唤醒停机模式-STM32开发实战 (2)

一、概述
本例程是用STOP休眠模式,用RTC的周期性自动唤醒功能来唤醒芯片。
根据手册的说明:这里有个可编程的可自动重装的向下计数器,按照相应的时钟频率赋予适当的值,每当向下计数到0时便产生一个唤醒标志,如果此时使能了相应的定时唤醒中断,它就可以把MCU从低功耗模式唤醒。需做如下四项基本的准备工作。
1、确定RTC时钟,即RTCCLK.可以是LSE、LSI、HSE/32其中一个。下面的例程中选用LSI.
2、为自动唤醒定时计数器选择合适的时钟源。
STM32CubeMX(stm32F030C8T6) 之RTC闹钟唤醒停机模式-STM32开发实战 (2)_第1张图片
3、RTC时钟进来分频之后达到1秒(1Hz),每一次时间更新RTC时钟寄存器(RTC_TR、RTC_DR),我们读取的数字就会更改。如果配置了中断,相应事件的时候,中断也会响应。如果配置了闹钟,同样达到了闹钟设定的值也会响应闹钟。
默认设置
AsynchPrediv = 127;
SynchPrediv = 255;

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

二、详细步骤

2-1 stm32MxCube 操作
STM32CubeMX(stm32F030C8T6) 之RTC闹钟唤醒停机模式-STM32开发实战 (2)_第2张图片

STM32CubeMX(stm32F030C8T6) 之RTC闹钟唤醒停机模式-STM32开发实战 (2)_第3张图片

STM32CubeMX(stm32F030C8T6) 之RTC闹钟唤醒停机模式-STM32开发实战 (2)_第4张图片

注意配置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);  // 获取闹钟定时

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

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

你可能感兴趣的:(101STM32基础,102STM32调试)