便携设备做电源这件事前期省下来的每一分功耗最后都会以返工工时的形式还回来。我最近刚收尾一个手持式数据采集终端的电源系统主控用的是 MK60DN512下文简称 K60电源管理核心用的是 PCA9422 这颗带充电功能的 PMIC整块板子从画原理图到调通低功耗模式前后折腾了将近两个月中间踩的坑足够写一本小册子。PCA9422 MK60DN512 这个组合并不算主流搭配网上能查到的现成资料不多大部分时间是我对着数据手册一行一行抠寄存器再用示波器和电子负载一点点试出来的。这篇记录想解决的问题很具体如果你手上正好要用一颗集成充电器、多路 DCDC/LDO 的 PMIC 去喂一颗 Cortex-M4 级别的 MCU并且对体积、待机电流、充电安全有要求那这篇文章里的参数计算、寄存器配置、布局要点和排查思路应该能让你少走一大半弯路。全文会从方案选型的理由讲起把充电电流、Buck 电感、反馈电阻这些需要算的东西算给你看再往下是 I2C 驱动、上电时序、低功耗配置这些必须落地的代码最后是调试阶段撞上的真实问题和我整理出来的速查表。新手可以照着抄参数有经验的同行可以重点看第 2、5 章的取舍逻辑。1. 需求拆解与整体架构设计1.1 便携设备对电源系统的四个硬约束做便携设备的电源设计跟做市电供电的板子完全是两种思维方式。市电供电的板子你只需要关心输出电压精度、纹波和散热功耗大一点顶多多交点电费便携设备不一样电池就那么点容量每一个微安的漏电流都会直接换算成用户手里的续航焦虑。所以我在动手之前先把约束条件列清楚一共四条后面所有的选型和参数都围着这四条转。第一条是体积约束。整机外壳内部留给电源部分的面积大概只有 22mm × 18mm高度限制在 5mm 以内。这个尺寸下你不可能摆一堆分立器件去做充电管理加多路降压光是充电 IC、两路 Buck、两路 LDO 加上各自的电感电容面积就已经爆了。这是最终选 PMIC 而不是分立方案的最直接原因高集成度把外围元件数量压到了十几颗。第二条是充电安全约束。设备用的是单节锂聚合物电池标称 3.7V满电 4.2V容量 500mAh。锂电这东西过充、过流、过温任何一个条件失控都可能出问题所以充电管理必须支持可编程的充电电流、可编程的终止电流、带 NTC 温度监测最好还有充电超时保护。这一条直接排除掉了一大批只能固定电流充电的简单方案。第三条是待机功耗约束。设备的使用场景是用一次放几天也就是大部分时间处于关机或者深度休眠状态用户按一下按键才唤醒工作几十秒。这种场景下待机电流的重要性远超运行效率我给自己定的目标是整机待机小于 60µA其中电源部分自己的静态电流要控制在 25µA 以内。这个指标看起来不苛刻实际做下来会发现在电池反灌、上下拉电阻、LDO 静态电流这些地方到处漏电。第四条是供电域约束。K60 这颗 MCU 本身需要 3.3V 的主供电内核由内部稳压器产生 1.8V 左右的电压另外还有 VDDA 模拟供电、VBAT 备份域供电。外设这边有 3.3V 的传感器、1.8V 的屏逻辑供电、以及 3.0V 的基准。四个不同的电压域如果每个都用一颗独立 LDO光是静态电流加起来就超标了必须用一颗能同时输出多路电压的器件统一管理。1.2 选型对比为什么是 PCA9422 K60把这四条约束往桌面上摆选型的路径其实就收窄了。我最初考虑过三条路线分立方案充电 IC 独立 Buck 独立 LDO、通用 PMIC不带充电器、以及带充电器的集成 PMIC。前两条路线我都实际画过一版原理图最后都放弃了原因值得说清楚因为很多人在选型阶段容易低估集成度带来的收益。分立方案的好处是每一颗芯片都能选到最优解充电可以选一颗专门的大电流线性充电器Buck 可以选 2MHz 高频小封装LDO 挑静态电流 1µA 的超低功耗款。听起来很美但把料单拉出来一算光是充电 IC、两路 Buck、两路 LDO 就是五颗芯片加上各自的电感和电容BOM 行数超过 30 行布板面积轻松超过 300mm²。更麻烦的是上电时序——五颗芯片各自有各自的使能引脚你得额外加一片时序控制逻辑或者用 MCU 的 GPIO 去拉而 MCU 自己还没上电的时候谁去拉这是个鸡生蛋的问题。通用 PMIC 解决了多路输出和时序问题但通常不带电池充电器你还是得单独加一颗充电 IC等于把问题拆成了两半。PCA9422 这类集成 PMIC 的价值就在这儿它把线性充电器、两路 Buck、两路 LDO、I2C 控制接口、以及上电时序控制全部塞进一颗 QFN 封装里。从系统层面看你只需要给它一路 VBUSUSB 5V和电池它就能自己管理充电路径、自动切换系统供电来源VBUS 在的时候用 VBUS拔掉用电池、按预设顺序输出各路电压。它内部还有一次性可编程的默认配置可以在没有 MCU 干预的情况下就输出一组安全电压等 MCU 起来之后再通过 I2C 改成实际需要的值。这个特性对调试阶段太友好了我第一版板子 MCU 还没跑起来的时候PMIC 已经能稳定输出 3.3V 让 MCU 有电可用了。K60 这边我选它的原因是外设够用、封装是 LQFP 便于手工焊接和调试、以及它的低功耗模式LLS、VLLS在 M4 阵营里做得比较扎实配合 PMIC 的电源域切换能做出比较极致的待机表现。代价是 K60 的供电结构比一般 MCU 复杂VDD、VDDA、VBAT、VOUT33 这几路要分别处理后文会专门讲。1.3 供电域划分与系统架构具体到这块板子我最终的供电域划分是这样的VBUS 从 USB Type-C 进来经过一颗限流开关和 TVS 保护后进入 PCA9422 的 VBUS 引脚。PCA9422 内部把 VBUS 分两路一路给充电器一路给系统供电同时监测电池电压当 VBUS 掉电时无缝切到电池。这一路叫 SYS 节点是整个系统的能量池所有下游转换都从这里取电。从 SYS 出来BUCK1 配成 3.3V/600mA给 K60 的 VDD、VDDA经过 LC 滤波、以及 3.3V 的传感器供电。BUCK2 配成 1.8V/400mA给屏的逻辑部分和一颗射频模块。LDO1 配成 3.0V/150mA专门给 ADC 基准和模拟前端因为 LDO 的噪声比 Buck 低一个量级基准这一路不能省。LDO2 配成 1.8V/100mA给 RTC 域和备份电路这一路在系统休眠时保持开启。还有一路 VSYS 直接引出给蜂鸣器这种对电压不敏感的负载。K60 内部自己还有一个 USB 收发器用的稳压器输出在 VOUT33 引脚上这个引脚必须外接电容否则 USB 功能会不稳定。另外 VBAT 引脚接的是纽扣电池或者直接接 3.0V 的 LDO2 输出用于 RTC 走时这一路的电流在几十微安级别不需要太大驱动能力。整个架构里有个容易被忽略的点地回流路径。充电的时候电流从 VBUS 经 PMIC 到电池放电的时候从电池经 PMIC 到负载两个方向的电流都很大峰值能到 500mA如果地平面切得不好这些大电流会在地平面上产生毫伏级的压差串到 ADC 的模拟地上直接反映成采样值的跳动。我在第一版板子上就吃了这个亏后来把功率地和模拟地做了单点连接才解决第 2 章会详细讲布局。2. PCA9422 外围电路与参数计算2.1 充电电流、终止电流与计时器参数怎么算充电参数这块很多人是拍脑袋定的反正能充进去就行。但锂电池的充电电流选得不对轻则充得慢重则循环寿命腰斩。我把当时的计算过程完整写出来你可以照着换成自己的电池参数。先说充电电流。锂电池的标准充电倍率一般是 0.5C 到 1CC 是电池容量。我用的电池是 500mAh0.5C 就是 250mA1C 是 500mA。便携设备追求的是充电时不要明显发热所以我选了 0.5C也就是 250mA。这里有个细节如果你的设备支持边充边用充电电流还要加上系统负载电流否则总电流会超过 USB 端口的供电能力。我这个设备边充边用时系统大概吃 120mA250 120 370mAUSB 2.0 端口标准供电是 500mA留了 130mA 余量可以接受。如果系统负载更大就得考虑把输入限流调高或者降低充电电流。然后是终止电流。充电进入恒压阶段后电流会自然衰减当衰减到某个值以下就应该判定充满并停止充电这个值一般取 C/10也就是 50mA。我这里取了 25mAC/20稍微激进一点好处是电池能充得更满坏处是恒压阶段拖得更久。实测下来从 90% 到 100% 这一段多花了大概 20 分钟能接受。充电时间的估算可以给你一个参考从 3.4V大概 10% 电量充到 4.2V恒流阶段按 250mA 走需要充进去的电量约 400mAh时间是 400/250 1.6 小时。恒压阶段大概再要 0.4 到 0.5 小时总计约 2 小时充满。实测结果在 2 小时 10 分左右跟估算比较接近。安全计时器这块我设的是 3 小时。这个值要比理论充电时间留出足够余量因为低温环境下充电会变慢如果计时器卡得太紧冬天在室外充电会被提前掐断。但也不能设太长否则电池异常时保护不及时。3 小时是个比较平衡的值。温度保护用的是外置 NTC10kΩ B3435 的 0603 电阻贴在电池附近。PMIC 内部有对应的偏置电流源和比较器通过 I2C 可以读到当前的温度区间也可以设置充电的允许温度窗口。我把窗口设成 0℃ 到 45℃超出范围就暂停充电但保持系统供电这样用户在极冷极热环境下至少还能用设备只是充不进电。参数取值计算依据电池容量500mAh硬件规格充电电流250mA0.5C兼顾发热与时间终止电流25mAC/20偏向充满安全计时3h理论 2h × 1.5 倍余量充电温度窗口0~45℃常规锂电安全区间输入限流450mAUSB 500mA 留余量满充电压4.20V电池规格注意满充电压这一项千万别图省事直接按 4.2V 写死。有些高压电芯标称 4.35V有些磷酸铁锂是 3.65V写错了轻则充不满重则过充。拿到电池第一件事是看规格书上的充电截止电压不是看标称电压。2.2 Buck 电感与输出电容的取值逻辑PCA9422 内部两路 Buck 的开关频率是固定的我手上这版是 2MHz。这个频率选得比较聪明一方面电感可以做得小另一方面开关噪声落在容易滤除的频段。但 2MHz 也意味着开关损耗会比 500kHz 的方案高一些轻载效率要做取舍。电感取值我的计算过程是这样的BUCK1 输出 3.3V最大负载 600mA但我按实际工作点 400mA 来算纹波电流取负载电流的 30%也就是 120mA。用降压电感的经典公式 L (Vin - Vout) × Vout / (Vin × fsw × ΔIL)代入 Vin 取电池典型电压 3.8VVout 3.3Vfsw 2MHzΔIL 0.12AL (3.8 - 3.3) × 3.3 / (3.8 × 2×10^6 × 0.12) 1.65 / 912000 ≈ 1.8µH算出来是 1.8µH我实际选的是 2.2µH 的标准值。为什么不选更接近的因为电感的直流电阻和饱和电流跟感值是一组互相牵制的参数2.2µH 这个规格在 3mm×3mm 尺寸下有更多低 DCR 的选项我选的这颗 DCR 是 45mΩ饱和电流 1.5A够用。感值偏大一点纹波电流会小一些输出纹波也更好代价是瞬态响应稍慢但这个系统没有大动态负载无所谓。BUCK2 输出 1.8V负载 400mA纹波取 0.1AVin 同样是 3.8VL (3.8 - 1.8) × 1.8 / (3.8 × 2×10^6 × 0.1) 3.6 / 760000 ≈ 4.7µH正好是标准值直接选 4.7µH。输出电容的计算用的是纹波电压公式 ΔVout ΔIL / (8 × fsw × Cout)反过来解 Cout ΔIL / (8 × fsw × ΔVout)。以 BUCK1 为例要求输出纹波小于 10mVCout 0.12 / (8 × 2×10^6 × 0.01) 0.12 / 160000 0.75µF理论值只要 0.75µF但实际不能这么选。这个公式只考虑了容性纹波没算 ESR 带来的纹波和负载瞬变时的电压跌落。我实际用了两颗 22µF 的 X5R 陶瓷电容并联ESR 控制在 3mΩ 以内实测纹波 6mV 左右瞬变跌落也在 30mV 以内。电容的直流偏压特性要注意22µF 的 0805 X5R 在 3.3V 偏压下实际容值会掉到 14µF 左右所以标称值要留足余量。反馈电阻这块BUCK1 如果用的是外部可调版本反馈基准电压是 0.6V那么分压比就是 (3.3/0.6) - 1 4.5。取下电阻 10kΩ上电阻就是 45kΩ标准值选 45.3kΩ实际输出 0.6 × (1 45.3/10) 3.318V误差 0.5%完全够用。电阻精度选 1% 的薄膜电阻温漂 50ppm/℃ 以内。这里顺带提一句分压电阻的阻值不是随便选的太小了会白白消耗静态电流太大了容易被噪声干扰10kΩ 这个量级是个比较舒服的折中。电源轨输出电感输出电容反馈分压BUCK13.3V/600mA2.2µH2×22µF45.3k/10kBUCK21.8V/400mA4.7µH2×22µF20k/10kLDO13.0V/150mA无10µF内部固定LDO21.8V/100mA无4.7µF内部固定2.3 PCB 布局三个必须守住的底线电源板画得好不好七分看布局三分看原理图。我第一版板子原理图完全正确但布局出了问题导致纹波大、待机电流超标、充电时 ADC 读数乱跳。第二版重画之后问题全消。总结下来有三条底线不能碰。第一条功率回路面积必须小。Buck 的输入电容、芯片的 SW 引脚、电感这三个点构成的回路是 di/dt 最大的地方面积越大辐射越强。我的做法是把输入电容紧贴芯片的 VIN 和 PGND 引脚放电容的接地端直接打过孔到地平面SW 走线短而粗电感紧接着 SW 引脚放。这个回路我控制在 20mm² 以内实测 SW 节点的振铃幅度比第一版小了一半。第二条地平面不能乱切。大电流回流和小信号地必须分开走但最后单点汇合。具体来说充电器的功率地、Buck 的功率地、电池的负极这些走一条粗的地线回到 PMIC 的 PGND而模拟部分ADC 基准、NTC 分压的地单独走最后在 PMIC 的 AGND 引脚附近用一个 0Ω 电阻或者一颗过孔连到主地。我在第一版里图省事整板铺了一个地结果充电电流 250mA 在地平面上产生的压降直接串到 ADC 参考地上采样值跳动超过 8 个 LSB。第三条电感和 NTC 要远离敏感走线。2MHz 的开关节点是个很强的干扰源如果电感离 ADC 输入走线太近会耦合进明显的开关频率纹波。我把电感放在板子边缘NTC 走线用包地处理两侧打满地过孔中间走信号线。NTC 本身是高阻信号10kΩ 量级特别容易被干扰包地这一步不能省。实操心得画完 PCB 之后别急着发板把电源部分的走线单独高亮出来看一眼。如果 SW 节点的走线比输出走线还长那基本可以判定布局有问题了重新摆一遍比调试一周划算。3. I2C 驱动与寄存器配置实操3.1 从零写一个能用的 I2C 读写封装K60 的 I2C 外设用起来不算难但寄存器配置有几个容易漏的位。我不打算在这儿贴完整的初始化代码那个抄手册就行重点讲我在实际调试中改过的几处关键配置这些是手册上不会强调但直接影响能不能通的地方。第一个是引脚的上拉。K60 的 I2C 引脚内部有可配置的上拉但阻值偏大几十 kΩ 量级在 400kHz 速率下上升沿会明显变缓。我一开始用内部上拉示波器上看 SCL 的上升时间超过了 300ns偶尔出现通信失败。后来换成外部 2.2kΩ 上拉上升时间降到 80ns 以内问题消失。上拉电阻的取值跟总线电容有关走线短的话 4.7kΩ 也行我板子上 I2C 走线不长但挂了两个从设备所以选了 2.2kΩ。第二个是时钟频率。K60 的 I2C 时钟源是总线时钟分频来的分频系数的计算容易算错。我用的总线时钟是 60MHz要得到 400kHz分频比是 150。K60 的 I2C 分频寄存器是 8 位最大 256150 在范围内。但要注意实际速率还受 SCL 高低电平占空比寄存器的影响如果这两个值配得不匹配实际速率会偏离目标值。我的做法是先按理论值配好再用示波器量实际频率微调分频值直到落在 380~420kHz 之间。第三个是超时和重试。I2C 总线有个经典问题从设备在传输过程中被复位会把 SDA 拉低不放导致总线死锁。标准解法是发 9 个 SCL 脉冲把从设备的数据移位出来再发一个 STOP 条件。我在驱动里加了这个恢复流程并且给每次传输设了 10ms 超时超时之后自动走恢复流程再重试一次。这套机制上线之后现场再没出现过需要断电重启才能恢复的情况。下面是我封装的读写函数做了地址对齐和错误重试#define PCA9422_ADDR 0x4B /* 7 位地址以手册为准 */ #define PCA9422_I2C I2C0 #define I2C_TIMEOUT_MS 10 #define I2C_RETRY_CNT 1 static int pca9422_write_reg(uint8_t reg, uint8_t val) { uint8_t buf[2] { reg, val }; int ret i2c_master_send(PCA9422_I2C, PCA9422_ADDR, buf, 2, I2C_TIMEOUT_MS); if (ret ! 0) { i2c_bus_recover(PCA9422_I2C); /* 9 时钟 STOP 恢复 */ ret i2c_master_send(PCA9422_I2C, PCA9422_ADDR, buf, 2, I2C_TIMEOUT_MS); } return ret; } static int pca9422_read_reg(uint8_t reg, uint8_t *val) { int ret i2c_master_send(PCA9422_I2C, PCA9422_ADDR, reg, 1, I2C_TIMEOUT_MS); if (ret 0) { ret i2c_master_recv(PCA9422_I2C, PCA9422_ADDR, val, 1, I2C_TIMEOUT_MS); } if (ret ! 0) { i2c_bus_recover(PCA9422_I2C); } return ret; }这段代码里有个细节值得说读寄存器的操作是先写寄存器地址再读数据中间不能插入 STOP 条件否则有些 PMIC 会认为事务结束读回来的是当前地址指针指向的值而不是目标寄存器的值。我第一版驱动里用了两个独立的事务读出来的数据全是 0xFF查了半天才发现是这个原因。正确的做法是用重复起始条件把两个阶段连起来很多 MCU 的 I2C 外设库把这个做成了i2c_master_send_recv这样的组合接口。3.2 上电时序先给谁上电、后给谁上电上电时序这件事做得好没人夸你做不好各种玄学问题。K60 对上电顺序没有特别严格的要求但有几个点必须注意。首先是 VBAT 和 VDD 的关系。K60 的 VBAT 是给 RTC 和备份寄存器供电的数据手册里明确写了 VBAT 不能比 VDD 高太多否则会有电流从 VBAT 倒灌到 VDD。我的处理是让 LDO21.8V给 VBAT 供电而 LDO2 的开启时序排在 BUCK1 之后这样 VDD 先建立VBAT 后建立不会出现倒灌。然后是 VDDA 和 VDD 的关系。VDDA 是模拟供电要求跟 VDD 的压差不超过 0.1V最好同时建立。我在 BUCK1 输出之后加了一颗磁珠和 10µF 电容做 LC 滤波再送给 VDDA。磁珠选的是 600Ω100MHz 的型号直流电阻 0.3Ω压降可以忽略。上电的时候 VDDA 比 VDD 稍微滞后一点点但由于 LC 的时间常数只有几十微秒实际测下来两者基本同步。PCA9422 这边提供了可配置的启动时序各路输出的开启顺序和间隔都能通过一次性可编程或者 I2C 设置。我配的顺序是 BUCK1 → BUCK2 → LDO2 → LDO1每路间隔 2ms。为什么是这个顺序BUCK1 给 MCU 供电必须最先建立MCU 起来之后才能通过 I2C 配置其他路LDO1 给 ADC 基准放在最后是为了让基准电压在系统其他部分稳定之后再建立避免基准受到上电冲击。注意如果你用的是 MCU 主动配置 PMIC 的架构那么 MCU 起来的时候 PMIC 还处于默认配置状态这个默认配置必须是安全的——电压不能超过下游器件的耐压输出能力不能被瞬间大电流拉垮。我在调试阶段遇到过 PMIC 默认输出 3.4V 而下游屏的逻辑耐压是 3.6V看着没问题但上电瞬间的过冲冲到 3.9V屏直接不亮。后来把默认配置改成 3.0V等 MCU 起来再拉到 3.3V问题解决。3.3 关键寄存器配置代码与逐行解释寄存器配置这块我不打算把整张表搬过来手册上有。我挑几个真正影响行为的配置项讲清楚每个值是怎么算出来的。下面这段是充电相关的初始化寄存器地址和位定义请以你手上那版数据手册为准我这里用的宏名是我自己整理的方便阅读。/* 充电电流设置目标 250mA */ /* 假设电流档位为 1mA/LSB寄存器值 250 */ #define CHG_CURRENT_250MA 250 /* 终止电流设置目标 25mA档位 1mA/LSB */ #define CHG_TERM_CURRENT_25MA 25 /* 满充电压 4.20V档位 25mV/LSB * 寄存器值 (4200 - 3000) / 25 48 */ #define CHG_VFLOAT_4200MV 48 /* 安全计时器3 小时档位 30min/LSB值 6 */ #define CHG_SAFETY_TIMER_3H 6 static int pca9422_charger_init(void) { uint8_t v; /* 1. 关闭充电改参数前必须先关 */ if (pca9422_write_reg(REG_CHG_CTRL0, 0x00) ! 0) return -1; /* 2. 充电电流 */ pca9422_write_reg(REG_CHG_ISET, CHG_CURRENT_250MA); /* 3. 终止电流与满充电压 */ v (CHG_VFLOAT_4200MV 2) | (CHG_TERM_CURRENT_25MA 3); pca9422_write_reg(REG_CHG_VFLOAT, v); /* 4. 安全计时器使能 时长 */ v 0x80 | CHG_SAFETY_TIMER_3H; pca9422_write_reg(REG_CHG_TIMER, v); /* 5. 温度保护窗口0~45℃ */ pca9422_write_reg(REG_CHG_TEMP, TEMP_WIN_0_45C); /* 6. 参数写完后使能充电并打开看门狗喂狗 */ v CHG_EN | CHG_WDT_EN; if (pca9422_write_reg(REG_CHG_CTRL0, v) ! 0) return -1; return 0; }这段代码里有三个为什么值得展开。第一个是改参数前先关充电。PMIC 的很多参数寄存器在充电进行中写入是无效的或者要等到下一个充电周期才生效手册里通常会写建议在 CHG_EN 0 时修改。我一开始没注意这条改了终止电流之后发现充电行为没变化还以为是芯片坏了后来才发现是写入被忽略。第二个是满充电压的编码方式。这类 PMIC 通常是基准值 档位 × 步进的编码比如 3.0V 基准、25mV 步进那么 4.2V 对应的就是 (4200 - 3000) / 25 48。如果你的电池是 4.35V就是 (4350 - 3000) / 25 54。这个计算一定要自己算一遍再写进代码别直接抄别人的常量因为不同批次、不同型号的基准值和步进都可能不一样。第三个是看门狗。这颗 PMIC 带一个充电看门狗如果在设定时间内 MCU 没有通过 I2C 喂狗它会自动把充电电流降到安全值。这个功能本意是防止 MCU 死机之后充电失控但在调试阶段特别烦人——我单步调试的时候经常超过超时时间充电就被限流了还以为是电池的问题。调试阶段可以先关掉看门狗量产版本一定要打开并且把喂狗周期放在一个低优先级的定时任务里周期取超时时间的 1/3。4. 固件侧电源管理与低功耗落地4.1 K60 侧的电源域与去耦处理K60 的供电引脚比一般 MCU 多第一次画原理图的人很容易漏。我把当时核对过的引脚清单列一下VDD 有 4 组每组都要单独去耦VSS 对应 4 组VDDA 一组VREFH 一组VBAT 一组VOUT33 一组。每一组 VDD 旁边都要放一颗 100nF 的 0402 电容紧贴引脚放另一侧打过孔到地平面。这四颗电容不能共用也不能省我见过有人为了省面积只放两颗结果跑高速的时候偶尔 HardFault查了很久才发现是电源噪声。VDDA 的处理要更讲究一些。除了 100nF 之外还要加一颗 1µF 的电容并且从 BUCK1 输出到 VDDA 之间串一颗磁珠。VREFH 是 ADC 的参考高电平如果外部有精密基准就接基准没有的话接 VDDA但同样要加 LC 滤波。我把 VREFH 单独用一颗 10µF 加 100nF 的组合去耦实测 ADC 的有效位数能提高 0.5 位左右。VOUT33 这个引脚容易被人忽略。它是 K60 内部 USB 稳压器的输出必须外接一颗 2.2µF 的电容否则 USB 收发器工作不正常表现为插上 USB 之后枚举失败或者时不时断开。我第一版板子漏了这颗电容USB 能识别但传大文件的时候会掉线加了电容之后稳定。实操心得K60 的 VDD 和 VDDA 之间如果有压差会导致内部 ESD 二极管导通产生额外的漏电流这个漏电流在低功耗模式下特别明显。我测到过 VDDA 比 VDD 高 0.3V 时休眠电流从 20µA 涨到 80µA。所以 LC 滤波的磁珠直流电阻要选小一点别用那种 DCR 一两欧的型号。4.2 低功耗模式选择与唤醒源配置K60 的低功耗模式有几种WAIT、STOP、VLPS、LLS、VLLS0~3。选择哪种取决于你要保留什么。我的场景是按键唤醒唤醒之后要快速恢复工作不需要保留 RAM 内容所以用 VLLS1 是最省电的。VLLS1 下整颗 MCU 的电流大概在 5µA 量级加上 PMIC 的静态电流 20µA 左右整机待机可以做到 30µA 以内。进入 VLLS1 之前要做几件事顺序不能错。先把所有不用的 GPIO 配成模拟输入或者输出低电平浮空的输入引脚会因为电平不定导致输入缓冲器来回翻转白白耗电。然后关闭所有外设时钟特别是 ADC 和定时器。再把 PMIC 上不需要保持的电源轨关掉比如 LDO1ADC 基准在休眠时没有意义直接关。最后配置唤醒源我用的是 PORT 模块的引脚中断按键接在 PTA4 上。这里有个坑我踩过唤醒源的引脚在进入低功耗之前必须配置好上下拉。如果按键是低电平有效引脚要上拉如果是高电平有效要下拉。我一开始配了内部上拉但外部还有一个 100kΩ 的下拉电阻两者打架休眠时这个引脚上有几十微安的电流流过待机电流直接翻倍。后来把外部下拉去掉只用内部上拉大概 30kΩ问题解决。另一个坑是PMIC 的 I2C 接口在 MCU 休眠期间的状态。如果 I2C 引脚在休眠时被配成复用功能但没有时钟引脚会保持最后一次的电平状态如果恰好是低电平PMIC 会认为总线忙某些功能会受影响。我的做法是在进入低功耗前把 I2C 引脚切成普通 GPIO 并输出高电平唤醒之后再切回复用功能。void enter_vlls1(void) { /* 1. 关闭不需要的电源轨 */ pca9422_write_reg(REG_LDO1_CTRL, 0x00); pca9422_write_reg(REG_BUCK2_CTRL, 0x00); /* 2. I2C 引脚切 GPIO 并拉高 */ PORT_PinMuxConfig(PORTE, 24, kPinAlt1); /* SCL - GPIO */ PORT_PinMuxConfig(PORTE, 25, kPinAlt1); /* SDA - GPIO */ GPIO_PinWrite(GPIOE, 24, 1); GPIO_PinWrite(GPIOE, 25, 1); /* 3. 未使用引脚配成模拟输入降低漏电 */ disable_unused_pins(); /* 4. 配置唤醒源 */ PORT_Init(PORTA); PORT_SetPinInterruptConfig(PORTA, 4, kPORT_InterruptFallingEdge); NVIC_EnableIRQ(PORTA_IRQn); /* 5. 清标志进 VLLS1 */ LLWU_Init(); LLWU_SetExternalWakeupPinMode(LLWU, 12, kLLWU_ExternalPinFallingEdge); SMC_SetPowerModeProtection(SMC, kSMC_AllowPowerModeVlls); SMC_SetPowerModeVlls(SMC); while (1) { __asm(nop); } }代码里的disable_unused_pins()这个函数值得单独说。它的作用是把所有没接外设的引脚都配成模拟输入ALT0 加禁用数字输入缓冲。这一步对低功耗的影响比很多人想象的大我有一次测待机电流 120µA找了两天最后发现是一个调试用的 UART 引脚浮空导致的。把全部未用引脚处理一遍之后电流降到 25µA。4.3 充电状态机与电量估算充电状态机这块PMIC 本身提供了状态寄存器可以读到充电中 / 充电完成 / 充电暂停 / 故障这几种状态还有故障类型过温、超时、电池异常。我的固件里用一个 500ms 的定时任务去轮询这个状态寄存器做三件事更新 UI 上的充电图标、处理异常状态、以及做电量估算。电量估算是个老生常谈的问题。单节锂电的电压和电量关系不是线性的3.7V 到 4.2V 这一段大概对应 80% 到 100%而 3.5V 到 3.7V 这一段对应 20% 到 80%。如果直接用电压除以 4.2 算百分比会出现前 80% 掉得飞快、后 20% 用很久的现象。我的做法是打一张 11 点的查找表用分段线性插值。电压(V)4.204.104.003.903.803.703.603.503.403.303.00电量(%)10092806650342012740这张表是我用电子负载做恒流放电实测出来的不同批次的电芯会有几个点的偏差但趋势一致。用这张表加上充电状态做修正电量显示的跳变问题基本解决了。还有一个细节是负载压降补偿。电池有内阻大电流放电的时候端电压会比开路电压低这时候如果直接读电压算电量会显示用着用着突然掉 10%。我的处理是在大电流负载比如射频发射的时候给测到的电压加上一个补偿量补偿量等于负载电流乘以电池内阻我用 150mΩ 估算。这个补偿不用很精确能把跳变压到 2% 以内就够了。5. 调试实录那些把人按在工位上的问题5.1 上电反复复位第一版板子焊完之后上电就是这个现象电源灯亮一下灭一下示波器上看 3.3V 轨每隔 20ms 左右跌一下MCU 根本没机会跑起来。第一反应是电源带不动负载但把 MCU 摘掉之后 3.3V 很稳说明问题在 MCU 侧。排查的思路是从复位源入手的。K60 有个复位状态寄存器记录上一次复位的原因。但问题是 MCU 跑不起来就读不到寄存器所以只能从硬件上找。我先量了 RESET 引脚发现它是被拉低的说明是外部复位信号。顺着 RESET 往回找发现板上有一颗外部复位芯片它的阈值设的是 3.08V而我的 3.3V 轨在上电过程中会在 3.0V 附近停留十几毫秒这期间复位芯片一直保持复位状态等电压爬到 3.08V 才释放。但问题是释放之后 MCU 启动电流突然增大3.3V 又跌回阈值以下再次复位形成循环。根因是 BUCK1 的软启动时间设得太短上电瞬间的浪涌电流把输出拉垮了。PMIC 的软启动时间是可以配的我把 BUCK1 的软启动从 0.5ms 改到 4ms上电斜率变缓浪涌电流从 800mA 降到 250mA问题解决。另外我把复位芯片的阈值改到 2.93V留出更多余量。这个问题的教训是上电时序不只是谁先谁后还包括多快。软启动时间这个参数在选型的时候很容易被忽略但它直接决定了上电瞬间的峰值电流。5.2 I2C 扫不到设备板子能跑起来之后第二关是 I2C 通信。我写了个简单的扫描程序遍历 0x00 到 0x7F结果一个设备都没扫到。这个问题的排查路径比较典型我按顺序说一下。第一步确认物理连接。用万用表量 PMIC 的 SCL、SDA 引脚对地电阻都是几十 kΩ上拉电阻的值说明没有短路。量 PMIC 的供电引脚3.3V 正常。第二步确认引脚复用。K60 的 I2C0 默认引脚是 PTE24/PTE25但我第一版原理图上把 I2C 画到了 PTE18/PTE19这两个引脚对应的是 I2C1。代码里配的是 I2C0引脚配的是 PTE24 的复用功能但实际板子的走线在 PTE18 上所以信号根本没出去。这个问题是我看原理图的时候想当然了把 I2C0 和 I2C1 的引脚搞混了。改代码用 I2C1 之后扫描立刻扫到了 0x4B。第三步确认上拉。虽然扫到了设备但偶尔会有 NAK。把内部上拉换成外部 2.2kΩ 之后彻底稳定。注意I2C 扫不到设备九成以上的原因在引脚复用和上拉这两件事上芯片坏了的概率极低。建议调试顺序是先量电平高电平是不是接近 VDD→ 再量波形有没有时钟脉冲→ 最后才是查地址。如果示波器上看不到时钟脉冲那一定是引脚复用或者外设没使能跟从设备没关系。5.3 充电发热与截止异常充电调试阶段遇到两个问题都跟温度有关。第一个是充电时 PMIC 明显发烫手摸上去烫得不敢停留。用热像仪看热点在芯片的充电功率管附近温度 78℃。算一下功耗充电电流 250mAVBUS 5V电池 3.7V线性充电器的功耗是 (5 - 3.7) × 0.25 0.325WQFN 封装的 θJA 大概 60℃/W温升 19.5℃环境温度 30℃ 的话结温应该是 50℃ 左右跟实测的 78℃ 对不上。后来发现是 PCB 的散热没做好。QFN 封装底部的散热焊盘我只打了 4 个过孔而且过孔没有做盖油处理散热效果很差。重新画板的时候把散热焊盘下的过孔加到 9 个孔径 0.3mm并且在背面铺了一块 15mm × 15mm 的铜皮温度降到 62℃。另外一个优化是把充电电流从 250mA 降到 200mA功耗降到 0.26W温度进一步降到 55℃。最后我权衡了一下充电时间从 2 小时变成 2.5 小时但温度控制住了选了 200mA。第二个问题是充电截止异常。有几次电池明明冲到 4.2V 了充电状态还是显示充电中一直不停。查了状态寄存器发现是安全计时器没有触发。原因是看门狗喂狗任务被我放在了低优先级充电后期系统负载轻任务调度不及时看门狗超时之后充电被限流电流一直维持在很小的值始终达不到终止电流的条件就卡在那儿了。把喂狗任务提到中等优先级并且把终止电流的判定时间窗口从 1 秒放宽到 10 秒问题解决。5.4 待机电流超标这个问题最折磨人。设计目标 60µA实测 180µA超标三倍。用高精度电流表串在电池回路里测然后逐个断开负载排除。先断 MCU 的供电电流降到 42µA说明 MCU 这边漏了 138µA。再断外部传感器降到 35µA说明传感器漏了 7µA。剩下 35µA 是 PMIC 自身的比手册标称的 20µA 高了 15µA。MCU 这 138µA 是怎么来的我用了最笨的办法在代码里把外设一个一个关掉每关一个测一次电流。最后定位到两个源头一个是 ADC 模块没有完全关闭只关了时钟但没断电漏了 60µA另一个是某个调试用的 UART 引脚浮空输入缓冲器在中间电平来回翻转漏了 70µA。ADC 的问题通过写它的电源控制寄存器彻底断电解决UART 引脚配成模拟输入解决。剩下 PMIC 的 15µA查了半天发现是 LDO2 在给 VBAT 供电。K60 的 RTC 在休眠的时候还要走时所以 VBAT 不能断但 VBAT 域的电流大概是 3µA加上 LDO2 自身的静态电流 12µA正好 15µA。后来我把 LDO2 的输出从 1.8V 改成 3.0VK60 的 VBAT 允许范围是 1.71V 到 3.6VLDO 在压差更小的情况下静态电流降到 6µA。最终整机待机 43µA达标。5.5 问题速查表把上面这些问题整理成一张表方便你在遇到类似现象的时候快速定位。现象可能原因排查方法解决方式上电反复复位软启动太快、浪涌拉垮电源示波器看电源轨上电斜率加长软启动时间复位芯片误触发复位阈值太接近工作电压量复位引脚波形降低复位阈值I2C 扫不到设备引脚复用配错、无上拉示波器看 SCL 有无脉冲核对引脚复用表、换外部上拉I2C 偶发 NAK上拉太弱、总线电容大量上升沿时间上拉改 2.2kΩ充电发烫散热焊盘过孔不足热像仪定位热点增加散热过孔和背面铜皮充电不截止看门狗超时导致限流读状态寄存器提高喂狗任务优先级待机电流超标未用引脚浮空、外设未断电逐个断开负载测量未用引脚配模拟输入ADC 读数跳动功率地与模拟地混在一起对比充电时的采样值单点接地、包地处理USB 枚举失败VOUT33 缺电容量 VOUT33 电压补 2.2µF 电容6. 实测数据与经验沉淀6.1 效率、温升、待机电流实测所有调试完成之后我做了一轮完整的实测数据如下。测试条件是室温 25℃用电子负载做恒流拉载电池用直流电源模拟设 3.8V串联 150mΩ 内阻。BUCK1 在 3.8V 输入、3.3V 输出下的效率轻载 10mA 时 82%中载 200mA 时 91%满载 600mA 时 88%。轻载效率偏低是因为开关损耗占比大这个在 2MHz 开关频率下是必然的如果你的应用大部分时间在轻载可以考虑让 PMIC 进入 PFM 模式轻载效率能提到 88% 左右代价是输出纹波会变大一点。BUCK2 在 3.8V 输入、1.8V 输出下的效率100mA 时 87%400mA 时 85%。压差大的时候效率会低一些这是 Buck 的固有特性。温升方面满载运行 30 分钟后BUCK1 的电感表面温度 48℃PMIC 表面 52℃充电时 PMIC 表面 55℃。整机放在密闭外壳里温升会再高 8℃ 左右所以设计的时候要留出余量。待机电流最终实测 43µA分解下来是 PMIC 20µA、MCU VLLS1 状态 12µA、RTC 域 6µA、其他漏电 5µA。按 500mAh 电池算纯待机可以撑 500/0.043 ≈ 11600 小时也就是一年多对于这种用一次放几天的设备来说完全够用。测试项条件实测值备注BUCK1 效率3.8V→3.3V, 200mA91%PWM 模式BUCK1 纹波200mA 负载6mV20MHz 带宽限制BUCK2 效率3.8V→1.8V, 200mA86%PWM 模式充电温升200mA 充电30℃环境 25℃待机电流VLLS1 PMIC 待机43µA电池端测量满充时间3.4V→4.2V2h28min200mA 充电上电时间冷启动到 MCU 运行38ms含软启动 4ms6.2 几条不走弯路的建议如果让我重新做一遍这个项目有几件事我会毫不犹豫地照做也有几件事我会换一种做法。第一件照做的事是先做电源评估板再做整板。我第一版直接把 PMIC 画进了整板结果电源出问题的时候分不清是电源本身的问题还是负载的问题。后来我单独做了一块 30mm × 30mm 的电源评估板把 PMIC 的所有输出都引出排针用跳线帽选择负载调起来效率高太多了。这块板子花了我半天时间画、两天时间打样但省下来的调试时间至少一周。第二件照做的事是把 I2C 通信做成可观测的。我在固件里加了一个环形缓冲区记录最近 32 次 I2C 事务的寄存器地址、写入值、返回值出问题的时候通过串口 dump 出来。这个机制帮我定位了好几次偶发通信失败如果没有它那种一天出现一两次的问题根本没法查。会换做法的是电池连接器的选型。我用的是一颗 2.0mm 间距的卧贴座子插拔力偏小样机摔过一次之后电池线松了设备直接断电。后来换成带锁扣的型号成本贵了两毛钱但可靠性提升明显。便携设备上电池连接器这种不起眼但一松就完蛋的器件千万别省。还有一个经验是关于调试接口的。我在板子上留了 4 个测试点VBUS、SYS、BUCK1 输出、电池正极。这四个点用示波器一夹就能看出能量流向判断是充电问题还是放电问题。测试点的焊盘用 1.5mm 直径的圆形裸铜别盖油这样探针能直接戳上去比找元件引脚方便得多。最后分享一个我在低功耗调试上用得很顺手的技巧准备一个能测微安级电流的治具串在电池和板子之间治具上带一个拨动开关可以瞬间短接。这样你既能测到真实的微安级待机电流又能在需要的时候不断电切换工作状态避免每次测量都要重新上电初始化。这个治具成本不到二十块钱但对低功耗调试的效率提升是数量级的。
阅读完成 · 觉得有帮助?