超低功耗产品必然涉及到“唤醒”机制,唤醒后执行 正常的功能代码,这个“唤醒”动作有多种,基本上有外部中断、中断事件、RTC自动唤醒等,外部中断多是IO口中断,比如按键触发,而RTC自动中断相比较“智能”一些,倒计时一定时间后,自动唤醒CPU,所以RTC自动唤醒基本上就是 为超低功耗定制 的,用起来非常方便,先看下STM32L151的RTC系统时钟树:
上图中的WUTR就是wake up auto-reload timer,即自动唤醒状态寄存器,而WUTF就是 wake up timerr function自动唤醒功能, 通过上图时钟树可知,RTC自动唤醒是一个16位的定时器(向下计数),数值范围自然是0~65535,也就是65536份,自动唤醒计时器的时钟来源有两种,分别是来自外部LSE(32.768khz)经过2、4、8、16分频,或者经过ck_apre和ck_spre分频,其中经过ck_apre和ck_spre分频出的频率默认为1HZ,从这一点就能看出来,这不是巧合,是故意设定的,方便工程师使用,在这种模式下,自动唤醒时间可以设定为1~65536s(18小时),如果这个时间还不够,还可以通过软件配置,达到18h~36h,
RTC的自动唤醒需要配合对应中断,才能唤醒CPU,手册上提到的是,所有的RTC中断(闹钟、自动唤醒、时间戳)内部都连接到了EXTI控制器上,如果使能RTC 唤醒中断,需要的操作如下:
① 配置EXTI Line20 中断,选择rising edge触发。
② 配置使能RTC_WKUP IRQ 通道,在NVIC中。
③ 配置RTC 产生RTC wakeup 计数器事件。
RTC自动唤醒功能配置代码如下:
void RTC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
//allow access to rtc
PWR_RTCAccessCmd(ENABLE);
//RESET RTC Domain
RCC_RTCResetCmd(ENABLE);
RCC_RTCResetCmd(DISABLE);
//LSE enable 32.768k
RCC_LSEConfig(RCC_LSE_ON);
//wait till LSE is ready
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{}
//rtc clock select
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
//enable rtc clock
RCC_RTCCLKCmd(ENABLE);
// wait for rtc APB registers synchronisation
RTC_WaitForSynchro();
//EXIT Config
EXTI_ClearITPendingBit(EXTI_Line20);
EXTI_InitStructure.EXTI_Line = EXTI_Line20;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
//enable the rtc wakeup interrupt
NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//rtc wakeup interrupt generation:clock source:RTCDiv_16,
//wakeup time base:4s
RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits);
RTC_SetWakeUpCounter(10);
//enable the wakeup interrupt
RTC_ITConfig(RTC_IT_WUT, ENABLE);
}
基本流程为:
① 使能PWR时钟。
② 取消RTC写保护。
③ 复位RTC
④ 使能LSE
⑤ 等待LSE就位
⑥ 选择RTC时钟源,肯定是选择LSE
⑦ 使能RTC时钟源
⑧ RTC同步就位
⑨ 配置EXTI_Line20,上升沿触发,并使能。
⑩ 配置wakeup 中断。
(11) 选择RTC wakeup时钟源, 这里就选择了RTC_WakeUpClock_CK_SPRE_16bits,也就是选择1Hz时钟。
(12)设定wakeup 定时器装载数,这里设定为10,说明就是10s唤醒。
(12) 使能wakeup 中断
进行初始化这些后,还需要编写wakeup 中断服务函数:
void RTC_WKUP_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_WUT) != RESET)
{
RTC_ClearITPendingBit(RTC_IT_WUT);
EXTI_ClearITPendingBit(EXTI_Line20);
}
}
重点就是要清除WUT和EXTI_Line20中断标志。
至此,RTC的自动唤醒机制已经搭建完成,但是此时还未触发自动唤醒,这个就需要在应用程序里,编写代码:
/*使能自动唤醒功能 */
RTC_WakeUpCmd(ENABLE);
/*控制CPU进入stop模式 */
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
/* CPU唤醒后,先停止自动唤醒功能 */
RTC_WakeUpCmd(DISABLE);
//
//
//PLL 初始化
//正常应用功能程序