저전력 설계 — 배터리로 몇 년을 버티기
코인셀(CR2032)은 약 220mAh 다. 평균 10µA 를 쓰면 2.5년, 1mA 를 쓰면 9일이다. 100배 차이가 제품의 성패를 가른다.
그리고 저전력 설계는 "CPU 를 얼마나 오래 재우느냐" 하나로 요약된다.
평균 전류가 전부다
전체 시간 중 깨어 있는 시간의 비율을 듀티 사이클이라 한다. 저전력 설계는 결국 이 값을 낮추는 일이다.
배터리 수명 = 용량(mAh) / 평균 전류(mA)
평균 전류 = (활성 전류 × 활성 시간 + 대기 전류 × 대기 시간) / 전체 시간
예: 10초마다 100ms 동안 센서를 읽고 나머지는 잔다
활성 20mA × 0.1s = 2.0 mA·s
대기 0.01mA × 9.9s = 0.099 mA·s
평균 2.099 / 10 = 0.21mA → 220mAh / 0.21 ≈ 1,047시간 ≈ 44일
활성 구간이 1%인데 전류의 95%를 쓴다. 그래서 두 방향이 있다 — 깨어 있는 시간을 줄이거나, 깨어 있을 때 더 빨리 끝내거나.
깨는 것 자체가 비용이다 — 짧은 주기에서 절전이 무너지는 이유
위 계산은 "활성 시간 = 작업 시간" 이라고 가정했다. 실제로는 잠에서 깨어 클럭이 안정될 때까지의 시간이 앞에 붙는다. Stop 모드 복귀는 보통 수 µs~수 ms 인데, 이 구간에도 활성에 가까운 전류가 흐른다.
작업이 2ms 인데 웨이크업이 5ms 라면 — 일보다 준비가 더 오래 걸린다. 주기별로 계산해 보자 (활성 20mA, Stop 10µA, 웨이크업 5ms + 작업 2ms).
| 측정 주기 | 실효 활성 비율 | 평균 전류 | CR2032(220mAh) 수명 |
|---|---|---|---|
| 10 ms | 70.0% | 14.00 mA | 0.7 일 |
| 100 ms | 7.0% | 1.41 mA | 6.5 일 |
| 1 s | 0.7% | 0.150 mA | 61 일 |
| 10 s | 0.1% | 0.024 mA | 382 일 |
주기를 10ms → 1s 로 늘리면 수명이 약 90배가 된다. 반대로 주기가 짧아질수록 "깨는 비용"이 전체를 지배해서, 어느 지점부터는 잠들지 않는 편이 오히려 나은 역전이 일어난다.
설계 규칙 — 웨이크업 시간 ≪ 작업 시간 이 성립해야 절전 모드가 의미가 있다. 그렇지 않다면 세 가지를 검토한다:
- 더 얕은 절전 모드를 쓴다(Sleep 은 복귀가 즉시지만 전류가 더 크다 — 교환 관계).
- 여러 작업을 모아서 한 번에 처리한다(배칭). 10ms 마다 깨는 대신 100ms 마다 깨어 10개를 처리.
- 주변장치가 CPU 없이 일하게 한다 — DMA·타이머·아날로그 워치독으로 CPU 를 안 깨우고 조건이 맞을 때만 인터럽트로 깨운다. 이게 가장 효과가 크다.
함정 — 데이터시트의 Stop 모드 전류(µA)만 보고 설계하면, 실측에서 훨씬 큰 값이 나온다. 웨이크업 구간, GPIO 가 뜬 상태로 남아 새는 전류, 외부 센서의 대기 전류가 데이터시트 숫자에 안 들어 있기 때문이다. 평균 전류는 반드시 실측한다.
활성 시간을 100ms → 50ms 로 줄이면 평균이 절반이 된다
대기 전류를 10µA → 2µA 로 줄여도 평균은 거의 안 변한다
어느 쪽이 이득인지는 비율이 정한다. 위 예에서는 활성 시간을 줄이는 것이 압도적이다. 반대로 하루에 한 번만 깨는 장치라면 대기 전류가 전부다.
저전력 모드
Run CPU 실행 중 수 mA ~ 수십 mA
Sleep CPU 정지, 주변장치 동작 수 mA
Stop 클럭 대부분 정지, SRAM 유지 수 µA
Standby 거의 전부 off, SRAM 손실 1µA 미만
| 깨우는 것 | 복귀 시간 | SRAM | |
|---|---|---|---|
| Sleep | 아무 인터럽트 | 즉시 | 유지 |
| Stop | EXTI·RTC 등 일부 | 수 µs~수십 µs | 유지 |
| Standby | 웨이크업 핀·RTC | 리셋과 동일 | 손실 |
// Sleep — 가장 쉽고 가장 자주 쓴다
__WFI(); // 인터럽트가 올 때까지 잔다
// Stop
PWR->CR |= PWR_CR_LPDS; // 저전력 레귤레이터
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__WFI();
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
SystemClock_Config(); // ★ 깨어난 뒤 클럭을 다시 세운다
// Standby
PWR->CR |= PWR_CR_PDDS | PWR_CR_CWUF;
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__WFI(); // 여기서 돌아오지 않는다 — 리셋된다
Stop 에서 깨어나면 클럭이 내부 저속으로 돌아가 있다. 다시 설정하지 않으면 통신 보율이 어긋나고 타이밍이 전부 틀어진다. 이걸 빠뜨리는 것이 가장 흔한 실수다.
Standby 는 SRAM 이 날아간다. 상태를 남기려면 백업 레지스터나 .noinit 영역, 또는 외부 저장이 필요하다.
// 어느 모드에서 깨어났는지 확인
if (PWR->CSR & PWR_CSR_SBF) { // Standby 에서 복귀
PWR->CR |= PWR_CR_CSBF;
restore_from_backup();
}
슈퍼루프를 재우는 방법
// ✗ 바쁜 대기 — CPU 가 계속 돈다
while (1) {
if (data_ready) process();
}
// ✓ 할 일이 없으면 잔다
while (1) {
if (data_ready) { data_ready = false; process(); }
__disable_irq();
if (!data_ready && !any_task_due()) { // 다시 확인하고
__WFI(); // 잔다 (WFI 는 인터럽트가 막혀도 깨어난다)
}
__enable_irq();
}
__disable_irq() 로 감싸는 이유가 중요하다. 확인과 __WFI() 사이에 인터럽트가 들어오면, 플래그를 세웠는데도 그 뒤에 잠들어 깰 기회를 놓친다(lost wakeup). 인터럽트를 막아 두면 __WFI 는 여전히 깨어나므로 이 구멍이 막힌다.
Cortex-M 의 WFI 는 PRIMASK 로 인터럽트가 막혀 있어도 '깨어난다'
→ ISR 은 안 돌지만 WFI 에서 빠져나와, enable 직후 처리된다
주변장치 클럭을 끈다
// 안 쓰는 것은 클럭을 끊는다 — 게이팅된 클럭은 전류를 거의 안 쓴다
RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIOCEN;
RCC->APB1ENR &= ~RCC_APB1ENR_TIM3EN;
"켜 두면 그냥 전류를 먹는다." 개발 중에 다 켜 두고 그대로 출시하는 경우가 흔하다.
// 미사용 핀은 아날로그 모드 — 떠 있으면 문턱 근처에서 진동해 누설이 생긴다
for (int p = 0; p < 16; p++)
if (!(used_mask & (1 << p))) GPIOA->MODER |= (0b11 << (p * 2));
떠 있는 입력 핀 하나가 수십 µA 를 먹을 수 있다. 핀이 많은 칩에서 이것만으로 대기 전류가 몇 배 차이 난다.
// 디버그 비트를 반드시 끈다 (앞 편에서 본 그것)
DBGMCU->CR &= ~(DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY);
"데이터시트는 2µA 인데 우리는 500µA 다" 의 1순위 원인이 이 비트다. 디버그 회로가 계속 살아 있기 때문이다.
클럭을 낮추는 것이 항상 이득은 아니다
동적 전력 ∝ 전압² × 주파수
주파수를 절반으로 낮추면 순간 전류는 절반이 되지만 일이 두 배 오래 걸린다. 총 에너지는 비슷하다.
168MHz 로 10ms 일하고 990ms 잔다 → 대개 이쪽이 낫다
42MHz 로 40ms 일하고 960ms 잔다
"빨리 끝내고 깊이 자라(race to sleep)" 가 기본 전략이다. 깨어 있는 동안 주변장치도 함께 켜져 있으므로, 그 시간을 줄이는 것이 이득이다.
다만 전압을 함께 낮출 수 있으면 이야기가 다르다 — 전압은 제곱으로 들어가므로, 저주파에서 전압 스케일링을 하면 실질적 절감이 된다.
RTC 로 깨우기
// 10초마다 깨어나는 구성
RTC->WUTR = 10 - 1; // 웨이크업 타이머
RTC->CR |= RTC_CR_WUTE | RTC_CR_WUTIE;
// Stop 모드로 들어가면 RTC 만 살아서 10초 뒤 깨운다
enter_stop_mode();
RTC 는 32.768kHz 저속 크리스털로 돌아 수백 nA 만 쓴다. 그래서 Stop·Standby 에서도 시간을 세고 깨울 수 있다.
// 주기적 센서 읽기의 전형적 구조
while (1) {
wake_peripherals(); // 필요한 것만 켠다
read_sensors();
if (should_transmit()) send_data(); // 매번 보내지 않는다
sleep_peripherals(); // 다시 끈다
enter_stop_until_rtc(10); // 10초 잔다
}
"매번 보내지 않는 것" 이 큰 절감이다. 무선 송신은 대개 가장 비싼 동작(수십~수백 mA)이라, 값이 변했을 때만 보내면 평균이 크게 준다.
무선이 지배한다
BLE 광고 1회 수 mA × 수 ms
LTE-M 접속·전송 100~200mA × 수 초 ← 이것 하나가 하루 예산을 먹는다
Wi-Fi 접속 200~300mA × 수 초
무선을 쓰는 순간 MCU 전류는 부차적이 된다. 그래서 설계가 "얼마나 자주 보낼 것인가" 로 옮겨 간다.
전송 주기를 1분 → 10분 으로 늘리면 평균이 거의 1/10 이 된다
데이터를 모아서 한 번에 보내면 접속 오버헤드를 나눠 쓴다
측정하지 않으면 모른다
전류계로는 부족하다 — 순간 피크와 평균을 동시에 봐야 한다
· 저항 + 오실로스코프 싸고 빠르다 (1Ω 을 직렬로, 전압을 본다)
· 전용 전력 분석기 µA~수백 mA 를 동시에 (동적 범위가 넓다)
전형적인 파형
20mA | .--. .--.
| | | | |
10uA +----' '--------------------' '---
|<-->|
활성 100ms 대기 9.9s
피크만 보고 놀라거나 평균만 보고 안심하면 안 된다. 배터리는 평균으로 소모되지만, 순간 피크가 크면 전압 강하로 리셋이 걸린다. 코인셀은 내부 저항이 커서 특히 그렇다.
// 측정을 위해 마커를 남긴다
PROBE_HI(); do_transmission(); PROBE_LO();
// 전류 파형과 프로브 신호를 같이 보면 어느 동작이 얼마를 먹는지 바로 보인다
앞 편의 GPIO 프로브를 전류 측정에 그대로 쓴다. 어느 구간이 비싼지가 눈으로 확인된다.
흔한 함정
| 증상 | 원인 |
|---|---|
| 대기 전류가 데이터시트의 100배 | 디버그 비트가 켜져 있다 |
| Stop 에서 깨어난 뒤 통신이 깨진다 | 클럭 재설정을 안 했다 |
| 가끔 깨어나지 못한다 | lost wakeup — 확인과 WFI 사이에 인터럽트 |
| 대기 전류가 들쭉날쭉하다 | 떠 있는 입력 핀 · 외부 회로의 누설 |
| 배터리가 예상보다 빨리 준다 | 순간 피크로 인한 전압 강하 → 유효 용량 감소 |
| 저온에서 수명이 급감 | 배터리 내부 저항이 커진다 — 설계 여유가 필요 |
// 외부 회로도 본다 — MCU 만 재우면 소용없다
// 풀업 저항 하나가 3.3V/10kΩ = 330µA 를 계속 먹을 수 있다
// LED 표시등, 센서 상시 전원, 레귤레이터 정지 전류(Iq) 를 모두 계산에 넣는다
MCU 를 2µA 로 만들어도 레귤레이터가 50µA 를 먹으면 의미가 없다. 저전력 제품에서는 부품 선정이 코드보다 중요한 경우가 많다.
한눈에 정리
- 평균 전류가 배터리 수명을 정한다 — 활성 1% 구간이 전류의 95%를 쓰는 것이 보통이다
- 활성 시간을 줄일지 대기 전류를 줄일지는 비율이 정한다
- Sleep/Stop/Standby — 복귀 시간과 SRAM 유지 여부가 다르다
- Stop 에서 깨면 클럭을 다시 세워야 한다 — 안 하면 통신이 전부 어긋난다
__WFI앞뒤를disable_irq로 감싼다 — lost wakeup 을 막는다. WFI 는 막혀 있어도 깨어난다- 안 쓰는 주변장치 클럭을 끄고, 미사용 핀은 아날로그로 — 떠 있는 핀 하나가 수십 µA
- 디버그 비트가 대기 전류 이상의 1순위 원인
- race to sleep — 빨리 끝내고 깊이 자는 편이 대개 낫다. 전압을 같이 낮출 수 있을 때만 저주파가 이득
- 무선이 예산을 지배한다 — 설계가 "얼마나 자주 보낼 것인가" 로 옮겨 간다
- 피크와 평균을 같이 본다 — 평균이 수명을, 피크가 리셋을 정한다
- MCU 밖도 계산에 넣는다 — 레귤레이터 정지 전류·풀업·상시 전원 센서
꼬리질문 대비
출처 — STMicroelectronics, RM0090 STM32F4 Reference Manual §5 PWR(Sleep·Stop·Standby) · STMicroelectronics, AN4365 STM32F4 저전력 모드 활용 · Arm, Cortex-M4 Devices Generic User Guide(DUI 0553) §2.5 전력 관리(WFI·WFE·SCR) · Nordic Semiconductor, 무선 전력 소비 특성 문서