PMBus 指令解析:READ_EIN (86h) / READ_EOUT (87h) 能量累加器,計算平均功率與效率
PMBus READ_EIN (86h) 與 READ_EOUT (87h) 完整解析:6 Bytes 的累加器、溢位計數與取樣計數格式,平均功率 = 能量計數差 ÷ 取樣計數差的原理,溢位處理、效率計算與 C 語言整數實作範例。
最後更新:
為什麼需要能量累加器
用 READ_PIN / READ_POUT 只能拿到某一瞬間的功率。如果負載變化很快(例如 CPU 負載跳動),主控端每秒讀幾次再平均,中間的變化全部漏掉,結果也會受到匯流排延遲和工作排程影響。
READ_EIN / READ_EOUT 把這件事交給電源內部處理:電源以固定的高速取樣率持續把每一筆功率值加進累加器,同時記錄取樣次數。主控端只要偶爾讀一次,就能算出兩次讀取之間的精確平均功率,不需要自己計時。
| 命令 | 代碼 | 累加的對象 |
|---|---|---|
READ_EIN | 86h | 輸入功率() |
READ_EOUT | 87h | 輸出功率() |
兩個命令的格式完全相同,以下說明同時適用。
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 | 欄位 | 長度 | 格式 | 說明 |
|---|---|---|---|---|
| 0 | Byte Count | 1 | — | 固定 06h |
| 1 ~ 2 | Accumulator | 2 | Direct | 功率取樣值的累加總和,到 7FFFh 後歸零 |
| 3 | Rollover Count | 1 | 無號數 | 累加器每溢位一次加 1 |
| 4 ~ 6 | Sample Count | 3 | 24 位元無號數 | 累加進去的取樣次數 |
計算原理
1. 組合完整的能量計數
累加器只有 15 位元有效(正功率的最高位元一定是 0),到 7FFFh 後歸零並讓溢位計數加 1。所以完整的能量計數是:
2. 平均功率:只需要兩個差值
在 、 各讀一次:
就是這段期間平均的功率 Direct 碼值,再用 Direct 公式換算成瓦特:
3. 需要能量(焦耳)時才要時間
可以用 取樣週期(取樣週期見規格書),或用主控端自己量的時間。
4. 平均效率
分別用 READ_EIN 和 READ_EOUT 算出平均輸入、輸出功率,再相除:
兩個命令各有自己的取樣計數,所以建議各自算出平均功率再相除,不要直接拿兩個能量差相除。
計算範例
以下假設 READ_POUT 的 Direct 係數為 m = 1、b = 0、R = 0(碼值直接等於瓦特,僅為示範)。
第一次讀取 READ_EOUT:06 E8 03 05 A0 86 01
| 欄位 | 原始值 | 數值 |
|---|---|---|
| Accumulator | E8 03 → 03E8h | 1000 |
| Rollover | 05h | 5 |
| Sample Count | A0 86 01 → 0186A0h | 100000 |
| 能量計數 | 164840 |
第二次讀取 READ_EOUT:06 E8 29 22 70 8E 01
| 欄位 | 原始值 | 數值 |
|---|---|---|
| Accumulator | E8 29 → 29E8h | 10728 |
| Rollover | 22h | 34 |
| Sample Count | 70 8E 01 → 018E70h | 102000 |
| 能量計數 | 1124840 |
平均輸出功率:
同一段時間用 READ_EIN 算出 的話:
溢位處理
| 計數器 | 寬度 | 溢位後 |
|---|---|---|
| 能量計數(Rollover × 32768 + Accumulator) | 8 + 15 = 23 位元 | 超過 後歸零 |
| 取樣計數 | 24 位元 | 超過 (16,777,215)後歸零 |
用無號數減法再取模就能自動處理溢位,不需要判斷大小:
部分元件提供擴充版命令(例如 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 公式 換算成毫瓦。以上面的範例,結果為 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,誤讀成 840300h | PMBus 一律低位元組在前,應為 000384h(900) |
| 把累加器當成焦耳,再除以時間 | 平均功率多除了一次時間 | 平均功率 = 能量計數差 ÷ 取樣計數差 |
| 只用累加器、忽略溢位計數 | 累加器幾毫秒就繞一圈,差值不可靠 | 能量計數 = Rollover × 32768 + Accumulator |
| 寫死取樣週期 | 換一款電源就算錯 | 平均功率不需要取樣週期;需要能量時,週期以規格書為準 |
工程實務提示
結論
READ_EIN (86h)/READ_EOUT (87h)用 Block Read 回傳 6 Bytes:累加器 2 + 溢位計數 1 + 取樣計數 3。- 累加器是功率 Direct 碼值的總和,不是焦耳。
- 平均功率 = 能量計數差 ÷ 取樣計數差,不需要知道時間;需要能量時再乘上時間。
- 效率 = 平均輸出功率 ÷ 平均輸入功率。
- 用無號減法 + 遮罩處理溢位,讀取間隔要短於溢位時間。
延伸閱讀: