Skip to content

Chapter 18

Hongjayeong-02 edited this page Oct 5, 2026 · 1 revision

18. Power Management — Standby Mode + Wakeup Pin

Verification Status

Build / Link       : PASS
B1 → Standby Entry : PASS
PA0 WKUP → Wakeup  : NOT VERIFIED

Standby 진입까지는 실제 NUCLEO-F439ZI에서 확인했다.

PA0(WKUP)을 이용한 wake-up은 점퍼선이 없어 아직 검증하지 못했다.


Goal

NUCLEO-F439ZI의 B1 버튼을 눌러 STM32F439ZI를 Standby Mode로 진입시킨다.

이후 PA0의 WKUP 기능을 사용하여 다시 system을 깨우는 구조를 이해한다.

Normal Operation
      ↓
B1 Press
      ↓
PC13 EXTI
      ↓
LED OFF
      ↓
Configure Standby
      ↓
SLEEPDEEP = 1
      ↓
__WFI()
      ↓
STANDBY

Wake-up 목표:

PA0 WKUP
LOW → HIGH
      ↓
Wakeup Event
      ↓
System Reset
      ↓
main() from beginning
      ↓
SBF Check
      ↓
"System resume from Standby....."

1. STM32 Low-Power Modes

STM32F4의 대표적인 low-power mode:

Mode | 절전 수준 | Wake-up | 특징 -- | -- | -- | -- Sleep | 낮음 | 빠름 | CPU core 중심으로 정지 Stop | 중간 | 보통 | 주요 system clock 정지 Standby | 가장 높음 | 가장 느림 | 대부분의 회로와 SRAM 상태 소실

5. Board Signals

이번 실습:

B1
→ PC13
→ EXTI13
→ Standby 진입

Wake-up target:

PA0
→ WKUP pin

LED:

LD1
→ PB0

LED 상태를 이용해:

ON
→ Normal Operation

OFF → Entering Standby

를 표시한다.


6. Practice Code

Project:

Inc/
├── gpio.h
├── gpio_exti.h
├── standby_mode.h
└── uart.h

Src/ ├── gpio.c ├── gpio_exti.c ├── standby_mode.c ├── uart.c └── main.c

Ch07:

GPIO

Ch10:

UART

Ch14:

EXTI

driver를 재사용한다.


7. standby_mode.h

#ifndef STANDBY_MODE_H__
#define STANDBY_MODE_H__

#include <stdint.h> #include "stm32f4xx.h"

/*

  • Configure PA0 as the external WKUP input. */ void wakeup_pin_init(void);

/*

  • Configure Standby mode and enter low-power state. */ void standby_wakeup_pin_setup(void);

/*

  • Return the current PA0 input state.
  • Complete archived function body was not preserved
  • in the current Notion source. */ uint8_t get_wakeup_pin_state(void);

#endif


8. wakeup_pin_init()

void wakeup_pin_init(void)
{
/*
* --------------------------------------------------------
* Step 1. Enable GPIOA Clock
* --------------------------------------------------------
*
* PA0 is used as the WKUP input pin.
*/
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
/*
 * --------------------------------------------------------
 * Step 2. Configure PA0 as Input
 * --------------------------------------------------------
 *
 * MODER<span>[1:0]</span>:
 *
 *     00 = Input
 *
 * Clear both bits.
 */
GPIOA-&gt;MODER &amp;= ~(1U &lt;&lt; 0);
GPIOA-&gt;MODER &amp;= ~(1U &lt;&lt; 1);


/*
 * --------------------------------------------------------
 * Step 3. Disable Internal Pull-up / Pull-down
 * --------------------------------------------------------
 *
 * PUPDR<span>[1:0]</span>:
 *
 *     00 = No Pull
 *
 * External wiring directly determines
 * whether PA0 is LOW or HIGH.
 */
GPIOA-&gt;PUPDR &amp;= ~(1U &lt;&lt; 0);
GPIOA-&gt;PUPDR &amp;= ~(1U &lt;&lt; 1);

}


9. standby_wakeup_pin_setup()

아래는 기존 실습에 보존된 핵심 코드다.

void standby_wakeup_pin_setup(void)
{
/*
* --------------------------------------------------------
* Step 1. Check Wakeup Pin State
* --------------------------------------------------------
*
* ORIGINAL PRACTICE CODE:
*/
while (get_wakeup_pin_state() == 0U)
{
}
/*
 * IMPORTANT — RE-TEST REQUIRED
 *
 * Existing notes described the code above as:
 *
 *     "wait until PA0 is stably LOW"
 *
 * However the actual C condition means:
 *
 *     while PA0 == 0
 *         keep waiting
 *
 * Therefore this loop exits only when:
 *
 *     PA0 != 0
 *     → HIGH
 *
 * The code and archived explanation do not agree.
 *
 * Because PA0 wake-up was NOT hardware-tested,
 * do not silently change this condition.
 *
 * Verify the intended PA0 sequence during re-test.
 */


/*
 * --------------------------------------------------------
 * Step 2. Temporarily Disable Wakeup Pin
 * --------------------------------------------------------
 *
 * PWR-&gt;CSR bit8 controls the wakeup pin.
 *
 * Disable it before clearing the old
 * wakeup condition.
 */
PWR-&gt;CSR &amp;= ~(1U &lt;&lt; 8);


/*
 * --------------------------------------------------------
 * Step 3. Clear Wakeup Flag
 * --------------------------------------------------------
 *
 * PWR-&gt;CR bit2:
 *
 *     CWUF
 *
 * Clears a previously latched wakeup event.
 */
PWR-&gt;CR |= (1U &lt;&lt; 2);


/*
 * --------------------------------------------------------
 * Step 4. Enable Wakeup Pin Again
 * --------------------------------------------------------
 *
 * PA0 WKUP detection becomes active.
 */
PWR-&gt;CSR |= (1U &lt;&lt; 8);


/*
 * --------------------------------------------------------
 * Step 5. Select Standby Mode
 * --------------------------------------------------------
 *
 * set_power_mode(PWR_MODE_STANDBY)
 *
 * configures PWR-&gt;CR.PDDS.
 *
 * Full helper implementation is not preserved
 * in the archived chapter source.
 */
set_power_mode(PWR_MODE_STANDBY);


/*
 * --------------------------------------------------------
 * Step 6. Enable Deep Sleep at Cortex-M4 Level
 * --------------------------------------------------------
 *
 * SCB SCR bit2:
 *
 *     SLEEPDEEP = 1
 *
 * Tells the Cortex-M core that WFI should
 * enter a deep low-power mode.
 */
SCB-&gt;SCR |= (1U &lt;&lt; 2);


/*
 * --------------------------------------------------------
 * Step 7. Wait For Interrupt
 * --------------------------------------------------------
 *
 * __WFI()
 *
 * WFI = Wait For Interrupt
 *
 * With:
 *
 *     PDDS = 1
 *     SLEEPDEEP = 1
 *
 * this instruction causes the MCU
 * to enter Standby.
 *
 * After this point normal code execution stops.
 */
__WFI();


/*
 * In Standby mode, successful wake-up causes
 * a reset-like restart.
 *
 * Therefore normal execution is not expected
 * to continue here.
 */

}


10. Critical Re-Test Item — PA0 Condition

이 chapter에서 가장 중요한 미확인 부분이다.

현재 코드:

while (get_wakeup_pin_state() == 0U)
{
}

logic:

PA0 = 0
↓
Stay in while()

PA0 = 1 ↓ Exit while()

즉 코드만 해석하면:

Wait until PA0 becomes HIGH

이다.

하지만 기존 설명은:

PA0이 LOW로 안정될 때까지 대기

라고 기록되어 있었다.

둘은 같은 의미가 아니다.

따라서 재실습에서는 debugger나 UART를 사용해서 반드시:

