PMBus 系列約 6 分鐘

PMBus 常見問題除錯:NACK、匯流排卡住、Timeout、數值錯誤一次解決

PMBus / SMBus 除錯實戰:找不到裝置、7 位元與 8 位元位址混淆、特定命令 NACK、讀值錯誤、SDA 被拉住的匯流排解鎖(含 C 語言範例)、SMBus Timeout 與 Clock Stretching、寫入沒反應、SMBALERT# 一直拉低,附除錯流程圖與檢查清單。

除錯流程

flowchart TD
    A["PMBus 通訊有問題"] --> B{"位址有 ACK 嗎?<br>(i2cdetect / 邏輯分析儀)"}
    B -- "沒有" --> C["1. 找不到裝置<br>位址、供電、上拉、Mux"]
    B -- "有" --> D{"所有命令都失敗?"}
    D -- "是" --> E{"SDA 或 SCL<br>一直是低電位?"}
    E -- "是" --> F["4. 匯流排卡住<br>執行 Bus Recovery"]
    E -- "否" --> G["5. Timeout / Clock Stretching"]
    D -- "只有部分命令" --> H["2. 特定命令失敗<br>讀 STATUS_CML"]
    D -- "通訊正常" --> I{"數值不對?"}
    I -- "是" --> J["3. 數值錯誤<br>格式、位元組順序、PAGE"]
    I -- "寫入沒效果" --> K["6. 寫入沒反應<br>寫保護、ON_OFF_CONFIG"]

1. 找不到裝置:位址沒有 ACK

最常見:7 位元與 8 位元位址混淆

規格書和工具對「位址」的寫法不一致,是第一名的問題來源:

寫法範例誰會這樣寫
7 位元位址0x58Linux i2c-tools、smbus2、多數 SMBus 規格
8 位元寫入位址0xB0部分規格書、邏輯分析儀
8 位元讀取位址0xB1部分規格書、邏輯分析儀

規格書寫「位址 B0h」時,Linux 上要用的是 0x58(0xB0 >> 1)。直接把 0xB0 當 7 位元位址,Linux 會回報位址無效(超過 0x7F);反過來,把 0x58 填進要求 8 位元位址的 MCU 函式庫,實際送出的會是 0x2C 的位址,當然沒有回應。

其他原因

檢查項目說明
電源有沒有供電給 PMBus 介面CRPS 這類電源的 PMBus 控制器由待機電源供電,沒插 AC 或待機電源異常時不會回應
位址設定腳位很多電源用 A0 / A1 等腳位(或插槽位置)決定位址,確認實際接法
上拉電阻SDA / SCL 必須有上拉電阻;太大會讓上升邊太慢,太小則拉不下來
電壓準位主控端與電源的 SMBus 電壓要相容,必要時加電位轉換
I2C Mux / Switch伺服器常在 BMC 和電源之間放 I2C 多工器(例如 PCA9548),沒有先選擇正確的通道就看不到電源
匯流排上有其他裝置拉住線路拔掉其他裝置逐一排除

2. 特定命令失敗:位址有 ACK,但命令 NACK 或回傳錯誤

位址回應正常但某些命令失敗時,先讀 STATUS_CML (7Eh),它會告訴你電源為什麼拒絕:

STATUS_CML 位元意義常見原因
b7無效 / 不支援的命令電源沒有實作這個命令,用 QUERY (1Ah) 確認
b6無效 / 不支援的資料寫入值超出範圍、保留位元不為 0、格式錯誤
b5PEC 錯誤PEC 算錯,或線路雜訊,詳見 PEC 解析
b1其他通訊錯誤交易長度不對,例如對 Byte 命令做 Word 讀取

另外幾個常見原因:

  • 交易類型用錯:VOUT_MODE 是 Byte、READ_VOUT 是 Word、MFR_ID 是 Block,查 PMBus 命令總表 確認。
  • 寫入保護:WRITE_PROTECT (10h) 開啟時,大部分寫入會被拒絕。
  • 多路輸出沒選對 PAGE:多輸出電源要先寫 PAGE (00h) 選擇輸出,否則讀寫的是另一路。

3. 數值錯誤:通訊正常,但數字不對

症狀可能原因
電壓大到離譜READ_VOUT 用了 LINEAR11 解碼,應該用 LINEAR16 + VOUT_MODE
溫度、電流出現 2000 多LINEAR11 的尾數沒有做二補數轉換
數值像是亂碼位元組順序讀反,PMBus 一律低位元組在前
電流差好幾倍Direct 格式的電流係數沒有考慮檢流電阻
讀值偶爾跳一下雜訊干擾,啟用 PEC 就能找出錯誤的交易
讀到另一路輸出的值多路輸出電源的 PAGE 沒有設定正確

不確定換算對不對時,可以把原始值貼到 PMBus 數值換算工具 驗證。

4. 匯流排卡住:SDA 一直是低電位

為什麼會卡住

最常見的情況:主控端在交易進行到一半時重置(MCU 重開機、BMC 驅動重新載入)。Slave 正在送資料位元,剛好送出 0 並拉住 SDA,等待下一個 SCL 脈衝,但主控端已經不記得這筆交易了。結果 SDA 一直被拉低,主控端也沒辦法送出 Start。

解法一:送 9 個 SCL 脈衝 + STOP(I2C 通用)

