PMBus 系列約 6 分鐘

PMBus 指令解析:READ_EIN (86h) / READ_EOUT (87h) 能量累加器,計算平均功率與效率

PMBus READ_EIN (86h) 與 READ_EOUT (87h) 完整解析:6 Bytes 的累加器、溢位計數與取樣計數格式,平均功率 = 能量計數差 ÷ 取樣計數差的原理,溢位處理、效率計算與 C 語言整數實作範例。

最後更新:

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

為什麼需要能量累加器

用 READ_PIN / READ_POUT 只能拿到某一瞬間的功率。如果負載變化很快(例如 CPU 負載跳動),主控端每秒讀幾次再平均,中間的變化全部漏掉,結果也會受到匯流排延遲和工作排程影響。

READ_EIN / READ_EOUT 把這件事交給電源內部處理:電源以固定的高速取樣率持續把每一筆功率值加進累加器,同時記錄取樣次數。主控端只要偶爾讀一次,就能算出兩次讀取之間的精確平均功率,不需要自己計時。

命令代碼累加的對象
READ_EIN86h輸入功率(VIN×IINV_{\text{IN}} \times I_{\text{IN}})
READ_EOUT87h輸出功率(VOUT×IOUTV_{\text{OUT}} \times I_{\text{OUT}})

兩個命令的格式完全相同,以下說明同時適用。

READ_PIN 還是 READ_EIN?

用途建議使用原因
面板、儀表板的即時顯示READ_PIN / READ_POUT一次讀取就有數值,反應快
機櫃能耗統計、電費分攤READ_EIN不會漏掉兩次讀取之間的負載變化
資料中心 PUE 計算READ_EIN需要長時間、準確的累積用電量
負載曲線分析、效率監控READ_EIN + READ_EOUT每個區間都能算出精確平均值

通訊協定格式:Block Read

使用 Block Read,Byte Count 固定為 06h:

S | Addr+W | A | 86h/87h | A | Sr | Addr+R | A | 06h | A
  | ACC_L | A | ACC_H | A | ROLL | A | CNT_L | A | CNT_M | A | CNT_H | [A | PEC] | NA | P
Byte欄位長度格式說明
0Byte Count1—固定 06h
1 ~ 2Accumulator2Direct功率取樣值的累加總和,到 7FFFh 後歸零
3Rollover Count1無號數累加器每溢位一次加 1
4 ~ 6Sample Count324 位元無號數累加進去的取樣次數

計算原理

1. 組合完整的能量計數

累加器只有 15 位元有效(正功率的最高位元一定是 0),到 7FFFh 後歸零並讓溢位計數加 1。所以完整的能量計數是:

Ecount=Rollover×32768+AccumulatorE_{\text{count}} = \text{Rollover} \times 32768 + \text{Accumulator}

2. 平均功率:只需要兩個差值

在 t1t_1、t2t_2 各讀一次:

Y‾=Ecount(t2)−Ecount(t1)Nsample(t2)−Nsample(t1)\overline{Y} = \frac{E_{\text{count}}(t_2) - E_{\text{count}}(t_1)}{N_{\text{sample}}(t_2) - N_{\text{sample}}(t_1)}

Y‾\overline{Y} 就是這段期間平均的功率 Direct 碼值,再用 Direct 公式換算成瓦特:

P‾=1m(Y‾×10−R−b)\overline{P} = \frac{1}{m}\left(\overline{Y} \times 10^{-R} - b\right)

3. 需要能量(焦耳)時才要時間

E=P‾×ΔtE = \overline{P} \times \Delta t

Δt\Delta t 可以用 ΔNsample×\Delta N_{\text{sample}} \times 取樣週期(取樣週期見規格書),或用主控端自己量的時間。

4. 平均效率

分別用 READ_EIN 和 READ_EOUT 算出平均輸入、輸出功率,再相除:

η=P‾OUTP‾IN×100%\eta = \frac{\overline{P}_{\text{OUT}}}{\overline{P}_{\text{IN}}} \times 100\%

兩個命令各有自己的取樣計數,所以建議各自算出平均功率再相除,不要直接拿兩個能量差相除。

計算範例

以下假設 READ_POUT 的 Direct 係數為 m = 1、b = 0、R = 0(碼值直接等於瓦特,僅為示範)。

第一次讀取 READ_EOUT:06 E8 03 05 A0 86 01

欄位原始值數值
AccumulatorE8 03 → 03E8h1000
Rollover05h5
Sample CountA0 86 01 → 0186A0h100000
能量計數5×32768+10005 \times 32768 + 1000164840

第二次讀取 READ_EOUT:06 E8 29 22 70 8E 01

