2、PMIC关键寄存器解析:寄存器映射表、关键控制位、读写时序

好,咱们进入PMIC最核心的部分——寄存器操作。

说实话,很多工程师拿到PMIC datasheet,第一反应就是翻到寄存器列表,然后开始对着代码一个个配。这种做法我早年也干过,结果呢?板子点不亮,查了半天发现是某个控制位的默认值和我想的不一样。所以这一节,我带你系统地过一遍PMIC寄存器该怎么看、怎么用。

2.1 PMIC寄存器映射表——你得知道它长什么样

MTK PMIC的寄存器空间,通常是16位地址、8位数据。你想想看,一颗PMIC里可能有几百个寄存器,总不能全放在一个页面上吧?所以PMIC用了分页(Page)机制

我习惯把PMIC寄存器映射表想象成一栋楼:

  • Page 就是楼层号
  • Register Address 就是房间号
  • Bit Field 就是房间里的开关

以MT8676配套的PMIC为例,典型的寄存器映射表长这样:

Page Register Addr Register Name Reset Value Description
0x0 0x0120 PMIC_RG_TOP_CFG 0x00 Top level configuration
0x0 0x0124 PMIC_RG_INT_EN 0x00 Interrupt enable register
0x1 0x0010 PMIC_RG_LDO_VOLT 0x1F LDO output voltage setting
0x2 0x0008 PMIC_RG_WAKEUP_CTRL 0x00 Wakeup source configuration
我的小技巧: 拿到一份新的PMIC datasheet,我第一件事就是看Reset Value列。如果某个寄存器的默认值不是0x00,那它很可能在硬件上电时就已经配置好了某些功能。比如LDO电压寄存器默认0x1F,意味着这颗LDO默认输出1.8V还是3.3V?你得去查电压编码表。

2.2 关键控制位——这些位你绕不开

PMIC里寄存器很多,但真正在休眠唤醒场景下需要关注的,其实就那么几个。我列一下我项目中经常打交道的:

2.2.1 电源域使能位(Power Domain Enable)

每个LDO或DCDC都有一个使能位。比如:

// 假设 LDO1 的使能位在 Page0, Addr 0x0120, Bit[0]
#define PMIC_LDO1_EN_ADDR    0x0120
#define PMIC_LDO1_EN_BIT     (1 << 0)

// 使能 LDO1
pmic_write_byte(PMIC_LDO1_EN_ADDR, PMIC_LDO1_EN_BIT);

// 关闭 LDO1
pmic_write_byte(PMIC_LDO1_EN_ADDR, 0x00);
注意: 我曾经踩过一个坑——某个LDO的使能位是反逻辑的。也就是说,写1是关闭,写0才是开启。这种反逻辑设计在一些老款PMIC上出现过。所以千万别想当然,一定要看datasheet里的描述。

2.2.2 休眠模式控制位(Sleep Mode Control)

这部分是座舱电源管理的核心。PMIC进入休眠后,哪些LDO保持供电,哪些关断,都由这些位控制。

举个例子:

// 寄存器:PMIC_RG_SLEEP_CTRL (Page0, Addr 0x0130)
// Bit[7:4] : LDO1~LDO4 在休眠时的行为
//   0x0: 跟随系统状态
//   0x1: 强制开启
//   0x2: 强制关闭

// 让 LDO1 在休眠时保持供电(给RTC用)
pmic_write_byte(0x0130, 0x10);  // LDO1 强制开启

嗯,这里要注意:强制开启的LDO,在休眠时功耗会增大。我一般只给RTC、唤醒逻辑电路这类必须供电的模块用。

2.2.3 唤醒源配置位(Wakeup Source Config)

PMIC怎么知道系统该醒了?靠的就是唤醒源。常见的唤醒源有:

  • GPIO唤醒: 比如ACC信号、车门解锁信号
  • 定时器唤醒: 比如每隔10秒唤醒一次做CAN报文检测
  • RTC闹钟唤醒: 比如每天早上6点唤醒车机

配置代码示例:

// 寄存器:PMIC_RG_WAKEUP_SRC (Page2, Addr 0x0008)
// Bit[0] : GPIO0 唤醒使能
// Bit[1] : GPIO1 唤醒使能
// Bit[2] : RTC 唤醒使能
// Bit[3] : 定时器唤醒使能

// 使能 GPIO0 和 RTC 唤醒
pmic_write_byte(0x0008, 0x05);  // Bit0 + Bit2
关键点: 唤醒源配置后,别忘了还要配置对应的GPIO为输入模式,并且使能PMIC的中断。否则信号来了,PMIC也不知道该干啥。

2.3 读写时序——搞错了板子就废了

PMIC的通信接口通常是I2C或SPI。MTK平台用I2C居多。读写时序看起来简单,但细节决定成败。

2.3.1 I2C写时序

标准的I2C写操作:

START + SlaveAddr(W) + ACK + RegAddr + ACK + Data + ACK + STOP

但PMIC有个特殊要求——Page切换。因为PMIC内部有多个Page,读写前必须先切换到目标Page。

我见过有人这么写代码:

// 错误示范:没有切换Page
i2c_write(PMIC_ADDR, 0x0120, 0x01);  // 直接写Page0的寄存器
// 如果当前在Page1,这行代码实际写的是Page1的0x0120寄存器,完全不是你想要的效果

正确的做法是:

// 正确示范:先切Page,再写寄存器
pmic_set_page(0x0);                  // 切换到Page0
i2c_write(PMIC_ADDR, 0x0120, 0x01); // 写Page0的寄存器

2.3.2 I2C读时序

读操作稍微复杂一点,需要先写寄存器地址,再重新发起读操作:

START + SlaveAddr(W) + ACK + RegAddr + ACK + STOP
START + SlaveAddr(R) + ACK + Data + NACK + STOP

代码实现:

uint8_t pmic_read_byte(uint16_t reg_addr)
{
    uint8_t val = 0;
    
    // 先写寄存器地址
    i2c_write(PMIC_ADDR, reg_addr, 0);  // 这里只发地址,不发数据
    // 再读数据
    i2c_read(PMIC_ADDR, &val, 1);
    
    return val;
}
我曾经踩过的坑: 有些PMIC在读操作后,会自动把Page指针复位到Page0。如果你连续读不同Page的寄存器,中间必须重新切Page。这个问题让我调了整整两天,最后用逻辑分析仪抓波形才发现。

2.3.3 时序参数要求

PMIC对I2C时序有严格要求,尤其是休眠唤醒场景下:

参数 最小值 典型值 说明
SCL频率 - 400kHz 标准模式100kHz,快速模式400kHz
t_HD:STA 0.6us - 起始条件保持时间
t_SU:STO 0.6us - 停止条件建立时间
t_BUF 1.3us - 停止和起始条件之间的总线空闲时间
我的建议: 在休眠唤醒场景下,我习惯把I2C频率降到100kHz。为什么?因为PMIC在休眠状态下,内部时钟可能不准,高频通信容易出错。虽然慢了点,但稳定第一。

2.4 实战经验总结

说了这么多,最后给你几个我实际项目中的经验:

  1. 读回验证: 写完寄存器后,一定要读回来确认。我遇到过I2C总线被拉死,写操作返回成功但实际没写进去的情况。
  2. 批量操作用Page Burst: 有些PMIC支持同一Page内的连续读写,效率高很多。比如配置多个LDO电压时,可以用burst write一次搞定。
  3. 保留位不要乱动: 寄存器里标记为Reserved的位,写0就对了。我曾经手贱写了个1,结果PMIC直接进入测试模式,功耗飙到200mA。
  4. 休眠前保存上下文: 如果你在休眠前修改了某些寄存器的值,记得在唤醒后恢复。我一般用一个结构体保存所有关键寄存器的值。

好了,PMIC寄存器这块就讲到这里。下一节我们聊聊具体的休眠唤醒流程,到时候你会看到这些寄存器是怎么串起来用的。