PA0 LOW일 때 함수 진행 여부
PA0 HIGH일 때 함수 진행 여부

를 먼저 확인한다.

이 부분은 PA0 wake-up 자체가 아직 미검증이므로 현재 임의로 수정하지 않는다.


11. main.c

#include "gpio.h"
#include "gpio_exti.h"
#include "standby_mode.h"
#include "uart.h"

/*

  • Check whether this boot occurred
  • after leaving Standby mode. */ static void check_reset_source(void);

/*

  • Called from EXTI interrupt path. */ static void exti_callback(void);

int main(void) { /* * -------------------------------------------------------- * Step 1. Initialize UART * -------------------------------------------------------- * * Used to report wakeup status. */ uart_init();

/*
 * --------------------------------------------------------
 * Step 2. Initialize LD1
 * --------------------------------------------------------
 *
 * PB0 → LD1
 */
led_init();


/*
 * --------------------------------------------------------
 * Step 3. Initialize PA0 Wakeup Input
 * --------------------------------------------------------
 */
wakeup_pin_init();


/*
 * --------------------------------------------------------
 * Step 4. Check Previous Reset Source
 * --------------------------------------------------------
 *
 * Standby wake-up causes main() to restart.
 *
 * Therefore check SBF early during boot.
 */
check_reset_source();


/*
 * --------------------------------------------------------
 * Step 5. Indicate Normal Operation
 * --------------------------------------------------------
 *
 * LED ON:
 *
 *     board is awake
 */
led_on();


/*
 * --------------------------------------------------------
 * Step 6. Configure B1 EXTI
 * --------------------------------------------------------
 *
 * B1:
 *
 *     PC13
 *        ↓
 *     EXTI13
 *        ↓
 *     Standby entry
 */
pc13_exti_init();


while (1)
{
    /*
     * Main loop has no polling task.
     *
     * B1 interrupt triggers Standby entry.
     */
}

}


12. check_reset_source()

static void check_reset_source(void)
{
/*
* --------------------------------------------------------
* Step 1. Enable Power Controller Clock
* --------------------------------------------------------
*
* PWR peripheral belongs to APB1.
*/
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
/*
 * --------------------------------------------------------
 * Step 2. Check Standby Flag
 * --------------------------------------------------------
 *
 * SBF:
 *
 *     Standby Flag
 *
 * If set:
 *
 *     this boot followed Standby mode.
 */
if ((PWR-&gt;CSR &amp; PWR_CSR_SBF)
    == PWR_CSR_SBF)
{
    /*
     * -----------------------------------------------
     * Clear Standby Flag
     * -----------------------------------------------
     *
     * CSBF:
     *
     *     Clear Standby Flag
     */
    PWR-&gt;CR |= PWR_CR_CSBF;


    /*
     * Inform the user that this boot occurred
     * after Standby wake-up.
     *
     * NOTE:
     *
     * This exact message is the EXPECTED output.
     *
     * Actual PA0 wake-up has not yet been verified.
     */
    uart_write_string(
        "System resume from Standby.....\r\n"
    );


    /*
     * ORIGINAL PRACTICE CODE
     *
     * Same PA0 condition appears here.
     *
     * Again:
     *
     *     while state == 0
     *
     * means the function waits while LOW
     * and exits when HIGH.
     *
     * Re-test required.
     */
    while (get_wakeup_pin_state() == 0U)
    {
    }
}


/*
 * --------------------------------------------------------
 * Step 3. Check Wakeup Flag
 * --------------------------------------------------------
 *
 * WUF:
 *
 *     Wakeup Flag
 */
if ((PWR-&gt;CSR &amp; PWR_CSR_WUF)
    == PWR_CSR_WUF)
{
    /*
     * Clear WUF for the next low-power cycle.
     */
    PWR-&gt;CR |= PWR_CR_CWUF;
}

}


13. EXTI Callback

