项目需求,需要实现设备低功耗功能,实现过程中遇到几个问题,以此记录总结。(stm32f103ret6)
问题一:执行PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);后,程序继续执行,看到的现象像是没有进入休眠。
调试步骤:
1.关闭自己的时钟配置,调用启动函数中默认的SystemInit()
2.关闭相关外设初始化,只开启串口一以追踪程序运行流程和一个IO中断以唤醒
现象:能进入终端并且正常唤醒
3.换为调用自己的时钟,休眠不了,一步步打开相关时钟配置没问题直到开启了
SysTick_Config(SystemCoreClock / 1000 ) //1ms定时器
就休眠失败
源码贴图如下:
原因:开始以为把所有的外设中断关闭就没事了,其实系统时钟开了个1Ms的系统定时中断(功能需要)
【解决方式】:
方式一:.注释SysTick_Config(SystemCoreClock / 1000)
方式二:.在进入休眠之前,关闭系统定时器,清空计数值
SysTick->CTRL = 0x00;//关闭定时器
SysTick->VAL = 0x00;//清空val,清空定时器
功能需要不能关闭,选择方式二
【补充说明】:
SysTick系统定时器是属于CM3内核中的一个外设,内嵌在NUIV中。它是一个24bit的向下递减计数器,每计数一次的时间为1/SYSCLK。当重装载数值寄存器地见到0的时候,系统定时器就产生一次中断,一次循环反复。我的累计值是SystemCoreClock / 1000,所以中断就是1ms一次。
相比定时器中断,产生之后需要清中断标志位,但是系统时钟中断是没有的,开始怀疑是不是中断产生之后标志位是不会复位的,导致停止模式进入被忽略,程序继续执行。后面找资料发现每次systick溢出后会置位计数标志位和中断标志位,计数标志位在计数器重装载后被清除,而中断标志位也会随着中断服务程序的响应被清除,所以这两个标志位是不需要手动清除的。
具体为什么开启系统定时器之后不能进入停止模式还需要查找原因,在此也请各位网友赐教。
问题二:成功进入休眠并退出后,串口打印失败
原因:
退出停止模式后,其他时钟还是保持原来的配置(原来我的配置是以外部高速时钟HSE不分频作为PLL时钟源,再经过9倍频得到,即72MHz,然后PLL作为系统时钟源,系统时钟一分频得到AHB总线时钟,AHB再 一分频作为高速总线时钟PCLK2,也就是72MHz,USART1用的就是高速总线时钟),这些配置是不会变的,但是退出停止模式后,内部高速时钟(HSI)默认变成了系统时钟源,造成时钟紊乱。
【其实不仅仅是串口,其他在进入停止模式之前的外设在退出停止模式之后都会工作不正常,重新配置系统时钟即可】
问题三:进入停止模式后整个电流还是很大
1.根据设备原理图查看IO外部引脚连接电路,闲置状态为低电平时,设置为下拉输入;闲置状态为高电平时,设置为上拉输入;闲置状态为悬空时设置为模拟输入;输出引脚根据功能需要设置就行
原因:当IO通过外围电路电阻接地被拉低时,如果设置为上拉输入,则在芯片内部的上拉电阻和外围的下拉电阻构成回路,电流损耗取决于这两个电阻;当IO通过外围电路电阻接电源被拉高时,则在芯片内部的下拉电阻和外围的上拉电阻构成回路,电流损耗也取决于这两个电阻;当悬空时,斯密特触发器是打开的,要判断输入的是高电平还是低电平,需要一点电流损耗,但是设备模拟输入,这个触发器是关闭的
2.排查外围电路,某些驱动、电源转换IC如果是由软件使能,如果进入停止模式之后不需要则关闭,因为本身就有工作电流;如果是由硬件使能,则需要查看芯片手册看静态工作电流是多少,如果比较高,只能修改硬件电路或者用低耗ic代替
3.外围是否有一些闭合回路,比如电压采集,虽然ADC已经关闭,但是这个分压电路还是有损耗的,大小一般取决于电阻和电源电压
4.另外如果在线调试时,进入停止模式后,调试失去作用,看看是不是SWDIO、SWCLK两个引脚也直接设置成了模拟输入,还关闭了调试功能:GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
RTC唤醒停止模式
外部中断功能已经实现,项目需求中另外还需要定时唤醒,现在选用RTC唤醒
EXTI有20个中断/事件线,每个GPIO都能被设置为输入线,占用EXTI0-EXTI15,有4根用于特定的外设事件触发
所以RTC的闹钟事件就能触发EXTI17中断(这就是为什么进入停止模式设置的是中断唤醒,但还能用RTC闹钟事件唤醒的原因,本质还是被外部中断唤醒)
全功能代码不需要改,在进入停止模式之前配置EXTI17中断,配置闹钟事件
/**************************************1.配置EXTI17中断*********************************/
EXTI_ClearITPendingBit(EXTI_Line17); //注意先清掉标志位,要不然可能会出现停止模式被忽略进 //入不了的问题,上面问题一截图有说明
EXTI_InitStructure.EXTI_Line = EXTI_Line17;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/**************************************2.配置中断控制器闹钟事件优先级*********************************/
NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn; //RTC在最开始初始化的时候是秒中断,这里添加闹钟事件
//中断,此时RTC秒钟秒中断和闹钟事件中断同时开启
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/**************************************3.设置闹钟时间,sec即想要唤醒的时间间隔*********************************/
RTC_SetAlarm(RTC_GetCounter() + sec); //用RTC_SetAlarm()函数配置闹钟时间,当计数器的值与闹钟寄存器的
//值相等时,可产生闹钟事件或中断
RTC_WaitForLastTask(); //等待确保已成功修改RTC寄存器
RTC_ITConfig(RTC_IT_ALR, ENABLE); //使能闹钟事件
RTC_WaitForLastTask();
以上三步在将要进入停止模式之前执行。
/**************************************RTC中断函数*********************************/
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET) //秒钟中断
{
RTC_GetTime(&SystemTime); //更新时间(项目需要)
RTC_ClearITPendingBit(RTC_IT_SEC);
RTC_WaitForLastTask();
}
if(RTC_GetITStatus(RTC_IT_ALR) != RESET) //闹钟中断
{
RTC_ClearITPendingBit(RTC_IT_ALR); //清中断
RTC_WaitForLastTask();
}
}
void RTCAlarm_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line17); //闹钟事件发生,会产生一个EXTI_17外部中断,此标志位要清除,否 //则下次停止模式进入失败,原因见问题一贴图
}
【补充】:1.每次进入停止模式之前,都重新执行最开始的三步配置,否则可能出现前几次闹钟事件会触发,后面触发失败的问 题。
2.停止模式唤醒之后,需要重新初始化系统时钟,这时候可以把闹钟中断关闭(因为进入之前会重新开启,避免没有 进入 停止模式时一直有闹钟事件产生)
以上为个人实验总结,不对之处还希望指正
原创文章,转载请注明出处:https://mp.csdn.net/postedit/85785336