多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

STM32与PMIC协同实现低功耗电源管理设计

STM32与PMIC协同实现低功耗电源管理设计 1. 电源管理这件事为什么值得交给一颗专用 PMIC1.1 MCU 直接供电方案的三个软肋很多从物联网、传感器项目入门的开发者一开始做板子都是这么干的从电池或者 USB 5V 拉进来一个 AMS1117 降到 3.3V 给单片机再挂几个 LDO 给外设完事。这种方案在功能简单、电流需求不大、对功耗不敏感的场景下确实能跑但一旦产品需要低功耗待机、多路电压轨、复杂的上下电时序问题就全来了。第一个软肋是电压建立时序不可控。多路 LDO 同时上电谁先起来完全取决于输入电容、负载电容和芯片启动阈值的微小差异今天这版板子上电正常明天换一批料就偶发某个外设初始化失败。你拿示波器去抓波形和波形之间根本不是一码事。第二个软肋是纹波和瞬态响应。数字核心电路在任务突发时会突然拉大电流LDO 的环路带宽不够电压直接被拉掉几百毫伏而 DCDC 如果布局布线功底不行纹波能到几十毫伏模拟前端直接没法用。分立方案里的电感、电容、反馈环路全都要自己调每一路都调一遍工程量翻倍。第三个软肋是低功耗切换不灵活。睡眠时要关掉的外设电源、待机时要降到的电压、唤醒时要快速恢复的轨全靠 MOS 管和 GPIO 组合代码写死人不说掉电顺序和重新上电顺序稍微写错板子就死给你看。所以越来越多的板子开始用「主控 MCU 专用 PMIC」的组合PMC 管功率路径MCU 管策略各干各的活。1.2 PMIC 加 MCU 的组合分工我这里说的 PCA9422就是一颗典型的 I2C 可配置多通道 PMIC内部集成了多路 DCDC 和 LDO带可编程的上电时序、动态电压调节DVS、软启动、电源良好指示PG以及欠压/过流/过温保护。相比之下STM32F405RG 是主控通过 I2C 总线告诉 PMIC「现在该输出多少伏、该开哪一路、该进什么模式」同时读取 PMIC 的状态和故障标志决定整个系统的下一步行为。你可以把 PMIC 理解成手脚把 STM32 理解成大脑。手脚负责具体的功率变换和物理保护大脑负责做判断开机时按什么顺序上电待机时关哪些路故障时怎么恢复。这样的好处是功率层面的细节被封装进了 PMIC软件层不需要去操作 MOS 管的占空比也不需要在中断里做 PID 调压代码逻辑大幅简化。1.3 这套组合适合什么样的项目从我实际经手的板子来看下面这几类项目最适合直接上这套组合第一类是电池供电的便携/可穿戴设备对整机待机电流有硬指标要求需要一个能逐路关断、能深度睡眠的电源方案第二类是多电压轨的处理器系统比如主控要 3.3V、内存要 1.8V、核心要 1.2V 甚至动态可调的 0.9V还要求严格的上电顺序第三类是对供电可靠性要求较高的设备需要 PGOOD 信号、欠压告警、过流保护这些机制而不是等到板子冒烟了才发现电源出了问题。如果你手头只是做一个传感器采集的小模块一颗 LDO 就够那确实不需要引入 PMIC别为了用而用。但如果你发现代码里已经有大量「开电、关电、延时、等稳定」的逻辑而且还在为偶发的上电失败和功耗超标头疼那我觉得是时候考虑这套方案了。2. 从系统需求倒推电源架构2.1 先列器件功耗需求再定供电轨很多人在选 PMIC 时上来就问「哪颗芯片好」我觉得顺序反了。正确做法是先把板子上所有负载列一遍包括电压、峰值电流、平均电流、纹波容忍度、上电顺序要求然后再决定需要几个通道、每通道是什么类型。这里我拿一个典型的便携设备来举例供电轨电压负载类型峰值电流上电顺序说明VCC_CORE0.9V/1.1V 动态主控数字核心800mA最先需要 DVSVDD_IO3.3VGPIO/外设200mA其次模拟与数字混合VDD_MEM1.8V外部存储/接口300mA核心之后要求纹波低AVDD2.8V模拟前端100mA最后LDO 提供纹波要求高VBAT_BACKUP3VRTC/备份域10mA常开可用 LDO 或直接电池这个表不用做到非常精确但要逼你把每一路的特性想清楚。比如 VCC_CORE 必须用 DCDC 大电流通道且要求动态调压AVDD 这种模拟供电则尽量用 LDO因为开关电源的纹波对模拟链路很不友好。2.2 PCA9422 通道规划与关键外围参数以 PCA9422 这类 PMIC 的典型架构来看通常包含两到三路同步降压 DCDC 和若干路 LDO每路输出通过 I2C 寄存器设压和使能。通道规划的核心原则是大电流、高效率的轨用 DCDC小电流、低纹波、对噪声敏感的轨用 LDO常开的待机轨用低静态电流通道。外围被动元件的选型往往决定了最终性能。DCDC 的电感要按“最大输出电流乘 1.2 到 1.5 倍”的裕量选饱和电流感值选择要看数据手册推荐的纹波电流比例一般取峰峰值电感电流为负载电流的 20% 到 40%。输入输出电容优先选 X7R/X5R 陶瓷电容容值不要只看标称还要看直流偏压特性——很多 MLCC 在加上七八成额定电压之后实际容量掉得厉害。LDO 的输出电容同样重要太小会导致环路不稳定太大则拖慢启动。我一般按 PMIC 手册推荐的容值范围取中间值并尽量留出位置实测后再微调。上电顺序跑不顺时先怀疑的不该是代码而是电容是不是太小、软启动是不是太短。2.3 STM32F405RG 那一侧的电源细节STM32F405RG 作为主控自己也有电源要求。它支持宽范围的 VDD通常 1.8V 到 3.6V我建议直接挂到 PMIC 的 3.3V 主轨上同时给独立的 VBAT 引脚接一颗小容量电池或超级电容保证系统主电源掉电后 RTC 和备份寄存器还能维持。很多人会忽略这个细节整机断电后时间一复位所有依赖时间戳的数据全部报废。另一个容易忽略的是 ADC 参考电压。STM32F405RG 有独立的 VREF 引脚如果用它做电池电压采集或者电流采样参考电压就不能直接用开关电源的 3.3V否则纹波会直接变成 ADC 读数噪声。我通常会用一颗低噪声 LDO 专门给 VREF 供电或者至少把 VREF 接到 PMIC 里噪声性能最好的那一路 LDO 上。2.4 上电时序怎么定谁来定上电时序是整机电源设计里最容易翻车的一块。很多 SoC、MCU 的文档都会画一张时序图要求核心电压必须在 IO 电压之前建立反过来先给 IO 供电然后核心没电片内 ESD 结构可能会通过 IO 引脚形成意想不到的电流路径严重时直接造成闩锁。PCA9422 这类 PMIC 的典型做法是把多路输出的使能顺序做成硬件引脚排序或内部寄存器可编程的 power sequence。硬件排序的好处是不依赖软件上电那一刻 MCU 还没跑起来它自己就能按顺序把每一路拉起来软件可编程的好处是灵活性高可以随时调整延时和间隔。我的建议是核心电压和 IO 电压这类「上电即需要」的轨用硬件引脚或 PMIC 内部默认时序不依赖 I2C后面那些唤醒后才需要的轨比如传感器电源、通信模块电源再由 MCU 起来之后通过 I2C 单独使能。这样既能保证最关键的时序不受软件影响又给低功耗留了灵活控制的余地。3. PCA9422 的 I2C 配置与 STM32F405RG 的控制链路3.1 硬件接口不复杂但上拉电阻别马虎PCA9422 的控制接口是标准的 I2CSTM32F405RG 这边用任意一个空闲的 I2C 外设接过去就行。这里我想多说一句I2C 是最容易「看起来通了、其实没通」的接口之一。SCL 和 SDA 必须有上拉电阻上拉阻值跟总线电容和通信速率强相关400kHz 快速模式一般用 2.2kΩ 到 4.7kΩ如果总线上挂的设备多、走线长阻值要相应调小。还要注意电平匹配。STM32F405RG 的 I2C 引脚工作在 3.3VPCA9422 如果也支持同一个电平那是最好如果 PMIC 的 I/O 供电轨是 1.8V就要确认是否兼容 3.3V 输入不兼容就得加电平转换。别小看这一点我曾经在项目里就因为这个电平问题PMIC 时而响应时而不响应后来逻辑分析仪一挂发现高电平根本没拉到芯片要求的阈值以上。3.2 初始化流程从复位到输出电压建立代码写法的核心就一句话先读后写确认芯片醒了再配置。MCU 上电时 PMIC 可能也在自启动两边谁先好不一定所以初始化第一步是轮询读取设备 ID读到了才说明 I2C 链路和芯片内部电源都稳定了。static uint8_t pmic_read_reg(uint8_t reg) { uint8_t val 0; HAL_I2C_Mem_Read(hi2c1, PMIC_I2C_ADDR 1, reg, I2C_MEMADD_SIZE_8BIT, val, 1, 100); return val; } static void pmic_write_reg(uint8_t reg, uint8_t val) { HAL_I2C_Mem_Write(hi2c1, PMIC_I2C_ADDR 1, reg, I2C_MEMADD_SIZE_8BIT, val, 1, 100); } void pmic_init(void) { uint8_t id 0; uint8_t status 0; // 1. 等待 PMIC 内部电源稳定能正确响应 I2C for (int i 0; i 50; i) { id pmic_read_reg(PMIC_REG_DEVID); if (id PMIC_DEVID_EXPECTED) break; HAL_Delay(10); } // 2. 配置 DCDC1 输出 3.3V 主轨 pmic_write_reg(PMIC_REG_DCDC1_VSEL, DCDC1_3V3); // 3. 配置 DCDC2 输出 1.8V 存储轨 pmic_write_reg(PMIC_REG_DCDC2_VSEL, DCDC2_1V8); // 4. 配置 LDO1 输出 2.8V 模拟轨 pmic_write_reg(PMIC_REG_LDO1_VSEL, LDO1_2V8); // 5. 使能输出 pmic_write_reg(PMIC_REG_ENABLE, PMIC_EN_DCDC1 | PMIC_EN_DCDC2 | PMIC_EN_LDO1); // 6. 等待电源良好确认输出真实建立后再继续跑业务 do { status pmic_read_reg(PMIC_REG_STATUS); } while (!(status PMIC_STATUS_PG_ALL)); }这段代码里有几个细节值得琢磨。第一读设备 ID 的循环不能没有超时上限否则 PMIC 硬件故障时 MCU 会死等第二配置电压和使能输出是分开的不要一次性设置否则可能在电压还没稳定时就让负载上电第三最后一步等待 PGOOD 很关键它保证后续所有外设初始化都是在电源真正就绪之后。3.3 动态电压调节的 I2C 写法DVS 是 PCA9422 这类 PMIC 在数字核心供电上的核心卖点。STM32F405RG 想要降功耗时可以先把内核时钟降下来再通过 I2C 把核心电压从 1.1V 降到 0.9V唤醒后再升回去。这个操作叫动态电压调节代码本身并不复杂void set_core_voltage(uint16_t mv) { uint8_t vsel voltage_to_vsel(mv); pmic_write_reg(PMIC_REG_DCDC_CORE_VSEL, vsel); // 必须等新的电压真正建立不能立刻继续操作 while (!(pmic_read_reg(PMIC_REG_STATUS) PMIC_STATUS_PG_CORE)) { // 加一个超时保护防止死循环 } }麻烦出在「什么时候可以调」和「调完什么时候算数」。调压动作必须发生在负载电流比较平稳的时候尽量避免在负载突发时做 DVS否则电压跌出核心的正常范围系统直接死机。调压完成后也不要立刻跑高性能任务我习惯在代码里加一个几十微秒到几百微秒的稳定窗口等 PGOOD 置位后再继续。4. 动态电压调节与多通道输出切换的实战细节4.1 DVS 用好了功耗到底能降多少动态电压调节的本质是让数字电路的工作电压贴着实际需要走。芯片的功耗大致满足 P ≈ C × V² × f电压从 1.1V 降到 0.9V功耗直接降到原来的 67% 左右这个收益是立竿见影的。当然降低电压的同时必须降低时钟频率否则时序不够芯片会算错数所以 DVS 总是和 DVFS动态电压频率调节配套使用。我见过不少团队在调功耗时只盯着睡眠模式却忽略了活动状态下的 DVS 优化。其实对于间歇性工作的设备任务处理期占空比不小把活动状态的核心电压和频率压到刚好够用的水平省下的电量往往比睡眠模式扣出来的还多。具体压到多少要拿示波器实测跑任务时的电压跌落留足裕量不要只看理论值。4.2 多通道输出切换的顺序与倒灌问题PMIC 的每一路输出都可以独立开关但独立不等于可以乱开乱关。最典型的错误是先关了大电流的主轨但某个外设还通过 GPIO 或接口从别的芯片反向供电到这条轨上形成倒灌。轻则 PMIC 误报故障重则电流路径完全超出设计预期烧掉芯片。正确的切换顺序是先关负载侧的高功耗外设和通信模块再关对应的供电轨唤醒时正好反过来先开 PMIC 输出等 PGOOD 稳定了再初始化外设。这句话写成代码很容易难在执行层面要严格按层级来。我会把外设的电源开关集中到一个电源状态机里管理不允许业务代码直接调 PMIC 寄存器所有开关行为都走统一的接口这样至少能在逻辑层避免交错操作。4.3 软启动与输出斜坡设置软启动解决的是上电浪涌问题。如果 PMIC 输出直接给一个大电容的负载轨供电上电瞬间相当于短路电流冲击很吓人。PCA9422 这类 PMIC 一般都有可编程的软启动时间或输出电压斜坡率配置一个适中的斜坡让电压在几百微秒内平滑上升可以显著降低输入端的电流尖峰。斜坡也不是越慢越好。太慢会导致某些对供电建立时间有要求的外设来不及完成复位加电后直接进入错误状态。我通常的做法是参考负载最严苛那一路的手册找到它允许的上电斜率和延迟窗口反推 PMIC 的斜坡设置。实测中我会在示波器上抓上电波形确认没有明显过冲和台阶才认为这组参数定下来了。5. 低功耗场景下的电源管理策略5.1 先搭一个整机电源状态表低功耗设计最忌讳想到哪改到哪。我习惯先在文档里把整个系统的运行状态列成一张表明确每个状态下每一路电源轨的开闭状态和 MCU 自己的工作模式状态MCU 模式VCC_COREVDD_IOVDD_MEMAVDD备注活动态运行 168MHz1.1V 开3.3V 开1.8V 开2.8V 开正常工作空闲态运行 64MHz0.9V 开3.3V 开1.8V 开2.8V 关降低调度频率睡眠态Stop 模式0.9V 开3.3V 开1.8V 开2.8V 关保留唤醒源待机态Standby 模式关3.3V 开关关仅保留唤醒轨关机态掉电关关关关RTC 备份域仍供电这张表的价值在于它能让你一眼看出电源状态和 MCU 状态的耦合关系。比如待机态只保留 VDD_IO 3.3V 作为唤醒源供电那 VDD_IO 就必须是一路静态功耗足够低的 LDO 或 DCDC否则整机待机电流就压不下去。5.2 STM32F405RG 睡眠-唤醒联动流程进入睡眠态时STM32F405RG 首先要让片内外设停止工作再让 PMIC 把不需要的通道关掉最后才进入 STOP 模式。顺序不能反如果先把 MCU 停了PMIC 就没人能下命令关通道了外设带电睡眠和正常工作几乎没区别功耗全白省。唤醒流程相反外部事件触发后 MCU 先醒立刻通过 I2C 打开被关掉的电源轨等 PGOOD 后再恢复外设状态。这里有个容易被忽略的点唤醒后第一件事是等电源稳定不是去读外设数据。否则一堆外设还在上电过程中寄存器读出来的全是复位初值状态机直接乱套。void enter_sleep_mode(void) { // 先让 PMIC 关掉模拟供电轨 pmic_write_reg(PMIC_REG_LDO1_EN, 0); // 再进入 MCU 睡眠 HAL_SuspendTick(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 下面是被唤醒后的恢复代码 HAL_ResumeTick(); pmic_write_reg(PMIC_REG_LDO1_EN, 1); while (!(pmic_read_reg(PMIC_REG_STATUS) PMIC_STATUS_PG_LDO1)) { // 等待电压建立 } restore_sensor_state(); }5.3 关机与重新开机的完整闭环关机看起来简单但要做好也不容易。长按关机键后系统要先保存关键参数再通知 PMIC 关闭所有主电源轨最后让系统进入一个「等待按键唤醒」的状态。PCA9422 这类 PMIC 通常支持按键触发开关机把按键接在 PMIC 的使能/按键脚上MCU 掉电后 PMIC 自己还保持微弱的监听电流按键来了再重新拉起主电源。这里要注意的是参数保存时机。掉电检测往往很突然MCU 能反应的时间只有几毫秒所以不能等掉电了才开始写 Flash。我是用 STM32F405RG 内置的 RTC 备份寄存器保存快速状态再用看门狗确保流程异常时能重启Flash 里只存不频繁变化的配置参数这样即使掉电保存失败也不会损坏重要数据。6. 电源监控、中断告警与保护联动6.1 该监控哪些指标PMIC 的寄存器里通常能读到输入电压、各路输出电压、芯片温度和各种故障标志。但我建议不要每路都轮询那样既浪费 I2C 总线时间也容易漏掉瞬时故障。正确做法是给 PMIC 的中断输出脚接到 STM32F405RG 的 EXTI 引脚上故障发生时 PMIC 主动拉中断MCU 才去读具体的故障状态。监控内容根据项目风险分级。必须监控的是过流、过温、输入欠压建议监控的是输出电压是否脱离调节范围可以忽略的是那些偶发但不会造成实际危害的警告位。分级越清楚告警处理代码就越简洁排查问题时也不会被一堆无关 flag 淹没。6.2 中断联动与故障处理的几个要点PMIC 的中断处理有几个容易出问题的点。第一个是边沿触发还是电平触发很多 PMIC 的中断脚是开漏输出、低电平有效故障没清除前一直拉低MCU 必须配置成电平触发并在中断里读取状态寄存器确认否则同样的中断会进无数次。第二个是中断标志的清零顺序要先读故障寄存器、再清中断标志有些芯片顺序反了会漏掉新故障。实际项目里我会把故障处理做成有限状态机收到中断后先关掉非关键的负载轨防止故障扩大然后读取故障标志定位原因如果检测到过温就逐步降载而不是立刻全系统断电如果检测到输入欠压就执行快速掉电保存流程。这部分逻辑要简洁直接因为中断上下文里做的事情越少越安全。6.3 欠压掉电的兜底方案输入电源比如电池即将耗尽时电压会缓慢下降PMIC 的欠压告警触发后留给系统的处理时间可能只有几十毫秒到几百毫秒。这个时间窗口内要完成的事情是固定的把关键上下文存进 RTC 备份寄存器、关闭所有非必要的电源轨、让 PMIC 进入最低功耗状态。我在代码里专门画了一条「欠压处理路径」跟正常关机路径分开。正常关机可以慢慢保存日志、优雅关闭通信链路但欠压掉电必须走最短路径任何多余的操作都是在跟时间赛跑。这里顺便提一句欠压阈值不要设得太接近正常工作电压否则电池内阻一波动PMIC 就会反复进出欠压状态整机反而变得不稳定。7. 实测中的坑与调试验证手段7.1 I2C 配置竞态问题PCA9422 和 MCU 的 I2C 链路最常见的坑出现在启动阶段。MCU 在板子上电后立刻去写 PMIC 寄存器但 PMIC 内部的基准电压源和参考时钟还没稳定I2C 从机根本没就绪MCU 这边读到 timeout 或者全 0xFF。很多人第一反应是怀疑 I2C 引脚配置、上拉电阻最后折腾半天才发现是时序竞态。我的做法是在 PMIC 的复位脚上加一个 RC 延时或者在软件里强制规定MCU 初始化里先延时 10 到 50 毫秒再去读设备 ID。这个延时不是为了好看而是给 PMIC 内部启动留出时间窗口。另一个方式是把 PMIC 的 nRESET 引脚接到 MCU 的 GPIO 上MCU 自己控制复位释放时机这样时序完全可控但前提是 MCU 的 GPIO 口的电平状态在上电过程中不能有毛刺。7.2 电感啸叫与轻载模式PCB 做好之后一上电能听到微弱的高频啸叫这种问题十有八九出在 DCDC 的轻载工作模式上。PMIC 为了在轻载时提高效率往往会让电感电流进入 PFM 模式或突发模式开关频率降下来之后掉进人耳可听范围。这不是芯片坏了但用户听到了就是产品缺陷。解决办法一般是两类一类是配置 PMIC 强制进入 PWM 模式牺牲一点轻载效率换取无噪运行另一类是在负载端做微调比如给某个 rail 加一个小的固定负载让电流始终躲开 PFM 和 PWM 的切换区。具体选哪种要看产品更在乎续航还是更在乎静音。如果切换模式下输出电压纹波还很差那多半是输出电容容量不够或者 ESR 偏大这部分要靠实测数据说话。7.3 纹波和瞬态响应示波器才是裁判万用表测电压只能告诉你平均值测不了动态行为。看电源质量示波器至少要能看三件事上电斜率、稳态纹波、负载瞬态响应。测纹波时探头要尽量短最好用接地弹簧而不要用接地鳄鱼夹否则探头本身的地线环路会引入大量噪声测出来的纹波根本不是你板子的真实水平。带宽限制建议开 20MHz把无关的高频噪声滤掉再评估。负载瞬态响应是另一个常见的盲区。我会用电子负载或者一个可控的开关负载在目标 rail 上做 10% 到 90% 的负载阶跃观察电压跌落到恢复稳定需要多长时间。如果跌落超过芯片允许的容差优先查输出电容容量其次是反馈网络参数最后才是电感的感值和饱和电流。电源调试的顺序不能乱否则容易越调越糊涂。7.4 热设计验证不能省PCA9422 这类 PMIC 的效率通常在 90% 上下但效率再高也有损耗损耗最后都变成热量。DCDC 的电感、功率管集成在芯片内部散热路径和布局非常相关。满载持续跑用热像仪看看芯片表面温度如果超过手册的推荐结温就要考虑加大散热焊盘过孔、加宽铜皮、甚至降低某个通道的负载。我习惯同时做两类热测试一类是整机正常工作下的稳态温升一类是极端负载下的温度极限。前者能反映真实产品体验后者能暴露设计裕量。我在实际项目里吃过亏芯片数据手册标了一大串电流能力但自己板子布局太紧凑满载跑十几分钟就过热降载性能数字全成摆设。后来重新画了 PCB 电源区的铜皮和过孔阵列问题才解决。最后再分享一个排查 I2C 故障的小习惯我会在调试阶段放一个逻辑分析仪专门抓 PMIC 的 SCL 和 SDA。很多偶发问题靠读代码根本看不出来但波形一出来是地址不对、速率太高、还是 PMIC 根本没 ACK一目了然。等系统稳定了再把这个调试接口去掉能省掉后面大量的猜测时间。
返回列表