最近工作一直在做低功耗相关的产品,这次用到了STM32L151的STOP模式,RTC定时唤醒STOP模式需要注意以下几点内容:
1.RTC需要正确配置,启动外部低速32.768Khz(外挂晶振)用来启用定时,经过测试还是比较准确的,如果外部没有挂晶振则可以用内部的低速时钟
2.做低功耗的话就是要将功耗降下来:所以IO的配置还是比较重要的,经过测试将不用的IO配置为模拟输入可以有效降低功耗(是否上下拉则看外设是否外接),在进入停止模式之前关闭中断/ADC/I2C等开启的外设,唤醒之后需要重新初始化外设
3.由于停机模式下,所有的时钟都停止了,下面的框图是RTC的时钟源描述所用选用的LSE
4.补充下关于STOP模式的描述:
下面描述的是:进入STOP模式下内部振荡器将关闭,寄存器内容保持原来的配置,IO保持进入模式之前的配置运行(这几点尤为重要)
这里描述退出STOP MODE的方式:中断或者中断事件(MSI振荡器选择系统时钟)
说说MSI基本校准原理:
通过32.768K晶振连接到TIM21当中,产生参考值与当前MSI振荡器进行对比,找到一个适合的差值,写入MSI校准寄存器中!即可完成。
在X-CUBE-RC-CALIB的参考代码中MSI MinError这种经典方案可以用来做校准。10个周期下来大概消耗 1.4s时间完成校准
接下来就用代码分模块描述关于RTC 和STOP:
RTC初始化部分
//RTC定时函数初始化
//需要注意外部时钟LSE初始化的时间太短的话是无法等待就绪的
//等待LSE晶振时间
#define HSE_STARTUP_TIMEOUT_Num 0xFFFFF
void SYS_RTCInit(uint32_t wkuptime)
{
uint8_t RTCStateFlag = 0;
__IO uint32_t XTUpCounter = 0;
static NVIC_InitTypeDef NVIC_InitStructure;
static EXTI_InitTypeDef EXTI_InitStructure;
static RTC_InitTypeDef RTC_InitStructure;
static RTC_TimeTypeDef RTC_TimeStructure;
static RTC_DateTypeDef RTC_DateStruct;
//使能PWR时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
/*!< Allow access to RTC 使能后备寄存器访问*/
PWR_RTCAccessCmd(ENABLE);
RCC_RTCResetCmd(ENABLE);
RCC_RTCResetCmd(DISABLE);
/*!< 使能外部低速时钟 */
RCC_LSEConfig(RCC_LSE_ON);
/*!< 等待外部低速时钟就绪 */
while ((RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)&&(XTUpCounter<HSE_STARTUP_TIMEOUT_Num))
{XTUpCounter++;}
if(XTUpCounter>=HSE_STARTUP_TIMEOUT_Num)
{
XTUpCounter = 0;
RCC_LSICmd(ENABLE);
/*!< 等待外部低速时钟就绪 */
while ((RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)&& (XTUpCounter<HSE_STARTUP_TIMEOUT_Num))
{XTUpCounter++;}
if(XTUpCounter<HSE_STARTUP_TIMEOUT_Num)
{
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //选择内部晶振作为RTC时钟源
RTCStateFlag = 2;
}
else
{
RTCStateFlag = 0;
}
}
else
{
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//选择外部晶振作为RTC时钟源
RTCStateFlag = 1;
}
/*****************************************************/
if(RTCStateFlag != 0)
{
/*!< 使能RTC时钟 */
RCC_RTCCLKCmd(ENABLE);
/*!< Wait for RTC APB registers synchronisation 等待时钟同步*/
RTC_WaitForSynchro();
/* 配置RTC数据寄存器以及时钟分频 */
RTC_InitStructure.RTC_AsynchPrediv = 0X7F;//同步 设置预分频为1S一次
RTC_InitStructure.RTC_SynchPrediv = 0XFF; //异步 255
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;//24小时制
/* 检查RTC初始化 */
if(RTC_Init(&RTC_InitStructure) == ERROR)
{
while(1);
}
/* Set the date: Thursday January 11th 2018,2018/01/25 星期四 */
RTC_DateStruct.RTC_Year = 0x18;
RTC_DateStruct.RTC_Month = RTC_Month_January;
RTC_DateStruct.RTC_Date = 0x27;
RTC_DateStruct.RTC_WeekDay = RTC_Weekday_Thursday;
RTC_SetDate(RTC_Format_BCD, &RTC_DateStruct);
/* Set the time to 10h 09mn 15s AM ,早上10:09:15*/
RTC_TimeStructure.RTC_H12 = RTC_H12_PM;
RTC_TimeStructure.RTC_Hours = 0x21;
RTC_TimeStructure.RTC_Minutes = 0x57;
RTC_TimeStructure.RTC_Seconds = 0x33;
RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);
/* 配置外部中断控制线20,实现RTC唤醒 */
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);
/*使能 RTC 中断 */
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//先关闭TRC中断使能
RTC_WakeUpCmd(DISABLE);
//为唤醒功能选择RTC配置好的时钟源
RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits);
//设置WAKE UP自动重装载寄存器
RTC_SetWakeUpCounter(wkuptime);//(32768hz/128)-1 = 255 1S
//清除RTC唤醒中断标志
RTC_ClearFlag(RTC_FLAG_WUTF);
//使能RTC唤醒中断
RTC_ITConfig(RTC_IT_WUT, ENABLE);
//使能唤醒功能
RTC_WakeUpCmd(ENABLE);
}
}
进入STOP的函数模块:进入STOP模式之前关闭所有外设,配置IO状态,退出STOP模式后重新选择系统时钟
/*
**待机模式下:所有时钟停止,RTC(LSE)中断唤醒,休眠前配置IO状态,唤醒后 RCC时钟源切换到外部高速时钟
*/
void RCC_Enter_Stop(void)
{
__IO uint32_t StartUpCounter1 = 0;
ADC_Cmd(ADC1,DISABLE); //关闭ADC1 电池电压检测
I2C_Cmd(I2C1,DISABLE); //关闭I2C1
Delay_ms(500);
/* Enter Stop Mode */
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); //停机模式
/***唤醒后mcu时钟源选择外部高速时钟***/
/* Enable HSE */
RCC_HSEConfig(RCC_HSE_ON);
/* Wait till HSE is ready */
while ((RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET)&& (StartUpCounter1<HSE_STARTUP_TIMEOUT_Num))
{StartUpCounter1++;}
if(StartUpCounter1<HSE_STARTUP_TIMEOUT_Num)
{
/* Enable PLL */
RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/* Select PLL as system clock source */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* Wait till PLL is used as system clock source */
while (RCC_GetSYSCLKSource() != 0x0C)
{}
}
}
主函数
void main()
{
/*外设初始化*/
SYS_RTCInit(60 * 2); //RTC定时初始化,定时时间2分钟
while(1)
{
/*
这里存放你的工作代码
*/
RCC_Enter_Stop(); //工作休眠
/*唤醒后重新初始化外设IO*/
/*
串口,I2C等等
*/
}
}
以上就是RTC唤醒低功耗 STOP模式的代码模块,预祝大家工作顺利 :)