欄位原始值數值
AccumulatorE8 29 → 29E8h10728
Rollover22h34
Sample Count70 8E 01 → 018E70h102000
能量計數34×32768+1072834 \times 32768 + 107281124840

平均輸出功率:

P‾OUT=1124840−164840102000−100000=9600002000=480 W\overline{P}_{\text{OUT}} = \frac{1124840 - 164840}{102000 - 100000} = \frac{960000}{2000} = 480\ \text{W}

同一段時間用 READ_EIN 算出 P‾IN=508 W\overline{P}_{\text{IN}} = 508\ \text{W} 的話:

η=480508×100%≈94.5%\eta = \frac{480}{508} \times 100\% \approx 94.5\%

溢位處理

計數器寬度溢位後
能量計數(Rollover × 32768 + Accumulator)8 + 15 = 23 位元超過 223−12^{23} - 1 後歸零
取樣計數24 位元超過 224−12^{24} - 1(16,777,215)後歸零

用無號數減法再取模就能自動處理溢位,不需要判斷大小:

ΔE=(E2−E1) mod 223ΔN=(N2−N1) mod 224\Delta E = (E_2 - E_1) \bmod 2^{23} \qquad \Delta N = (N_2 - N_1) \bmod 2^{24}

部分元件提供擴充版命令(例如 READ_EIN_EXT),回傳更寬的計數器以延長溢位時間;也有元件的溢位點和上面不同。請以產品規格書為準。

C 語言實作

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

typedef struct {
    uint32_t energy;    /* 23 位元:Rollover × 32768 + Accumulator */
    uint32_t samples;   /* 24 位元 */
} energy_reading_t;

/* rx 為 Block Read 讀回的 7 Bytes(含 Byte Count) */
bool parse_energy(const uint8_t rx[7], energy_reading_t *r)
{
    if (rx[0] != 6)
        return false;

    uint32_t acc  = ((uint32_t)rx[1] | (uint32_t)rx[2] << 8) & 0x7FFFu;
    uint32_t roll = rx[3];

    r->energy  = roll * 32768u + acc;
    r->samples = (uint32_t)rx[4] | (uint32_t)rx[5] << 8 | (uint32_t)rx[6] << 16;
    return true;
}

/* 兩次讀取之間的平均 Direct 碼值 × 1000(無號減法 + 遮罩自動處理溢位) */
bool energy_avg_code_milli(const energy_reading_t *a, const energy_reading_t *b,
                           int64_t *avg_milli)
{
    uint32_t de = (b->energy  - a->energy)  & 0x7FFFFFu;   /* mod 2^23 */
    uint32_t dn = (b->samples - a->samples) & 0xFFFFFFu;   /* mod 2^24 */

    if (dn == 0)
        return false;
    *avg_milli = ((int64_t)de * 1000 + dn / 2) / dn;
    return true;
}

energy_avg_code_milli() 回傳的是平均碼值的千倍,最後再套用 Direct 公式 X=(Y×10−R−b)/mX = (Y \times 10^{-R} - b) / m 換算成毫瓦。以上面的範例,結果為 480000,搭配 m = 1、b = 0、R = 0 即為 480 W。

常見錯誤

錯誤結果正確做法
把累加器當 LINEAR11 解碼數值完全錯誤(例如 80F0h 會被解成 N = −16,得到約 0.0037)累加器是 Direct 碼值的總和
漏掉 Rollover Count,把 Byte 3 ~ 5 當取樣計數取樣計數錯位,平均功率完全錯誤6 Bytes = 累加器 2 + 溢位計數 1 + 取樣計數 3
取樣計數用高位元組在前讀取線上收到 84 03 00,誤讀成 840300hPMBus 一律低位元組在前,應為 000384h(900)
把累加器當成焦耳,再除以時間平均功率多除了一次時間平均功率 = 能量計數差 ÷ 取樣計數差
只用累加器、忽略溢位計數累加器幾毫秒就繞一圈,差值不可靠能量計數 = Rollover × 32768 + Accumulator
寫死取樣週期換一款電源就算錯平均功率不需要取樣週期;需要能量時,週期以規格書為準

工程實務提示

結論

  • READ_EIN (86h) / READ_EOUT (87h) 用 Block Read 回傳 6 Bytes:累加器 2 + 溢位計數 1 + 取樣計數 3。
  • 累加器是功率 Direct 碼值的總和,不是焦耳。
  • 平均功率 = 能量計數差 ÷ 取樣計數差,不需要知道時間;需要能量時再乘上時間。
  • 效率 = 平均輸出功率 ÷ 平均輸入功率。
  • 用無號減法 + 遮罩處理溢位,讀取間隔要短於溢位時間。

延伸閱讀: