韌體約 5 分鐘

PMBus 指令解析:VOUT_MODE (20h) + VOUT_COMMAND (21h) 與 LINEAR16 格式

搞懂 PMBus 輸出電壓怎麼設定:VOUT_MODE (20h) 的模式與指數、VOUT_COMMAND (21h) 的 LINEAR16 換算,附指數解析度速查表、LINEAR11 差異比較與 C 語言整數換算範例。

最後更新:

📘 參考規格書PMBus · PMBus Power System Management Protocol Specification Part II原廠文件 ↗

兩個命令的分工

命令代碼長度作用
VOUT_MODE20h1 Byte告訴主控端:輸出電壓相關數值用哪種格式、指數是多少
VOUT_COMMAND21h2 Bytes設定目標輸出電壓(數值依 VOUT_MODE 的格式解讀)

哪些命令使用 VOUT_MODE 的格式

VOUT_MODE 不只影響 VOUT_COMMAND,所有以「伏特」為單位的輸出電壓命令都用同一個格式與指數:

類別命令
電壓設定VOUT_COMMAND (21h)、VOUT_TRIM (22h)、VOUT_CAL_OFFSET (23h)、VOUT_MAX (24h)、VOUT_MIN (2Bh)
MarginVOUT_MARGIN_HIGH (25h)、VOUT_MARGIN_LOW (26h)
保護門檻VOUT_OV_FAULT_LIMIT (40h)、VOUT_OV_WARN_LIMIT (42h)、VOUT_UV_WARN_LIMIT (43h)、VOUT_UV_FAULT_LIMIT (44h)
Power GoodPOWER_GOOD_ON (5Eh)、POWER_GOOD_OFF (5Fh)
量測READ_VOUT (8Bh)

VOUT_MODE (20h)

傳輸格式

使用 SMBus Read Byte(部分元件也允許 Write Byte,但多數為唯讀):

讀取:S | Addr+W | A | 20h | A | Sr | Addr+R | A | Data | [PEC] | NA | P

位元結構

b[7:5] Mode格式b[4:0] Parameter 的意義
000Linear(LINEAR16)指數 N,5 位元二補數(−16 ~ +15)
001VIDVID 碼表的類型(依各家 VR 規範)
010Direct不使用;換算係數由 COEFFICIENTS (30h) 或規格書提供
011IEEE 754 半精度浮點不使用

實務上電源模組絕大多數使用 Linear 格式,以下都以 Linear 為主。VID 與 Direct 格式的換算方式,請見 PMBus 數值格式深度解析。

主控端解析流程

flowchart TD
    A["讀取 VOUT_MODE (20h)"] --> B{"b[7:5]?"}
    B -- "000 Linear" --> C["b[4:0] 轉成 5 位元二補數 → N"]
    B -- "001 VID" --> V["依 b[4:0] 指定的 VID 碼表換算"]
    B -- "010 Direct" --> D["用 m、b、R 係數換算"]
    C --> E["讀 READ_VOUT (8Bh) 等命令<br>得到 16 位元無號數 Y"]
    E --> F["V = Y × 2^N"]

VOUT_MODE 在大多數電源上是唯讀的,數值由韌體依內部 ADC / DAC 的解析度預先決定,但仍建議開機時讀一次,不要在程式裡寫死。

指數 N 的解讀

b[4:0] 是 5 位元二補數:

  • 00h ~ 0Fh → N = 0 ~ +15
  • 10h ~ 1Fh → N = 值 − 32,也就是 −16 ~ −1

例如 VOUT_MODE = 17h:

17h=000⏟Linear 10111⏟N⇒N=23−32=−9\texttt{17h} = \underbrace{000}_{\text{Linear}}\,\underbrace{10111}_{N} \quad\Rightarrow\quad N = 23 - 32 = -9

VOUT_COMMAND (21h)

傳輸格式

使用 SMBus Write Word / Read Word,低位元組先傳(Little-Endian):

寫入:S | Addr+W | A | 21h | A | Data Low | A | Data High | A | [PEC | A] | P

LINEAR16 換算公式

Linear 模式下,VOUT_COMMAND 是 16 位元無號整數的尾數 Y,搭配 VOUT_MODE 的指數 N:

VOUT=Y×2NY=round⁡ ⁣(VOUT×2−N)V_{\text{OUT}} = Y \times 2^{N} \qquad\qquad Y = \operatorname{round}\!\left(V_{\text{OUT}} \times 2^{-N}\right)

換算範例

範例 1:12 V 輸出,VOUT_MODE = 17h(N = −9)

Y=12×29=12×512=6144=1800hY = 12 \times 2^{9} = 12 \times 512 = 6144 = \texttt{1800h}

所以寫入 VOUT_COMMAND = 1800h(線上傳送 00h、18h)。

範例 2:1.05 V 輸出,VOUT_MODE = 14h(N = −12)

Y=1.05×4096=4300.8  ⇒  4301=10CDhY = 1.05 \times 4096 = 4300.8 \;\Rightarrow\; 4301 = \texttt{10CDh}

實際設定值為 4301/4096≈1.05005 V4301 / 4096 \approx 1.05005\,\text{V},誤差來自解析度限制。

指數與解析度速查表

指數越小(越負),解析度越細,但能表示的最大電壓越低:

VOUT_MODEN解析度(1 LSB)最大可表示電壓常見用途
18h−83.906 mV255.99 V高壓輸出
17h−91.953 mV127.99 V12 V / 48 V 電源
16h−100.977 mV63.99 V
15h−110.488 mV31.99 V
14h−120.244 mV15.99 V低壓 POL
13h−130.122 mV7.99 VCPU / FPGA 核心電壓

最大電壓 =65535×2N= 65535 \times 2^{N}。實際可設定的上限還會受 VOUT_MAX (24h) 限制。

容易搞混:LINEAR16 vs LINEAR11

PMBus 有兩種 Linear 格式。輸出電壓用 LINEAR16,其他大部分數值(電流、輸入電壓、溫度、功率)用 LINEAR11:

項目LINEAR16LINEAR11
用在哪裡VOUT_COMMAND、READ_VOUT 等輸出電壓命令READ_IOUT、READ_VIN、READ_TEMPERATURE_1 等
尾數 Y16 位元無號數(整個 Word)b[10:0],11 位元二補數
指數 N在 VOUT_MODE 的 b[4:0]在同一個 Word 的 b[15:11]
公式Y×2NY \times 2^{N}Y×2NY \times 2^{N}
flowchart LR
    subgraph L16["LINEAR16(VOUT_COMMAND)"]
        direction TB
        A1["VOUT_MODE b[4:0] = N"]
        A2["Data[15:0] = Y(無號)"]
    end
    subgraph L11["LINEAR11(READ_IOUT 等)"]
        direction TB
        B1["Data[15:11] = N"]
        B2["Data[10:0] = Y(二補數)"]
    end

讀取與設定範例

Linux(i2c-tools)

以 PMBus 位址 0x58、I2C bus 1 為例(請換成你系統上的實際值):

# 讀 VOUT_MODE(1 Byte)
i2cget -y 1 0x58 0x20 b

# 讀 VOUT_COMMAND 與 READ_VOUT(Word,i2cget 會自動組合低/高位元組)
i2cget -y 1 0x58 0x21 w
i2cget -y 1 0x58 0x8b w

# 設定 VOUT_COMMAND = 0x1800(N = -9 時為 12 V)
i2cset -y 1 0x58 0x21 0x1800 w

C 語言:整數換算(不需要浮點運算)

MCU 上常常不想用 float,以下用 mV 整數換算:

#include <stdbool.h>
#include <stdint.h>

/* VOUT_MODE 是否為絕對值 Linear 格式(b[7:5] = 000) */
static bool vout_mode_is_linear(uint8_t vout_mode)
{
    return (vout_mode & 0xE0u) == 0;
}

/* 取出 5 位元二補數指數 N */
static int vout_exponent(uint8_t vout_mode)
{
    int n = vout_mode & 0x1F;
    return (n & 0x10) ? n - 32 : n;
}

/* LINEAR16 → mV(四捨五入) */
uint32_t linear16_to_mv(uint16_t y, uint8_t vout_mode)
{
    int n = vout_exponent(vout_mode);
    uint64_t uv = (uint64_t)y * 1000u;          /* 先放大 1000 倍 */

    if (n >= 0)
        return (uint32_t)(uv << n);
    return (uint32_t)((uv + (1ull << (-n - 1))) >> -n);
}

/* mV → LINEAR16(四捨五入,超出範圍時回傳 0xFFFF) */
uint16_t mv_to_linear16(uint32_t mv, uint8_t vout_mode)
{
    int n = vout_exponent(vout_mode);
    uint64_t y;

    if (n <= 0)
        y = (((uint64_t)mv << -n) + 500u) / 1000u;
    else
        y = ((uint64_t)mv + (500ull << n)) / (1000ull << n);

    return (y > 0xFFFFu) ? 0xFFFFu : (uint16_t)y;
}

使用方式:

uint8_t mode = 0x17;                       /* 從 VOUT_MODE 讀回 */
if (vout_mode_is_linear(mode)) {
    uint16_t y = mv_to_linear16(12000, mode);   /* → 0x1800 */
    uint32_t mv = linear16_to_mv(y, mode);      /* → 12000 */
}

工程實務提示

結論

  • VOUT_MODE (20h) 決定格式與指數,VOUT_COMMAND (21h) 提供尾數,兩者合起來才是電壓。
  • Linear 格式:V=Y×2NV = Y \times 2^{N},N 是 VOUT_MODE b[4:0] 的 5 位元二補數。
  • 常見 VOUT_MODE = 17h → N = −9 → 12 V = 1800h。
  • 電壓輸出相關命令用 LINEAR16,電流、溫度等其他命令用 LINEAR11,不要混用。
  • 程式裡要先讀 VOUT_MODE 再換算,不要寫死指數。

延伸閱讀: