:MicroPython實(shí)現(xiàn)鋰電池電量檢測)
這段時(shí)間給一個(gè)手持小設(shè)備做電源管理主控選了樹莓派RP2350也就是Pico 2上那顆新MCU電池呢是常見的3.7V鋰電池。設(shè)備要做電量顯示最初我用電阻分壓加ADC讀電壓、再換算成電量結(jié)果在低電量階段誤差大得離譜同一個(gè)電池放電電流大一點(diǎn)電壓就掉到3.2V放一會(huì)又回彈。后來換上了MAX17048電量計(jì)I2C接口讀電壓和剩余電量都變成幾行代碼的事從接線到跑通實(shí)測下來確實(shí)5分鐘就能看到數(shù)據(jù)。這篇文章把完整接線、原理、MicroPython代碼和常見坑全記錄下來適合正在做Pico 2項(xiàng)目、畢設(shè)電源管理或者想給DIY設(shè)備加電量顯示的朋友參考。1. 為什么不用電阻分壓加ADCMAX17048的定位1.1 電量測量的三種常見方案對(duì)比先別急著看代碼我覺得有必要把為什么選MAX17048講清楚。很多做嵌入式的小伙伴一開始都會(huì)想著“不就是測個(gè)電壓嘛ADC加兩個(gè)電阻就搞定了”我以前也是這么干的但踩過幾次坑之后發(fā)現(xiàn)事情沒那么簡單。現(xiàn)在主流的電量測量方案大概有三類。第一類是純電壓讀取法就是電阻分壓加上單片機(jī)ADC查一個(gè)電壓-電量對(duì)照表來換算。這個(gè)方法成本最低但是有兩個(gè)很致命的問題鋰電池的放電曲線并不是一條直線在3.7V到3.3V這段尤其陡峭電壓稍微波動(dòng)一點(diǎn)算出來的電量就偏差好幾個(gè)百分點(diǎn)而且電池內(nèi)阻會(huì)隨著放電電流變化電機(jī)一轉(zhuǎn)、屏幕一亮電壓馬上掉下去你讀到的值根本不是真實(shí)的開路電壓而是帶負(fù)載的瞬時(shí)電壓這時(shí)候算電量就是自欺欺人。第二類是庫侖計(jì)法也叫電流積分法通過一顆精密采樣電阻串聯(lián)在電池回路里用ADC持續(xù)采樣電流并積分從而得到充放電電量。這個(gè)方法精度不錯(cuò)但需要額外考慮采樣電阻的溫漂、放大電路的零點(diǎn)漂移BOM成本和PCB面積都上去了對(duì)于一個(gè)小型手持設(shè)備來說有點(diǎn)重。第三類就是MAX17048這類ModelGauge電量計(jì)方案。它不需要外置采樣電阻芯片直接采集電池正端電壓內(nèi)部通過電壓曲線、電壓變化率以及電池老化模型來估算剩余電量。整體外圍電路就是一個(gè)去耦電容。它把復(fù)雜擬合過程全部做在芯片內(nèi)部對(duì)外提供I2C接口你讀兩個(gè)寄存器就能拿到電壓和SOCState of Charge剩余電量百分比這也是它在消費(fèi)電子產(chǎn)品里應(yīng)用非常廣的原因。方案精度外圍成本適用場景電阻分壓ADC較低受負(fù)載影響大最低玩具、對(duì)電量要求不高的設(shè)備庫侖計(jì)較高高需要精密采樣電阻高精度監(jiān)測、容量分析MAX17048 ModelGauge中等偏上低只需一顆電容手持設(shè)備、低功耗設(shè)備1.2 我選擇MAX17048的3個(gè)實(shí)際理由第一是成本低模塊在市面上幾塊錢到十幾塊錢就能買到芯片本身封裝也很小適合集成到自己的PCB里。第二是開發(fā)效率高I2C通信大家都很熟而且它提供的SOC寄存器直接就是百分比數(shù)據(jù)不需要自己查表擬合省掉了最麻煩的調(diào)試環(huán)節(jié)。第三是功耗低MAX17048的工作電流據(jù)說在微安級(jí)別對(duì)于電池供電的設(shè)備來說很友好不會(huì)因?yàn)榧恿穗娏坑?jì)反而加快掉電。當(dāng)然它也有不擅長的地方。ModelGauge本質(zhì)上還是基于電壓估算的如果你要求的是“絕對(duì)嚴(yán)謹(jǐn)?shù)膸靵黾?jí)計(jì)量精度”它肯定不如帶采樣電阻的庫侖計(jì)芯片。但是在大多數(shù)產(chǎn)品里用戶只是想看到“還有多少電、還能用多久”一個(gè)不太離譜的數(shù)值MAX17048完全夠用。至少在Pico 2這種級(jí)別的項(xiàng)目里它的精度和成本平衡得非常漂亮。2. 硬件準(zhǔn)備與接線5分鐘里最關(guān)鍵的1分鐘2.1 物料清單和選型建議做這個(gè)實(shí)驗(yàn)不需要太多東西我列一下我實(shí)際用的物料。樹莓派RP2350開發(fā)板也就是Raspberry Pi Pico 2一定注意別買成老的RP2040 Pico雖然代碼邏輯一樣但標(biāo)題既然說了RP2350我們就用新的芯片。MAX17048電量計(jì)模塊買那種已經(jīng)把I2C上拉電阻做好的成品模塊最省事很多模塊還帶引腳座直接杜邦線就能連。3.7V鋰電池最好是帶保護(hù)板的防止實(shí)驗(yàn)過程中過放。面包板、杜邦線若干如果模塊沒焊排針需要先焊一下。有條件的話準(zhǔn)備一塊萬用表排查問題時(shí)會(huì)很有幫助。這里有個(gè)容易被忽略的點(diǎn)MAX17048的VCC工作電壓范圍一般是2.5V到4.5V所以可以直接接鋰電池正極不需要額外穩(wěn)壓。它測的就是VCC引腳上的電池電壓。但如果你的模塊上帶穩(wěn)壓電路那就要看模塊說明書搞清楚VCC到底應(yīng)該接電池還是接3.3V。2.2 接線表和接線要點(diǎn)接線非常簡單我使用的是RP2350的I2C0控制器引腳配置為SDAGP0、SCLGP1。這個(gè)引腳不是焊死的MicroPython允許你把I2C功能映射到其他支持的GPIO上但GP0和GP1是最常用、最容易記的一組。MAX17048模塊引腳接到哪里VCC鋰電池正極GND鋰電池負(fù)極同時(shí)接到Pico 2的GNDSDAPico 2的GP0SCLPico 2的GP1接線順序我建議先接電源和地再接SDA和SCL這樣不容易亂。特別要注意的是共地問題MAX17048的GND必須和RP2350的GND連在一起否則I2C信號(hào)根本沒有參考電平通信是不可能正常的。我之前遇到過一次怎么掃描都找不到設(shè)備最后發(fā)現(xiàn)是電池負(fù)極和開發(fā)板沒有共地兩個(gè)地之間懸空總線完全癱瘓。還有一個(gè)細(xì)節(jié)是MAX17048模塊上最好焊一個(gè)0.1uF的陶瓷電容在VCC和GND之間。很多成品模塊已經(jīng)帶了但如果是自己搭芯片這個(gè)電容一定要加。原因很簡單電量計(jì)芯片內(nèi)部需要穩(wěn)定的參考電壓來做AD轉(zhuǎn)換電源紋波太大會(huì)讓讀出來的電壓值來回跳。加了去耦電容之后讀數(shù)會(huì)穩(wěn)定不少。2.3 I2C上拉電阻的兩種常見情況I2C總線的工作原理是開漏輸出也就是說設(shè)備只能把SDA和SCL拉低不能主動(dòng)拉高所以必須外部接上拉電阻到電源。絕大多數(shù)MAX17048成品模塊都自帶4.7k或者10k的上拉電阻接上就能用。但如果你買的是裸芯片那就得自己處理上拉電阻。我建議在SDA和SCL上各接一個(gè)4.7k電阻另一端接到3.3V。為什么不建議直接拉到電池正極因?yàn)殡姵貪M電的時(shí)候電壓能到4.2V而RP2350的GPIO耐壓一般不推薦超過3.6V如果模塊的上拉電阻是拉到電池端的SDA/SCL的高電平就會(huì)被拉到4.2V長時(shí)間運(yùn)行對(duì)GPIO有損傷風(fēng)險(xiǎn)。有些模塊自己做了電平轉(zhuǎn)換那就沒問題沒有的話最好把上拉電阻拉到3.3V。不過由于RP2350的GPIO內(nèi)部有鉗位二極管短時(shí)間實(shí)驗(yàn)一般不會(huì)燒但長期產(chǎn)品設(shè)計(jì)不值得這么賭。3. 核心原理一次弄懂MAX17048的I2C寄存器3.1 MAX17048到底是怎么算出電量的既然用到了這個(gè)東西最好還是理解一下它的工作思路以后換芯片、調(diào)參數(shù)心里才有底。MAX17048用的是Maxim的ModelGauge算法它不需要采樣電阻而是通過電池電壓的變化來推斷剩余電量。你可以這樣理解鋰電池在靜置狀態(tài)下電壓和電量之間存在一條相對(duì)穩(wěn)定的關(guān)系曲線這就是OCV開路電壓曲線。但設(shè)備一旦開始工作電池內(nèi)阻會(huì)讓端電壓下降而且下降的幅度和電流、溫度、電池老化程度都有關(guān)系。如果單純根據(jù)當(dāng)前電壓查表年輕電池和老化電池的結(jié)果完全不一樣。MAX17048的做法是持續(xù)采樣電壓并結(jié)合電壓變化率和歷史數(shù)據(jù)來動(dòng)態(tài)修正估算結(jié)果。啟動(dòng)后它其實(shí)會(huì)花一點(diǎn)時(shí)間“學(xué)習(xí)”當(dāng)前電池的狀態(tài)這也是為什么有些用戶剛上電時(shí)發(fā)現(xiàn)SOC并不是很準(zhǔn)用一段時(shí)間之后就穩(wěn)了。對(duì)開發(fā)者來說這些過程都是芯片內(nèi)部自動(dòng)完成的我們只需要通過I2C讀寄存器就行。最重要的兩個(gè)寄存器是VCELL和SOC一個(gè)給電壓一個(gè)給百分比。3.2 常用寄存器速查表MAX17048的寄存器基本都是16位也就是每次讀兩個(gè)字節(jié)高位在前。我整理了一份常用寄存器表方便大家對(duì)照使用。寄存器地址名稱功能說明數(shù)據(jù)換算0x00STATUS狀態(tài)寄存器包含低電量標(biāo)志、SOC變化標(biāo)志按位解析0x02VCELL電池電壓讀數(shù)原始值乘以1.25單位mV0x04SOC剩余電量原始值除以256單位%0x06VERSION芯片版本號(hào)只讀0x0CCONFIG配置寄存器可設(shè)置報(bào)警閾值、休眠等按位解析VCELL的LSB最低有效位是1.25mV也就是說你讀到一個(gè)16位原始值raw實(shí)際的電壓毫伏數(shù)就是raw乘1.25。舉個(gè)例子如果寄存器值是0x0F20十進(jìn)制3872那電壓就是3872乘以1.25等于4840mV也就是4.84V。不過正常鋰電池滿電也就4.2V左右所以讀出來的值通常不會(huì)超過這個(gè)。SOC寄存器稍微特殊一點(diǎn)它的高字節(jié)是百分比整數(shù)部分低字節(jié)是百分比小數(shù)部分整體除以256就能得到一個(gè)小數(shù)形式的百分比。比如寄存器值0x6400十進(jìn)制25600除以256就是100.0%表示電量滿格。如果寄存器值0x3200十進(jìn)制12800除以256就是50.0%。3.3 I2C地址和讀寫時(shí)序的坑MAX17048的7位I2C地址是0x36。這個(gè)地址在使用時(shí)有一個(gè)特別容易混淆的點(diǎn)MicroPython的readfrom_mem函數(shù)里我們傳的是7位地址0x36但如果你用Linux系統(tǒng)的i2c-tools或者某些庫可能會(huì)遇到要傳8位地址的情況寫地址是0x6C讀地址是0x6D。其實(shí)0x6C就是0x36左移一位得到的0x6D就是左移一位再加1。很多新手掃不到設(shè)備就是因?yàn)榈刂沸问礁沐e(cuò)了。I2C讀取一個(gè)16位寄存器的完整過程是這樣的主機(jī)先發(fā)出START條件然后發(fā)送設(shè)備地址加寫位也就是0x6C從機(jī)應(yīng)答接著發(fā)送寄存器地址比如0x04從機(jī)應(yīng)答然后主機(jī)發(fā)出重復(fù)起始條件Repeated START再發(fā)送設(shè)備地址加讀位也就是0x6D從機(jī)應(yīng)答最后主機(jī)連續(xù)讀兩個(gè)字節(jié)收到第二個(gè)字節(jié)后主機(jī)回復(fù)NACK并發(fā)送STOP條件。如果你用邏輯分析儀抓波形看到的就是這么一串。MicroPython的readfrom_mem函數(shù)把這套流程封裝好了但明白原理對(duì)排查問題很有幫助。I2C總線為什么需要上拉電阻、為什么是開漏輸出這里也順帶說一句。開漏輸出意味著設(shè)備只能拉低總線不能主動(dòng)輸出高電平高電平完全靠上拉電阻提供。這樣設(shè)計(jì)的最大好處是多個(gè)設(shè)備可以安全地共用總線不會(huì)因?yàn)橐粋€(gè)設(shè)備輸出高、另一個(gè)輸出低而導(dǎo)致短路。通信雙方通過控制SDA和SCL的拉低時(shí)序來傳輸數(shù)據(jù)邏輯上有點(diǎn)像幾個(gè)水龍頭共用一根排水管誰放水誰拉低不放水的時(shí)候管道靠上拉電阻維持“空管”狀態(tài)。4. 實(shí)操環(huán)節(jié)RP2350上跑通MicroPython完整代碼4.1 第一步給RP2350燒錄MicroPython固件如果你手頭的Pico 2還是全新的第一次使用需要燒錄MicroPython固件。步驟很簡單去MicroPython官網(wǎng)的Download頁面找到針對(duì)RP2350或者Raspberry Pi Pico 2的.uf2文件下載下來。然后按住Pico 2板子上的BOOTSEL按鍵不放用USB線連接到電腦這時(shí)候電腦會(huì)出現(xiàn)一個(gè)名為RP2350的U盤盤符。把下載好的.uf2文件直接拖進(jìn)這個(gè)U盤板子會(huì)自動(dòng)重啟MicroPython固件就燒錄完成了。開發(fā)環(huán)境我推薦用Thonny界面簡潔還能直接在編輯器里設(shè)置解釋器為MicroPython (Raspberry Pi Pico 2)。如果你喜歡命令行也可以用串口終端連接板子上的虛擬串口直接進(jìn)REPL交互環(huán)境。這一步其實(shí)非??熘灰碳螺d好了整個(gè)燒錄過程也就十幾秒。4.2 完整代碼可直接復(fù)制運(yùn)行下面是完整的MicroPython代碼實(shí)現(xiàn)了I2C掃描、讀取電壓、讀取SOC、循環(huán)打印和低電壓提醒。代碼里加了詳細(xì)注釋即使你之前沒用過I2C照著敲也能跑起來。from machine import Pin, I2C import utime # MAX17048的7位I2C地址 MAX17048_ADDR 0x36 # 常用寄存器地址 REG_VCELL 0x02 # 電池電壓寄存器的16位讀數(shù)LSB 1.25mV REG_SOC 0x04 # 剩余電量寄存器除以256得到百分比 # 初始化I2C0SDAGP0SCLGP1400kHz i2c I2C(0, sclPin(1), sdaPin(0), freq400_000) def check_device(): 掃描I2C總線確認(rèn)MAX17048是否在線 devices i2c.scan() if MAX17048_ADDR in devices: return True return False def read_vcell(): 讀取電池電壓返回單位V data i2c.readfrom_mem(MAX17048_ADDR, REG_VCELL, 2) raw int.from_bytes(data, big) # 原始值 * 1.25mV再轉(zhuǎn)成V return raw * 1.25 / 1000.0 def read_soc(): 讀取剩余電量百分比返回單位% data i2c.readfrom_mem(MAX17048_ADDR, REG_SOC, 2) raw int.from_bytes(data, big) # 高字節(jié)為整數(shù)百分比低字節(jié)為小數(shù)部分除以256得到百分比 return raw / 256.0 if not check_device(): print(未發(fā)現(xiàn)MAX17048請(qǐng)檢查接線和供電) raise SystemExit print(MAX17048 連接正常) print(Vcell( V ) SOC( % )) while True: v read_vcell() soc read_soc() print({:.3f} {:.1f}.format(v, soc)) # 簡單低電壓提醒低于3.0V時(shí)打印警告 if v 3.0: print(警告電池電壓過低) utime.sleep(1)代碼邏輯不復(fù)雜check_device先通過i2c.scan()確認(rèn)0x36地址在總線上read_vcell和read_soc分別讀取兩個(gè)關(guān)鍵寄存器并做了單位換算主循環(huán)每秒打印一次電壓和電量同時(shí)判斷電壓是不是跌破了3.0V。這里要注意的是readfrom_mem的第三個(gè)參數(shù)是讀取字節(jié)數(shù)對(duì)于VCELL和SOC都要讀2字節(jié)因?yàn)樗鼈兪?6位寄存器。如果只讀1字節(jié)你拿到的就只有高字節(jié)SOC的小數(shù)部分和VCELL的完整分辨率都會(huì)丟。int.from_bytes(data, big)是以大端方式把字節(jié)數(shù)組拼成整數(shù)I2C設(shè)備寄存器基本都遵循大端。4.3 運(yùn)行效果和常見輸出解讀接好線、燒好固件之后運(yùn)行代碼我這里的實(shí)際輸出大概是這樣的。MAX17048 連接正常 Vcell( V ) SOC( % ) 4.112 98.4 4.105 98.0 4.101 97.7你可以拿萬用表測一下電池正負(fù)極的實(shí)際電壓和代碼里打印的Vcell對(duì)比正常情況下誤差應(yīng)該在幾十毫伏以內(nèi)。如果誤差很大優(yōu)先懷疑模塊焊接或供電有問題。SOC會(huì)在充放電過程中緩慢變化不會(huì)像電壓那樣瞬間跳動(dòng)這恰恰說明電量計(jì)做了一定平滑處理比直接讀ADC要穩(wěn)定得多。跑通這一步之后你就可以把代碼集成到自己的項(xiàng)目里。比如加一個(gè)OLED屏幕顯示電量百分比或者在電量低于20%時(shí)讓蜂鳴器報(bào)警甚至把數(shù)據(jù)通過USB串口發(fā)到上位機(jī)畫實(shí)時(shí)曲線。代碼本身不需要大改核心就是讀寄存器的三個(gè)函數(shù)業(yè)務(wù)邏輯想怎么加都行。5. 常見問題與排查實(shí)錄幫你少走彎路5.1 I2C掃描不到0x36地址這是出現(xiàn)頻率最高的問題沒有之一。我總結(jié)了幾個(gè)排查方向按順序試基本都能解決。先看供電MAX17048必須要有VCC供電才能工作也就是鋰電池要接在模塊上很多模塊沒有電池就表現(xiàn)為掃描不到。然后用萬用表量一下模塊上的VCC和GND之間電壓確認(rèn)確實(shí)在2.5V到4.5V這個(gè)區(qū)間。再看接線SDA和SCL是不是接反了GP0和GP1有沒有弄混。邏輯線交叉接反是最常見的新手錯(cuò)誤因?yàn)楹芏嗄K排針順序是VCC、GND、SDA、SCL但有些模塊可能是VCC、SDA、SCL、GND拿到板子先對(duì)著絲印看清楚。接著看上拉電阻如果模塊沒帶電阻而你又沒外接那總線根本沒有高電平設(shè)備再正常也掃描不到。用萬用表量SDA或SCL對(duì)GND電壓正常應(yīng)該接近3.3V如果一直是0說明上拉電阻缺失或者SCL/SDA被拉死。最后別忘了排除地址格式問題。i2c.scan()返回的是7位地址所以你應(yīng)該在里面找到0x36。如果你手里模塊的資料上寫了0x6C那是8位寫地址換算成7位要右移一位也就是0x36。我見過好幾個(gè)朋友在代碼里寫0x6C然后怎么掃都找不到其實(shí)不是設(shè)備問題是地址格式理解錯(cuò)了。5.2 SOC讀數(shù)明顯不準(zhǔn)或者長時(shí)間卡在某個(gè)值如果你剛焊好板子第一次上電讀取SOC發(fā)現(xiàn)它和實(shí)際電量對(duì)不上先別急著懷疑芯片壞了。ModelGauge算法有一個(gè)自學(xué)習(xí)過程它需要根據(jù)電池的充放電歷史慢慢修正。我的建議是讓電池完整充放電一兩個(gè)循環(huán)SOC會(huì)逐漸趨向真實(shí)值。還有一種情況是電池帶保護(hù)板而且保護(hù)板因?yàn)檫^放已經(jīng)進(jìn)入保護(hù)狀態(tài)。這個(gè)時(shí)候你會(huì)量到電池有電壓但無法正常放電SOC可能顯示得奇奇怪怪。解決辦法是換一塊確認(rèn)正常的電池或者對(duì)保護(hù)板做個(gè)重新激活。溫度對(duì)SOC也有影響冬天在室外做測試電池電壓會(huì)偏低SOC可能會(huì)比預(yù)期低一些。MAX17048內(nèi)部雖然有一些溫度修正邏輯但不同電池的特性差異還是會(huì)在極端溫度下體現(xiàn)出來。如果你是做低溫產(chǎn)品最好在實(shí)際使用溫度下做一輪充放電校準(zhǔn)然后把CONFIG寄存器里的RCOMP補(bǔ)償值調(diào)整一下不過這個(gè)屬于進(jìn)階調(diào)優(yōu)正常室內(nèi)項(xiàng)目可以不折騰。5.3 通信偶爾失敗、讀數(shù)卡死的處理方法如果代碼運(yùn)行一段時(shí)間后突然讀不到數(shù)據(jù)或者程序卡在I2C讀取上多半是總線進(jìn)入了異常狀態(tài)。比較常見的原因是設(shè)備地址占線或者時(shí)序被干擾。最簡單的處理辦法是給I2C初始化加個(gè)異常捕獲每次讀取失敗就重新初始化一次總線這樣比讓程序死等要穩(wěn)定得多。還有一個(gè)值得養(yǎng)成的習(xí)慣是降低I2C頻率。400kHz是高速模式大多數(shù)情況下沒問題但如果你的杜邦線很長或者面包板寄生電容太大400kHz的邊沿會(huì)變差進(jìn)而導(dǎo)致通信誤碼。這時(shí)候把freq改成100_000問題往往迎刃而解。I2C的通信速率對(duì)MAX17048這種電量計(jì)來說完全夠用別為了追求高速而犧牲穩(wěn)定性。對(duì)了調(diào)試I2C還有一個(gè)利器就是邏輯分析儀。網(wǎng)上那種幾十塊錢的8通道邏輯分析儀配Sigrok或者PulseView軟件把SDA和SCL兩根線夾上去就能直接解碼出I2C的地址、寄存器號(hào)和數(shù)據(jù)。我在排查問題的時(shí)候最喜歡用它來確認(rèn)主控到底有沒有發(fā)對(duì)地址、設(shè)備有沒有回ACK。有時(shí)候代碼看起來正確但波形一抓就發(fā)現(xiàn)起始條件拼錯(cuò)了這一類問題用邏輯分析儀十秒鐘就能定位比瞎改代碼效率高得多。6. 項(xiàng)目擴(kuò)展從讀電量到完整低電量管理6.1 低電量報(bào)警的實(shí)現(xiàn)思路讀完電壓和SOC很多人下一步就想著做低電量保護(hù)。MAX17048的STATUS寄存器里有低電量標(biāo)志位同時(shí)CONFIG寄存器可以配置報(bào)警閾值。比較簡單的做法是在主循環(huán)里直接判斷SOC小于20%就點(diǎn)亮一個(gè)LED小于10%就關(guān)掉一些外設(shè)。更嚴(yán)謹(jǐn)?shù)淖龇ㄊ亲x取STATUS寄存器讓芯片在電量跨過閾值時(shí)主動(dòng)拉低ALRT引腳觸發(fā)RP2350的外部中斷。不過要注意MAX17048判斷的報(bào)警閾值默認(rèn)是32%還是多少來著不同配置不太一樣具體要去看寄存器說明。我實(shí)際做的時(shí)候并沒有依賴芯片的中斷而是直接在主循環(huán)里輪詢SOC然后根據(jù)電壓值做雙重判斷。因?yàn)閷?duì)于手持設(shè)備來說1秒鐘的輪詢周期已經(jīng)很快了沒必要為了省電去摳中斷。我建議你在做低電量邏輯時(shí)不要只看電壓值也不要只看SOC而是用SOC為主、電壓為輔。SOC相對(duì)平滑適合做趨勢判斷電壓響應(yīng)快適合做緊急保護(hù)。比如SOC低于15%開始提醒用戶電壓低于3.1V直接強(qiáng)制關(guān)機(jī)這種組合邏輯會(huì)可靠很多。6.2 加上OLED顯示電量給Pico 2外接一個(gè)SSD1306的0.96寸OLED顯示效果會(huì)直觀很多。MicroPython的ssd1306驅(qū)動(dòng)是很成熟的開源庫I2C地址一般是0x3C。你的RP2350已經(jīng)初始化了I2C0你可以把OLED也掛到同一條I2C總線上因?yàn)镮2C本身支持多設(shè)備MAX17048的0x36和OLED的0x3C不會(huì)沖突。接線就更簡單了OLED的SDA和SCL分別接GP0和GP1VCC和GND接3.3V和地。然后代碼里初始化兩個(gè)設(shè)備循環(huán)讀取MAX17048的數(shù)據(jù)再通過OLED的text方法把電壓和電量畫出來。如果你在畫電池圖標(biāo)的時(shí)候注意一下坐標(biāo)把百分比數(shù)字放在圖標(biāo)中間看起來就跟真實(shí)手機(jī)狀態(tài)欄差不多了。這里有個(gè)小提示OLED屏幕刷新不要太猛每秒刷新一次就好刷新太快不僅肉眼分辨不出來還會(huì)浪費(fèi)電。既然項(xiàng)目本身是做電源管理那么功耗意識(shí)從第一天開始就要有。6.3 多電池、多路電量計(jì)怎么做如果項(xiàng)目里有兩塊電池比如一個(gè)主電池加一個(gè)備用電池你可以讓RP2350分時(shí)讀取兩個(gè)MAX17048。但要注意I2C地址沖突的問題因?yàn)镸AX17048的地址固定是0x36你沒法直接用軟件區(qū)分兩塊電池。解決辦法有兩個(gè)。第一個(gè)辦法是用RP2350的兩個(gè)獨(dú)立I2C控制器I2C0掛第一塊電池的電量計(jì)I2C1掛第二塊用兩套SDA和SCL引腳分開讀取互不干擾代碼也只多幾行初始化。第二個(gè)辦法是給每塊電池的電量計(jì)增加I2C開關(guān)比如用TCA9548A多路復(fù)用器選通不同通道再去讀取適合需要接很多塊電池的場景。對(duì)于大多數(shù)人來說第一塊電池的電量計(jì)能不能穩(wěn)定跑通才是重點(diǎn)。等你把單個(gè)MAX17048徹底吃透再考慮擴(kuò)展多電池方案也不遲。這個(gè)芯片的I2C時(shí)序不復(fù)雜寄存器結(jié)構(gòu)也清晰是個(gè)非常適合練手的外設(shè)。最后再分享一個(gè)我的實(shí)際感受MAX17048這種“接口簡單但內(nèi)部有算法”的芯片特別適合做嵌入式項(xiàng)目的電源管理模塊。比起自己在ADC前端做濾波、建表、擬合曲線直接在I2C上讀兩個(gè)寄存器就能拿到穩(wěn)定的SOC開發(fā)效率和可維護(hù)性都高非常多。你把通用的I2C讀寫封裝成小函數(shù)以后換任何一塊I2C設(shè)備都能復(fù)用。這篇內(nèi)容里提到的地址格式、上拉電阻、共地這些問題其實(shí)也不只適用于MAX17048你在調(diào)其他I2C傳感器時(shí)同樣會(huì)碰到。先把這條路走通后面做低功耗設(shè)備、做電源管理都會(huì)順手很多。