STM32进入standby时功耗异常分析
- 1. 前言
- 2. 背景和现象
- 3. 分析过程
- Step-1 复现异常(追踪异常的发生场景)
- Step-2 定位与故障相关硬件(缩小排查范围)
- Step-3 尝试恢复异常(进一步摸索异常时现象)
- Step-4 软件Debug(定位问题)
- Step-5 验证想法
- 4. 分析原因
- 5. 解决方法
- 附录A:STM32进入Standby状态完整的代码
1. 前言
本文主要用于记录STM32单片机功耗异常的分析过程,直接看 分析原因 部分可以快速知道问题原因和解决方法。
2. 背景和现象
- 背景:使用STM32F031系列的单片机开发锂电池应用,在电池长时间不使用时,MCU需要进入极低功耗的Standby状态。
- 开发过程: 开发过程中没有遇到太大障碍,参考ST的官方代码,实现了Standby功能。
- 异常现象:但是在生产测试时发现,每次烧录新的程序,单片机进入Standby状态后,功耗异常。
各场景的功耗见下表:
模式 | 常规工作模式 | Standby模式(正常) | Standby模式(异常) |
---|---|---|---|
功耗 | 9mA | 2uA | 0.7mA |
3. 分析过程
Step-1 复现异常(追踪异常的发生场景)
拿了几块功耗正常的板子,对开发与测试的场景进行了多次测试验证,发现了以下规律:
测试场景编号 | 开发环境 | 编译器 | 烧录器 | Standby功耗 |
---|---|---|---|---|
TD1 | Eclipse (Oxygen-4.73) | Arm-GCC | J-Link | 偶发性异常 |
TD2 | Keil-MDK (v5.23) | MDK默认编译器 | J-Link | 正常 |
TD3 | Keil-MDK (v5.23) | MDK默认编译器 | ST-Link | 正常 |
测试场景编号 | 生产烧录环境 | 烧录器 | 烧录器驱动 | Standby功耗 |
---|---|---|---|---|
TP1 | STM32 ST-Link Utility (v3.7.0) | ST-Link | v4.4.0 | 全部异常 |
TP2 | SEGGER J-Flash (v5.00) | J-Link | v6.10 | 正常 |
本步骤结论:特定场景的软件下载方式,会触发这个异常。
补充说明:在这一步时,怀疑烧录软件配置选项有问题,于是把烧录上位机所有的选项都改了一遍,但是没有效果。
Step-2 定位与故障相关硬件(缩小排查范围)
之前测试的是整个板子的功耗,需要在这一步定位是板上的哪个器件引起的功耗异常。
因为在烧录后会引起这个故障,所以MCU的嫌疑最大。于是,用一块只贴有MCU及其外围器件的板子(MCU核心板)再次进行了Step-1的几项测试。测得MCU自身的功耗与Step-1测的板功耗结果很接近。看来,板子功耗异常是MCU引起的。
使用功耗正常、功耗异常的两块MCU核心板进行对比测试,用示波器量了MCU所有管脚的状态。发现在进入Standby状态后,异常功耗MCU的SWDIO-Pin为高电平,正常的为低电平。
查看ST Reference manual(RM0091),MCU进入Standby状态,PA13(SWDIO-Pin)是高阻态。
那么,Standby状态下SWIO-Pin输出为高电平应该是个异常状态。
是因为没有固定这个脚电平?
于是,做了两个验证:
- 外部下拉:在SWDIO脚加一个下拉电路到GND
- 内部下拉:MCU进入Standby状态前,将SWDIO配置为下拉输入模式
结果这两种方式都没有效果。可以认定,SWDIO的高电平,与管脚电平未固定没有关系,是SWDIO自身输出(或内部上拉到)高电平。
本步骤结论:板子功耗异常是MCU引起的,此时MCU的SWDIO脚会异常输出(或内部上拉到)高电平。
Step-3 尝试恢复异常(进一步摸索异常时现象)
继续在MCU核心板上测试,尝试恢复Standby功耗异常的MCU,试了以下几种方法:
恢复方式 | SWDIO状态 | Standby功耗 |
---|---|---|
唤醒信号给到Wakeup Pin (Wakeup event) | 高电平 | 未恢复 |
拉低Reset Pin到GND1秒 (System Reset) | 高电平 | 未恢复 |
使用Step-1中的5种方式重新烧录程序 (Download) | 高电平 | 未恢复 |
断开板子电源后重新上电 (Power-on Reset) | 低电平 | 恢复 |
本步骤结论:只有Power-on reset才能恢复功耗异常。
Step-4 软件Debug(定位问题)
这一步的思路是 单片机状态的差异,往往会体现在它的寄存器值上。
经过前几步的分析,得到了一些结论:
- 烧录软件后会触发异常。
- 异常与SWDIO-Pin状态有很大的相关性。
- Power-on reset可以恢复异常,System reset无法恢复异常。
以上这些结论指向MCU的调试功能。于是,将排查重点放到调试相关的寄存器上。
查看ST Reference manual(RM0091)对DBG寄存器的描述(下图),发现:
- DBG相关的寄存器可以在调试/烧录时被Debugger所配置,这一点与结论1吻合。
- DBG相关的寄存器只在Power-on reset时恢复默认值,这一点与结论3吻合。
使用Eclipse+J-Link调试功耗异常的MCU核心板,读取DBG相关的寄存器,其中DBGMCU-CR寄存器的DBG_STANDBY位为1。
查看ST Reference manual(RM0091)对该位的描述(下图)。DBG_STANDBY位为1时,进入Standby模式,HCLK不会停止,而是转换为内部RC电路(内部低速晶振)作为时钟源继续运行。所以就会在异常的Standby状态下处于 比正常工作模式低,比常规standby模式高的功耗。
又拿了个standby模式下功耗正常的MCU核心板测了下,它的DBG_STANDBY位为0。看来很可能是这个原因。
Step-5 验证想法
在代码中增加主动将DBG_STANDBY清0的动作,放在进入Standby之前。
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_DBGMCUEN);
CLEAR_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STANDBY);
补充说明:这里需要注意,因为DBG也是外设的一种,操作DBG寄存器前要使能其外设时钟。
将这个程序使用Step-1中的几种方式烧录到MCU中,Standby功耗都正常。
本步骤结论:DBG_STANDBY位在烧录时被Debugger配置为1,使得MCU进入Standby功耗异常。
4. 分析原因
通过以上几个步骤的排查,再结合Step-1的测试,发现与烧录上位机STM32 ST-Link Utility的设置有关。
其中有一项配置“Enable debug in low power mode”,如果勾选了。再连接MCU,点击“connect”后,会将MCU的DBG_STANDBY位写1,且在烧录完成、断开连接后不会清除。
又进行了4项测试:
测试编号 | MCU核心板类型 | 是否勾选Enable debug in low power mode | Standby功耗 |
---|---|---|---|
1 | 功耗正常 | 勾选 | 异常 |
2 | 功耗正常 | 不勾选 | 正常 |
3 | 功耗异常 |
勾选 | 异常 |
4 | 功耗异常 |
勾选 | 异常 |
结论:
- 使用STM32 ST-Link Utility烧录,如果在设置中勾选了“Enable debug in low power mode”,MCU进入Standby功耗会异常。
- 如果对功耗正常的板子不勾选“Enable debug in low power mode”进行烧录,MCU进入Standby功耗正常。
- 功耗异常的MCU无法通过不勾选“Enable debug in low power mode”再次烧录的方式恢复。
5. 解决方法
参考ST Reference manual(RM0091), 如果MCU进入Standby功耗异常,以下三种方式可以恢复:
- 使用Debugger将DBG_STANDBY清0
- 在代码中添加了进入Standby关闭DBG_STANDBY语句,重新烧录程序(参考代码见附录)
- 将MCU断电,Power-on reset后恢复正常功耗
附录A:STM32进入Standby状态完整的代码
下面是STM32进入Standby模式的常规代码
void Bsp_Enter_Standby(void)
{
PWR->CSR |= PWR_CSR_EWUP1;
PWR->CR |= PWR_CR_CWUF;
PWR->CR |= PWR_CR_CSBF;
PWR->CR |= PWR_CR_PDDS;
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__WFI();
}
下面是添加关闭DBG_STANDBY的代码
void Bsp_Enter_Standby(void)
{
PWR->CSR |= PWR_CSR_EWUP1;
#if (!defined(DEBUG) || !defined(USE_DBG_STANDBY))
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_DBGMCUEN);
CLEAR_BIT(DBGMCU->CR, DBGMCU_CR_DBG_STANDBY);
#endif
PWR->CR |= PWR_CR_CWUF;
PWR->CR |= PWR_CR_CSBF;
PWR->CR |= PWR_CR_PDDS;
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__WFI();
}