跳转至

SMBus协议详解

概述

SMBus(System Management Bus,系统管理总线)是一种基于I2C的双线通信协议,专门为系统管理和电源管理应用而设计。SMBus由Intel在1995年提出,现已成为PC和嵌入式系统中电源管理、电池管理、温度监控等应用的标准协议。

SMBus在I2C的基础上增加了更严格的电气规范、超时机制、包错误检查(PEC)等特性,使其更适合关键系统管理任务。

完成本教程学习后,你将能够:

  • 理解SMBus协议规范和设计理念
  • 掌握SMBus与I2C的关键区别
  • 实现SMBus命令协议(Quick Command、Send/Receive Byte、Read/Write Word等)
  • 掌握PEC(Packet Error Checking)的计算和验证
  • 实现SMBus超时检测和错误恢复机制
  • 开发SMBus主机和从机设备驱动
  • 应用SMBus于电池管理、温度监控等实际场景

背景知识

SMBus的诞生背景

为什么需要SMBus

  1. 系统管理需求
  2. PC系统需要监控电源、温度、风扇等
  3. 需要可靠的低速通信协议
  4. I2C虽然简单,但缺乏系统管理所需的特性

  5. 可靠性要求

  6. 系统管理涉及关键功能(电源、温度)
  7. 需要错误检测机制
  8. 需要超时保护避免总线挂死

  9. 互操作性

  10. 不同厂商的设备需要互通
  11. 需要标准化的命令集
  12. 需要明确的电气规范

  13. 成本考虑

  14. 基于成熟的I2C技术
  15. 无需额外硬件
  16. 易于实现和部署

SMBus应用领域

典型应用场景

应用领域 具体应用 典型设备
电源管理 电源监控、电压调节 PMBus电源、DC-DC转换器
电池管理 智能电池、充电控制 Smart Battery、充电器
温度监控 系统温度、风扇控制 温度传感器、风扇控制器
内存管理 SPD读取、内存配置 DIMM模块、内存控制器
显示管理 DDC/CI通信 显示器、显卡
系统监控 硬件监控、故障检测 监控芯片、BMC

SMBus在PC系统中的应用

CPU
 ├─ 内存控制器 ──→ DIMM (SPD via SMBus)
 ├─ 南桥芯片
 │   │
 │   ├─ SMBus控制器
 │   │   │
 │   │   ├─ 温度传感器 (0x48)
 │   │   ├─ 风扇控制器 (0x2E)
 │   │   ├─ 电源管理IC (0x10)
 │   │   ├─ 智能电池 (0x0B)
 │   │   └─ EEPROM (0x50)
 │   │
 │   └─ 其他外设
 └─ 显卡 ──→ 显示器 (DDC/CI via SMBus)

特点:
- 所有系统管理设备通过SMBus连接
- 操作系统可以监控和控制硬件
- 支持热插拔和动态配置

SMBus版本演进

SMBus规范版本

  1. SMBus 1.0 (1995)
  2. 初始版本
  3. 定义基本协议和命令
  4. 时钟频率10-100kHz

  5. SMBus 1.1 (1998)

  6. 增加PEC支持
  7. 改进超时规范
  8. 增加ARP(Address Resolution Protocol)

  9. SMBus 2.0 (2000)

  10. 支持更高速度(最高100kHz)
  11. 改进电气规范
  12. 增加新命令

  13. SMBus 3.0 (2014)

  14. 支持更高速度(最高1MHz)
  15. 增加快速模式+
  16. 改进PEC机制
  17. 向后兼容旧版本

SMBus与I2C的区别

协议层面的区别

核心差异对比

特性 I2C SMBus 说明
时钟频率 100kHz/400kHz/3.4MHz 10-100kHz (2.0), 最高1MHz (3.0) SMBus更保守
超时机制 必须(25-35ms) SMBus强制超时
最小时钟频率 无限制 10kHz 防止时钟拉伸过长
PEC支持 可选 SMBus增加错误检测
逻辑电平 0.3VDD/0.7VDD 固定阈值 SMBus更严格
上升时间 <1000ns <1000ns 相同
下降时间 <300ns <300ns 相同
地址范围 0x00-0x7F 0x08-0x77 SMBus保留部分地址
时钟拉伸 允许 有限制(25ms) SMBus限制拉伸时间

电气特性区别

电压电平规范

I2C电平(相对于VDD):
- VIL (输入低电平): < 0.3 × VDD
- VIH (输入高电平): > 0.7 × VDD
- VOL (输出低电平): < 0.4V @ 3mA

SMBus电平(固定阈值):
- VIL (输入低电平): < 0.8V
- VIH (输入高电平): > 2.1V
- VOL (输出低电平): < 0.4V @ 4mA

关键区别:
1. SMBus使用固定电压阈值,不依赖VDD
2. SMBus输出驱动能力更强(4mA vs 3mA)
3. SMBus更适合3.3V和5V混合系统

上拉电阻要求

I2C上拉电阻:
- 范围:1kΩ - 10kΩ
- 根据总线电容和速度计算
- 灵活性高

SMBus上拉电阻:
- 推荐:10kΩ - 100kΩ
- 通常使用较大值(降低功耗)
- 适合低速应用

计算公式:
Rp(min) = (VDD - 0.4V) / 0.004A = (VDD - 0.4) / 0.004
Rp(max) = tr / (0.8473 × Cb)

示例(VDD=3.3V, Cb=100pF, tr=1000ns):
Rp(min) = (3.3 - 0.4) / 0.004 = 725Ω
Rp(max) = 1000ns / (0.8473 × 100pF) = 11.8kΩ
推荐值:4.7kΩ

超时机制

SMBus超时规范

超时类型:

1. 时钟低电平超时(TLOW:SEXT):
   - 最小值:25ms
   - 典型值:25-35ms
   - 触发条件:SCL被拉低超过此时间
   - 作用:防止从机时钟拉伸过长

2. 时钟高电平超时(THIGH:MAX):
   - 最大值:50μs(SMBus 2.0)
   - 作用:限制时钟高电平时间

3. 累积时钟低电平超时:
   - 最大值:25-35ms
   - 包括所有时钟拉伸的累积时间
   - 超时后必须复位总线

超时处理流程:
START → 传输数据 → 时钟拉伸 → 超时检测 → 复位总线

示例:
主机发送数据 → 从机拉低SCL(处理数据)→ 
如果超过25ms → 主机检测超时 → 
主机发送STOP → 复位总线 → 重试或报错

超时实现方法

// 软件超时检测
#define SMBUS_TIMEOUT_MS  30

bool SMBus_WaitSCL_High(void) {
    uint32_t start_time = HAL_GetTick();

    while (GPIO_ReadPin(SCL_PORT, SCL_PIN) == 0) {
        if (HAL_GetTick() - start_time > SMBUS_TIMEOUT_MS) {
            // 超时,复位总线
            SMBus_BusReset();
            return false;
        }
    }

    return true;
}

// 硬件超时(使用定时器)
void SMBus_StartTimeout(void) {
    // 启动25ms定时器
    TIM_SetCounter(TIM2, 0);
    TIM_Cmd(TIM2, ENABLE);
}

void SMBus_StopTimeout(void) {
    TIM_Cmd(TIM2, DISABLE);
}