static void exti_callback(void)
{
/*
* --------------------------------------------------------
* Step 1. Turn LED OFF
* --------------------------------------------------------
*
* This is the last visible indication
* before entering Standby.
*
* It must happen BEFORE __WFI().
*/
led_off();
/*
 * --------------------------------------------------------
 * Step 2. Enter Standby
 * --------------------------------------------------------
 */
standby_wakeup_pin_setup();


/*
 * Once __WFI() enters Standby,
 * normal execution stops.
 */

}


14. Why LED OFF Before __WFI()?

Consider:

standby_wakeup_pin_setup();
led_off();

If standby_wakeup_pin_setup() reaches:

__WFI();

and Standby is successfully entered:

CPU execution stops

so:

led_off();

is never executed.

Therefore correct ordering:

led_off();

standby_wakeup_pin_setup();

이다.

즉:

LED OFF
↓
WFI
↓
Standby

이다.


15. Standby Entry Flow

B1 press:

B1
↓
PC13 Rising Edge
↓
EXTI13
↓
EXTI15_10_IRQHandler
↓
exti_callback()
↓
LED OFF
↓
Wakeup Flag Clear
↓
Wakeup Pin Enable
↓
PDDS = 1
↓
SLEEPDEEP = 1
↓
__WFI()
↓
STANDBY

이 부분은 실제 board에서 확인했다.


16. Why SLEEPDEEP + PDDS?

둘은 역할이 다르다.

SLEEPDEEP

ARM Cortex-M4 core setting:

SCB->SCR |= (1U << 2);

의미:

WFI
↓
Deep Sleep

PDDS

STM32-specific power setting:

PWR->CR.PDDS = 1

의미:

Deep Sleep Mode
중에서도
Standby 선택

따라서:

SLEEPDEEP only
→ Deep Sleep category

PDDS + SLEEPDEEP → Standby

로 이해한다.


17. __WFI()

__WFI();

는 ARM instruction:

Wait For Interrupt

이다.

일반 상황에서는 CPU가 interrupt/event를 기다리며 sleep할 때 사용한다.

이번에는 미리:

SLEEPDEEP = 1
PDDS      = 1

을 설정했기 때문에:

__WFI()
↓
Standby

로 이어진다.


18. What Happens to SRAM?

Standby에서는 SRAM 상태가 유지되지 않는다.

예:

uint32_t counter = 1234;

Standby 진입 전:

counter = 1234

라고 해도 wake-up 후:

이 변수 값에서 계속 실행

하는 구조가 아니다.

Wake-up:

Reset
↓
Startup
↓
.data initialize
↓
.bss clear
↓
main()

과정을 다시 거친다.

따라서 Standby는:

Pause / Resume

가 아니라:

Power Down
+
Reboot

에 가깝다.


19. Standby Flag — SBF

Wake-up 후 CPU는 다시 main()부터 시작하기 때문에 software 입장에서는:

Power-on reset?
NRST?
Standby wake?

를 구분해야 한다.

여기서:

PWR->CSR & PWR_CSR_SBF

를 확인한다.

SBF = 1
↓
Previous state was Standby

따라서:

uart_write_string(
"System resume from Standby.....\r\n"
);

과 같은 처리가 가능하다.


20. Wakeup Flag — WUF

Wakeup event가 발생하면:

WUF = 1

이 될 수 있다.

다음 low-power cycle을 위해 이전 flag를 남겨두면 안 된다.

따라서:

PWR->CR |= PWR_CR_CWUF;

로 clear한다.

패턴:

Check Event
↓
Handle Event
↓
Clear Flag
↓
Prepare Next Event

이다.


21. PA0 WKUP Re-Test Procedure

이 부분은 아직 실제 검증 전이다.

점퍼선을 준비한 후:

Initial State

PA0
→ GND
→ LOW

Enter Standby

B1 Press
↓
LED OFF
↓
Standby

Wake-up Test

PA0을:

LOW
↓
HIGH

으로 변경한다.

예:

GND
↓
3.3 V

Expected:

Wakeup Event
↓
System Reset
↓
main()
↓
SBF detected
↓
UART:
System resume from Standby.....
↓
LD1 ON

