问题:# g" a0 w- H. N; Y: ?
该问题由某客户提出,发生在 STM32F401CEY6 401CEY6 401CEY6 器件上。据其工程师讲述:在程序中,在进入 STOP 模式之前他已经将 STM32F401 的 I/O 口做完处理了,但是电流仍然比数据手册写的数值还要大很多,不知道是在哪里消耗了电流。希望能帮他验证一下。& `: X) E4 c7 C r' R- y# q
. t z8 j0 G3 l/ M
调研:& Z. B# `$ M; M# b4 t% G) m
我们先来看一下 STM32F401 的数据手册中,对其在 STOP 模式下工作的电流的参数:
5 i7 @: W8 m. N' g- [3 R4 j
! ?2 m5 x1 q* a# H7 k! d
. [/ K W# w- ~$ D3 \* T* h! O
/ c6 {0 z7 z' r) u7 d E
按客户的要求,我们主要是验证在 STOP 模式下,调压器在低功耗模式下的电流,也就是表格中典型电流为 42uA 的那个值。3 ^/ m% ~( L8 [" S. x; [
我们使用 STM32F401 的 Discovery 板进行验证,因为在 Discovery 板上只需要把 Idd 的那个 JP2 的跳线帽拿掉接上电流表就可以测试电流了,当然,我们必须要检查一下,这个电流供进来给 MCU 作为VDD 时,还可能连接什么电路会导致电流消耗的,检查了一遍,主要是要注意 BOOT0 以及 BOOT1脚。
7 S0 L- Y! K; s/ g
5 E) ]7 U4 v" f% |; }: R- Z$ W. q0 u
6 L- d. E' k5 L0 A+ d$ \( k8 U
! ]1 q6 ~" i: N$ _- h4 N
由于运行用户代码,所以 BOOT0 和 BOOT1 都将接到低电平上,所以电路上要确保 SB19 和 SB20 短路,但是我们可以看到,BOOT1 上面从 VDD 到 R30 到 R31 再到 GND,将会有一个电流产生,所以必须要把 R30 取下来。BOOT0 由于本身 R29 就没有接,所以倒是没有关系。
+ `: }$ w5 q$ w( V, G( D好,那现在开始验证电流,我们使用 32F401CDISCOVERY 的固件库中的 PWR_STOP 的例程来验证。因为不使用 RTC,所以我们将程序中的 RTC 部分删掉:将 stm32f4xx_it.c 中的 voidRTC_WKUP_IRQHandler(void)函数中的内容删除,再将 main.c 中的 static void RTC_Config(void)函数删除,在 int main(void)主函数中也清除掉相关的内容,主函数如下:' ~, O) [" B ]/ x
- int main(void)
& U5 e* ?7 V7 [2 j7 ?/ N, S) n1 R& b - {: W' z0 [* h- A1 R$ B/ t2 c
- /*!< At this stage the microcontroller clock setting is already configured,
) {' V( R/ n$ J/ F; U" i - this is done through SystemInit() function which is called from startup, J, Z- K7 E& L$ P/ }
- file (startup_stm32f401xx.s) before to branch to application main.# B4 {- L" P; r, K9 @0 D, f
- To reconfigure the default setting of SystemInit() function, refer to
0 O* R0 i4 s O% Z I - system_stm32f4xx.c file
0 j* ^. h- S( L; D7 L0 z, J - */
3 i* A! p) q1 \) n
" f# p% \" o( F0 x- /* Configure the SysTick to generate an interrupt each 250 ms */
2 ?8 d4 P M! l) A" w - SysTick_Configuration();
! }0 T. _' u2 K- [0 @ u" E: l - /* Initialize LEDs and User Button mounted on STM32F401C-DISCO board */
: n* X5 v7 ~( A4 r8 A2 d - STM_EVAL_LEDInit(LED3);3 k2 A4 d: f# H8 l( B! i' F
- STM_EVAL_LEDInit(LED6);( g! c$ E d3 }; I
- /* Key button (EXTI_Line0) will be used to wakeup ths system from STOP mode */: g( n# `) J$ v- G* M) x6 z
- STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_EXTI);9 \& ?' I! H8 l& ?; e
- while (1)
% @! t. w& e2 W+ p; y - {( H" O! \) _' ~
- /* Insert 5 second delay */# ]# j. P' R) [3 A) C9 Z
- Delay(20);4 l: a' c* P1 U) y5 C
- /* Turn OFF LED3 */
+ B5 r* @, c' @# o. o4 ^ - STM_EVAL_LEDOff(LED3);$ H2 J( j0 e" _* L
- /* Enter Stop Mode */
f) j, _- W" ^8 v3 s1 Y - PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);4 V0 q+ [2 z4 Y4 g2 K
- /* Configures system clock after wake-up from STOP: enable HSE, PLL and select% h- Z% P/ g G& T& X! T
- PLL as system clock source (HSE and PLL are disabled in STOP mode) */7 w& l4 ~, n2 J; X$ }6 R- s
- SYSCLKConfig_STOP();
: I: `% k+ Z7 P: j* Y - }
4 h, |3 H5 H. e% E0 `; q - }
复制代码 & C* j* m# s7 Y5 _
编译,下载测试电流,进入 STOP 模式后电流为 2.23mA 左右。3 x$ _# N$ L" h' E A: M
大家应该已经发现,此例程在进入 STOP 模式之前,并没有对 I/O 口进行处理,导致很多配置为输入浮空的 I/O 口在受到外界干扰的时候,状态不定,消耗了大量的电流。在实际应用中,在进入 STOP模式前,可以根据实际情况,将 I/O 口设置成模拟输入,输入+上下拉电阻,或者输出固定电平。所以,在这里,我们将我们没用到的 I/O 通通设置为模拟输入,只保留 PA0 作为按键,产生外部中断来将 MCU 从 STOP 模式中唤醒。配置 I/O 口后,为了方便从 STOP 模式唤醒后还能直观地看到 LED灯,我们需要在 stm32f4xx_it.c 的 void EXTI0_IRQHandler(void)函数中加入 LED6 的初始化函数STM_EVAL_LEDInit(LED6),在 main()主函数的 SYSCLKConfig_STOP()之后加上STM_EVAL_LEDInit(LED3)。
& G9 r; D3 s! H9 O& _void EXTI0_IRQHandler(void)如下:' ^* S% l" f* I& I0 g; U; V u
- void EXTI0_IRQHandler(void)+ j" b: x3 W ]& r2 T4 ]; B/ ?$ a$ x
- {
1 j, w0 `8 F5 i( G l7 p - if(EXTI_GetITStatus(USER_BUTTON_EXTI_LINE) != RESET)
6 C. @) J) S' @: @2 h, O F# ] - {: G0 q; F% s @( y5 ~9 h0 Z
- /* Clear the USER Button EXTI line pending bit */! K& j- h) F+ e
- EXTI_ClearITPendingBit(USER_BUTTON_EXTI_LINE);/ ^& C( i& P/ T( a! Q: y4 E
- /* Toggle LED6 */
, R; r! X6 D6 b - STM_EVAL_LEDInit(LED6);
+ `8 S7 P+ `5 ]) ~$ _7 s1 m, ~ - STM_EVAL_LEDToggle(LED6);
* N2 R' f6 w6 s - }
9 \( @+ z, D6 i. d9 Q7 j - }
复制代码 ( j% W- g/ U0 U
Int main(void)中的主循环 while(1)中的内容如下: L* |! `/ I( v2 C4 H! g( Q1 k
- while (1)
6 z. M3 D- S6 p! o( n' e6 y - {0 n" K# e! o8 Y/ @! H1 V9 O
- /* Insert 5 second delay */6 ^$ c; R8 g, Q" S2 z/ X' P
- Delay(20);
" Y6 w2 G4 U+ H0 M1 }5 ^' e! f! ^! U9 s - /* Turn OFF LED3 */
/ q6 J+ o0 p8 N J) A- ? e; n - STM_EVAL_LEDOff(LED3);" i; y$ W9 d1 G( @% z
& {1 [" o& q0 l/ l& L: A. {- /* Configure all I/O ports */5 P, |9 }; F" l! F$ Z
- GPIO_InitTypeDef GPIO_InitStructure;/ n3 c# d) P# X X& E
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
8 V; A0 h% ]# U3 J/ z" g - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);: V) ]% n! x5 C& [& o# l; _
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);& w: I: k$ j8 c7 Q' W; x+ E
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);8 s4 z+ M _, Z9 W
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
3 P7 I: {8 _- F! ^$ ]: m) ~+ c1 J - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
, o) S$ |5 N5 `6 F% U* n' ~0 r6 |! V - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
0 j! I/ C, i5 c. s8 g3 T8 b - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
# Q5 x: W' D+ h" o' K - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
W5 R$ Q+ ~3 c" x8 J3 i - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
) V& J1 C% X+ [' S - GPIO_Init(GPIOB, &GPIO_InitStructure);
9 L/ D$ R" U( o6 I7 @3 l4 q% H - GPIO_Init(GPIOC, &GPIO_InitStructure);9 h# T, |# z; ? ]# p" F5 ^
- GPIO_Init(GPIOD, &GPIO_InitStructure);; _/ O i4 n- s: E O2 d
- GPIO_Init(GPIOE, &GPIO_InitStructure);
( Q( u2 d( ?1 R& ^* D$ k - GPIO_InitStructure.GPIO_Pin = (~GPIO_Pin_0);
8 F9 n3 \4 t) [* v- N% ~; I2 ]/ I E - GPIO_Init(GPIOA, &GPIO_InitStructure);' q" _6 |7 }. C
- /* Enter Stop Mode */
6 m' ~( S1 S! f9 \* Q - PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
$ d: s7 W- Y# |- d. M - /* Configures system clock after wake-up from STOP: enable HSE, PLL and select" e3 M3 c" W Z! Z/ z. `
- PLL as system clock source (HSE and PLL are disabled in STOP mode) */
& Y* g, @/ t5 h4 B6 L - SYSCLKConfig_STOP();: A8 v9 y7 n3 |' J% W3 N3 D
- STM_EVAL_LEDInit(LED3);% O3 `# K' F" P! I% p
- }
复制代码 : q J" v4 I; ^0 o9 z1 I1 N
编译,下载测试电流,进入 STOP 模式后电流为 155uA 左右。这不对啊,距离我们看到的 42uA 还有很大的差距。客户就是在这里不知道为什么了。5 T) c1 o: ~, k7 \' z; `
我们来继续查找原因,客户跟 32F401CDISCOVERY 板一样,使用了外部 8MHz 晶振,所以当使用HSE 的时候,PH0/PH1 被作为 OSC_IN 和 OSC_OUT。问题来了,当进入 STOP 模式时,HSE RC振荡器被禁止,PH0/PH1 这个时候就不再是 OSC_IN 和 OSC_OUT 了,变成普通的 I/O 口,而且是处理浮空输入状态。所以我们还得需要对这两个引脚的 I/O 配置进行处理。再来修改 main()主函数中的while(1)主循环,加入对 PH0/PH1 的处理,程序如下:9 g% [: [ i* [' [5 K) g4 I- m
- while (1)
( P- Y) w# W8 C+ w( ^ - {
6 s- e# O. g+ h, N" U& _ - /* Insert 5 second delay */; b0 f7 X9 ]8 d+ O) \, A
- Delay(20);1 {2 j" L" R* m) z+ t
- /* Turn OFF LED3 */# J+ O2 G8 n( P, j
- STM_EVAL_LEDOff(LED3);
+ x6 [7 u3 A) H7 s - / t" x% D% }" q( j/ u# B5 I
- /* Configure all I/O ports */
6 y) |2 g# A1 c; y - GPIO_InitTypeDef GPIO_InitStructure;# g2 H8 V3 T2 l
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);/ n: Q6 q- K7 }3 M& ^7 q" ]4 D5 G
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); R" X) [( a9 b: P
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);, g% u1 P/ S; h- j
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);$ {. P8 W/ ]" C8 d: B8 x
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); {/ N8 S3 R1 m. D+ z
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE);
M ?$ d& T; L8 V- C/ A& }7 v4 @ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
' r+ e' m5 f( W2 q7 [' I - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
4 g7 N7 M/ Q3 `( X6 }0 q - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
7 C* j2 L1 J# W4 {# Z. E6 v7 C! V9 l - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;/ c4 V; c0 h. m! }/ {; N; G2 W5 A3 o7 g
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;% m& q" x* P2 N' O1 f5 y5 }
- GPIO_Init(GPIOB, &GPIO_InitStructure);) L0 b% ?, c# e
- GPIO_Init(GPIOC, &GPIO_InitStructure);
; v2 q7 h. q# ^- [1 C - GPIO_Init(GPIOD, &GPIO_InitStructure);
1 J$ I) L+ e2 D z9 D1 }% u - GPIO_Init(GPIOE, &GPIO_InitStructure);$ C0 D+ L9 ^ |, e, U& Q! A
- GPIO_Init(GPIOH, &GPIO_InitStructure);* ^4 L! u. _+ z$ p W! D
- GPIO_InitStructure.GPIO_Pin = (~GPIO_Pin_0);) A/ J) U3 d" k3 f( @8 D t! A: p
- GPIO_Init(GPIOA, &GPIO_InitStructure);: ^: J4 y, w+ r4 h5 o3 S
- /* Enter Stop Mode */
4 O$ f( ^, G" E4 S6 o - PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);6 A/ J/ q: |, S% G1 Y1 U1 j7 s
- /* Configures system clock after wake-up from STOP: enable HSE, PLL and select+ _: k8 q& a) Q9 s
- PLL as system clock source (HSE and PLL are disabled in STOP mode) */
4 d- a+ V/ g4 ^% D/ m; A4 S - SYSCLKConfig_STOP();0 y& \7 ?9 q* f/ O! S
- STM_EVAL_LEDInit(LED3);0 Y7 e: n/ u4 c" J0 x
- }
复制代码
9 a6 D& `$ T, P编译,下载测试电流,进入 STOP 模式后电流为 38.5uA。已经比我们从表格中看到 42uA 还小了,任务完成。当然,数据手册中 42uA 是在 VDD=3.3V 的情况下数值,而我们的 Discovery 板的 VDD 为3V。6 i0 o, ]" F& B! Q+ ^$ D% I
( b: U! ~& ~4 p0 f
我们还可以再测一下让 Flash 进入掉电模式下的电流,修改 while(1)程序如下:0 n$ D3 {* t/ e6 e/ w, o( f* w
- while (1)
( }: C$ \) I# A& J6 U3 Z - {. _# z$ Z3 k- D
- /* Insert 5 second delay */1 m; I5 M$ G' ~" a3 |: T9 S
- Delay(20);
6 ]8 o3 I* c# ] a: c! U- Q/ C! c$ P) S - /* Turn OFF LED3 */
+ v& E/ ?7 F3 l) W, ] - STM_EVAL_LEDOff(LED3);
% ?" L/ k+ V( C$ B( z! z
: _ ], d9 P- b+ S8 C1 T3 b- /* Configure all I/O ports */5 s2 t9 s' N/ Z- ^* h
- GPIO_InitTypeDef GPIO_InitStructure;# o& a$ P" Z( O! ?5 V Q1 D a
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
+ v3 {% a- s+ y$ x - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);0 E! d! D8 p; G. H/ N; q
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
0 a8 Z( V, u* R( \# ?3 G - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
& ? [. e+ G! L+ c. ` - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);# s9 h! u/ { u
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE);
/ y5 X$ d" P: {4 ~ ]; I - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;; G v! {6 H2 b" d' E, b8 s9 `6 u
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;0 V( P0 F1 w8 T+ \+ S7 ?
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
4 W% \2 t c$ u) `; ^ - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;# _1 _' x7 z4 {% y. ?9 a; L
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;# l! E H6 `/ m8 t* v
- GPIO_Init(GPIOB, &GPIO_InitStructure);1 b. q; S0 k O
- GPIO_Init(GPIOC, &GPIO_InitStructure);
. t, n$ ^: @$ S5 r' Y7 t/ o8 F; x* y - GPIO_Init(GPIOD, &GPIO_InitStructure);
0 A. i& v$ S! l, T8 v' g - GPIO_Init(GPIOE, &GPIO_InitStructure);
- ~% D4 n/ Y4 l- [& Y* e - GPIO_Init(GPIOH, &GPIO_InitStructure);
: k+ y i9 f+ W% w$ ~) @, e/ A - GPIO_InitStructure.GPIO_Pin = (~GPIO_Pin_0);! _) a# W) J) z3 V* l* \
- GPIO_Init(GPIOA, &GPIO_InitStructure);+ w) k, ] f5 T( d7 ^ s* s
- /* Configure flash power down mode */9 |' s" [& F& M, q
- PWR_FlashPowerDownCmd(ENABLE); G! O( J7 M9 O; [* `6 v Q9 \
- /* Enter Stop Mode */
Y* ^) K! J9 o - PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);% s, ~' y h7 ~. F' T2 o! R
- /* Configures system clock after wake-up from STOP: enable HSE, PLL and select
8 I3 O; O0 y1 L( u: P* W# [; ] - PLL as system clock source (HSE and PLL are disabled in STOP mode) */+ m& t# P$ q) X, v
- SYSCLKConfig_STOP();
; U- m# S& J% Q& \ - STM_EVAL_LEDInit(LED3);. P1 w' Z$ S1 B: x+ I: C
- }
复制代码 0 @( d; q1 s: n1 r! `
编译,下载测试电流,进入 STOP 模式后电流为 10.2uA,与数据手册中的典型值基本一致。
9 @: r# D: Z& ?; q( u- c1 J5 l8 i. u
结论:
3 c; E' B. k1 @; n客户在硬件上使用了 HSE,而在进入 STOP 模式前,只对其他 I/O 进行了处理,漏掉了 HSE 对应的两个I/O 口,导致电流过大。1 N9 b9 P7 k/ q
1 B6 L# g6 l. O2 Q: ^处理:* k. j; D+ |1 ?( F+ f
对 PH0/PH1 进行处理,进入 STOP 模式前配置成模拟输入。
" \; T+ f* Y4 E @* I4 J" y) `7 a7 l
建议:
9 ^0 _! b: H1 o在进入 STOP 模式之前,一定要根据实际电路情况,细致地对各个 I/O 口进行处理。
( Q0 t0 F7 v) }# W x2 J+ o& v, S" k- ]- ?
' B3 w4 D+ t: L: l8 s% `' Z3 E0 @8 g4 q& Y
' g5 N" [5 Q' d: D7 s
|