void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update)) {
        // 超时中断
        SMBus_TimeoutHandler();
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

地址保留

SMBus保留地址

地址范围:0x00 - 0x7F (7位地址)

保留地址:
0x00      : 通用调用地址(General Call)
0x01      : CBUS地址
0x02      : 保留用于不同总线格式
0x03      : 保留用于未来使用
0x04-0x07 : Hs-mode主机代码
0x08-0x77 : 可用设备地址(SMBus设备使用此范围)
0x78-0x7B : 10位地址
0x7C-0x7F : 保留

I2C vs SMBus地址使用:
I2C: 0x00-0x7F 都可用(除特殊地址)
SMBus: 0x08-0x77 可用(更严格)

注意:
- SMBus设备不应使用0x00-0x07
- 避免使用0x78-0x7F
- 推荐使用0x10-0x6F范围

兼容性分析

I2C设备在SMBus总线上

兼容性矩阵:

特性                I2C设备  SMBus总线  兼容性
电气特性            ✓        ✓         通常兼容
时钟频率            ✓        ✓         需要≤100kHz
超时机制            ✗        ✓         I2C设备可能不支持
PEC                 ✗        可选       I2C设备忽略PEC
地址范围            0x00-0x7F 0x08-0x77 需要检查地址

兼容性建议:
1. I2C设备可以在SMBus总线上工作
2. 但I2C设备不支持超时和PEC
3. 主机需要处理I2C设备的特殊性
4. 建议混合系统使用I2C模式

SMBus设备在I2C总线上

兼容性考虑:

1. 电气兼容性:
   - SMBus设备通常可以在I2C总线上工作
   - 需要确认电压电平兼容

2. 超时问题:
   - SMBus设备期望主机支持超时
   - I2C主机可能不支持
   - 可能导致通信失败

3. PEC问题:
   - SMBus设备可能要求PEC
   - I2C主机不支持PEC
   - 需要禁用PEC或使用兼容模式

4. 时钟拉伸:
   - SMBus设备可能长时间拉伸时钟
   - I2C主机需要支持

建议:
- 使用SMBus兼容的I2C控制器
- 或在软件层实现SMBus特性

SMBus协议规范

协议层次结构

SMBus协议栈

┌─────────────────────────────────┐
│      应用层(Application)       │
│  - 设备特定命令                  │
│  - 数据解释                      │
├─────────────────────────────────┤
│      命令层(Command)           │
│  - Quick Command                │
│  - Send/Receive Byte            │
│  - Read/Write Byte/Word         │
│  - Block Read/Write             │
│  - Process Call                 │
├─────────────────────────────────┤
│      传输层(Transaction)       │
│  - START/STOP条件               │
│  - 地址传输                      │
│  - 数据传输                      │
│  - ACK/NACK                     │
│  - PEC校验                       │
├─────────────────────────────────┤
│      物理层(Physical)          │
│  - 电气特性                      │
│  - 时序要求                      │
│  - 总线仲裁                      │
└─────────────────────────────────┘

基本传输格式

SMBus传输结构

基本格式:
START → 地址+R/W → ACK → 数据 → ACK → ... → STOP

详细格式:
S  : START条件
Sr : 重复START条件
P  : STOP条件
A  : ACK(确认)
N  : NACK(非确认)
Wr : 写位(0)
Rd : 读位(1)

示例1:写单字节
S → [Addr+Wr] → A → [Data] → A → P

示例2:读单字节
S → [Addr+Rd] → A → [Data] → N → P

示例3:写寄存器
S → [Addr+Wr] → A → [Cmd] → A → [Data] → A → P

示例4:读寄存器
S → [Addr+Wr] → A → [Cmd] → A → 
Sr → [Addr+Rd] → A → [Data] → N → P

SMBus命令协议

1. Quick Command(快速命令)

用途:
- 简单的设备开关控制
- 设备存在检测
- 无数据传输

格式:
S → [Addr+Wr/Rd] → A → P

特点:
- 最简单的SMBus命令
- 只传输地址和读写位
- 通过R/W位传递1位信息

应用示例:
- 开关设备电源
- 设备响应测试

代码实现:
```c
// Quick Command实现
bool SMBus_QuickCommand(uint8_t addr, bool read) {
    // 发送START
    SMBus_Start();

    // 发送地址+R/W位
    if (!SMBus_SendByte((addr << 1) | (read ? 1 : 0))) {
        SMBus_Stop();
        return false;
    }

    // 发送STOP
    SMBus_Stop();

    return true;
}

// 使用示例:设备存在检测
bool SMBus_DevicePresent(uint8_t addr) {
    return SMBus_QuickCommand(addr, false);
}

2. Send Byte(发送字节)

用途:
- 发送单个字节到设备
- 简单的命令或数据传输

格式:
S → [Addr+Wr] → A → [Data] → A → P

特点:
- 传输1字节数据
- 无命令码
- 适合简单设备

应用示例:
- 设置设备模式
- 发送控制命令

代码实现:
```c
// Send Byte实现
bool SMBus_SendByte(uint8_t addr, uint8_t data) {
    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据
    if (!SMBus_WriteByte(data)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

// 使用示例:设置设备模式
void SetDeviceMode(uint8_t addr, uint8_t mode) {
    SMBus_SendByte(addr, mode);
}

3. Receive Byte(接收字节)

用途:
- 从设备读取单个字节
- 读取状态或简单数据

格式:
S → [Addr+Rd] → A → [Data] → N → P

特点:
- 读取1字节数据
- 无命令码
- 主机发送NACK结束

应用示例:
- 读取设备状态
- 读取传感器数据

代码实现:
```c
// Receive Byte实现
bool SMBus_ReceiveByte(uint8_t addr, uint8_t *data) {
    SMBus_Start();

    // 发送地址+读位
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取数据(发送NACK)
    *data = SMBus_ReadByte(false);

    SMBus_Stop();
    return true;
}

// 使用示例:读取温度
uint8_t ReadTemperature(uint8_t addr) {
    uint8_t temp;
    if (SMBus_ReceiveByte(addr, &temp)) {
        return temp;
    }
    return 0xFF;  // 错误值
}

4. Write Byte(写字节)

用途:
- 写入数据到指定寄存器
- 最常用的SMBus命令

格式:
S → [Addr+Wr] → A → [Cmd] → A → [Data] → A → P

特点:
- 包含命令码(寄存器地址)
- 写入1字节数据
- 支持寄存器访问

应用示例:
- 配置设备寄存器
- 写入控制参数

代码实现:
```c
// Write Byte实现
bool SMBus_WriteByte(uint8_t addr, uint8_t cmd, uint8_t data) {
    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据
    if (!SMBus_WriteByte(data)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

// 使用示例:配置传感器
void ConfigureSensor(uint8_t addr) {
    SMBus_WriteByte(addr, 0x01, 0x60);  // 配置寄存器
    SMBus_WriteByte(addr, 0x02, 0x80);  // 控制寄存器
}

5. Read Byte(读字节)

用途:
- 从指定寄存器读取数据
- 最常用的SMBus读命令

格式:
S → [Addr+Wr] → A → [Cmd] → A → 
Sr → [Addr+Rd] → A → [Data] → N → P

特点:
- 包含命令码
- 使用重复START
- 读取1字节数据

应用示例:
- 读取寄存器值
- 读取传感器数据

代码实现:
```c
// Read Byte实现
bool SMBus_ReadByte(uint8_t addr, uint8_t cmd, uint8_t *data) {
    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读位
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取数据
    *data = SMBus_ReadByte(false);  // NACK

    SMBus_Stop();
    return true;
}

// 使用示例:读取状态寄存器
uint8_t ReadStatus(uint8_t addr) {
    uint8_t status;
    if (SMBus_ReadByte(addr, 0x00, &status)) {
        return status;
    }
    return 0xFF;
}

6. Write Word(写字)

用途:
- 写入16位数据到寄存器
- 传输双字节数据

格式:
S → [Addr+Wr] → A → [Cmd] → A → [DataLow] → A → [DataHigh] → A → P

特点:
- 传输2字节数据
- 低字节在前(LSB first)
- 适合16位寄存器

应用示例:
- 设置16位配置值
- 写入阈值参数

代码实现:
```c
// Write Word实现
bool SMBus_WriteWord(uint8_t addr, uint8_t cmd, uint16_t data) {
    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送低字节
    if (!SMBus_WriteByte(data & 0xFF)) {
        SMBus_Stop();
        return false;
    }

    // 发送高字节
    if (!SMBus_WriteByte((data >> 8) & 0xFF)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

// 使用示例:设置温度阈值
void SetTemperatureThreshold(uint8_t addr, int16_t threshold) {
    // 温度值通常是16位有符号数
    SMBus_WriteWord(addr, 0x05, (uint16_t)threshold);
}

7. Read Word(读字)

用途:
- 从寄存器读取16位数据
- 读取双字节值

格式:
S → [Addr+Wr] → A → [Cmd] → A → 
Sr → [Addr+Rd] → A → [DataLow] → A → [DataHigh] → N → P

特点:
- 读取2字节数据
- 低字节在前
- 最后一个字节NACK

应用示例:
- 读取16位传感器数据
- 读取配置值

代码实现:
```c
// Read Word实现
bool SMBus_ReadWord(uint8_t addr, uint8_t cmd, uint16_t *data) {
    uint8_t low, high;

    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读位
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取低字节(ACK)
    low = SMBus_ReadByte(true);

    // 读取高字节(NACK)
    high = SMBus_ReadByte(false);

    SMBus_Stop();

    // 组合数据
    *data = (high << 8) | low;

    return true;
}

// 使用示例:读取电压值
float ReadVoltage(uint8_t addr) {
    uint16_t raw_value;
    if (SMBus_ReadWord(addr, 0x09, &raw_value)) {
        // 转换为实际电压(假设LSB=1mV)
        return raw_value / 1000.0;
    }
    return 0.0;
}

8. Process Call(处理调用)

用途:
- 发送16位数据,接收16位结果
- 类似函数调用
- 用于计算或转换

格式:
S → [Addr+Wr] → A → [Cmd] → A → [DataLow] → A → [DataHigh] → A → 
Sr → [Addr+Rd] → A → [ResultLow] → A → [ResultHigh] → N → P

特点:
- 写入2字节,读取2字节
- 单次事务完成
- 适合需要处理的操作

应用示例:
- 温度转换
- 数据校准
- 计算操作

代码实现:
```c
// Process Call实现
bool SMBus_ProcessCall(uint8_t addr, uint8_t cmd, 
                       uint16_t data_in, uint16_t *data_out) {
    uint8_t low, high;

    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送输入数据(低字节)
    if (!SMBus_WriteByte(data_in & 0xFF)) {
        SMBus_Stop();
        return false;
    }

    // 发送输入数据(高字节)
    if (!SMBus_WriteByte((data_in >> 8) & 0xFF)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读位
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取结果(低字节,ACK)
    low = SMBus_ReadByte(true);

    // 读取结果(高字节,NACK)
    high = SMBus_ReadByte(false);

    SMBus_Stop();

    // 组合结果
    *data_out = (high << 8) | low;

    return true;
}

// 使用示例:温度校准
int16_t CalibrateTemperature(uint8_t addr, int16_t raw_temp) {
    uint16_t calibrated;
    if (SMBus_ProcessCall(addr, 0x10, (uint16_t)raw_temp, &calibrated)) {
        return (int16_t)calibrated;
    }
    return raw_temp;  // 校准失败,返回原始值
}

9. Block Write(块写入)

用途:
- 写入多字节数据块
- 传输大量数据

格式:
S → [Addr+Wr] → A → [Cmd] → A → [Count] → A → 
[Data1] → A → [Data2] → A → ... → [DataN] → A → P

特点:
- 可传输1-32字节
- 包含字节计数
- 适合批量写入

应用示例:
- 写入配置数据
- 固件更新
- 批量参数设置

代码实现:
```c
// Block Write实现
bool SMBus_BlockWrite(uint8_t addr, uint8_t cmd, 
                      uint8_t *data, uint8_t count) {
    // 检查数据长度
    if (count == 0 || count > 32) {
        return false;
    }

    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送字节计数
    if (!SMBus_WriteByte(count)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据块
    for (uint8_t i = 0; i < count; i++) {
        if (!SMBus_WriteByte(data[i])) {
            SMBus_Stop();
            return false;
        }
    }

    SMBus_Stop();
    return true;
}

// 使用示例:写入配置数据
void WriteConfiguration(uint8_t addr) {
    uint8_t config[] = {
        0x01, 0x02, 0x03, 0x04,  // 配置参数
        0x05, 0x06, 0x07, 0x08
    };

    SMBus_BlockWrite(addr, 0x20, config, sizeof(config));
}

10. Block Read(块读取)

用途:
- 读取多字节数据块
- 接收大量数据

格式:
S → [Addr+Wr] → A → [Cmd] → A → 
Sr → [Addr+Rd] → A → [Count] → A → 
[Data1] → A → [Data2] → A → ... → [DataN] → N → P

特点:
- 可读取1-32字节
- 从机返回字节计数
- 最后一个字节NACK

应用示例:
- 读取传感器数据
- 读取设备信息
- 批量数据采集

代码实现:
```c
// Block Read实现
bool SMBus_BlockRead(uint8_t addr, uint8_t cmd, 
                     uint8_t *data, uint8_t *count) {
    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读位
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取字节计数
    *count = SMBus_ReadByte(true);  // ACK

    // 检查计数有效性
    if (*count == 0 || *count > 32) {
        SMBus_Stop();
        return false;
    }

    // 读取数据块
    for (uint8_t i = 0; i < *count; i++) {
        // 最后一个字节发送NACK
        bool ack = (i < *count - 1);
        data[i] = SMBus_ReadByte(ack);
    }

    SMBus_Stop();
    return true;
}

// 使用示例:读取设备信息
void ReadDeviceInfo(uint8_t addr) {
    uint8_t info[32];
    uint8_t count;

    if (SMBus_BlockRead(addr, 0x30, info, &count)) {
        printf("Device Info (%d bytes):\n", count);
        for (uint8_t i = 0; i < count; i++) {
            printf("%02X ", info[i]);
        }
        printf("\n");
    }
}

11. Block Write-Block Read Process Call(块处理调用)

用途:
- 发送数据块,接收数据块
- 复杂的数据处理
- 批量转换操作

格式:
S → [Addr+Wr] → A → [Cmd] → A → [WriteCount] → A → 
[WriteData1] → A → ... → [WriteDataN] → A → 
Sr → [Addr+Rd] → A → [ReadCount] → A → 
[ReadData1] → A → ... → [ReadDataM] → N → P

特点:
- 写入和读取数据块
- 单次事务完成
- 最复杂的SMBus命令

应用示例:
- 批量数据转换
- 加密/解密操作
- 复杂计算

代码实现:
```c
// Block Write-Block Read Process Call实现
bool SMBus_BlockProcessCall(uint8_t addr, uint8_t cmd,
                            uint8_t *write_data, uint8_t write_count,
                            uint8_t *read_data, uint8_t *read_count) {
    // 检查写入数据长度
    if (write_count == 0 || write_count > 32) {
        return false;
    }

    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送写入字节计数
    if (!SMBus_WriteByte(write_count)) {
        SMBus_Stop();
        return false;
    }

    // 发送写入数据
    for (uint8_t i = 0; i < write_count; i++) {
        if (!SMBus_WriteByte(write_data[i])) {
            SMBus_Stop();
            return false;
        }
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读位
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取字节计数
    *read_count = SMBus_ReadByte(true);

    // 检查计数有效性
    if (*read_count == 0 || *read_count > 32) {
        SMBus_Stop();
        return false;
    }

    // 读取数据块
    for (uint8_t i = 0; i < *read_count; i++) {
        bool ack = (i < *read_count - 1);
        read_data[i] = SMBus_ReadByte(ack);
    }

    SMBus_Stop();
    return true;
}

// 使用示例:批量数据转换
void ConvertSensorData(uint8_t addr) {
    uint8_t raw_data[] = {0x12, 0x34, 0x56, 0x78};
    uint8_t converted_data[32];
    uint8_t result_count;

    if (SMBus_BlockProcessCall(addr, 0x40, 
                               raw_data, sizeof(raw_data),
                               converted_data, &result_count)) {
        printf("Converted %d bytes\n", result_count);
    }
}

SMBus命令总结

命令对比表

命令 写入字节数 读取字节数 用途 复杂度
Quick Command 0 0 设备控制/检测 ★☆☆☆☆
Send Byte 1 0 发送命令 ★☆☆☆☆
Receive Byte 0 1 读取状态 ★☆☆☆☆
Write Byte 2 0 写寄存器 ★★☆☆☆
Read Byte 1 1 读寄存器 ★★☆☆☆
Write Word 3 0 写16位数据 ★★☆☆☆
Read Word 1 2 读16位数据 ★★☆☆☆
Process Call 3 2 数据处理 ★★★☆☆
Block Write 2-33 0 批量写入 ★★★★☆
Block Read 1 1-32 批量读取 ★★★★☆
Block Process Call 2-33 1-32 批量处理 ★★★★★

包错误检查(PEC)

PEC概述

什么是PEC

PEC (Packet Error Checking) 是SMBus的可选错误检测机制:

特点:
- 基于CRC-8算法
- 多项式:x^8 + x^2 + x + 1 (0x07)
- 初始值:0x00
- 检测单字节和多字节错误
- 可选功能(设备可以不支持)

优势:
- 提高通信可靠性
- 检测传输错误
- 适合关键应用

劣势:
- 增加1字节开销
- 增加计算复杂度
- 不是所有设备都支持

PEC计算算法

CRC-8计算原理

多项式:P(x) = x^8 + x^2 + x + 1
二进制:100000111
十六进制:0x07

计算步骤:
1. 初始化CRC = 0x00
2. 对每个字节:
   a. CRC = CRC XOR 字节
   b. 对8位循环:
      - 如果CRC最高位为1:
        CRC = (CRC << 1) XOR 0x07
      - 否则:
        CRC = CRC << 1
3. 最终CRC值即为PEC

示例计算:
数据:0x5A
初始CRC:0x00

步骤1:CRC = 0x00 XOR 0x5A = 0x5A (01011010)
步骤2:处理8位
  位7(1): CRC = (0x5A << 1) XOR 0x07 = 0xB4 XOR 0x07 = 0xB3
  位6(0): CRC = 0xB3 << 1 = 0x66
  位5(1): CRC = (0x66 << 1) XOR 0x07 = 0xCC XOR 0x07 = 0xCB
  位4(1): CRC = (0xCB << 1) XOR 0x07 = 0x96 XOR 0x07 = 0x91
  位3(0): CRC = 0x91 << 1 = 0x22
  位2(1): CRC = (0x22 << 1) XOR 0x07 = 0x44 XOR 0x07 = 0x43
  位1(0): CRC = 0x43 << 1 = 0x86
  位0(1): CRC = (0x86 << 1) XOR 0x07 = 0x0C XOR 0x07 = 0x0B

结果:PEC = 0x0B

软件实现

// CRC-8查找表(优化性能)
static const uint8_t crc8_table[256] = {
    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
    0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
    0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
    0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
    0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
    0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
    0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
    0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
    0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
    0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
    0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
    0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
    0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
    0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
    0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
    0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
    0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
    0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
    0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
    0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
    0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
    0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
    0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
    0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
    0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
    0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
    0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};

// 使用查找表计算CRC-8
uint8_t SMBus_CalculatePEC_Table(uint8_t *data, uint8_t len) {
    uint8_t crc = 0x00;

    for (uint8_t i = 0; i < len; i++) {
        crc = crc8_table[crc ^ data[i]];
    }

    return crc;
}

// 不使用查找表的实现(节省内存)
uint8_t SMBus_CalculatePEC(uint8_t *data, uint8_t len) {
    uint8_t crc = 0x00;

    for (uint8_t i = 0; i < len; i++) {
        crc ^= data[i];

        for (uint8_t bit = 0; bit < 8; bit++) {
            if (crc & 0x80) {
                crc = (crc << 1) ^ 0x07;
            } else {
                crc = crc << 1;
            }
        }
    }

    return crc;
}

// 增量计算PEC(逐字节更新)
uint8_t SMBus_UpdatePEC(uint8_t crc, uint8_t data) {
    return crc8_table[crc ^ data];
}

PEC在SMBus命令中的应用

带PEC的命令格式

Write Byte with PEC:
S → [Addr+Wr] → A → [Cmd] → A → [Data] → A → [PEC] → A → P

Read Byte with PEC:
S → [Addr+Wr] → A → [Cmd] → A → 
Sr → [Addr+Rd] → A → [Data] → A → [PEC] → N → P

PEC计算范围:
- 包括所有传输的字节
- 包括地址字节(带R/W位)
- 不包括START、STOP、ACK/NACK

示例:Write Byte (Addr=0x5A, Cmd=0x10, Data=0x25)
传输序列:
S → [0xB4] → A → [0x10] → A → [0x25] → A → [PEC] → A → P
     ^^^^         ^^^^         ^^^^         ^^^^
   Addr+Wr(0)    Cmd         Data         PEC

PEC计算:
数据序列:0xB4, 0x10, 0x25
CRC = 0x00
CRC = CRC8(0x00, 0xB4) = 0x3E
CRC = CRC8(0x3E, 0x10) = 0x7C
CRC = CRC8(0x7C, 0x25) = 0x42
PEC = 0x42

带PEC的命令实现

// Write Byte with PEC
bool SMBus_WriteByteWithPEC(uint8_t addr, uint8_t cmd, uint8_t data) {
    uint8_t pec_data[3];
    uint8_t pec;

    // 准备PEC计算数据
    pec_data[0] = (addr << 1) | 0;  // 地址+写位
    pec_data[1] = cmd;
    pec_data[2] = data;

    // 计算PEC
    pec = SMBus_CalculatePEC(pec_data, 3);

    SMBus_Start();

    // 发送地址+写位
    if (!SMBus_WriteByte(pec_data[0])) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据
    if (!SMBus_WriteByte(data)) {
        SMBus_Stop();
        return false;
    }

    // 发送PEC
    if (!SMBus_WriteByte(pec)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

// Read Byte with PEC
bool SMBus_ReadByteWithPEC(uint8_t addr, uint8_t cmd, uint8_t *data) {
    uint8_t pec_data[4];
    uint8_t received_pec;
    uint8_t calculated_pec;

    SMBus_Start();

    // 发送地址+写位
    pec_data[0] = (addr << 1) | 0;
    if (!SMBus_WriteByte(pec_data[0])) {
        SMBus_Stop();
        return false;
    }

    // 发送命令码
    pec_data[1] = cmd;
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读位
    pec_data[2] = (addr << 1) | 1;
    if (!SMBus_WriteByte(pec_data[2])) {
        SMBus_Stop();
        return false;
    }

    // 读取数据(ACK)
    *data = SMBus_ReadByte(true);
    pec_data[3] = *data;

    // 读取PEC(NACK)
    received_pec = SMBus_ReadByte(false);

    SMBus_Stop();

    // 验证PEC
    calculated_pec = SMBus_CalculatePEC(pec_data, 4);

    if (received_pec != calculated_pec) {
        printf("PEC Error: Expected 0x%02X, Got 0x%02X\n", 
               calculated_pec, received_pec);
        return false;
    }

    return true;
}

// Block Write with PEC
bool SMBus_BlockWriteWithPEC(uint8_t addr, uint8_t cmd,
                             uint8_t *data, uint8_t count) {
    if (count == 0 || count > 32) {
        return false;
    }

    // 准备PEC计算
    uint8_t crc = 0x00;

    SMBus_Start();

    // 发送地址+写位
    uint8_t addr_byte = (addr << 1) | 0;
    if (!SMBus_WriteByte(addr_byte)) {
        SMBus_Stop();
        return false;
    }
    crc = SMBus_UpdatePEC(crc, addr_byte);

    // 发送命令码
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }
    crc = SMBus_UpdatePEC(crc, cmd);

    // 发送字节计数
    if (!SMBus_WriteByte(count)) {
        SMBus_Stop();
        return false;
    }
    crc = SMBus_UpdatePEC(crc, count);

    // 发送数据块
    for (uint8_t i = 0; i < count; i++) {
        if (!SMBus_WriteByte(data[i])) {
            SMBus_Stop();
            return false;
        }
        crc = SMBus_UpdatePEC(crc, data[i]);
    }

    // 发送PEC
    if (!SMBus_WriteByte(crc)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

PEC错误处理

PEC错误处理策略

// PEC错误统计
typedef struct {
    uint32_t total_transactions;
    uint32_t pec_errors;
    uint32_t retry_success;
    uint32_t retry_failed;
} SMBus_Statistics_t;

SMBus_Statistics_t smbus_stats = {0};

// 带重试的PEC读取
bool SMBus_ReadByteWithPEC_Retry(uint8_t addr, uint8_t cmd, 
                                 uint8_t *data, uint8_t max_retries) {
    smbus_stats.total_transactions++;

    for (uint8_t retry = 0; retry < max_retries; retry++) {
        if (SMBus_ReadByteWithPEC(addr, cmd, data)) {
            if (retry > 0) {
                smbus_stats.retry_success++;
            }
            return true;
        }

        smbus_stats.pec_errors++;

        // 延时后重试
        HAL_Delay(10);
    }

    smbus_stats.retry_failed++;
    return false;
}

// 打印统计信息
void SMBus_PrintStatistics(void) {
    printf("\n=== SMBus Statistics ===\n");
    printf("Total Transactions: %lu\n", smbus_stats.total_transactions);
    printf("PEC Errors: %lu (%.2f%%)\n", 
           smbus_stats.pec_errors,
           (float)smbus_stats.pec_errors / smbus_stats.total_transactions * 100);
    printf("Retry Success: %lu\n", smbus_stats.retry_success);
    printf("Retry Failed: %lu\n", smbus_stats.retry_failed);
}

超时处理机制

超时类型和检测

超时检测实现

// 超时配置
#define SMBUS_TIMEOUT_MS        30      // 总超时
#define SMBUS_CLOCK_LOW_MAX_MS  25      // 时钟低电平最大时间
#define SMBUS_BYTE_TIMEOUT_MS   10      // 单字节超时

// 超时检测结构
typedef struct {
    uint32_t start_time;
    uint32_t timeout_ms;
    bool enabled;
} SMBus_Timeout_t;

SMBus_Timeout_t smbus_timeout;

// 启动超时计时
void SMBus_TimeoutStart(uint32_t timeout_ms) {
    smbus_timeout.start_time = HAL_GetTick();
    smbus_timeout.timeout_ms = timeout_ms;
    smbus_timeout.enabled = true;
}

// 停止超时计时
void SMBus_TimeoutStop(void) {
    smbus_timeout.enabled = false;
}

// 检查是否超时
bool SMBus_IsTimeout(void) {
    if (!smbus_timeout.enabled) {
        return false;
    }

    uint32_t elapsed = HAL_GetTick() - smbus_timeout.start_time;
    return (elapsed >= smbus_timeout.timeout_ms);
}

// 等待SCL高电平(带超时)
bool SMBus_WaitSCL_High_Timeout(void) {
    SMBus_TimeoutStart(SMBUS_CLOCK_LOW_MAX_MS);

    while (GPIO_ReadPin(SCL_PORT, SCL_PIN) == 0) {
        if (SMBus_IsTimeout()) {
            printf("Timeout: SCL stuck low\n");
            SMBus_TimeoutStop();
            return false;
        }
    }

    SMBus_TimeoutStop();
    return true;
}

// 等待ACK(带超时)
bool SMBus_WaitACK_Timeout(void) {
    SMBus_TimeoutStart(SMBUS_BYTE_TIMEOUT_MS);

    // 释放SDA
    SDA_HIGH();

    // 等待从机拉低SDA(ACK)
    while (GPIO_ReadPin(SDA_PORT, SDA_PIN) == 1) {
        if (SMBus_IsTimeout()) {
            printf("Timeout: No ACK received\n");
            SMBus_TimeoutStop();
            return false;
        }
    }

    SMBus_TimeoutStop();
    return true;
}

超时恢复机制

总线复位和恢复

// 总线复位
void SMBus_BusReset(void) {
    printf("Performing SMBus bus reset...\n");

    // 1. 确保SDA和SCL都是高电平
    SDA_HIGH();
    SCL_HIGH();
    HAL_Delay(1);

    // 2. 发送9个时钟脉冲清除总线
    for (int i = 0; i < 9; i++) {
        SCL_LOW();
        HAL_Delay_us(5);
        SCL_HIGH();
        HAL_Delay_us(5);

        // 检查SDA是否释放
        if (GPIO_ReadPin(SDA_PORT, SDA_PIN) == 1) {
            break;
        }
    }

    // 3. 发送STOP条件
    SDA_LOW();
    HAL_Delay_us(5);
    SCL_HIGH();
    HAL_Delay_us(5);
    SDA_HIGH();
    HAL_Delay_us(5);

    printf("Bus reset complete\n");
}

// 超时错误处理
typedef enum {
    SMBUS_TIMEOUT_NONE = 0,
    SMBUS_TIMEOUT_CLOCK_LOW,
    SMBUS_TIMEOUT_NO_ACK,
    SMBUS_TIMEOUT_BYTE_TRANSFER,
    SMBUS_TIMEOUT_TRANSACTION
} SMBus_TimeoutType_t;

void SMBus_HandleTimeout(SMBus_TimeoutType_t type) {
    printf("SMBus Timeout: ");

    switch (type) {
        case SMBUS_TIMEOUT_CLOCK_LOW:
            printf("Clock stuck low\n");
            break;
        case SMBUS_TIMEOUT_NO_ACK:
            printf("No ACK received\n");
            break;
        case SMBUS_TIMEOUT_BYTE_TRANSFER:
            printf("Byte transfer timeout\n");
            break;
        case SMBUS_TIMEOUT_TRANSACTION:
            printf("Transaction timeout\n");
            break;
        default:
            printf("Unknown\n");
            break;
    }

    // 执行总线复位
    SMBus_BusReset();

    // 延时后重新初始化
    HAL_Delay(100);
}

// 带超时保护的写字节
bool SMBus_WriteByte_Protected(uint8_t data) {
    SMBus_TimeoutStart(SMBUS_BYTE_TIMEOUT_MS);

    for (int bit = 7; bit >= 0; bit--) {
        // 检查超时
        if (SMBus_IsTimeout()) {
            SMBus_HandleTimeout(SMBUS_TIMEOUT_BYTE_TRANSFER);
            return false;
        }

        // 设置数据位
        if (data & (1 << bit)) {
            SDA_HIGH();
        } else {
            SDA_LOW();
        }
        HAL_Delay_us(5);

        // 时钟高电平
        SCL_HIGH();
        if (!SMBus_WaitSCL_High_Timeout()) {
            SMBus_HandleTimeout(SMBUS_TIMEOUT_CLOCK_LOW);
            return false;
        }
        HAL_Delay_us(5);

        // 时钟低电平
        SCL_LOW();
        HAL_Delay_us(5);
    }

    // 等待ACK
    if (!SMBus_WaitACK_Timeout()) {
        SMBus_HandleTimeout(SMBUS_TIMEOUT_NO_ACK);
        return false;
    }

    SMBus_TimeoutStop();
    return true;
}

SMBus驱动完整实现

驱动架构设计

驱动层次结构

┌─────────────────────────────────┐
│      应用层                      │
│  - 设备特定驱动                  │
│  - 业务逻辑                      │
├─────────────────────────────────┤
│      SMBus协议层                 │
│  - 命令实现                      │
│  - PEC计算                       │
│  - 超时处理                      │
├─────────────────────────────────┤
│      SMBus传输层                 │
│  - START/STOP                   │
│  - 字节读写                      │
│  - ACK/NACK                     │
├─────────────────────────────────┤
│      硬件抽象层(HAL)           │
│  - GPIO操作                      │
│  - 延时函数                      │
│  - 中断处理                      │
└─────────────────────────────────┘

硬件抽象层

GPIO和延时函数

// GPIO定义
#define SCL_PORT    GPIOB
#define SCL_PIN     GPIO_PIN_6
#define SDA_PORT    GPIOB
#define SDA_PIN     GPIO_PIN_7

// GPIO操作宏
#define SCL_HIGH()  HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_SET)
#define SCL_LOW()   HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_RESET)
#define SDA_HIGH()  HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_SET)
#define SDA_LOW()   HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_RESET)

#define SCL_READ()  HAL_GPIO_ReadPin(SCL_PORT, SCL_PIN)
#define SDA_READ()  HAL_GPIO_ReadPin(SDA_PORT, SDA_PIN)

// 微秒延时(根据实际MCU调整)
void HAL_Delay_us(uint32_t us) {
    uint32_t ticks = us * (SystemCoreClock / 1000000);
    while (ticks--) {
        __NOP();
    }
}

// GPIO初始化
void SMBus_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能GPIO时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();

    // 配置SCL和SDA为开漏输出
    GPIO_InitStruct.Pin = SCL_PIN | SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 初始状态为高电平
    SCL_HIGH();
    SDA_HIGH();
}

传输层实现

基本传输函数

// SMBus时序参数(100kHz)
#define SMBUS_DELAY_US  5   // 半个时钟周期

// 发送START条件
void SMBus_Start(void) {
    SDA_HIGH();
    SCL_HIGH();
    HAL_Delay_us(SMBUS_DELAY_US);

    SDA_LOW();
    HAL_Delay_us(SMBUS_DELAY_US);

    SCL_LOW();
    HAL_Delay_us(SMBUS_DELAY_US);
}

// 发送STOP条件
void SMBus_Stop(void) {
    SDA_LOW();
    HAL_Delay_us(SMBUS_DELAY_US);

    SCL_HIGH();
    HAL_Delay_us(SMBUS_DELAY_US);

    SDA_HIGH();
    HAL_Delay_us(SMBUS_DELAY_US);
}

// 写入一个字节
bool SMBus_WriteByte(uint8_t data) {
    // 发送8位数据
    for (int bit = 7; bit >= 0; bit--) {
        if (data & (1 << bit)) {
            SDA_HIGH();
        } else {
            SDA_LOW();
        }
        HAL_Delay_us(SMBUS_DELAY_US);

        SCL_HIGH();
        HAL_Delay_us(SMBUS_DELAY_US);

        SCL_LOW();
        HAL_Delay_us(SMBUS_DELAY_US);
    }

    // 读取ACK
    SDA_HIGH();  // 释放SDA
    HAL_Delay_us(SMBUS_DELAY_US);

    SCL_HIGH();
    HAL_Delay_us(SMBUS_DELAY_US);

    bool ack = (SDA_READ() == 0);  // ACK = 低电平

    SCL_LOW();
    HAL_Delay_us(SMBUS_DELAY_US);

    return ack;
}

// 读取一个字节
uint8_t SMBus_ReadByte(bool ack) {
    uint8_t data = 0;

    SDA_HIGH();  // 释放SDA

    // 读取8位数据
    for (int bit = 7; bit >= 0; bit--) {
        HAL_Delay_us(SMBUS_DELAY_US);

        SCL_HIGH();
        HAL_Delay_us(SMBUS_DELAY_US);

        if (SDA_READ()) {
            data |= (1 << bit);
        }

        SCL_LOW();
    }

    // 发送ACK/NACK
    if (ack) {
        SDA_LOW();   // ACK
    } else {
        SDA_HIGH();  // NACK
    }
    HAL_Delay_us(SMBUS_DELAY_US);

    SCL_HIGH();
    HAL_Delay_us(SMBUS_DELAY_US);

    SCL_LOW();
    HAL_Delay_us(SMBUS_DELAY_US);

    SDA_HIGH();  // 释放SDA

    return data;
}

协议层实现

完整的SMBus驱动

// SMBus驱动结构
typedef struct {
    bool initialized;
    bool pec_enabled;
    uint32_t timeout_ms;
    SMBus_Statistics_t stats;
} SMBus_Driver_t;

SMBus_Driver_t smbus_driver = {
    .initialized = false,
    .pec_enabled = false,
    .timeout_ms = SMBUS_TIMEOUT_MS,
    .stats = {0}
};

// 初始化SMBus
bool SMBus_Init(bool enable_pec) {
    // 初始化GPIO
    SMBus_GPIO_Init();

    // 配置PEC
    smbus_driver.pec_enabled = enable_pec;

    // 总线复位
    SMBus_BusReset();

    smbus_driver.initialized = true;

    printf("SMBus initialized (PEC: %s)\n", 
           enable_pec ? "Enabled" : "Disabled");

    return true;
}

// Quick Command
bool SMBus_QuickCommand(uint8_t addr, bool read) {
    if (!smbus_driver.initialized) {
        return false;
    }

    SMBus_Start();

    bool result = SMBus_WriteByte((addr << 1) | (read ? 1 : 0));

    SMBus_Stop();

    return result;
}

// Send Byte
bool SMBus_SendByte(uint8_t addr, uint8_t data) {
    if (!smbus_driver.initialized) {
        return false;
    }

    SMBus_Start();

    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte(data)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

// Receive Byte
bool SMBus_ReceiveByte(uint8_t addr, uint8_t *data) {
    if (!smbus_driver.initialized) {
        return false;
    }

    SMBus_Start();

    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    *data = SMBus_ReadByte(false);  // NACK

    SMBus_Stop();
    return true;
}

// Write Byte (with optional PEC)
bool SMBus_WriteByte_Data(uint8_t addr, uint8_t cmd, uint8_t data) {
    if (!smbus_driver.initialized) {
        return false;
    }

    if (smbus_driver.pec_enabled) {
        return SMBus_WriteByteWithPEC(addr, cmd, data);
    }

    SMBus_Start();

    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte(data)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

// Read Byte (with optional PEC)
bool SMBus_ReadByte_Data(uint8_t addr, uint8_t cmd, uint8_t *data) {
    if (!smbus_driver.initialized) {
        return false;
    }

    if (smbus_driver.pec_enabled) {
        return SMBus_ReadByteWithPEC(addr, cmd, data);
    }

    SMBus_Start();

    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Start();  // 重复START

    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    *data = SMBus_ReadByte(false);  // NACK

    SMBus_Stop();
    return true;
}

// Write Word
bool SMBus_WriteWord_Data(uint8_t addr, uint8_t cmd, uint16_t data) {
    if (!smbus_driver.initialized) {
        return false;
    }

    SMBus_Start();

    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte(data & 0xFF)) {  // 低字节
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte((data >> 8) & 0xFF)) {  // 高字节
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();
    return true;
}

// Read Word
bool SMBus_ReadWord_Data(uint8_t addr, uint8_t cmd, uint16_t *data) {
    if (!smbus_driver.initialized) {
        return false;
    }

    uint8_t low, high;

    SMBus_Start();

    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Start();  // 重复START

    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    low = SMBus_ReadByte(true);   // ACK
    high = SMBus_ReadByte(false); // NACK

    SMBus_Stop();

    *data = (high << 8) | low;

    return true;
}

实际应用案例

案例1:智能电池管理(Smart Battery)

Smart Battery System规范

Smart Battery System (SBS) 是基于SMBus的电池管理标准:

组件:
- Smart Battery(智能电池)
- Smart Battery Charger(智能充电器)
- System Management Bus(SMBus)
- Host Controller(主机控制器)

地址分配:
0x0B: Smart Battery
0x09: Smart Battery Charger
0x16: Smart Battery Selector

标准命令:
0x08: Temperature(温度)
0x09: Voltage(电压)
0x0A: Current(电流)
0x0D: Relative State of Charge(相对电量)
0x0F: Battery Status(电池状态)
0x10: Cycle Count(循环次数)

Smart Battery驱动实现

// Smart Battery地址和命令
#define SMART_BATTERY_ADDR      0x0B

#define SB_CMD_TEMPERATURE      0x08
#define SB_CMD_VOLTAGE          0x09
#define SB_CMD_CURRENT          0x0A
#define SB_CMD_AVG_CURRENT      0x0B
#define SB_CMD_MAX_ERROR        0x0C
#define SB_CMD_RSOC             0x0D  // Relative State of Charge
#define SB_CMD_ASOC             0x0E  // Absolute State of Charge
#define SB_CMD_REMAINING_CAP    0x0F
#define SB_CMD_FULL_CAP         0x10
#define SB_CMD_RUN_TIME         0x11
#define SB_CMD_AVG_TIME         0x12
#define SB_CMD_BATTERY_STATUS   0x16
#define SB_CMD_CYCLE_COUNT      0x17
#define SB_CMD_DESIGN_CAP       0x18
#define SB_CMD_DESIGN_VOLTAGE   0x19
#define SB_CMD_SPEC_INFO        0x1A
#define SB_CMD_MFG_DATE         0x1B
#define SB_CMD_SERIAL_NUM       0x1C
#define SB_CMD_MFG_NAME         0x20
#define SB_CMD_DEVICE_NAME      0x21
#define SB_CMD_DEVICE_CHEM      0x22

// Smart Battery数据结构
typedef struct {
    int16_t temperature;      // 0.1K
    uint16_t voltage;         // mV
    int16_t current;          // mA
    uint16_t rsoc;            // %
    uint16_t remaining_cap;   // mAh
    uint16_t full_cap;        // mAh
    uint16_t status;          // 状态标志
    uint16_t cycle_count;     // 循环次数
} SmartBattery_Data_t;

// 读取电池温度
float SmartBattery_ReadTemperature(void) {
    uint16_t temp_raw;

    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_TEMPERATURE, &temp_raw)) {
        // 温度单位:0.1K,转换为摄氏度
        float temp_k = temp_raw / 10.0;
        float temp_c = temp_k - 273.15;
        return temp_c;
    }

    return -999.0;  // 错误值
}

// 读取电池电压
float SmartBattery_ReadVoltage(void) {
    uint16_t voltage_mv;

    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_VOLTAGE, &voltage_mv)) {
        return voltage_mv / 1000.0;  // 转换为V
    }

    return 0.0;
}

// 读取电池电流
float SmartBattery_ReadCurrent(void) {
    int16_t current_ma;

    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_CURRENT, 
                           (uint16_t*)&current_ma)) {
        return current_ma / 1000.0;  // 转换为A
    }

    return 0.0;
}

// 读取电池电量(百分比)
uint8_t SmartBattery_ReadSOC(void) {
    uint16_t soc;

    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_RSOC, &soc)) {
        return (uint8_t)soc;
    }

    return 0;
}

// 读取电池状态
uint16_t SmartBattery_ReadStatus(void) {
    uint16_t status;

    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_BATTERY_STATUS, &status)) {
        return status;
    }

    return 0xFFFF;
}

// 解析电池状态
void SmartBattery_ParseStatus(uint16_t status) {
    printf("Battery Status: 0x%04X\n", status);

    if (status & 0x8000) printf("  - Over Charged Alarm\n");
    if (status & 0x4000) printf("  - Terminate Charge Alarm\n");
    if (status & 0x1000) printf("  - Over Temp Alarm\n");
    if (status & 0x0800) printf("  - Terminate Discharge Alarm\n");
    if (status & 0x0200) printf("  - Remaining Capacity Alarm\n");
    if (status & 0x0100) printf("  - Remaining Time Alarm\n");
    if (status & 0x0080) printf("  - Initialized\n");
    if (status & 0x0040) printf("  - Discharging\n");
    if (status & 0x0020) printf("  - Fully Charged\n");
    if (status & 0x0010) printf("  - Fully Discharged\n");
}

// 读取电池信息字符串
bool SmartBattery_ReadString(uint8_t cmd, char *buffer, uint8_t max_len) {
    uint8_t data[32];
    uint8_t count;

    if (!SMBus_BlockRead(SMART_BATTERY_ADDR, cmd, data, &count)) {
        return false;
    }

    // 限制长度
    if (count > max_len - 1) {
        count = max_len - 1;
    }

    // 复制字符串
    memcpy(buffer, data, count);
    buffer[count] = '\0';

    return true;
}

// 读取完整电池信息
void SmartBattery_ReadFullInfo(SmartBattery_Data_t *data) {
    char str_buffer[32];

    printf("\n=== Smart Battery Information ===\n");

    // 读取制造商
    if (SmartBattery_ReadString(SB_CMD_MFG_NAME, str_buffer, sizeof(str_buffer))) {
        printf("Manufacturer: %s\n", str_buffer);
    }

    // 读取设备名称
    if (SmartBattery_ReadString(SB_CMD_DEVICE_NAME, str_buffer, sizeof(str_buffer))) {
        printf("Device Name: %s\n", str_buffer);
    }

    // 读取化学类型
    if (SmartBattery_ReadString(SB_CMD_DEVICE_CHEM, str_buffer, sizeof(str_buffer))) {
        printf("Chemistry: %s\n", str_buffer);
    }

    // 读取序列号
    uint16_t serial;
    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_SERIAL_NUM, &serial)) {
        printf("Serial Number: %u\n", serial);
    }

    // 读取设计容量
    uint16_t design_cap;
    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_DESIGN_CAP, &design_cap)) {
        printf("Design Capacity: %u mAh\n", design_cap);
    }

    // 读取设计电压
    uint16_t design_voltage;
    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_DESIGN_VOLTAGE, &design_voltage)) {
        printf("Design Voltage: %u mV\n", design_voltage);
    }

    // 读取循环次数
    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_CYCLE_COUNT, &data->cycle_count)) {
        printf("Cycle Count: %u\n", data->cycle_count);
    }

    // 读取实时数据
    printf("\n--- Real-time Data ---\n");
    printf("Temperature: %.1f °C\n", SmartBattery_ReadTemperature());
    printf("Voltage: %.3f V\n", SmartBattery_ReadVoltage());
    printf("Current: %.3f A\n", SmartBattery_ReadCurrent());
    printf("State of Charge: %u %%\n", SmartBattery_ReadSOC());

    // 读取剩余容量
    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_REMAINING_CAP, &data->remaining_cap)) {
        printf("Remaining Capacity: %u mAh\n", data->remaining_cap);
    }

    // 读取满充容量
    if (SMBus_ReadWord_Data(SMART_BATTERY_ADDR, SB_CMD_FULL_CAP, &data->full_cap)) {
        printf("Full Charge Capacity: %u mAh\n", data->full_cap);
    }

    // 读取并解析状态
    data->status = SmartBattery_ReadStatus();
    SmartBattery_ParseStatus(data->status);
}

// 电池监控任务
void SmartBattery_MonitorTask(void) {
    static uint32_t last_update = 0;
    uint32_t current_time = HAL_GetTick();

    // 每5秒更新一次
    if (current_time - last_update >= 5000) {
        SmartBattery_Data_t battery_data;

        battery_data.temperature = (int16_t)(SmartBattery_ReadTemperature() * 10);
        battery_data.voltage = (uint16_t)(SmartBattery_ReadVoltage() * 1000);
        battery_data.current = (int16_t)(SmartBattery_ReadCurrent() * 1000);
        battery_data.rsoc = SmartBattery_ReadSOC();

        // 检查电池状态
        if (battery_data.rsoc < 20) {
            printf("WARNING: Low battery (%u%%)\n", battery_data.rsoc);
        }

        if (battery_data.temperature > 450) {  // 45°C
            printf("WARNING: High temperature (%.1f°C)\n", 
                   battery_data.temperature / 10.0);
        }

        last_update = current_time;
    }
}

案例2:温度传感器(LM75)

LM75温度传感器

LM75是常见的SMBus温度传感器:

特性:
- SMBus/I2C兼容
- 9位温度分辨率(0.5°C)
- 温度范围:-55°C to +125°C
- 可编程温度阈值
- 中断输出

地址:0x48-0x4F(可配置)

寄存器:
0x00: Temperature(温度,只读)
0x01: Configuration(配置)
0x02: T_hyst(滞后温度)
0x03: T_os(过温阈值)

LM75驱动实现

// LM75地址和寄存器
#define LM75_ADDR           0x48

#define LM75_REG_TEMP       0x00
#define LM75_REG_CONF       0x01
#define LM75_REG_THYST      0x02
#define LM75_REG_TOS        0x03

// LM75配置位
#define LM75_CONF_SHUTDOWN  0x01
#define LM75_CONF_OS_MODE   0x02
#define LM75_CONF_OS_POL    0x04
#define LM75_CONF_FAULT_Q1  0x08
#define LM75_CONF_FAULT_Q2  0x10

// 初始化LM75
bool LM75_Init(uint8_t addr) {
    // 读取配置寄存器验证设备存在
    uint8_t config;
    if (!SMBus_ReadByte_Data(addr, LM75_REG_CONF, &config)) {
        printf("LM75 not found at 0x%02X\n", addr);
        return false;
    }

    // 配置为正常模式
    config &= ~LM75_CONF_SHUTDOWN;
    SMBus_WriteByte_Data(addr, LM75_REG_CONF, config);

    printf("LM75 initialized at 0x%02X\n", addr);
    return true;
}

// 读取温度
float LM75_ReadTemperature(uint8_t addr) {
    uint16_t temp_raw;

    if (!SMBus_ReadWord_Data(addr, LM75_REG_TEMP, &temp_raw)) {
        return -999.0;
    }

    // LM75温度格式:11位,0.125°C/LSB
    // 高字节是整数部分,低字节高5位是小数部分
    int16_t temp_value = (int16_t)temp_raw;
    temp_value >>= 5;  // 右移5位得到11位温度值

    float temperature = temp_value * 0.125;

    return temperature;
}

// 设置过温阈值
bool LM75_SetThreshold(uint8_t addr, float temp_os, float temp_hyst) {
    // 转换温度为LM75格式
    int16_t os_raw = (int16_t)(temp_os / 0.5) << 7;
    int16_t hyst_raw = (int16_t)(temp_hyst / 0.5) << 7;

    // 写入阈值
    if (!SMBus_WriteWord_Data(addr, LM75_REG_TOS, (uint16_t)os_raw)) {
        return false;
    }

    if (!SMBus_WriteWord_Data(addr, LM75_REG_THYST, (uint16_t)hyst_raw)) {
        return false;
    }

    printf("LM75 thresholds set: OS=%.1f°C, HYST=%.1f°C\n", temp_os, temp_hyst);
    return true;
}

// 使用示例
void LM75_Example(void) {
    uint8_t lm75_addr = LM75_ADDR;

    // 初始化
    if (!LM75_Init(lm75_addr)) {
        return;
    }

    // 设置阈值
    LM75_SetThreshold(lm75_addr, 50.0, 45.0);

    // 读取温度
    while (1) {
        float temp = LM75_ReadTemperature(lm75_addr);
        printf("Temperature: %.2f °C\n", temp);

        HAL_Delay(1000);
    }
}

案例3:EEPROM(24C02)

24C02 EEPROM

24C02是256字节的I2C/SMBus EEPROM:

特性:
- 容量:256字节(2Kbit)
- 页大小:8字节
- 写周期:5ms
- SMBus兼容

地址:0x50-0x57(可配置)

操作:
- 字节写入
- 页写入(最多8字节)
- 当前地址读取
- 随机地址读取
- 顺序读取

24C02驱动实现

// 24C02地址
#define EEPROM_ADDR     0x50

// 24C02参数
#define EEPROM_SIZE     256
#define EEPROM_PAGE_SIZE 8

// 写入单字节
bool EEPROM_WriteByte(uint8_t addr, uint8_t mem_addr, uint8_t data) {
    if (!SMBus_WriteByte_Data(addr, mem_addr, data)) {
        return false;
    }

    // 等待写入完成(5ms)
    HAL_Delay(5);

    return true;
}

// 读取单字节
bool EEPROM_ReadByte(uint8_t addr, uint8_t mem_addr, uint8_t *data) {
    return SMBus_ReadByte_Data(addr, mem_addr, data);
}

// 页写入(最多8字节)
bool EEPROM_WritePage(uint8_t addr, uint8_t mem_addr, 
                      uint8_t *data, uint8_t len) {
    if (len == 0 || len > EEPROM_PAGE_SIZE) {
        return false;
    }

    // 检查是否跨页
    if ((mem_addr / EEPROM_PAGE_SIZE) != 
        ((mem_addr + len - 1) / EEPROM_PAGE_SIZE)) {
        printf("Error: Page boundary crossed\n");
        return false;
    }

    SMBus_Start();

    // 发送设备地址
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送内存地址
    if (!SMBus_WriteByte(mem_addr)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据
    for (uint8_t i = 0; i < len; i++) {
        if (!SMBus_WriteByte(data[i])) {
            SMBus_Stop();
            return false;
        }
    }

    SMBus_Stop();

    // 等待写入完成
    HAL_Delay(5);

    return true;
}

// 顺序读取
bool EEPROM_ReadSequential(uint8_t addr, uint8_t mem_addr,
                          uint8_t *buffer, uint8_t len) {
    if (len == 0) {
        return false;
    }

    SMBus_Start();

    // 发送设备地址+写
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送内存地址
    if (!SMBus_WriteByte(mem_addr)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送设备地址+读
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取数据
    for (uint8_t i = 0; i < len; i++) {
        bool ack = (i < len - 1);  // 最后一个字节NACK
        buffer[i] = SMBus_ReadByte(ack);
    }

    SMBus_Stop();

    return true;
}

// 写入多字节(自动处理页边界)
bool EEPROM_WriteMulti(uint8_t addr, uint8_t mem_addr,
                       uint8_t *data, uint16_t len) {
    uint16_t written = 0;

    while (written < len) {
        // 计算当前页剩余空间
        uint8_t page_offset = (mem_addr + written) % EEPROM_PAGE_SIZE;
        uint8_t page_remaining = EEPROM_PAGE_SIZE - page_offset;

        // 计算本次写入长度
        uint8_t write_len = (len - written) < page_remaining ? 
                           (len - written) : page_remaining;

        // 写入一页
        if (!EEPROM_WritePage(addr, mem_addr + written, 
                             data + written, write_len)) {
            return false;
        }

        written += write_len;
    }

    return true;
}

// 使用示例
void EEPROM_Example(void) {
    uint8_t write_data[] = "Hello, SMBus!";
    uint8_t read_data[32];

    // 写入数据
    printf("Writing to EEPROM...\n");
    if (EEPROM_WriteMulti(EEPROM_ADDR, 0x00, 
                         write_data, sizeof(write_data))) {
        printf("Write successful\n");
    }

    // 读取数据
    printf("Reading from EEPROM...\n");
    if (EEPROM_ReadSequential(EEPROM_ADDR, 0x00, 
                             read_data, sizeof(write_data))) {
        read_data[sizeof(write_data)] = '\0';
        printf("Read data: %s\n", read_data);
    }
}

故障排查与调试

常见问题诊断

问题1:设备无响应

// 诊断函数
void SMBus_DiagnoseNoResponse(uint8_t addr) {
    printf("\n=== Diagnosing SMBus Device 0x%02X ===\n", addr);

    // 1. 检查总线状态
    printf("1. Checking bus status...\n");
    if (SCL_READ() == 0) {
        printf("   ERROR: SCL stuck low!\n");
        printf("   Possible causes:\n");
        printf("   - Device holding SCL (clock stretching)\n");
        printf("   - Short circuit on SCL\n");
        printf("   - Faulty device\n");
        SMBus_BusReset();
        return;
    }

    if (SDA_READ() == 0) {
        printf("   ERROR: SDA stuck low!\n");
        printf("   Possible causes:\n");
        printf("   - Device holding SDA\n");
        printf("   - Short circuit on SDA\n");
        printf("   - Bus not properly terminated\n");
        SMBus_BusReset();
        return;
    }

    printf("   OK: Bus lines are high\n");

    // 2. 检查上拉电阻
    printf("2. Checking pull-up resistors...\n");
    printf("   Measure voltage on SCL and SDA\n");
    printf("   Should be close to VDD (3.3V or 5V)\n");

    // 3. 尝试Quick Command
    printf("3. Trying Quick Command...\n");
    if (SMBus_QuickCommand(addr, false)) {
        printf("   OK: Device responded to Quick Command\n");
    } else {
        printf("   ERROR: No response to Quick Command\n");
        printf("   Possible causes:\n");
        printf("   - Wrong device address\n");
        printf("   - Device not powered\n");
        printf("   - Device in sleep mode\n");
        printf("   - Timing issues\n");
    }

    // 4. 扫描总线
    printf("4. Scanning SMBus...\n");
    SMBus_ScanBus();
}

// 总线扫描
void SMBus_ScanBus(void) {
    printf("\nScanning SMBus (0x08-0x77)...\n");

    uint8_t found_count = 0;

    for (uint8_t addr = 0x08; addr <= 0x77; addr++) {
        if (SMBus_QuickCommand(addr, false)) {
            printf("Device found at 0x%02X\n", addr);
            found_count++;
        }
    }

    if (found_count == 0) {
        printf("No devices found!\n");
    } else {
        printf("Found %d device(s)\n", found_count);
    }
}

问题2:PEC错误

// PEC错误诊断
void SMBus_DiagnosePECError(uint8_t addr, uint8_t cmd) {
    printf("\n=== Diagnosing PEC Errors ===\n");

    // 1. 禁用PEC重试
    printf("1. Testing without PEC...\n");
    bool old_pec = smbus_driver.pec_enabled;
    smbus_driver.pec_enabled = false;

    uint8_t data;
    if (SMBus_ReadByte_Data(addr, cmd, &data)) {
        printf("   OK: Communication works without PEC\n");
        printf("   Data: 0x%02X\n", data);
    } else {
        printf("   ERROR: Communication fails even without PEC\n");
        smbus_driver.pec_enabled = old_pec;
        return;
    }

    // 2. 测试PEC计算
    printf("2. Testing PEC calculation...\n");
    uint8_t test_data[] = {0xB4, 0x10, 0x25};
    uint8_t pec = SMBus_CalculatePEC(test_data, 3);
    printf("   Test PEC: 0x%02X (expected: 0x42)\n", pec);

    if (pec != 0x42) {
        printf("   ERROR: PEC calculation incorrect!\n");
        printf("   Check CRC-8 implementation\n");
    }

    // 3. 启用PEC重试
    printf("3. Testing with PEC...\n");
    smbus_driver.pec_enabled = true;

    uint8_t success_count = 0;
    uint8_t fail_count = 0;

    for (int i = 0; i < 10; i++) {
        if (SMBus_ReadByte_Data(addr, cmd, &data)) {
            success_count++;
        } else {
            fail_count++;
        }
        HAL_Delay(10);
    }

    printf("   Results: %d success, %d failed\n", success_count, fail_count);

    if (fail_count > 0) {
        printf("   Possible causes:\n");
        printf("   - Electrical noise\n");
        printf("   - Poor signal quality\n");
        printf("   - Timing issues\n");
        printf("   - Device PEC implementation error\n");
    }

    smbus_driver.pec_enabled = old_pec;
}

问题3:超时错误

// 超时诊断
void SMBus_DiagnoseTimeout(uint8_t addr) {
    printf("\n=== Diagnosing Timeout Issues ===\n");

    // 1. 测量时钟拉伸时间
    printf("1. Measuring clock stretch time...\n");

    uint32_t max_stretch = 0;
    uint32_t total_stretch = 0;
    uint32_t stretch_count = 0;

    for (int i = 0; i < 10; i++) {
        SMBus_Start();

        uint32_t start = HAL_GetTick();
        SMBus_WriteByte((addr << 1) | 0);
        uint32_t stretch = HAL_GetTick() - start;

        if (stretch > 0) {
            total_stretch += stretch;
            stretch_count++;
            if (stretch > max_stretch) {
                max_stretch = stretch;
            }
        }

        SMBus_Stop();
        HAL_Delay(10);
    }

    if (stretch_count > 0) {
        printf("   Max stretch: %lu ms\n", max_stretch);
        printf("   Avg stretch: %lu ms\n", total_stretch / stretch_count);

        if (max_stretch > SMBUS_CLOCK_LOW_MAX_MS) {
            printf("   WARNING: Clock stretch exceeds SMBus limit!\n");
            printf("   SMBus limit: %d ms\n", SMBUS_CLOCK_LOW_MAX_MS);
            printf("   Device may not be SMBus compliant\n");
        }
    } else {
        printf("   No clock stretching detected\n");
    }

    // 2. 测试不同超时值
    printf("2. Testing different timeout values...\n");

    uint32_t timeout_values[] = {10, 25, 50, 100};

    for (int i = 0; i < 4; i++) {
        smbus_driver.timeout_ms = timeout_values[i];

        uint8_t data;
        bool result = SMBus_ReadByte_Data(addr, 0x00, &data);

        printf("   Timeout %lu ms: %s\n", 
               timeout_values[i], 
               result ? "OK" : "FAILED");
    }

    // 恢复默认超时
    smbus_driver.timeout_ms = SMBUS_TIMEOUT_MS;
}

调试工具

逻辑分析仪配置

推荐设置:
- 采样率:≥1MHz(对于100kHz SMBus)
- 触发:SCL下降沿
- 解码:I2C/SMBus协议
- 显示:地址、数据、ACK/NACK、PEC

关键信号:
- SCL:时钟信号
- SDA:数据信号
- 可选:中断信号、复位信号

分析要点:
1. 检查START/STOP条件
2. 验证地址和R/W位
3. 检查ACK/NACK
4. 测量时序参数
5. 验证PEC值
6. 检查时钟拉伸

软件调试输出

// 调试级别
typedef enum {
    SMBUS_DEBUG_NONE = 0,
    SMBUS_DEBUG_ERROR,
    SMBUS_DEBUG_WARN,
    SMBUS_DEBUG_INFO,
    SMBUS_DEBUG_VERBOSE
} SMBus_DebugLevel_t;

SMBus_DebugLevel_t smbus_debug_level = SMBUS_DEBUG_INFO;

// 调试打印宏
#define SMBUS_DEBUG(level, fmt, ...) \
    do { \
        if (level <= smbus_debug_level) { \
            printf("[SMBus] " fmt "\n", ##__VA_ARGS__); \
        } \
    } while(0)

// 带调试的写字节
bool SMBus_WriteByte_Debug(uint8_t data) {
    SMBUS_DEBUG(SMBUS_DEBUG_VERBOSE, "Writing byte: 0x%02X", data);

    bool result = SMBus_WriteByte(data);

    if (result) {
        SMBUS_DEBUG(SMBUS_DEBUG_VERBOSE, "  ACK received");
    } else {
        SMBUS_DEBUG(SMBUS_DEBUG_ERROR, "  NACK received!");
    }

    return result;
}

// 事务跟踪
typedef struct {
    uint32_t timestamp;
    uint8_t addr;
    uint8_t cmd;
    uint8_t data[32];
    uint8_t len;
    bool is_read;
    bool success;
    uint8_t pec;
} SMBus_Transaction_t;

#define SMBUS_TRACE_SIZE 16
SMBus_Transaction_t smbus_trace[SMBUS_TRACE_SIZE];
uint8_t smbus_trace_index = 0;

// 记录事务
void SMBus_TraceTransaction(uint8_t addr, uint8_t cmd, 
                            uint8_t *data, uint8_t len,
                            bool is_read, bool success) {
    SMBus_Transaction_t *t = &smbus_trace[smbus_trace_index];

    t->timestamp = HAL_GetTick();
    t->addr = addr;
    t->cmd = cmd;
    t->len = len;
    t->is_read = is_read;
    t->success = success;

    if (len > 0 && len <= 32) {
        memcpy(t->data, data, len);
    }

    smbus_trace_index = (smbus_trace_index + 1) % SMBUS_TRACE_SIZE;
}

// 打印事务历史
void SMBus_PrintTrace(void) {
    printf("\n=== SMBus Transaction Trace ===\n");

    for (int i = 0; i < SMBUS_TRACE_SIZE; i++) {
        int idx = (smbus_trace_index + i) % SMBUS_TRACE_SIZE;
        SMBus_Transaction_t *t = &smbus_trace[idx];

        if (t->timestamp == 0) continue;

        printf("[%lu] Addr:0x%02X Cmd:0x%02X %s Len:%d %s\n",
               t->timestamp, t->addr, t->cmd,
               t->is_read ? "READ" : "WRITE",
               t->len,
               t->success ? "OK" : "FAIL");

        if (t->len > 0) {
            printf("  Data: ");
            for (int j = 0; j < t->len; j++) {
                printf("%02X ", t->data[j]);
            }
            printf("\n");
        }
    }
}

高级主题

ARP(Address Resolution Protocol)

SMBus ARP概述

ARP是SMBus 2.0引入的地址分配协议:

目的:
- 动态分配设备地址
- 解决地址冲突
- 支持热插拔

ARP设备类型:
- ARP Master:主机控制器
- ARP Slave:支持ARP的设备

ARP地址:
0x61: ARP地址(所有ARP设备响应)

ARP命令:
- Prepare to ARP
- Reset Device
- Get UDID
- Assign Address

ARP实现示例

// ARP地址和命令
#define SMBUS_ARP_ADDR          0x61

#define ARP_CMD_PREPARE         0x01
#define ARP_CMD_RESET_DEVICE    0x02
#define ARP_CMD_GET_UDID        0x03
#define ARP_CMD_ASSIGN_ADDR     0x04

// UDID结构(128位唯一标识符)
typedef struct {
    uint8_t device_capabilities;
    uint8_t version;
    uint16_t vendor_id;
    uint16_t device_id;
    uint16_t interface;
    uint16_t subsystem_vendor_id;
    uint16_t subsystem_device_id;
    uint32_t vendor_specific;
} SMBus_UDID_t;

// Prepare to ARP
bool SMBus_ARP_Prepare(void) {
    return SMBus_SendByte(SMBUS_ARP_ADDR, ARP_CMD_PREPARE);
}

// Reset Device (General Call)
bool SMBus_ARP_ResetDevice(void) {
    SMBus_Start();

    // 通用调用地址
    if (!SMBus_WriteByte(0x00)) {
        SMBus_Stop();
        return false;
    }

    // Reset命令
    if (!SMBus_WriteByte(0x06)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();

    HAL_Delay(100);  // 等待设备复位

    return true;
}

// Get UDID
bool SMBus_ARP_GetUDID(SMBus_UDID_t *udid) {
    uint8_t data[17];  // 16字节UDID + 1字节地址
    uint8_t count;

    if (!SMBus_BlockRead(SMBUS_ARP_ADDR, ARP_CMD_GET_UDID, data, &count)) {
        return false;
    }

    if (count != 17) {
        return false;
    }

    // 解析UDID
    memcpy(udid, data, 16);

    return true;
}

// Assign Address
bool SMBus_ARP_AssignAddress(SMBus_UDID_t *udid, uint8_t new_addr) {
    uint8_t data[17];

    // 准备数据:16字节UDID + 1字节新地址
    memcpy(data, udid, 16);
    data[16] = new_addr << 1;  // 地址左移1位

    return SMBus_BlockWrite(SMBUS_ARP_ADDR, ARP_CMD_ASSIGN_ADDR, data, 17);
}

// ARP枚举所有设备
void SMBus_ARP_EnumerateDevices(void) {
    printf("\n=== SMBus ARP Enumeration ===\n");

    // 1. 准备ARP
    printf("1. Preparing ARP...\n");
    if (!SMBus_ARP_Prepare()) {
        printf("   Failed to prepare ARP\n");
        return;
    }

    // 2. 枚举设备
    printf("2. Enumerating devices...\n");

    uint8_t assigned_addr = 0x10;  // 起始地址
    uint8_t device_count = 0;

    while (device_count < 10) {  // 最多10个设备
        SMBus_UDID_t udid;

        // 获取UDID
        if (!SMBus_ARP_GetUDID(&udid)) {
            break;  // 没有更多设备
        }

        printf("   Device %d:\n", device_count);
        printf("     Vendor ID: 0x%04X\n", udid.vendor_id);
        printf("     Device ID: 0x%04X\n", udid.device_id);

        // 分配地址
        if (SMBus_ARP_AssignAddress(&udid, assigned_addr)) {
            printf("     Assigned address: 0x%02X\n", assigned_addr);
            assigned_addr++;
            device_count++;
        } else {
            printf("     Failed to assign address\n");
            break;
        }
    }

    printf("Total devices enumerated: %d\n", device_count);
}

Zone Read/Write

Zone操作概述

Zone Read/Write是SMBus 3.0引入的高级功能:

特点:
- 支持大数据块传输(>32字节)
- 分区访问
- 提高传输效率

Zone Write格式:
S → [Addr+Wr] → A → [Cmd] → A → [Zone] → A → 
[Count] → A → [Data...] → A → P

Zone Read格式:
S → [Addr+Wr] → A → [Cmd] → A → [Zone] → A → 
Sr → [Addr+Rd] → A → [Count] → A → [Data...] → N → P

Zone操作实现

// Zone Write
bool SMBus_ZoneWrite(uint8_t addr, uint8_t cmd, uint8_t zone,
                     uint8_t *data, uint8_t count) {
    if (count == 0 || count > 255) {
        return false;
    }

    SMBus_Start();

    // 发送地址+写
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送区域号
    if (!SMBus_WriteByte(zone)) {
        SMBus_Stop();
        return false;
    }

    // 发送字节计数
    if (!SMBus_WriteByte(count)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据
    for (uint8_t i = 0; i < count; i++) {
        if (!SMBus_WriteByte(data[i])) {
            SMBus_Stop();
            return false;
        }
    }

    SMBus_Stop();

    return true;
}

// Zone Read
bool SMBus_ZoneRead(uint8_t addr, uint8_t cmd, uint8_t zone,
                    uint8_t *data, uint8_t *count) {
    SMBus_Start();

    // 发送地址+写
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送命令
    if (!SMBus_WriteByte(cmd)) {
        SMBus_Stop();
        return false;
    }

    // 发送区域号
    if (!SMBus_WriteByte(zone)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 读取字节计数
    *count = SMBus_ReadByte(true);

    if (*count == 0 || *count > 255) {
        SMBus_Stop();
        return false;
    }

    // 读取数据
    for (uint8_t i = 0; i < *count; i++) {
        bool ack = (i < *count - 1);
        data[i] = SMBus_ReadByte(ack);
    }

    SMBus_Stop();

    return true;
}

Host Notify Protocol

Host Notify概述

Host Notify允许从机主动通知主机:

特点:
- 从机作为主机发起通信
- 用于中断/事件通知
- 无需轮询

Host Notify地址:
0x08: Host Notify地址

格式:
S → [0x08+Wr] → A → [DevAddr] → A → [Data] → A → P

应用:
- 中断通知
- 事件报告
- 状态变化

Host Notify实现

// Host Notify地址
#define SMBUS_HOST_NOTIFY_ADDR  0x08

// 从机发送Host Notify(从机端实现)
bool SMBus_SendHostNotify(uint8_t device_addr, uint16_t data) {
    SMBus_Start();

    // 发送Host Notify地址+写
    if (!SMBus_WriteByte((SMBUS_HOST_NOTIFY_ADDR << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送设备地址
    if (!SMBus_WriteByte(device_addr << 1)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据(低字节)
    if (!SMBus_WriteByte(data & 0xFF)) {
        SMBus_Stop();
        return false;
    }

    // 发送数据(高字节)
    if (!SMBus_WriteByte((data >> 8) & 0xFF)) {
        SMBus_Stop();
        return false;
    }

    SMBus_Stop();

    return true;
}

// 主机接收Host Notify(主机端实现)
typedef struct {
    uint8_t device_addr;
    uint16_t data;
    uint32_t timestamp;
} HostNotify_Event_t;

#define HOST_NOTIFY_QUEUE_SIZE 8
HostNotify_Event_t host_notify_queue[HOST_NOTIFY_QUEUE_SIZE];
uint8_t host_notify_head = 0;
uint8_t host_notify_tail = 0;

// Host Notify中断处理
void SMBus_HostNotify_IRQHandler(void) {
    uint8_t device_addr;
    uint16_t data;

    // 读取设备地址
    device_addr = SMBus_ReadByte(true);

    // 读取数据
    uint8_t low = SMBus_ReadByte(true);
    uint8_t high = SMBus_ReadByte(false);
    data = (high << 8) | low;

    // 添加到队列
    uint8_t next = (host_notify_tail + 1) % HOST_NOTIFY_QUEUE_SIZE;
    if (next != host_notify_head) {
        host_notify_queue[host_notify_tail].device_addr = device_addr >> 1;
        host_notify_queue[host_notify_tail].data = data;
        host_notify_queue[host_notify_tail].timestamp = HAL_GetTick();
        host_notify_tail = next;
    }
}

// 处理Host Notify事件
void SMBus_ProcessHostNotify(void) {
    while (host_notify_head != host_notify_tail) {
        HostNotify_Event_t *event = &host_notify_queue[host_notify_head];

        printf("Host Notify from 0x%02X: Data=0x%04X Time=%lu\n",
               event->device_addr, event->data, event->timestamp);

        // 处理事件
        // ...

        host_notify_head = (host_notify_head + 1) % HOST_NOTIFY_QUEUE_SIZE;
    }
}

性能优化

传输速度优化

时钟频率选择

// 不同速度的时序参数
typedef struct {
    uint32_t clock_freq;      // Hz
    uint32_t delay_us;        // 半周期延时
    uint32_t setup_time_us;   // 建立时间
    uint32_t hold_time_us;    // 保持时间
} SMBus_Timing_t;

// 标准速度配置
const SMBus_Timing_t smbus_timing_configs[] = {
    // 10kHz(最低速度)
    {10000, 50, 5, 5},

    // 50kHz(低速)
    {50000, 10, 2, 2},

    // 100kHz(标准速度)
    {100000, 5, 1, 1},

    // 400kHz(快速模式,SMBus 3.0)
    {400000, 1, 0, 0}
};

// 设置SMBus速度
void SMBus_SetSpeed(uint32_t freq_hz) {
    for (int i = 0; i < 4; i++) {
        if (smbus_timing_configs[i].clock_freq == freq_hz) {
            // 更新全局时序参数
            // ...
            printf("SMBus speed set to %lu Hz\n", freq_hz);
            return;
        }
    }

    printf("Unsupported frequency: %lu Hz\n", freq_hz);
}

DMA传输

使用DMA加速数据传输

// DMA配置(以STM32为例)
void SMBus_DMA_Init(void) {
    // 配置I2C DMA
    // 注意:需要硬件I2C控制器支持

    // TX DMA配置
    hdma_i2c_tx.Instance = DMA1_Channel6;
    hdma_i2c_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_i2c_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_i2c_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_i2c_tx.Init.Mode = DMA_NORMAL;
    hdma_i2c_tx.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&hdma_i2c_tx);

    // RX DMA配置
    hdma_i2c_rx.Instance = DMA1_Channel7;
    hdma_i2c_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    // ... 类似配置
    HAL_DMA_Init(&hdma_i2c_rx);
}

// DMA Block Write
bool SMBus_BlockWrite_DMA(uint8_t addr, uint8_t cmd,
                          uint8_t *data, uint8_t count) {
    // 使用硬件I2C + DMA
    HAL_I2C_Mem_Write_DMA(&hi2c1, addr << 1, cmd, 
                          I2C_MEMADD_SIZE_8BIT, data, count);

    // 等待传输完成
    while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) {
        // 可以在这里处理其他任务
    }

    return true;
}

批量操作优化

减少START/STOP开销

// 批量读取多个寄存器
bool SMBus_ReadMultipleRegisters(uint8_t addr, uint8_t start_reg,
                                 uint8_t *data, uint8_t count) {
    // 使用顺序读取,只需一次START/STOP
    SMBus_Start();

    // 发送地址+写
    if (!SMBus_WriteByte((addr << 1) | 0)) {
        SMBus_Stop();
        return false;
    }

    // 发送起始寄存器地址
    if (!SMBus_WriteByte(start_reg)) {
        SMBus_Stop();
        return false;
    }

    // 重复START
    SMBus_Start();

    // 发送地址+读
    if (!SMBus_WriteByte((addr << 1) | 1)) {
        SMBus_Stop();
        return false;
    }

    // 连续读取多个字节
    for (uint8_t i = 0; i < count; i++) {
        bool ack = (i < count - 1);
        data[i] = SMBus_ReadByte(ack);
    }

    SMBus_Stop();

    return true;
}

// 使用示例:读取传感器的多个寄存器
void ReadSensorData(uint8_t addr) {
    uint8_t data[6];

    // 一次读取6个连续寄存器
    if (SMBus_ReadMultipleRegisters(addr, 0x00, data, 6)) {
        int16_t ax = (data[0] << 8) | data[1];
        int16_t ay = (data[2] << 8) | data[3];
        int16_t az = (data[4] << 8) | data[5];

        printf("Accel: X=%d Y=%d Z=%d\n", ax, ay, az);
    }
}

缓存和预取

数据缓存策略

// 寄存器缓存
typedef struct {
    uint8_t addr;
    uint8_t reg;
    uint8_t value;
    uint32_t timestamp;
    bool valid;
} RegCache_t;

#define REG_CACHE_SIZE 16
RegCache_t reg_cache[REG_CACHE_SIZE];

// 缓存读取
bool SMBus_ReadByte_Cached(uint8_t addr, uint8_t reg, 
                           uint8_t *data, uint32_t cache_time_ms) {
    uint32_t current_time = HAL_GetTick();

    // 查找缓存
    for (int i = 0; i < REG_CACHE_SIZE; i++) {
        if (reg_cache[i].valid &&
            reg_cache[i].addr == addr &&
            reg_cache[i].reg == reg) {

            // 检查缓存是否过期
            if (current_time - reg_cache[i].timestamp < cache_time_ms) {
                *data = reg_cache[i].value;
                return true;  // 使用缓存值
            }
        }
    }

    // 缓存未命中,读取设备
    if (!SMBus_ReadByte_Data(addr, reg, data)) {
        return false;
    }

    // 更新缓存
    for (int i = 0; i < REG_CACHE_SIZE; i++) {
        if (!reg_cache[i].valid) {
            reg_cache[i].addr = addr;
            reg_cache[i].reg = reg;
            reg_cache[i].value = *data;
            reg_cache[i].timestamp = current_time;
            reg_cache[i].valid = true;
            break;
        }
    }

    return true;
}

// 清除缓存
void SMBus_ClearCache(void) {
    for (int i = 0; i < REG_CACHE_SIZE; i++) {
        reg_cache[i].valid = false;
    }
}

最佳实践

设计建议

1. 硬件设计

上拉电阻选择:
- 计算公式:Rp = (VDD - VOL) / IOL
- 考虑总线电容
- 推荐值:4.7kΩ - 10kΩ

PCB布线:
- SCL和SDA走线尽量短
- 避免长距离走线
- 远离高频信号
- 添加地平面

ESD保护:
- 外部接口必须添加ESD保护
- 推荐使用TVS二极管
- 靠近连接器放置

电源去耦:
- 每个设备添加0.1μF去耦电容
- 靠近VCC引脚放置
- 添加10μF大电容

2. 软件设计

// 错误处理
typedef enum {
    SMBUS_OK = 0,
    SMBUS_ERROR_TIMEOUT,
    SMBUS_ERROR_NACK,
    SMBUS_ERROR_PEC,
    SMBUS_ERROR_BUS_BUSY,
    SMBUS_ERROR_INVALID_PARAM
} SMBus_Error_t;

// 统一的错误处理
SMBus_Error_t SMBus_Transaction(uint8_t addr, uint8_t cmd,
                                uint8_t *data, uint8_t len,
                                bool is_read) {
    // 参数检查
    if (len > 32) {
        return SMBUS_ERROR_INVALID_PARAM;
    }

    // 总线忙检查
    if (smbus_driver.busy) {
        return SMBUS_ERROR_BUS_BUSY;
    }

    smbus_driver.busy = true;

    // 执行事务
    bool result;
    if (is_read) {
        result = SMBus_BlockRead(addr, cmd, data, &len);
    } else {
        result = SMBus_BlockWrite(addr, cmd, data, len);
    }

    smbus_driver.busy = false;

    if (!result) {
        // 根据具体错误返回
        if (smbus_timeout.enabled && SMBus_IsTimeout()) {
            return SMBUS_ERROR_TIMEOUT;
        }
        return SMBUS_ERROR_NACK;
    }

    return SMBUS_OK;
}

3. 可靠性设计

// 重试机制
#define SMBUS_MAX_RETRIES 3

SMBus_Error_t SMBus_Transaction_Retry(uint8_t addr, uint8_t cmd,
                                      uint8_t *data, uint8_t len,
                                      bool is_read) {
    SMBus_Error_t error;

    for (int retry = 0; retry < SMBUS_MAX_RETRIES; retry++) {
        error = SMBus_Transaction(addr, cmd, data, len, is_read);

        if (error == SMBUS_OK) {
            return SMBUS_OK;
        }

        // 错误处理
        if (error == SMBUS_ERROR_TIMEOUT) {
            SMBus_BusReset();
        }

        // 延时后重试
        HAL_Delay(10);
    }

    return error;
}

// 看门狗保护
void SMBus_WatchdogTask(void) {
    static uint32_t last_activity = 0;
    uint32_t current_time = HAL_GetTick();

    // 检查总线活动
    if (smbus_driver.busy) {
        last_activity = current_time;
    }

    // 如果总线长时间忙,执行复位
    if (smbus_driver.busy && 
        (current_time - last_activity > 1000)) {
        printf("SMBus watchdog timeout, resetting bus\n");
        SMBus_BusReset();
        smbus_driver.busy = false;
    }
}

调试技巧

1. 逐步验证

验证步骤:
1. 硬件检查
   - 测量VCC电压
   - 检查SCL/SDA上拉
   - 验证连接

2. 基本通信
   - Quick Command
   - Send/Receive Byte
   - 验证ACK

3. 寄存器访问
   - Read/Write Byte
   - Read/Write Word
   - 验证数据

4. 高级功能
   - Block操作
   - PEC校验
   - 超时处理

2. 常见错误模式

错误模式识别:

1. 所有设备无响应
   → 检查上拉电阻
   → 检查电源
   → 检查GPIO配置

2. 特定设备无响应
   → 检查设备地址
   → 检查设备电源
   → 检查设备使能

3. 间歇性错误
   → 检查信号质量
   → 检查电源纹波
   → 检查EMI干扰

4. PEC错误
   → 检查PEC计算
   → 检查信号完整性
   → 降低速度测试

工具与资源

开发工具

1. 硬件工具

逻辑分析仪:
- Saleae Logic
- DSLogic
- PulseView + 廉价分析仪

特点:
- 支持I2C/SMBus协议解码
- 实时波形显示
- 触发和搜索功能
- 导出数据分析

SMBus适配器:
- Total Phase Aardvark
- Bus Pirate
- USB-to-SMBus适配器

功能:
- 主机模式
- 从机模拟
- 总线监控
- 脚本自动化

示波器:
- 测量信号质量
- 检查时序参数
- 分析噪声
- 验证电平

2. 软件工具

Linux SMBus工具:
- i2c-tools
  - i2cdetect: 扫描总线
  - i2cdump: 转储设备寄存器
  - i2cget: 读取寄存器
  - i2cset: 写入寄存器

使用示例:
# 扫描I2C总线0
i2cdetect -y 0

# 读取设备0x48的寄存器0x00
i2cget -y 0 0x48 0x00

# 写入设备0x48的寄存器0x01,值0x60
i2cset -y 0 0x48 0x01 0x60

# 转储设备0x50的所有寄存器
i2cdump -y 0 0x50

Python库:
- smbus2
- pysmbus
- python-smbus

示例代码:
```python
from smbus2 import SMBus

# 打开I2C总线
bus = SMBus(1)

# 读取字节
data = bus.read_byte_data(0x48, 0x00)
print(f"Temperature: {data}")

# 写入字节
bus.write_byte_data(0x48, 0x01, 0x60)

# 读取字
data = bus.read_word_data(0x48, 0x00)
print(f"Value: {data}")

# 块读取
data = bus.read_i2c_block_data(0x50, 0x00, 16)
print(f"Data: {data}")

bus.close()

参考资料

官方规范

SMBus规范文档:
1. SMBus Specification Version 3.0
   - 完整协议定义
   - 电气特性
   - 命令集
   - 下载:smbus.org

2. I2C-bus Specification
   - I2C基础协议
   - 与SMBus的关系
   - 下载:nxp.com

3. Smart Battery Data Specification
   - 智能电池协议
   - 命令定义
   - 数据格式

4. PMBus Specification
   - 电源管理总线
   - 基于SMBus
   - 电源控制命令

推荐书籍

1. "I2C Manual" - NXP
   - I2C协议详解
   - 硬件设计指南
   - 应用案例

2. "The I2C Bus: From Theory to Practice"
   - 理论基础
   - 实践应用
   - 故障排查

3. "Embedded Systems Design using the TI MSP430 Series"
   - 包含I2C/SMBus章节
   - 实际项目案例

在线资源

网站:
- smbus.org: SMBus官方网站
- i2c-bus.org: I2C协议资源
- embeddedrelated.com: 嵌入式论坛

教程:
- SparkFun I2C Tutorial
- Adafruit I2C/SMBus Guide
- Texas Instruments Application Notes

开源项目:
- Linux Kernel I2C/SMBus驱动
- Arduino Wire库
- Zephyr RTOS I2C驱动

常用芯片数据手册

SMBus控制器

1. Intel ICH系列
   - 集成SMBus控制器
   - PC主板常用

2. NXP PCA9564
   - 独立SMBus控制器
   - 并行接口

3. TI TCA9548A
   - I2C/SMBus多路复用器
   - 8通道

4. Microchip MCP2221
   - USB转I2C/SMBus
   - 开发调试用

SMBus设备

温度传感器:
- LM75 (NXP)
- TMP75 (TI)
- ADT7410 (Analog Devices)

电源管理:
- LTC3880 (Analog Devices)
- UCD9090 (TI)
- MAX34440 (Maxim)

EEPROM:
- 24C02/24C64 (Microchip)
- AT24C256 (Atmel)
- M24C64 (STMicroelectronics)

智能电池:
- BQ20Z75 (TI)
- MAX17055 (Maxim)

总结

关键要点

SMBus核心特性

  1. 基于I2C
  2. 兼容I2C物理层
  3. 增加系统管理特性
  4. 更严格的规范

  5. 超时机制

  6. 强制25-35ms超时
  7. 防止总线挂死
  8. 提高可靠性

  9. PEC校验

  10. CRC-8错误检测
  11. 可选功能
  12. 提高数据完整性

  13. 标准命令集

  14. Quick Command
  15. Byte/Word操作
  16. Block操作
  17. Process Call

  18. 应用领域

  19. 电源管理
  20. 电池管理
  21. 温度监控
  22. 系统管理

实现要点

硬件设计: - 正确选择上拉电阻 - 添加ESD保护 - 注意PCB布线 - 电源去耦

软件实现: - 实现完整的命令集 - 添加超时保护 - 支持PEC校验 - 错误处理和重试

调试技巧: - 使用逻辑分析仪 - 逐步验证功能 - 识别错误模式 - 记录事务日志

学习路径

初级阶段: 1. 理解I2C基础 2. 学习SMBus与I2C的区别 3. 实现基本命令(Quick Command、Byte操作) 4. 完成简单设备驱动(温度传感器)

中级阶段: 1. 实现完整命令集 2. 添加PEC支持 3. 实现超时机制 4. 开发复杂设备驱动(智能电池)

高级阶段: 1. 实现ARP协议 2. 支持Host Notify 3. 性能优化 4. 多主机仲裁

下一步学习

相关主题

  1. PMBus协议
  2. 基于SMBus的电源管理协议
  3. 更多电源控制命令
  4. 遥测和监控功能

  5. I3C协议

  6. 下一代I2C/SMBus
  7. 更高速度
  8. 更多功能

  9. CAN总线

  10. 另一种系统管理总线
  11. 更高可靠性
  12. 汽车应用

  13. SPI总线

  14. 更高速度
  15. 全双工通信
  16. 不同的应用场景

参考资料

  1. System Management Bus (SMBus) Specification Version 3.0, SBS Implementers Forum, 2014
  2. I2C-bus specification and user manual, NXP Semiconductors, Rev. 6, 2014
  3. Smart Battery Data Specification, Revision 1.1, SBS Implementers Forum, 1998
  4. Power Management Bus (PMBus) Specification, Part I and II, PMBus-IF, 2015
  5. Intel 8 Series/C220 Series Chipset Family Platform Controller Hub (PCH) Datasheet
  6. Texas Instruments, "Understanding the I2C Bus", Application Report SLVA704
  7. Analog Devices, "SMBus and I2C: Compatible or Not?", Technical Article MS-2469
  8. Microchip, "I2C Overview and Use of the PICmicro MSSP Module", AN735

相关主题