22. Re-Test Priority

실기 재검증 때 순서는 다음이 좋다.

1. PA0 IDR 확인
2. get_wakeup_pin_state() return 확인
3. while 조건 확인
4. WUF clear 확인
5. EWUP enable 확인
6. PDDS 확인
7. SLEEPDEEP 확인
8. WFI
9. PA0 LOW → HIGH
10. SBF 확인

특히 가장 먼저:

while (get_wakeup_pin_state() == 0U)
{
}

가 원래 의도한 sequence와 맞는지 확인해야 한다.


23. Actual Verification Result

현재 실제 확인한 부분:

Build
PASS

Download PASS

B1 EXTI PASS

LED OFF before Standby PASS

Standby Entry PASS

실제 현상:

Normal
→ LD1 ON

B1 Press → LD1 OFF

Standby → UART communication stops

Standby 진입 자체는 확인했다.


24. Pending Verification

아직 확인하지 못한 부분:

PA0 WKUP
↓
Standby Exit
↓
System Reset
↓
SBF = 1
↓
UART Resume Message

이유:

PA0 wake-up test용 jumper wire 없음

따라서 현재:

Standby Entry

Verified

Wakeup

Pending

으로 기록한다.


25. Build Result

기존 실습 기록:

text = 1660 bytes
data = 0 bytes
bss  = 2560 bytes
Warnings = 0

26. Debugger Verification

PWR Clock

확인:

RCC->APB1ENR

Expected:

PWREN = 1

Wakeup Pin

확인:

PWR->CSR

PA0 wakeup function enable bit 확인.


Standby Selection

확인:

PWR->CR

Expected before WFI:

PDDS = 1

Cortex-M Deep Sleep

확인:

SCB->SCR

Expected:

SLEEPDEEP = 1

Wakeup After Re-Test

PA0 wake 성공 후:

PWR->CSR

에서:

SBF
WUF

상태를 확인한다.


27. Screenshots to Add

현재 추가 가능:

images/ch18_build.png
images/ch18_before_standby.png
images/ch18_standby_led_off.png
images/ch18_power_registers.png

재실습 후 추가:

images/ch18_pa0_low.png
images/ch18_pa0_high.png
images/ch18_wakeup_flags.png
images/ch18_resume_uart.png
images/ch18_wakeup.gif

가장 중요한 최종 evidence:

LD1 ON
↓
B1 Press
↓
LD1 OFF
↓
Standby
↓
PA0 LOW → HIGH
↓
Reset
↓
UART:
System resume from Standby.....
↓
LD1 ON

이다.


28. Key Points

B1
↓
EXTI
↓
LED OFF
↓
PWR.PDDS
+
SCB.SLEEPDEEP
↓
__WFI()
↓
STANDBY

Wake-up:

PA0 WKUP
↓
Reset
↓
main()
↓
SBF
↓
Wakeup Detection
  • Standby는 STM32F4의 가장 깊은 저전력 mode 중 하나다.

  • Standby에서는 SRAM과 일반 execution context가 유지되지 않는다.

  • Standby wake-up 후에는 main()부터 다시 실행된다.

  • SCB->SCR.SLEEPDEEP은 Cortex-M core 차원의 deep-sleep 설정이다.

  • PWR->CR.PDDS는 STM32에서 Standby mode를 선택한다.

  • __WFI()가 실제 low-power mode 진입 trigger가 된다.

  • PA0은 WKUP input으로 사용한다.

  • SBF를 통해 Standby에서 깨어난 boot인지 확인한다.

  • WUF는 wakeup event flag다.

  • B1 → EXTI → Standby 진입까지는 실제 보드에서 검증했다.

  • PA0 wake-up은 아직 hardware 미검증이다.

  • 기존 while (get_wakeup_pin_state() == 0) 코드와 “LOW 안정 대기” 설명이 일치하지 않으므로 재실습에서 반드시 확인한다.

Clone this wiki locally