Repository navigation
Chapter 18
Verification Status
Build / Link : PASS B1 → Standby Entry : PASS PA0 WKUP → Wakeup : NOT VERIFIEDStandby 진입까지는 실제 NUCLEO-F439ZI에서 확인했다.
PA0(WKUP)을 이용한 wake-up은 점퍼선이 없어 아직 검증하지 못했다.
NUCLEO-F439ZI의 B1 버튼을 눌러 STM32F439ZI를 Standby Mode로 진입시킨다.
이후 PA0의 WKUP 기능을 사용하여 다시 system을 깨우는 구조를 이해한다.
Normal Operation
↓
B1 Press
↓
PC13 EXTI
↓
LED OFF
↓
Configure Standby
↓
SLEEPDEEP = 1
↓
__WFI()
↓
STANDBYWake-up 목표:
PA0 WKUP
LOW → HIGH
↓
Wakeup Event
↓
System Reset
↓
main() from beginning
↓
SBF Check
↓
"System resume from Standby....."STM32F4의 대표적인 low-power mode:
Mode | 절전 수준 | Wake-up | 특징 -- | -- | -- | -- Sleep | 낮음 | 빠름 | CPU core 중심으로 정지 Stop | 중간 | 보통 | 주요 system clock 정지 Standby | 가장 높음 | 가장 느림 | 대부분의 회로와 SRAM 상태 소실이번 실습:
B1
→ PC13
→ EXTI13
→ Standby 진입Wake-up target:
PA0
→ WKUP pinLED:
LD1
→ PB0LED 상태를 이용해:
ON
→ Normal Operation
OFF
→ Entering Standby
를 표시한다.
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:
GPIOCh10:
UARTCh14:
EXTIdriver를 재사용한다.
#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
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->MODER &= ~(1U << 0);
GPIOA->MODER &= ~(1U << 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->PUPDR &= ~(1U << 0);
GPIOA->PUPDR &= ~(1U << 1);
}
아래는 기존 실습에 보존된 핵심 코드다.
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->CSR bit8 controls the wakeup pin.
*
* Disable it before clearing the old
* wakeup condition.
*/
PWR->CSR &= ~(1U << 8);
/*
* --------------------------------------------------------
* Step 3. Clear Wakeup Flag
* --------------------------------------------------------
*
* PWR->CR bit2:
*
* CWUF
*
* Clears a previously latched wakeup event.
*/
PWR->CR |= (1U << 2);
/*
* --------------------------------------------------------
* Step 4. Enable Wakeup Pin Again
* --------------------------------------------------------
*
* PA0 WKUP detection becomes active.
*/
PWR->CSR |= (1U << 8);
/*
* --------------------------------------------------------
* Step 5. Select Standby Mode
* --------------------------------------------------------
*
* set_power_mode(PWR_MODE_STANDBY)
*
* configures PWR->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->SCR |= (1U << 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.
*/
}
이 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 자체가 아직 미검증이므로 현재 임의로 수정하지 않는다.
#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.
*/
}
}
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->CSR & PWR_CSR_SBF)
== PWR_CSR_SBF)
{
/*
* -----------------------------------------------
* Clear Standby Flag
* -----------------------------------------------
*
* CSBF:
*
* Clear Standby Flag
*/
PWR->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->CSR & PWR_CSR_WUF)
== PWR_CSR_WUF)
{
/*
* Clear WUF for the next low-power cycle.
*/
PWR->CR |= PWR_CR_CWUF;
}
}
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.
*/
}
Consider:
standby_wakeup_pin_setup();
led_off();If standby_wakeup_pin_setup() reaches:
__WFI();and Standby is successfully entered:
CPU execution stopsso:
led_off();is never executed.
Therefore correct ordering:
led_off();
standby_wakeup_pin_setup();
이다.
즉:
LED OFF
↓
WFI
↓
Standby이다.
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에서 확인했다.
둘은 역할이 다르다.
ARM Cortex-M4 core setting:
SCB->SCR |= (1U << 2);의미:
WFI
↓
Deep SleepSTM32-specific power setting:
PWR->CR.PDDS = 1의미:
Deep Sleep Mode
중에서도
Standby 선택따라서:
SLEEPDEEP only
→ Deep Sleep category
PDDS + SLEEPDEEP
→ Standby
로 이해한다.
__WFI();는 ARM instruction:
Wait For Interrupt이다.
일반 상황에서는 CPU가 interrupt/event를 기다리며 sleep할 때 사용한다.
이번에는 미리:
SLEEPDEEP = 1
PDDS = 1을 설정했기 때문에:
__WFI()
↓
Standby로 이어진다.
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에 가깝다.
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"
);과 같은 처리가 가능하다.
Wakeup event가 발생하면:
WUF = 1이 될 수 있다.
다음 low-power cycle을 위해 이전 flag를 남겨두면 안 된다.
따라서:
PWR->CR |= PWR_CR_CWUF;로 clear한다.
패턴:
Check Event
↓
Handle Event
↓
Clear Flag
↓
Prepare Next Event이다.
이 부분은 아직 실제 검증 전이다.
점퍼선을 준비한 후:
PA0
→ GND
→ LOWB1 Press
↓
LED OFF
↓
StandbyPA0을:
LOW
↓
HIGH으로 변경한다.
예:
GND
↓
3.3 VExpected:
Wakeup Event
↓
System Reset
↓
main()
↓
SBF detected
↓
UART:
System resume from Standby.....
↓
LD1 ON실기 재검증 때 순서는 다음이 좋다.
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와 맞는지 확인해야 한다.
현재 실제 확인한 부분:
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 진입 자체는 확인했다.
아직 확인하지 못한 부분:
PA0 WKUP
↓
Standby Exit
↓
System Reset
↓
SBF = 1
↓
UART Resume Message이유:
PA0 wake-up test용 jumper wire 없음따라서 현재:
Standby Entry
Verified
Wakeup
Pending
으로 기록한다.
기존 실습 기록:
text = 1660 bytes
data = 0 bytes
bss = 2560 bytesWarnings = 0확인:
RCC->APB1ENRExpected:
PWREN = 1확인:
PWR->CSRPA0 wakeup function enable bit 확인.
확인:
PWR->CRExpected before WFI:
PDDS = 1확인:
SCB->SCRExpected:
SLEEPDEEP = 1PA0 wake 성공 후:
PWR->CSR에서:
SBF
WUF상태를 확인한다.
현재 추가 가능:
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이다.
B1
↓
EXTI
↓
LED OFF
↓
PWR.PDDS
+
SCB.SLEEPDEEP
↓
__WFI()
↓
STANDBYWake-up:
PA0 WKUP
↓
Reset
↓
main()
↓
SBF
↓
Wakeup DetectionStandby는 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 안정 대기” 설명이 일치하지 않으므로 재실습에서 반드시 확인한다.