首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >我拿MPM54524设计了一个桌面开源可调电源

我拿MPM54524设计了一个桌面开源可调电源

作者头像
云深无际
发布2025-07-18 09:46:47
发布2025-07-18 09:46:47
8130
举报
文章被收录于专栏:云深之无迹云深之无迹

一觉醒来不知道谁送了我一颗 MPS的 MPM54524(属实是掩耳盗铃了),这颗也是 FPGA 供电以及多路大电流应用的王者,那今天的小朋友就它了!

我就在想可以做点什么,直到在闲鱼看到了这个:

一个降压的PD输出电源,这小体积,可以控制,不就是梦中情机吗?
一个降压的PD输出电源,这小体积,可以控制,不就是梦中情机吗?

一个降压的PD输出电源,这小体积,可以控制,不就是梦中情机吗?

54524还支持4路输出,玩法更多
54524还支持4路输出,玩法更多

54524还支持4路输出,玩法更多

老哥也给出了一个上位机,当然我也给出了一个简版的上位机
老哥也给出了一个上位机,当然我也给出了一个简版的上位机

老哥也给出了一个上位机,当然我也给出了一个简版的上位机

提前看效果:

这个是初版

后面又加了一些功能:

较为完善的demo

(怕有人不看下面的文字,所以提前上视频)

因为MPM54524 是一款带I2C接口的全集成、5A、4 路输出电源模块。它内置启动与关断排序控制,同时具备可调软启动 (SS)、补偿功能和多个保护阈值,提供了一个完整的电源解决方案。

蛮合适的
蛮合适的

蛮合适的

4路输出正好满足一些调试需求,如果不够可以扩展
4路输出正好满足一些调试需求,如果不够可以扩展

4路输出正好满足一些调试需求,如果不够可以扩展

在3.3V下效率还更高。

以往电源需要很多外置的元件来辅助,现在一颗芯片就都完成
以往电源需要很多外置的元件来辅助,现在一颗芯片就都完成

以往电源需要很多外置的元件来辅助,现在一颗芯片就都完成

保护功能齐全,只要前面搞个二极管就可以给电池供电了:

功能简称

全称

作用

应用意义

OCP

Over Current Protection

过流保护

防止短时电流过大烧坏芯片

SCP

Short Circuit Protection

输出短路保护

输出接地等极端场景自我断电

OVP

Over Voltage Protection

过压保护

防止反馈失控或外部干扰导致高压输出

OTP

Over Temperature Protection

过热保护

芯片内部温度过高时主动关闭输出

UVLO

Under Voltage Lockout

欠压锁定

输入电压不足时延迟启动,避免不稳定工作

EVL板也很简洁,这次也是使用它来运行最小的demo
EVL板也很简洁,这次也是使用它来运行最小的demo

EVL板也很简洁,这次也是使用它来运行最小的demo

MPS官方有个GUI可以完成这个功能的,但是调试器太贵了,我这里给出了廉价的方案:

就是这个
就是这个

就是这个

360+
360+

360+

这个接口要用,PUSH一下
这个接口要用,PUSH一下

这个接口要用,PUSH一下

以及为了后面的兄弟可以复用,减少对环境的依赖性,我这里使用了Python的接口(调死我了):

CP2112
CP2112

CP2112

是一款完美的小芯片,调试其它的IIC设备也可以
是一款完美的小芯片,调试其它的IIC设备也可以

是一款完美的小芯片,调试其它的IIC设备也可以

最终的样机是这样:

测试
测试

测试

正点原子做主供电,因为是MPM54524(点击直达这颗料)是降压输出,电流最大到5A:

这里最大的电流也就是这样设置了
这里最大的电流也就是这样设置了

这里最大的电流也就是这样设置了

控制的架构是这样的:

代码语言:javascript
复制
    ┌────────────┐       I²C (通过桥接器)
    │   上位机   │◄────────────────────┐
    │ (Python/Qt)│                    │
    └────────────┘                    ▼
     ↑ GUI控制 + 显示           ┌────────────┐
     │                         │ USB-I2C桥接 │  (如 CH341, MCP2221, FT4222)
     └─────────────串口/USB────▶│ 或 MCU转发  │
                               └────────────┘
                                       │
                                 I²C 总线
                                       ▼
                              ┌────────────────┐
                              │  MPM54524GCQ   │
                              └────────────────┘

这颗芯片还是很有特点的,稍微讲一下我看到有趣的功能:

Buck A 和 B 可并联支持 10A 输出,并支持交错相位以优化纹波(Interleaving)。

交错相位是一种控制策略,它使得多个降压通道(Buck A/B/C/D)的开关时序错开固定角度(通常为90°或180°),实现以下目标:

使各通道的导通(On-Time)均匀分布在一个周期内,从而分散输入/输出电流脉冲,降低系统纹波。

假设系统总频率为 1MHz(周期 = 1µs):

四个 Buck 通道同时工作但 分布在不同相位

Buck A: 0°

Buck B: 90°

Buck C: 180°

Buck D: 270°

这意味着它们的开关事件间隔为:1μs ÷ 4 = 250ns→ 所有通道平均分布在每个周期,电流纹波合成后会部分抵消,而不是集中在同一时刻叠加。

在数据手册第 18–19 页中有如下关键描述:

“Figure 4 shows that Buck A/B/C/D are frequency-locked with fixed 90° phase shift.”

这里
这里

这里

默认状态下,A-B-C-D 四通道交错工作,相位间隔为 90°;

如果启用并联模式(A+B),则 A 和 B 之间使用 180° 相移(见 Figure 5),以便互补导通达到交错均流效果。

就是保证一个周期内队外输出不这么变,内部交替进行
就是保证一个周期内队外输出不这么变,内部交替进行

就是保证一个周期内队外输出不这么变,内部交替进行


寄存器配置

寄存器地址:14h, bit[5] = 1 → 启用 Buck A 和 Buck B 的并联交错模式(Interleaved Dual-Phase Mode)

并联模式下,需强制绑定反馈点(VSA+ = VSB+);**VSA+ 必须连接 VSB+**,表示这两个输出电压完全一致,共用反馈。

Buck C 和 D 不支持并联工作,仅交错控制;交错模式适用于大负载或动态电流需求场景,不建议在极轻负载下启用并联。

所以我这次就没有设置这个选项,因为样板上面好像没有感测线的接口。

AVP(Active Voltage Positioning)功能:输出电压随负载电流下调(VDROOP = IOUT × RDROOP),有助于电流自动均流。

核心思想是:

随着负载电流增加,输出电压按比例下调,从而实现更佳的瞬态响应和并联均流性能。

换句话说:负载电流越大,输出电压 VOUT 自动下降;有点类似“动态压降”或“虚拟阻抗控制”。这个功能可以上负载来看

数学模型(手册第 19 页)

AVP 功能遵循以下两个公式:

① 压降电压:

:压降值;

:实际输出电流;

:设定的等效电阻,典型值为 40mΩ

② 实际输出电压为:

当负载突增时,VOUT 本来就预留压降空间,减少过冲;可以做瞬态抑制。也可以多个通道(如 A/B/C/D)并联时,AVP 会自动调节电压大小,根据负载电流自动趋向均衡;降低大负载下的功耗与散热压力;最后因为系统不再强依赖容性响应,减小 VOUT 振幅,可用更小电容。

启用 AVP?

MPM54524 支持对 Buck A/B 和 C/D 分别独立启用 AVP。

寄存器地址 71h:bit[7] = 1:开启 Buck A/B 的 AVP

寄存器地址 79h:bit[7] = 1:开启 Buck C/D 的 AVP

配合并联工作

当多个 Buck 输出并联(如 Buck A 和 B、或者全部四路 A+B+C+D)时:

各通道根据自身电流大小会形成不同压降;

压降大 → 电压低 → 分担电流少;

压降小 → 电压高 → 分担电流多;

最终达到被动均流(通过电压调节驱动电流)

所以 AVP 实现的是负载自适应电压调节 + 被动电流均流,而不是主动控制电流。

我们来发挥传统艺能,看看可视化的操作:

image-20250628083444880
image-20250628083444880

展示了 MPM54524 在启用 AVP(Active Voltage Positioning)时,不同参考电压 下,输出电压 随负载电流 的线性下降关系。

每条曲线代表一个不同的设定参考电压(1.0V、1.2V、1.5V、3.3V);

横轴是输出电流(从 0A 到 5A);

纵轴是实际输出电压;

斜率是固定的,由内部设定的 决定;

说明随着负载电流增加,输出电压按比例线性下调。

轻载时电压较高,重载时自动压降改善并联电源之间的均流分配帮助缓冲负载突变下的电压跳变降低对大电容的依赖(减小COUT)

上面说了四通道的事情

四通道并联时的电流分担
四通道并联时的电流分担

四通道并联时的电流分担

这张图展示了 MPM54524 在 AVP 模式下四通道并联时的电流分担和总输出电压的变化趋势

Channel A/B/C/D Current(彩色实线):代表每个通道均匀分担总电流 ,从 0A 到 20A,每个通道上限为 5A;

Shared VOUT(虚线):是所有通道输出一致的电压,其值随总电流线性下降,反映了 AVP 机制的作用。

这里的数据可能有点错误,但是趋势没有问题,就是电流大,电压小因为功率一定。

IIC控制

很多的电源芯片有IIC接口,但是详细说明的不多:

芯片还有两个额外的地址线
芯片还有两个额外的地址线

芯片还有两个额外的地址线

这个是限制,需要加限流的上拉电阻
这个是限制,需要加限流的上拉电阻

这个是限制,需要加限流的上拉电阻

框图如此
框图如此

框图如此

官方的板子都是AD绘制的,ADDR1和2都拉高的
官方的板子都是AD绘制的,ADDR1和2都拉高的

官方的板子都是AD绘制的,ADDR1和2都拉高的

如果在使用过程中有问题可以去论坛问:

MPS老哥现场安排
MPS老哥现场安排

MPS老哥现场安排

大概五分钟内回复吧,这里是发帖的时候写出标题了
大概五分钟内回复吧,这里是发帖的时候写出标题了

大概五分钟内回复吧,这里是发帖的时候写出标题了

“Two ADDR pins are available to configure bit[0] to bit[3]...”

MPM54524 支持最多 8种地址配置,通过两个引脚 ADDR1ADDR2 设定 I²C 地址的低四位(bit[0]~bit[3]):

ADDR1

ADDR2

I²C 地址(0XXX xxxx)

Low

Low

0XXX 1001

Low

Float

0XXX 1100

Low

High

0XXX 1110

Float

Low

0XXX 1010

Float

Float

0XXX 1000

Float

High

0XXX 1111

High

Low

0XXX 1011

High

Float

0XXX 1101

High

High

0XXX 0111

表中 “0XXX” 的高位部分可以通过写入寄存器 0x73h 设置 bit[4]~bit[6],实现地址扩展(详见手册注释 XXX refers to portion configurable by customer)。

如果不使用IIC控制的话,默认的0xxx其实就是0x000,EVL也就是0x07,使用的时候记得算对。

还是算一下吧!

bit[6:4] = 0b000(默认上电值)

ADDR[3:0] = 0111(由 High, High 决定)

完整地址为:0b00000111 = 0x07

实际 I²C 地址(7 位):0x07h

使用时:

写地址(8 位) = 0x07 << 1 | 0 = 0x0E

读地址(8 位) = 0x07 << 1 | 1 = 0x0F

但是这个IIC不是纯IIC,而是专门电源管理的PMBUS:

在这里
在这里

在这里

STM32F401
STM32F401

STM32F401

以前老不知道这个PMBus是什么,这次就解惑了。

类型

含义

R

只读(Read Only)

W

只写(Write Only)

R/W

可读可写

Send

写入命令后立即执行,无需数据

主要是这个Send命令是不一样的,其实就直接清楚寄存器位而已。

也就是这个
也就是这个

也就是这个

我们知道这些知识就可以了!

这个芯片不复杂,但是上位通讯麻烦,我这里使用了标准的HID库来进行封装,在封装前要再看一下通讯协议:

写入流程(典型写寄存器):
代码语言:javascript
复制
START
→ 发送从机地址 + 写 (0x25<<1 | 0 = 0x4A)
→ 发送寄存器地址 (如 0x15)
→ 发送数据 (如 0x88 设置电压值)
→ STOP
读取流程(典型读寄存器):
代码语言:javascript
复制
START
→ 发送从机地址 + 写 (0x4A)
→ 发送寄存器地址 (如 0x15)
→ RESTART
→ 发送从机地址 + 读 (0x4B)
→ 读取数据(如读回寄存器值)
→ STOP

为了最精简地控制 MPM54524GCQ 电源芯片,只需要保留 I²C 的字节级读写功能,并去除多余功能,构建一个轻量的控制类即可。

读任意寄存器:read_byte_data(addr, reg)

写任意寄存器:write_byte_data(addr, reg, val)

然后代码我传到了GitHub,这里也写一下重要的部分:

代码语言:javascript
复制
import hid
import time

class MPM54524:
    def __init__(self, address=0x25):
        self.h = hid.device()
        self.h.open(0x10C4, 0xEA90)  # CP2112 的 VID, PID
        self.address = address  # 默认 I2C 从地址

    def write_reg(self, reg, val):
        """向寄存器写入一个字节"""
        self.h.write([0x14, self.address << 1, 0x02, reg, val])

    def read_reg(self, reg):
        """读取寄存器一个字节"""
        self.h.write([0x11, self.address << 1, 0x00, 0x01, 0x01, reg])  # Write+Read 请求
        for _ in range(10):
            self.h.write([0x15, 0x01])  # 查询状态
            resp = self.h.read(7)
            if resp[0] == 0x16 and resp[2] == 5:
                self.h.write([0x12, 0x00, 0x01])  # 强制读取
                resp = self.h.read(4)
                return resp[3]
        raise IOError("Read timeout or failure")

    def close(self):
        self.h.close()

使用示例:

代码语言:javascript
复制
if __name__ == '__main__':
    dev = MPM54524(address=0x25)  # 从机地址视 ADDR1/ADDR2 引脚而定
    val = dev.read_reg(0x00)  # 读取 STATUS_0
    print(f"STATUS_0 = 0x{val:02X}")

    dev.write_reg(0x0C, 0x0F)  # 启用 Buck A~D 输出
    dev.close()
成功
成功

成功

若你只关心电压设置(例如 BuckA):

代码语言:javascript
复制
dev.write_reg(0x15, 0x88)  # 设置 BuckA 输出电压(示例值)

只关心故障检测:

代码语言:javascript
复制
status = dev.read_reg(0x00)
if status & 0x40:
    print("过温故障")
if status & 0x20:
    print("Buck A 输出异常")
输出都是正确的
输出都是正确的

输出都是正确的

OK,万里长征第一步,接下来激素封装这个寄存器库了。

写一个完整的 MPM54524 Python 控制类,已封装所有主要寄存器操作,并支持:

读取/写入任意寄存器(byte)

快捷读写特定功能寄存器(STATUS、ENABLE、电压设定等)

错误处理

可拓展性强(后续支持 word/block)

完整 Python 控制类 MPM54524.py

代码语言:javascript
复制
import hid
import time

class MPM54524:
    VENDOR_ID = 0x10C4  # CP2112 VID
    PRODUCT_ID = 0xEA90 # CP2112 PID

    def __init__(self, address=0x25):
        self.h = hid.device()
        self.h.open(self.VENDOR_ID, self.PRODUCT_ID)
        self.address = address  # 7-bit I²C 地址(取决于 ADDR1/2 引脚)
        time.sleep(0.1)

    def close(self):
        self.h.close()

    def write_reg(self, reg_addr, value):
        """写 1 字节到寄存器"""
        self.h.write([0x14, self.address << 1, 0x02, reg_addr, value])

    def read_reg(self, reg_addr):
        """读 1 字节寄存器"""
        self.h.write([0x11, self.address << 1, 0x00, 0x01, 0x01, reg_addr])
        for _ in range(10):
            self.h.write([0x15, 0x01])
            resp = self.h.read(7)
            if resp[0] == 0x16 and resp[2] == 5:
                self.h.write([0x12, 0x00, 0x01])
                resp = self.h.read(4)
                return resp[3]
        raise IOError(f"Read failed for register 0x{reg_addr:02X}")

    # ---------- 常用寄存器封装 ----------

    def get_status_0(self):
        """读取 STATUS_0 状态寄存器 (00h)"""
        return self.read_reg(0x00)

    def clear_status(self):
        """清除故障(07h = CLEAR_0)"""
        self.write_reg(0x07, 0x01)

    def enable_buck_channels(self, enable_mask=0x0F):
        """
        控制 BuckA~D 使能(0Ch 寄存器)
        bit7~4 控制各 Buck 输出通道:1 = enable,0 = disable
        默认 0x0F = 全部开启
        """
        self.write_reg(0x0C, (enable_mask & 0x0F) << 4)

    def disable_buck_channels(self):
        self.enable_buck_channels(0x00)

    def set_buck_voltage(self, channel, value):
        """
        设置某一路 Buck 输出电压值(寄存器 0x15~0x18)
        value = 电压设定值(具体映射查数据手册)
        """
        reg_map = {'A': 0x15, 'B': 0x16, 'C': 0x17, 'D': 0x18}
        if channel in reg_map:
            self.write_reg(reg_map[channel], value)
        else:
            raise ValueError("Invalid channel. Use A/B/C/D.")

    def read_buck_voltage(self, channel):
        reg_map = {'A': 0x15, 'B': 0x16, 'C': 0x17, 'D': 0x18}
        if channel in reg_map:
            return self.read_reg(reg_map[channel])
        raise ValueError("Invalid channel. Use A/B/C/D.")

    def read_current_monitor(self, channel):
        reg_map = {'A': 0x02, 'B': 0x03, 'C': 0x04, 'D': 0x05}
        if channel in reg_map:
            return self.read_reg(reg_map[channel])
        raise ValueError("Invalid channel. Use A/B/C/D.")

    def get_temperature(self):
        """读取温度 ADC 值(24h)"""
        return self.read_reg(0x24)

    def get_device_id(self):
        """读取芯片 ID(31h)"""
        return self.read_reg(0x31)

    # ---------- 工具辅助 ----------

    def print_status(self):
        val = self.get_status_0()
        print(f"STATUS_0 = 0x{val:02X}")
        if val & 0x40: print("过温故障")
        if val & 0x20: print("Buck A 输出异常")
        if val & 0x10: print("Buck B 输出异常")
        if val & 0x08: print("Buck C 输出异常")
        if val & 0x04: print("Buck D 输出异常")
        if val & 0x02: print("VIN 过压")

示例用法

代码语言:javascript
复制
if __name__ == "__main__":
    dev = MPM54524(address=0x25)

    dev.print_status()                # 打印 STATUS_0 状态
    dev.clear_status()               # 清除故障
    dev.enable_buck_channels()       # 启动 A~D 通道
    dev.set_buck_voltage('A', 0x88)  # 设置 BuckA 电压(例值)
    val = dev.read_buck_voltage('A')
    print(f"BuckA voltage setting = 0x{val:02X}")

    current = dev.read_current_monitor('A')
    print(f"BuckA 电流值(ADC) = {current}")

    print(f"温度寄存器 = {dev.get_temperature()}")
    print(f"芯片 ID = {dev.get_device_id():02X}")

    dev.close()
这是最一开始的UI
这是最一开始的UI

这是最一开始的UI

这里在之后需要微调,显示不是很正常。

第二版是最稳的版本
第二版是最稳的版本

第二版是最稳的版本

左上角的各种芯片信息,也有单路或者4路控制

最后一版是功能最多的,有功耗计算以及寄存器读写
最后一版是功能最多的,有功耗计算以及寄存器读写

最后一版是功能最多的,有功耗计算以及寄存器读写

文末小彩蛋,众所周知起名字最难了:

幻想时间
幻想时间

幻想时间

程序什么的,我整理好再发出来。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-07-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 寄存器配置
  • 数学模型(手册第 19 页)
    • ① 压降电压:
    • ② 实际输出电压为:
  • 启用 AVP?
  • 配合并联工作
  • IIC控制
    • 实际 I²C 地址(7 位):0x07h
      • 写入流程(典型写寄存器):
      • 读取流程(典型读寄存器):
    • 使用示例:
    • 若你只关心电压设置(例如 BuckA):
    • 只关心故障检测:
  • 完整 Python 控制类 MPM54524.py
  • 示例用法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档