主控端把 SCL / SDA 切成 GPIO,送出最多 9 個時脈,讓 Slave 把剩下的位元送完並釋放 SDA,接著送出 STOP:

/* 依平台實作:開汲極 GPIO 操作與延遲 */
void scl_release(void);   /* SCL 輸出高阻抗(由上拉拉高) */
void scl_drive_low(void);
void sda_release(void);
void sda_drive_low(void);
int  sda_read(void);
void delay_us(unsigned us);

/* I2C / SMBus Bus Recovery:成功回傳 0,SDA 仍被拉住回傳 -1 */
int i2c_bus_recover(void)
{
    sda_release();
    for (int i = 0; i < 9 && !sda_read(); i++) {
        scl_drive_low();
        delay_us(5);
        scl_release();
        delay_us(5);
    }
    if (!sda_read())
        return -1;

    /* STOP:SCL 為高時,SDA 由低變高 */
    scl_drive_low();
    delay_us(5);
    sda_drive_low();
    delay_us(5);
    scl_release();
    delay_us(5);
    sda_release();
    delay_us(5);
    return 0;
}

9 個脈衝的原因:Slave 最多卡在一個位元組的 8 個資料位元 + 1 個 ACK 之中,9 個時脈一定能讓它走完。

解法二:利用 SMBus Timeout

SMBus / PMBus 裝置有一個 I2C 沒有的特性:SCL 低電位超過 Timeout(約 25 ~ 35 ms)時,裝置必須重置自己的通訊介面。所以主控端也可以把 SCL 拉低 35 ms 以上再放開,讓所有 SMBus 裝置釋放匯流排。

Linux 上,許多 I2C 控制器驅動內建 Bus Recovery,偵測到匯流排卡住時會自動嘗試。

5. Timeout 與 Clock Stretching

主控端停頓太久,Slave 自己放棄

SMBus 的 Timeout 是雙面刃:它能防止匯流排永久卡住,但主控端在交易中途停頓超過 25 ~ 35 ms,PMBus 裝置就會放棄這筆交易。後續讀到的資料可能錯位,或直接 NACK。

常見原因與對策:

原因對策
用 GPIO 軟體模擬 I2C,中途被中斷打斷改用硬體 I2C,或在交易期間關閉中斷
作業系統排程延遲使用核心 I2C 驅動,避免在使用者空間逐位元控制
除錯時在交易中途下中斷點中斷點設在交易完成之後

Clock Stretching 不相容

Slave 準備資料需要時間時,可以拉住 SCL 讓主控端等待(Clock Stretching)。電源的 MCU 在讀取 ADC、寫入 NVM 時常常這樣做。

  • 主控端不支援 Clock Stretching 時,會在 Slave 還沒準備好時繼續送時脈,讀到錯誤資料。已知 Raspberry Pi 的硬體 I2C 對 Clock Stretching 處理有問題,可以降低時脈或改用 i2c-gpio 軟體 I2C。
  • 時脈太快:用 CAPABILITY (19h) 確認電源支援的最高速度,不確定時先用 100 kHz。

6. 寫入沒反應

症狀原因
寫 OPERATION 不會開關機ON_OFF_CONFIG 的 CMD = 0,電源只聽 CONTROL 腳位
所有寫入都被拒絕WRITE_PROTECT (10h) 開啟
電壓設定沒有變超過 VOUT_MAX,被限制在上限;或電源不開放調整
設定在重新上電後消失只寫入 RAM,沒有用 STORE_USER_ALL (15h) 等命令存入 NVM
連續寫入後面幾筆失敗前一個命令還在處理(例如寫入 NVM),STATUS_WORD 的 BUSY 位元為 1,要等待後再送

7. SMBALERT# 一直是低電位

原因處理方式
故障還存在清除後會立刻重新拉低,先排除故障,詳見 CLEAR_FAULTS
多個裝置共用 SMBALERT#用 ARA(0x0C)找出是哪一個裝置發出的
只讀 STATUS 沒有清除讀 STATUS 不會釋放 SMBALERT#,要送 CLEAR_FAULTS
預期中的警告一直觸發用 SMBALERT_MASK (1Bh) 屏蔽

除錯工具

工具用途
邏輯分析儀看實際波形:位址、ACK / NACK、時序、Clock Stretching。許多軟體提供 I2C / SMBus 解碼
示波器看上升邊、雜訊、電壓準位,判斷上拉電阻是否合適
i2c-tools快速讀寫單一命令,排除主控端程式的問題,見 Python / Linux 實作
PMBus 數值換算工具驗證 LINEAR11 / LINEAR16 / Direct 換算、PEC、STATUS_WORD 解碼

檢查清單

遇到問題時依序確認:

  1. ☐ 位址是 7 位元(0x58),不是 8 位元(0xB0)
  2. ☐ 電源的 PMBus 介面有供電(待機電源)
  3. ☐ SDA / SCL 有上拉,電壓準位相容
  4. ☐ I2C Mux 已切到正確通道
  5. ☐ 時脈不超過電源支援的速度(先用 100 kHz)
  6. ☐ 命令的交易類型正確(Byte / Word / Block)
  7. ☐ 讀過 STATUS_CML,確認沒有命令或 PEC 錯誤
  8. ☐ 數值格式正確(LINEAR11 / LINEAR16 / Direct),位元組低位在前
  9. ☐ 多路輸出電源已設定 PAGE
  10. ☐ 寫入後有讀回確認

延伸閱讀: