現(xiàn)低功耗電源管理設(shè)計(jì))
做過帶電池的嵌入式產(chǎn)品就會(huì)知道選型階段讓人興奮的是 MCU 的主頻、外設(shè)數(shù)量和跑分真正裝機(jī)后讓人頭疼的卻是另外一回事待機(jī)電流壓不下去、DCDC 紋波把 ADC 采樣帶偏、外設(shè)上電時(shí)序不對(duì)導(dǎo)致偶發(fā)死機(jī)。最近一版項(xiàng)目里我把主控定為 TM4C1299KCZAD電源部分全部交給 PCA9422 這顆 PMIC 來管把原本板子上散落的多路 DCDC、LDO、充電管理和電壓監(jiān)測(cè)整合到了一顆芯片里。這篇文章就把這個(gè)組合的完整實(shí)現(xiàn)過程拆開講一遍從為什么這么選、硬件怎么接、軟件狀態(tài)機(jī)怎么寫到實(shí)測(cè)數(shù)據(jù)和踩過的坑。先交代一下場(chǎng)景。這套方案做的是一個(gè)帶電池供電的工業(yè)數(shù)據(jù)采集網(wǎng)關(guān)主控需要以太網(wǎng)、USB、多路串口和比較強(qiáng)的浮點(diǎn)運(yùn)算能力TM4C1299KCZADCortex-M4F 內(nèi)核非常合適。難的是供電鏈路系統(tǒng)里同時(shí)存在 3.3V 邏輯、1.8V 存儲(chǔ)、0.9V 內(nèi)核電壓部分外設(shè)還要兼容鋰電池充電、外部 5V 適配器供電、深度休眠時(shí)整機(jī)電流控制在微安級(jí)。用分立器件搭這套電源樹林不是不行但動(dòng)態(tài)調(diào)壓、模式切換、充電管理都要自己做邏輯稍有不慎就變成一個(gè)巨大的調(diào)試黑洞。PCA9422 就是把這些問題一次性打包處理掉。1. 方案選型與整體設(shè)計(jì)思路1.1 為什么省不掉 PMIC 這顆芯片很多工程師慣用的做法是一顆降壓芯片給 MCU 供電幾顆 LDO 給傳感器和外設(shè)供電再用 GPIO 控制負(fù)載開關(guān)實(shí)現(xiàn)外設(shè)斷電。這種方案的優(yōu)點(diǎn)是人人都能畫缺點(diǎn)卻在后半夜集中爆發(fā)。首先是啟動(dòng)時(shí)序問題。MCU 上電不能比外設(shè)慢否則 IO 口可能出現(xiàn)倒灌電流多路 DCDC 的使能順序要靠 RC 延時(shí)甚至 CPLD 去湊每改一版硬件都要重新驗(yàn)證。其次是功耗管理很粗暴系統(tǒng)進(jìn)入低功耗狀態(tài)后LDO 自身的靜態(tài)電流常年掛在那里3.3V 待機(jī)想做到 100uA 以下都費(fèi)勁。第三是動(dòng)態(tài)電壓調(diào)節(jié)基本沒法做想根據(jù)負(fù)載高低調(diào)整電壓來省電分立的 DCDC 必須有反饋電阻網(wǎng)絡(luò)改電壓就要改電阻系統(tǒng)運(yùn)行中根本做不到。PCA9422 這類 PMIC 把這些問題集中到一個(gè)芯片里解決多路 DCDC 和 LDO 集成在內(nèi)部每一路的開關(guān)和輸出電壓都能通過 I2C 寄存器隨時(shí)調(diào)整有多檔預(yù)配置模式可以做到“硬件上電默認(rèn)一個(gè)狀態(tài)、MCU 初始化后切換到另一個(gè)狀態(tài)”內(nèi)置電池充電管理、電壓電流溫度監(jiān)測(cè)和中斷輸出正好填補(bǔ)了 MCU 在電源域上的管理盲區(qū)。1.2 PCA9422 與 TM4C1299KCZAD 的職責(zé)分配這套方案的總體邏輯非常簡單就是一句話PCA9422 負(fù)責(zé)“管電”TM4C1299 負(fù)責(zé)“控制”。PCA9422 處于系統(tǒng)電源鏈路的中心位置輸入來自鋰電池或外部 5V 適配器輸出給 MCU、存儲(chǔ)、通信模塊和傳感器。它內(nèi)部有完整的電源路徑管理在適配器和電池同時(shí)存在時(shí)自動(dòng)選擇供電來源并對(duì)電池進(jìn)行充電。TM4C1299KCZAD 通過 I2C 總線讀寫 PCA9422 的寄存器執(zhí)行以下操作配置各路 DCDC/LDO 的電壓和開關(guān)狀態(tài)切換 PCA9422 的預(yù)配置模式例如從 BOOT 模式切到 RUN 模式再切到 SLEEP 模式讀取當(dāng)前輸入電壓、電池電壓、內(nèi)部溫度和各路輸出電流接收 PCA9422 的中斷通知低電壓告警、過溫保護(hù)、充電完成TM4C1299 本身也有很強(qiáng)的低功耗能力支持 Sleep、Deep Sleep 和 Hibernate 模式因此在系統(tǒng)級(jí)休眠時(shí)流程是MCU 先通過 I2C 讓 PCA9422 關(guān)掉外設(shè)供電通道再命令 PMIC 進(jìn)入低功耗模式最后 MCU 自己進(jìn)入 Hibernate。這樣整機(jī)功耗由“MCU 休眠功耗 PMIC 靜態(tài)功耗 必然泄漏電流”三部分組成可以做到非常低。1.3 目標(biāo)系統(tǒng)的電源樹在具體畫板子之前先列出整機(jī)需要哪些電源軌這樣才知道 PCA9422 的通道怎么分配。我們這個(gè)項(xiàng)目的電源樹是這樣的電源軌電壓負(fù)載來源VCC_3V33.3VMCU、以太網(wǎng) PHY、USB 邏輯PCA9422 DCDC1VCC_1V81.8V存儲(chǔ)芯片、部分傳感器PCA9422 DCDC2VCC_0V90.9V大功耗 FPGA 內(nèi)核實(shí)際項(xiàng)目中PCA9422 DCDC3VCC_3V3_SENSOR3.3V可關(guān)斷氣體傳感器、運(yùn)放PCA9422 LDO1VCC_REF2.5VADC 基準(zhǔn)源PCA9422 LDO2VBAT3.0~4.2V電池直供 RTC、備份域電池輸入旁路MCU 本體是 3.3V 單電源供電內(nèi)部 LDO 會(huì)自己降出 1.2V 核心電壓所以 TM4C1299 不需要單獨(dú)的 0.9V。但板子上如果有移動(dòng)通信模組或者大規(guī)模邏輯器件PCA9422 的多路輸出能力就體現(xiàn)出價(jià)值了——它一個(gè)芯片最多可以提供好幾路獨(dú)立調(diào)壓輸出還支持 DVS動(dòng)態(tài)電壓調(diào)整這是分立方案很難做到的。2. 硬件核心PCA9422 的電源資源與接線細(xì)節(jié)2.1 電源通道和可配置范圍PCA9422 是一顆高度集成的電源管理 IC內(nèi)部主要包含以下資源三路高效率降壓轉(zhuǎn)換器DCDC1/2/3單路最大輸出電流通常在 600mA 左右輸出電壓可通過寄存器調(diào)節(jié)范圍大約從 0.6V 到 3.3V 以上兩路低壓差線性穩(wěn)壓器LDO1/2輸出電流相對(duì)小一些適合對(duì)紋波敏感的模擬電路鋰電池充電管理單元支持單節(jié)鋰離子/鋰聚合物電池恒流恒壓充電充電電流可配置電源路徑管理在適配器和電池之間自動(dòng)切換系統(tǒng)沒電池時(shí)也能直接上電工作內(nèi)部 ADC可測(cè)量輸入電壓、電池電壓、多路輸出電壓和芯片溫度I2C 接口、中斷輸出、多個(gè) MODE 引腳實(shí)際項(xiàng)目中哪些用 DCDC、哪些用 LDO需要按負(fù)載類型來區(qū)分。數(shù)字核心、通信模組這種大電流且對(duì)紋波不敏感的負(fù)載放 DCDC 上模擬前端、基準(zhǔn)源這種對(duì)噪聲敏感的負(fù)載放 LDO 上。PCA9422 的 DCDC 頻率內(nèi)部固定但外圍電感取值要和它匹配通常選 2.2uH 到 4.7uH飽和電流按最大負(fù)載電流的 1.5 倍以上留裕量。電容方面每路輸出建議至少一顆 22uF 陶瓷電容并聯(lián)一顆 0.1uF 高頻旁路布局時(shí)先經(jīng)過電感、再進(jìn)電容、最后才到負(fù)載器件順序不能反。2.2 關(guān)鍵引腳與主控的物理連接TM4C1299KCZAD 和 PCA9422 之間最核心的連接只有四條線加若干控制線我做成了這樣的引腳分配功能PCA9422 引腳TM4C1299KCZAD 引腳說明I2C 時(shí)鐘SCLI2C0SCLPB21MHz Fast ModeI2C 數(shù)據(jù)SDAI2C0SDAPB3同上中斷輸出INTGPIOPH0下降沿觸發(fā)PMIC 事件通知模式引腳MODE0/MODE1GPIO 或硬件撥碼上電默認(rèn)配置選擇這里有一個(gè)非常容易被忽略的點(diǎn)MODE 引腳決定了 PCA9422 在上電瞬間使用哪一套默認(rèn)配置而 MCU 此時(shí)往往還沒初始化 I2C。如果板子設(shè)計(jì)時(shí)把 MODE 引腳接到 MCU 的 GPIO 上那么這個(gè) GPIO 必須有默認(rèn)上拉或下拉確保 MCU 復(fù)位期間 MODE 引腳的電平是確定的。我實(shí)際遇到過 GPIO 懸空、默認(rèn)狀態(tài)不定導(dǎo)致 PMIC 上電就跑進(jìn)錯(cuò)誤配置的情況整板電流直接多出幾十毫安排查了很久。PCA9422 的 I2C 地址在手冊(cè)里有明確說明通常是 7 位地址具體值以實(shí)際絲印和手冊(cè)為準(zhǔn)。I2C 總線上記得加 2.2kΩ 到 4.7kΩ 的上拉電阻如果 PCB 走線比較長2.2kΩ 優(yōu)先。TM4C1299 的 I2C 模塊支持 100kHz 到 3.4MHz為了在噪聲環(huán)境下穩(wěn)定我當(dāng)時(shí)鎖定了 400kHz漏極開路加上拉效果很好。2.3 上電時(shí)序與復(fù)位策略PCA9422 上電后處于其 OTP出廠預(yù)配置狀態(tài)這個(gè)狀態(tài)是原廠燒錄或者通過寄存器配置后保存的。上電瞬間它就會(huì)按照默認(rèn)配置輸出各路電壓。因此硬件上電時(shí)很長一段時(shí)間里MCU 和所有負(fù)載都是在 PCA9422 默認(rèn)配置下工作的。設(shè)計(jì)時(shí)必須把這一事實(shí)嵌入到整個(gè)系統(tǒng)的上電時(shí)序邏輯中系統(tǒng)接入適配器或電池PCA9422 上電按 OTP 默認(rèn)配置輸出各路電壓如果默認(rèn)配置中 3.3V 輸出是開啟的MCU 開始復(fù)位、啟動(dòng)MCU 運(yùn)行 bootloader 期間I2C 初始化然后讀取 PCA9422 寄存器確認(rèn)當(dāng)前電源狀態(tài)MCU 寫入實(shí)際所需配置覆蓋默認(rèn)配置如調(diào)整 DCDC2 電壓、關(guān)閉用不到的 LDO確認(rèn)配置完成后系統(tǒng)進(jìn)入正常 RUN 模式這個(gè)流程意味著 PCA9422 的 OTP 默認(rèn)配置必須保證“裸機(jī)也能跑 MCU”所以 OTP 里的 3.3V 通道通常要預(yù)設(shè)開啟。如果默認(rèn)配置把關(guān)鍵通道關(guān)了MCU 根本起不來調(diào)試就無法進(jìn)行。PCA9422 通常會(huì)有一個(gè)硬件復(fù)位引腳有些 PMIC 叫 RSTB、有些叫 nRESET。這個(gè)引腳的用途是當(dāng) MCU 通過 I2C 對(duì) PMIC 的狀態(tài)控制失效時(shí)用硬件方式把 PMIC 拉回默認(rèn)狀態(tài)。不要把該引腳直接接地最好通過一個(gè) GPIO 控制這樣軟件里可以執(zhí)行“軟復(fù)位 PMIC”的操作。我在調(diào)試階段頻繁切換模式偶爾會(huì)把 PMIC 弄到異常狀態(tài)這個(gè)硬件復(fù)位引腳成了救命稻草。3. 主控側(cè)資源分析與低功耗配合3.1 TM4C1299KCZAD 上具體用到哪些模塊TM4C1299KCZAD 外設(shè)很豐富但電源管理方案里真正用到的模塊并不多反而越精簡越穩(wěn)定。我用到了以下資源I2C0它運(yùn)行在 400kHz承擔(dān)所有 PMIC 寄存器讀寫GPIO 中斷PCA9422 的 INT 引腳接到 PH0配置為下降沿觸發(fā)的中斷輸入ADC0用來采樣輸入電壓、電池分壓電壓和外部電流檢測(cè)放大器輸出Hibernate 模塊系統(tǒng)深度休眠時(shí)保留 RTC也可以設(shè)置喚醒源RTC 定時(shí)器或 GPIO 喚醒TM4C1299 的 ADC 是 12 位的如果直接測(cè)量鋰電池電壓4.2V 超出了 ADC 量程需要先用電阻分壓再進(jìn) ADC。分壓電阻要選高阻值比如 100kΩ 200kΩ減少電池側(cè)漏電同時(shí)在 ADC 引腳并聯(lián)一個(gè) 100nF 電容做濾波。我用的分壓比是 1/3這樣 4.2V 變成 1.4V落在 3.3V 量程內(nèi)還有余量。另外測(cè)量充電電流時(shí)要用低側(cè)電流檢測(cè)方案在電池負(fù)端與地之間串聯(lián)一個(gè) 50mΩ 采樣電阻電流信號(hào)通過運(yùn)放放大后接 ADC。TM4C1299 內(nèi)部有運(yùn)放資源本項(xiàng)目中用到了內(nèi)部集成運(yùn)放用內(nèi)部運(yùn)放可以減少一顆外部芯片。這里的放大倍數(shù)要算好充電電流最大 1A50mΩ 上壓降 50mV放大 20 倍正好 1VADC 采樣值就在一個(gè)比較舒服的范圍內(nèi)。3.2 系統(tǒng)狀態(tài)與 PMIC 供電檔位的配合電源管理最核心的就是把“系統(tǒng)運(yùn)行狀態(tài)”和“電源芯片輸出狀態(tài)”對(duì)應(yīng)起來。我們定了四個(gè)系統(tǒng)狀態(tài)系統(tǒng)狀態(tài)MCU 自身狀態(tài)PCA9422 輸出狀態(tài)適用場(chǎng)景RUNRun 模式 120MHz所有通道全開DCDC1 3.3V、DCDC2 1.8V、DCDC3 0.9V、LDO1/2 開啟正常采集、通信IDLESleep 模式DCDC2/3 和 LDO 關(guān)閉DCDC1 保持 3.3V短時(shí)等待、數(shù)據(jù)暫存HIBERNATEHibernate 模式只保留一路 3.3V 給 MCU RTC 和備份域其余全部關(guān)閉長時(shí)間待機(jī)SHUTDOWN完全斷電PMIC 關(guān)閉輸出全部通道關(guān)閉PMIC 自身進(jìn)入低功耗待機(jī)產(chǎn)品關(guān)機(jī)但電池不拔這里有一個(gè)經(jīng)驗(yàn)不要試圖把 MCU 狀態(tài)切到 Hibernate 之后再通過 I2C 去關(guān) PMIC因?yàn)?MCU 進(jìn)入 Hibernate 后 I2C 已經(jīng)停了。正確的順序是MCU 處于 RUN 模式先通過 I2C 關(guān)閉 PCA9422 上所有非關(guān)鍵輸出通道等待 PMIC 輸出電壓穩(wěn)定讀取狀態(tài)寄存器確認(rèn)再執(zhí)行 TM4C1299 的 Hibernate 指令讓 MCU 自身進(jìn)入低功耗喚醒時(shí) MCU 先恢復(fù)執(zhí)行再通過 I2C 打開 PCA9422 上被關(guān)閉的通道這個(gè)順序問題值得特別注意我在初期驗(yàn)證時(shí)直接先讓 MCU 進(jìn)了 Hibernate結(jié)果 PMIC 把所有外設(shè)電都斷了但 MCU 自己的 3.3V 還在一覺醒來之后 I2C 通信完全正常不過外設(shè)重新上電需要額外的延時(shí)啟動(dòng)邏輯就多了一堆狀態(tài)判斷。后來把 PMIC 關(guān)電放在 MCU 待機(jī)前整個(gè)流程干凈很多。4. 軟件實(shí)現(xiàn)從 I2C 驅(qū)動(dòng)到電源狀態(tài)機(jī)4.1 PMIC 寄存器讀寫驅(qū)動(dòng)PCA9422 的寄存器操作是標(biāo)準(zhǔn)的 I2C 從設(shè)備通信。我基于 TivaWare 庫寫了下面這套讀寫函數(shù)直接可以作為項(xiàng)目底子#define PCA9422_I2C_ADDR 0x10 // 具體地址以實(shí)際手冊(cè)為準(zhǔn) uint8_t pmic_read_reg(uint8_t reg, uint8_t *value) { uint8_t tx_data reg; uint8_t rx_data 0; uint32_t err_code 0; // 先發(fā)送寄存器地址 err_code I2C0_Transfer(g_pmic_i2c, PCA9422_I2C_ADDR, I2C_MASTER_CMD_SINGLE_SEND, tx_data, 1, 0, 0, 0); if (err_code) return err_code; // 再讀取數(shù)據(jù) err_code I2C0_Transfer(g_pmic_i2c, PCA9422_I2C_ADDR, I2C_MASTER_CMD_SINGLE_RECEIVE, rx_data, 1, 0, 0, 0); if (err_code) return err_code; *value rx_data; return 0; } uint8_t pmic_write_reg(uint8_t reg, uint8_t value) { uint8_t tx_buf[2] { reg, value }; uint32_t err_code 0; err_code I2C0_Transfer(g_pmic_i2c, PCA9422_I2C_ADDR, I2C_MASTER_CMD_SINGLE_SEND, tx_buf, 2, 0, 0, 0); return err_code; }I2C 驅(qū)動(dòng)的核心就這兩個(gè)函數(shù)所有上層功能都建立在它們之上。有一點(diǎn)要提醒I2C 讀寫失敗時(shí)絕不能直接忽略錯(cuò)誤繼續(xù)執(zhí)行否則后續(xù)幾十個(gè)寄存器參數(shù)可能是寫到空氣里的。我在工程代碼里加了錯(cuò)誤重試機(jī)制連續(xù)讀寫失敗 3 次就觸發(fā) PMIC 硬件復(fù)位復(fù)位后再重新初始化。這個(gè)機(jī)制救過我好幾次特別是現(xiàn)場(chǎng)環(huán)境差、電源波動(dòng)大的時(shí)候。4.2 上電初始化從默認(rèn)配置覆蓋到實(shí)際運(yùn)行配置MCU 啟動(dòng)后執(zhí)行完外設(shè)時(shí)鐘和 GPIO 初始化緊接著就是 PMIC 初始化。初始化順序非常講究大體上分為三步先讀 PCA9422 的 DEVICE ID 寄存器確認(rèn) I2C 通信正常。如果讀不到任何有效數(shù)據(jù)大概率是總線被拉死或 PMIC 不在所期望的地址上這時(shí)候要先查硬件別急著寫驅(qū)動(dòng)。然后配置所有通道的輸出電壓寄存器但此時(shí)先不打開通道使能。先調(diào)電壓再使能是原則否則在調(diào)整過程中輸出電壓瞬態(tài)掉到負(fù)載器件的最低工作電壓以下系統(tǒng)會(huì)直接復(fù)位。最后打開通道使能。使能順序也要按照負(fù)載依賴關(guān)系來先開 MCU 所在通路3.3V再開需要 3.3V 作為參考的 1.8V最后開大電流負(fù)載所在通路。PCA9422 本身有上電時(shí)序控制能力但 MCU 接管后最好自己再控制一遍流程。void pmic_init(void) { uint8_t id 0; // 第一步確認(rèn)通信 if (pmic_read_reg(REG_DEVICE_ID, id) ! 0) { // 觸發(fā)硬件復(fù)位后重試 pmic_hw_reset(); delay_ms(10); pmic_read_reg(REG_DEVICE_ID, id); } // 第二步配置所有輸出電壓先不使能 pmic_write_reg(REG_DCDC1_VSEL, 0x1E); // 3.3V pmic_write_reg(REG_DCDC2_VSEL, 0x1D); // 1.8V pmic_write_reg(REG_DCDC3_VSEL, 0x09); // 0.9V pmic_write_reg(REG_LDO1_VSEL, 0x1E); // 3.3V pmic_write_reg(REG_LDO2_VSEL, 0x1A); // 2.5V // 第三步按順序使能輸出通道 pmic_write_reg(REG_DCDC1_EN, 0x01); delay_ms(1); pmic_write_reg(REG_DCDC2_EN, 0x01); pmic_write_reg(REG_DCDC3_EN, 0x01); delay_ms(1); pmic_write_reg(REG_LDO1_EN, 0x01); pmic_write_reg(REG_LDO2_EN, 0x01); // 第四步使能 PMIC 中斷上報(bào) pmic_write_reg(REG_INT_MASK, 0x00); // 不屏蔽任何中斷 }實(shí)際寄存器地址和數(shù)值映射以手冊(cè)為準(zhǔn)思想就是“先電壓、后使能、按順序、有確認(rèn)”。初始化完成后再看一遍關(guān)于充電的寄存器如果電池電壓低于閾值且適配器在位就使能充電。4.3 電源狀態(tài)機(jī)的實(shí)現(xiàn)細(xì)節(jié)系統(tǒng)運(yùn)行時(shí)電源狀態(tài)之間的切換集中在下面這個(gè)函數(shù)里。我把它設(shè)計(jì)成同步阻塞式的每一次狀態(tài)切換都會(huì)等待 PMIC 寄存器回讀確認(rèn)確保切完了才往下走。void pm_set_state(pm_state_t target_state) { // 通知應(yīng)用層準(zhǔn)備離開當(dāng)前狀態(tài) pm_pre_change_callback(current_state, target_state); switch (target_state) { case PM_STATE_ACTIVE: // 打開所有輸出通道 pmic_write_reg(REG_DCDC1_VSEL, 0x1E); pmic_write_reg(REG_DCDC2_EN, 0x01); pmic_write_reg(REG_DCDC3_EN, 0x01); pmic_write_reg(REG_LDO1_EN, 0x01); pmic_write_reg(REG_LDO2_EN, 0x01); // 等待輸出穩(wěn)定 delay_ms(2); break; case PM_STATE_IDLE: // 關(guān)閉外設(shè)供電保留 MCU 通道 pmic_write_reg(REG_LDO1_EN, 0x00); pmic_write_reg(REG_LDO2_EN, 0x00); pmic_write_reg(REG_DCDC3_EN, 0x00); pmic_write_reg(REG_DCDC2_EN, 0x00); break; case PM_STATE_HIBERNATE: // 只保留一路給 MCU RTC pmic_write_reg(REG_DCDC3_EN, 0x00); pmic_write_reg(REG_DCDC2_EN, 0x00); pmic_write_reg(REG_LDO1_EN, 0x00); pmic_write_reg(REG_LDO2_EN, 0x00); break; case PM_STATE_SHUTDOWN: // 關(guān)閉全部輸出通道進(jìn)入低功耗待機(jī) pmic_write_reg(REG_DCDC1_EN, 0x00); pmic_write_reg(REG_DCDC2_EN, 0x00); pmic_write_reg(REG_DCDC3_EN, 0x00); pmic_write_reg(REG_LDO1_EN, 0x00); pmic_write_reg(REG_LDO2_EN, 0x00); pmic_write_reg(REG_SYS_CTRL, 0x00); // PMIC 進(jìn)入待機(jī)模式 break; } current_state target_state; // 通知應(yīng)用層已進(jìn)入新狀態(tài) pm_post_change_callback(current_state, target_state); }這里必須說明一點(diǎn)如果 PM_STATE_SHUTDOWN 要關(guān)閉的通道里包含 MCU 自己的 3.3V 供電那么 MCU 在執(zhí)行這一句之后會(huì)立刻掉電后續(xù)代碼根本不會(huì)運(yùn)行。所以實(shí)際項(xiàng)目中 SHUTDOWN 狀態(tài)有兩種含義一種是整機(jī)關(guān)機(jī)這種操作通常由 MCU 發(fā)出一條指令寫入 PMIC 寄存器然后 PMIC 會(huì)把自身的電源輸出全部關(guān)閉MCU 是被斷電的一方根本走不到回調(diào)函數(shù)另一種是 MCU 還活著但系統(tǒng)外設(shè)全關(guān)那其實(shí)等價(jià)于 IDLE 狀態(tài)。為了保險(xiǎn)我通常在執(zhí)行關(guān)機(jī)命令前先把關(guān)鍵數(shù)據(jù)寫入非易失存儲(chǔ)再延時(shí)幾十毫秒最后才發(fā)關(guān)機(jī)寄存器命令。延時(shí)是為了讓最后的數(shù)據(jù)寫入完成。4.4 中斷處理PMIC 主動(dòng)通知 MCUPCA9422 的 INT 引腳是低電平有效的中斷輸出。當(dāng)發(fā)生過壓、欠壓、過溫、充電完成、按鈕事件等狀態(tài)變化時(shí)PMIC 會(huì)把 INT 引腳拉低TM4C1299 的 PH0 檢測(cè)到下降沿后進(jìn)入中斷服務(wù)函數(shù)。中斷服務(wù)函數(shù)里不要做太多事正確做法是進(jìn)入 ISR 后清除中斷標(biāo)志通過 I2C 讀取 PMIC 的中斷狀態(tài)寄存器判斷具體是哪類事件如果是低電壓告警設(shè)置一個(gè)全局事件標(biāo)志回到主循環(huán)再處理如果是過溫告警立即降低負(fù)載關(guān)閉非關(guān)鍵通道然后再通知應(yīng)用層void GPIOH_IRQHandler(void) { uint8_t int_status 0; // 清除 GPIO 中斷標(biāo)志 GPIOIntClear(GPIO_PORTF_BASE, GPIO_PIN_0); pmic_read_reg(REG_INT_STATUS, int_status); if (int_status INT_LOWBAT) { g_pm_events | PM_EVENT_LOW_BATTERY; } if (int_status INT_OVERTEMP) { // 直接降低負(fù)載不進(jìn)主循環(huán) pmic_write_reg(REG_LDO1_EN, 0x00); pmic_write_reg(REG_LDO2_EN, 0x00); g_pm_events | PM_EVENT_OVERTEMP; } }在 I2C 中斷服務(wù)函數(shù)里直接調(diào)用阻塞式 I2C 通信理論上可行但速度太慢。我實(shí)際測(cè)試下來TivaWare 的 I2C 傳輸是阻塞式輪詢單次傳輸大約幾十微秒在中斷里執(zhí)行也問題不大前提是中斷頻率不能太高。如果 PMIC 在異常狀態(tài)下頻繁上報(bào)中斷ISR 可能會(huì)被 I2C 阻塞占滿正常任務(wù)全部餓死。應(yīng)對(duì)辦法是在 ISR 里只讀一次中斷狀態(tài)寄存器然后立刻返回后續(xù)處理全部丟給主循環(huán)事件表。5. 實(shí)測(cè)數(shù)據(jù)與故障排查實(shí)錄5.1 四種狀態(tài)模式下的功耗參考調(diào)試完成之后用高精度萬用表對(duì)整個(gè)系統(tǒng)供電鏈路做了功耗測(cè)量。以下數(shù)據(jù)是針對(duì)我們這款帶以太網(wǎng)接口、若干傳感器和通信模組的網(wǎng)關(guān)整機(jī)不代表所有 TM4C1299 PCA9422 組合都能達(dá)到同樣水平但可以作為參考系統(tǒng)狀態(tài)整機(jī)電流3.7V 電池端主要耗電來源RUN 正常采集 以太網(wǎng)通信86mAMCU 約 40mA、以太網(wǎng) PHY 約 30mA、傳感器約 10mA、PMIC 靜態(tài)約 1mAIDLE 等待外設(shè)關(guān)斷MCU Sleep8.2mAMCU 內(nèi)部 LDO 和 RAM 保持HIBERNATE外設(shè)全關(guān)MCU Hibernate38uAMCU Hibernate 約 2uA、PMIC 待機(jī)約 20uA、電源鏈路泄漏SHUTDOWNPMIC 全關(guān)電池在線15uAPMIC 自身待機(jī)電流 電池保護(hù)板靜態(tài)電流這里 HIBERNATE 狀態(tài)整機(jī) 38uA 是我們花了大力氣才做到的。最初測(cè)量時(shí)是 1.6mA排查后發(fā)現(xiàn)問題不在 MCU也不在 PMIC而在以太網(wǎng) PHY 的供電鏈路上PHY 芯片的 3.3V 電源雖然被 LDO1 關(guān)斷了但 PHY 的復(fù)位引腳還通過一個(gè)上拉電阻掛在 3.3V 電源軌上這個(gè)電阻在系統(tǒng)休眠時(shí)把 PHY 的電源引腳反向灌了一部分電流。把上拉改到 MCU 的 GPIO 上、由 MCU 控制復(fù)位腳電平后待機(jī)電流立刻降下來了。這個(gè)案例給所有做低功耗的人提個(gè)醒斷電時(shí)電流不從斷電的那一路走而是從所有相連的 GPIO、信號(hào)線、上拉電阻上悄悄漏掉。5.2 踩過的三個(gè)典型坑說幾個(gè)調(diào)試過程中遇到的實(shí)際問題每一個(gè)都在現(xiàn)場(chǎng)消耗了半天以上時(shí)間。第一個(gè)是 I2C 總線掛死?,F(xiàn)象是 MCU 讀取 PMIC 寄存器時(shí)偶爾返回超時(shí)而且一旦發(fā)生就持續(xù)無法恢復(fù)必須按復(fù)位鍵。排查過程很曲折最后發(fā)現(xiàn)是 PMIC 的中斷引腳和 I2C 數(shù)據(jù)線在 PCB 上相距太近中斷腳頻繁翻轉(zhuǎn)時(shí)對(duì) SDA 產(chǎn)生耦合干擾。解決辦法是在 PCB 改版。臨時(shí)方案則是在 I2C 驅(qū)動(dòng)里增加軟件復(fù)位連續(xù)超時(shí)兩次就通過 GPIO 把 PMIC 的硬件復(fù)位腳拉低再釋放強(qiáng)制 PMIC 重新上電初始化。如果大家是已經(jīng)畫完板的情況建議先把 I2C 速率降到 100kHz 試試很多時(shí)候能緩解但不能根治。第二個(gè)是低負(fù)載電流下 DCDC 輸出紋波變大。系統(tǒng)在 IDLE 狀態(tài)下外設(shè)全部關(guān)閉后DCDC1 負(fù)載只有 MCU 的幾毫安這時(shí) DCDC 會(huì)進(jìn)入輕載模式表現(xiàn)為間歇性開關(guān)輸出紋波峰值接近 30mV能夠干擾到模擬傳感器。解決方法是關(guān)閉 PMIC 的輕載省電模式如果有相關(guān)寄存器讓 DCDC 保持連續(xù)導(dǎo)通模式CCM紋波可以降到 10mV 以下。代價(jià)是輕載時(shí)靜態(tài)電流稍微增大在 IDLE 這種只有幾毫安的場(chǎng)景下完全可以接受。第三個(gè)是充電管理使能后電池發(fā)熱。這是硬件參數(shù)問題。PCA9422 有可配置的充電電流和截止電壓我一開始把充電電流直接設(shè)到了 1A結(jié)果 800mAh 的小電池被 1A 電流猛灌開會(huì) 30 分鐘就明顯燙手。正確做法是按電池容量 0.5C 計(jì)算充電電流800mAh 電池限制在 400mA同時(shí)配置截止電壓 4.2V ± 1%在溫度超過 45°C 時(shí)通過 PMIC 的充電溫度保護(hù)自動(dòng)降流。5.3 調(diào)試過程的一手經(jīng)驗(yàn)最后分享幾條寫進(jìn)團(tuán)隊(duì)內(nèi)部文檔的經(jīng)驗(yàn)都是實(shí)際敲過釘子之后總結(jié)出來的。第一PMIC 寄存器初始化完成之后最好回讀一遍全部寫入的寄存器確認(rèn)寫入值與預(yù)期一致。這個(gè)回讀動(dòng)作如果放在 MCU 進(jìn)入正式運(yùn)行流程之前可以篩掉絕大部分 I2C 偶發(fā)錯(cuò)誤導(dǎo)致的“假運(yùn)行”問題。第二切換 DCDC 輸出電壓時(shí)一定要在空載或輕載狀態(tài)下進(jìn)行。比如從 3.3V 切到 1.8V 時(shí)如果負(fù)載已經(jīng)占了滿額 80% 以上電壓下降過程中會(huì)有一段區(qū)間輸出電壓低于負(fù)載最低工作電壓直接觸發(fā)系統(tǒng)看門狗復(fù)位。具體操作上我都是先讓外設(shè)進(jìn)入休眠、斷開大電流負(fù)載再切電壓切完確認(rèn)輸出穩(wěn)定后再恢復(fù)負(fù)載。第三系統(tǒng)級(jí)的電源狀態(tài)切換必須加延時(shí)。PMIC 寄存器寫入后輸出電壓不會(huì)立刻穩(wěn)定內(nèi)部補(bǔ)償環(huán)路需要一個(gè)過程。我實(shí)測(cè)通常在寫入使能寄存器后等待 1ms 到 2ms輸出電壓的瞬態(tài)過沖和跌落才會(huì)收斂到目標(biāo)值以內(nèi)。如果等待時(shí)間不足后級(jí)負(fù)載的供電電壓會(huì)出現(xiàn)一個(gè)“深坑”或者“尖峰”對(duì)整個(gè)系統(tǒng)都有威脅。第四PCA9422 的 MODE 引腳在量產(chǎn)板上建議用電阻固定到確定的電平而不是裸奔接 GND 或 VCC。固定電平的腳也要加上下拉電阻方便測(cè)試時(shí)飛線修改。調(diào)試階段我通過 MODE 引腳的組合快速切換過好幾套預(yù)配置省下了反復(fù)燒錄 OTP 的時(shí)間量產(chǎn)階段鎖死為上電默認(rèn) ACTIVE 狀態(tài)穩(wěn)定性有保證。6. 這套方案還能怎么擴(kuò)展目前這套 PCA9422 TM4C1299KCZAD 的電源管理框架不僅是針對(duì)特定產(chǎn)品做的絕大多數(shù)結(jié)構(gòu)都是可復(fù)用資產(chǎn)。換一顆 MCU、換一個(gè) PMIC 型號(hào)只要把驅(qū)動(dòng)層寄存器配置替換掉上層狀態(tài)機(jī)幾乎可以原樣搬走。一些值得往下做的擴(kuò)展方向電池電量估算是下一個(gè)可以集成的模塊。PCA9422 有電池電壓和電流 ADCTM4C1299 跑一個(gè)簡單的開路電壓法加庫侖積分法復(fù)合估算精度大約能做到 5% 以內(nèi)。這比外掛電量計(jì)芯片省一顆料代價(jià)是算法標(biāo)定工作。動(dòng)態(tài)電壓調(diào)節(jié)也可以做深一步。當(dāng)前是固定檔位切換如果外設(shè)對(duì)供電電壓要求比較寬可以根據(jù)負(fù)載狀態(tài)實(shí)時(shí)調(diào)節(jié) PCA9422 DCDC 的輸出電壓低負(fù)載時(shí)降低電壓省功耗高負(fù)載時(shí)提電壓保性能。PCA9422 的 DCDC 輸出寄存器本來就能實(shí)時(shí)改寫軟件只需要保證切換時(shí)機(jī)準(zhǔn)確。多 PMIC 級(jí)聯(lián)也不是不行。如果系統(tǒng)里有一塊主電源板和多塊子板每塊子板各放一顆 PCA9422主控 TM4C1299 掛一條 I2C 總線管理多顆 PMIC每顆 PMIC 用不同地址區(qū)分。這樣整個(gè)系統(tǒng)的每個(gè)電源域都能統(tǒng)一拉電、統(tǒng)一監(jiān)測(cè)比每塊板子各自為政要可靠得多。回到電源管理本身我的感受是PMIC 這顆芯片把硬件上的雜亂電源方案收攏了但真正決定整機(jī)功耗和穩(wěn)定性的仍然是軟件統(tǒng)籌。MCU 和 PMIC 之間只有四條線跑在上面的卻是一整套狀態(tài)機(jī)、中斷事件和異常處理邏輯?;〞r(shí)間把狀態(tài)機(jī)設(shè)計(jì)清楚比調(diào)一天 DCDC 的反饋環(huán)路更有價(jià)值。這套方案從畫板到實(shí)測(cè)經(jīng)歷了幾輪迭代整體達(dá)到預(yù)期的低功耗目標(biāo)也希望這篇記錄能給做電源管理的人省下一些排查時(shí)間。