嵌入式系統低功耗設計完全指南
物聯網裝置通常需要電池供電,如何讓裝置運作數月甚至數年是核心挑戰。本文將從晶片選型、電路設計、韌體優化三個層面,分享低功耗設計的實戰經驗。
功耗基礎概念
電池壽命計算
電池壽命的基本公式:
電池壽命 (小時) = 電池容量 (mAh) / 平均電流 (mA)
但實際情況更複雜,需要考慮:
- 工作模式週期性切換
- 電池自放電(約 1-3% / 月)
- 溫度影響
- 電池老化
功耗來源分析
典型物聯網裝置的功耗分布:
| 模組 | 工作電流 | 佔比 |
|---|---|---|
| MCU 運算 | 5-50 mA | 20% |
| 無線通訊 | 10-100 mA | 50% |
| 感測器 | 1-10 mA | 15% |
| 電源轉換 | 損耗 5-15% | 15% |
晶片選型
超低功耗 MCU 比較
| 晶片 | 待機電流 | 工作電流 | 喚醒時間 |
|---|---|---|---|
| STM32L4 | 30 nA | 28 μA/MHz | 5 μs |
| nRF52840 | 0.4 μA | 51 μA/MHz | 3 μs |
| ESP32-S3 | 7 μA | 240 μA/MHz | 250 μs |
| MSP430FR | 350 nA | 100 μA/MHz | 3.5 μs |
選型建議
- 需要 BLE:Nordic nRF52 系列
- 需要 WiFi:ESP32-C6(低功耗 WiFi)
- 純感測應用:STM32L0/L4 系列
- 極低功耗:MSP430FR 系列
電路設計技巧
1. 電源開關
使用 MOSFET 或負載開關 IC 控制周邊電源:
┌─────────────────┐
VCC ──────┬───────┤ LDO 3.3V │
│ └────────┬────────┘
│ │
│ ┌────────┴────────┐
│ │ MCU (Always ON) │
│ └────────┬────────┘
│ │ GPIO
│ ▼
│ ┌────────────────┐
└───────┤ 負載開關 IC │
│ (TPS22860) │
└────────┬───────┘
│
┌────────┴────────┐
│ 感測器/模組 │
│ (關閉時 0 電流) │
└─────────────────┘
2. 上拉電阻優化
I2C 上拉電阻選擇影響功耗:
- 10kΩ:標準選擇,平衡功耗與訊號品質
- 4.7kΩ:高速傳輸,功耗較高
- 內建上拉:功耗最低,但可能訊號不佳
3. 去耦電容
使用適當的去耦電容減少高頻雜訊:
// 典型配置
// - 100nF:每個 VCC 腳位
// - 10μF:電源輸入
// - 1μF:敏感類比電路
韌體優化
1. 睡眠模式選擇
以 STM32L4 為例,有多種低功耗模式:
// STM32L4 低功耗模式選擇
void enter_low_power_mode(LowPowerMode mode) {
switch (mode) {
case LP_SLEEP:
// 僅停止 CPU,周邊持續運作
// 電流:約 200 μA
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,
PWR_SLEEPENTRY_WFI);
break;
case LP_STOP0:
// 停止 CPU 和大部分時鐘
// 電流:約 12 μA
HAL_PWREx_EnterSTOP0Mode(PWR_STOPENTRY_WFI);
break;
case LP_STOP1:
// 更低功耗 STOP 模式
// 電流:約 5 μA
HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);
break;
case LP_STOP2:
// 最低功耗 STOP 模式
// 電流:約 1.5 μA
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
break;
case LP_STANDBY:
// 僅保留 RTC 和少量暫存器
// 電流:約 280 nA
HAL_PWR_EnterSTANDBYMode();
break;
case LP_SHUTDOWN:
// 最低功耗,需外部喚醒
// 電流:約 30 nA
HAL_PWREx_EnterSHUTDOWNMode();
break;
}
}
2. 時鐘管理
動態調整時鐘頻率:
// 降低時鐘頻率以節省功耗
void reduce_clock_speed(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 切換到 MSI 4MHz(低功耗時鐘源)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6; // 4 MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系統時鐘
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
// 關閉不需要的高速時鐘
__HAL_RCC_PLL_DISABLE();
__HAL_RCC_HSE_CONFIG(RCC_HSE_OFF);
}
3. 週邊電源管理
只在需要時啟用周邊:
// 感測器量測週期
void sensor_measure_cycle(void) {
// 1. 開啟感測器電源
HAL_GPIO_WritePin(SENSOR_PWR_GPIO, SENSOR_PWR_PIN, GPIO_PIN_SET);
// 2. 等待穩定(查閱 datasheet)
HAL_Delay(10);
// 3. 啟用 ADC
__HAL_RCC_ADC_CLK_ENABLE();
HAL_ADC_Init(&hadc1);
// 4. 讀取資料
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
uint16_t value = HAL_ADC_GetValue(&hadc1);
// 5. 關閉 ADC
HAL_ADC_DeInit(&hadc1);
__HAL_RCC_ADC_CLK_DISABLE();
// 6. 關閉感測器電源
HAL_GPIO_WritePin(SENSOR_PWR_GPIO, SENSOR_PWR_PIN, GPIO_PIN_RESET);
}
實戰範例:3 年電池壽命設計
設計目標:CR2450 電池(620mAh),運作 3 年以上。
功耗預算
目標壽命:3 年 = 26,280 小時
電池容量:620 mAh(考慮 80% 可用)= 496 mAh
平均電流上限:496 / 26,280 = 18.9 μA
工作週期設計
| 狀態 | 電流 | 時間 | 週期 |
|---|---|---|---|
| 深度睡眠 | 1 μA | 59 秒 | 每分鐘 |
| 感測器量測 | 500 μA | 100 ms | 每分鐘 |
| BLE 廣播 | 8 mA | 3 ms | 每 10 分鐘 |
平均電流計算
感測器:(500 μA × 0.1s) / 60s = 0.83 μA
BLE:(8 mA × 0.003s) / 600s = 0.04 μA
睡眠:1 μA × (59.9/60) = 0.998 μA
總計:0.83 + 0.04 + 1.0 ≈ 1.87 μA
預估壽命:496 mAh / 0.00187 mA = 265,240 小時 = 30 年
(實際會因電池自放電而縮短至 3-5 年)
實務經驗:nRF52840 + Zephyr 穿戴專案的實測功耗
上面的數字多半來自 datasheet 與理論試算,但實機量到的功耗往往跟試算表差一截。以下是我們在一個 nRF52840 + Zephyr RTOS(Nordic nRF Connect SDK)的穿戴式多感測器專案中,用電源分析儀實際量到的平均電流(鋰電池 4.2V、BLE 連線中、MTU 247):
| 啟用的子系統 | 實測平均電流 @4.2V |
|---|---|
| 僅 BLE 連線 | 1.2 ~ 1.4 mA |
| BLE + 類比前端(ECG) | 2.0 ~ 2.4 mA |
| BLE + 類比前端 + IMU | 2.5 ~ 3.0 mA |
| 全功能(含 Flash 寫入) | 3.2 ~ 3.6 mA |
幾個實機才學得到、datasheet 不會寫的點:
1. 一次只加一個子系統來量,不要一次全開。 我們逐一打開 BLE、類比前端、IMU、Flash 寫入,每加一個就量一次。這樣才知道每個子系統「真正」吃多少電,而不是事後對著一個總數字猜——上表 IMU 約 +0.5mA 的差值就是這樣拆出來的。
2. 先確認 DC/DC 真的有開。 nRF52840 內建 DC/DC 轉換器,但預設未必啟用。把 CONFIG_BOARD_ENABLE_DCDC=y(或對應的 regulator devicetree node)打開後,無線階段電流明顯下降。這是「改一行 config 就省一截」的免費午餐,卻最容易漏掉。
3. BLE 耗電的本質是「連線間隔 × 每次醒來做多少事」。 我們把 MTU 拉到 247 bytes,讓同樣的資料量用更少的連線事件送完——radio 醒著的總時間變短,比單純拉長連線間隔更有效。TX 功率設 +8dBm 會推高峰值電流,但若換到更穩定的連線、減少重傳,整體平均電流反而可能下降,要實測權衡。
4. 出貨版務必把 debug 路徑全關掉。 開發版掛著 RTT / SystemView / Shell / UART console,這些會讓 MCU 無法真正進入最深睡眠。出貨韌體我們關掉 CONFIG_RTT_CONSOLE、CONFIG_LOG_BACKEND_RTT、CONFIG_SHELL、CONFIG_UART_CONSOLE,並啟用 CONFIG_NRF_APPROTECT_LOCK(順帶防止讀回韌體)。光是關掉這些 debug backend,待機電流就降了一個量級。
教訓:低功耗不是設計完就結束,而是「拿電源分析儀逐項量、逐項砍」的迭代過程。理論試算給你方向,實機量測才給你真相。
常見問題排查
功耗異常高?
- 檢查浮空腳位:未使用的 GPIO 設為輸入下拉
- 檢查 debug 介面:SWD 可能持續供電
- 檢查 LED:確認關閉狀態指示燈
- 檢查 LDO 靜態電流:選用超低靜態電流 LDO
睡眠後無法喚醒?
- 確認 RTC 時鐘源配置正確
- 確認喚醒源(GPIO、RTC、UART)設定
- 檢查中斷優先級
低功耗設計是一門需要軟硬體配合的技術。如果你有電池供電的產品需求,歡迎聯繫我們討論。