一直都在说IIC里面的SMBUS,单好像一直都没有使用过,所以想查查资料,看看SMBUS到底是啥?干啥用的?
一、SMBUS 典型应用场合
SMBUS(系统管理总线)是 I2C 的子集,更强调 "系统管理" 和 "可靠性",核心应用在需要低速、短距离、低成本的硬件监控 / 管理场景,常见场景如下:
-
硬件健康监控
服务器、工控机、主板中监控核心电压、CPU / 芯片温度、风扇转速(符合 IPMI/PC99 规范),比如通过 SMBUS 读取电源管理芯片 (PMIC) 的电压数据。
-
电池管理系统(BMS)
笔记本、手机、无人机的锂电池管理,通过 SMBUS 读取电池的剩余电量 (SoC)、循环次数、温度、充放电状态。
-
低速外设通信
智能家居 / 工业场景中的温湿度传感器、电流传感器、EEPROM、LED 驱动芯片(要求通信简单且功耗低)。
-
系统配置管理
主板 / 外设的配置信息读取(如内存 SPD 芯片、扩展卡的硬件参数),无需高速通信,优先保证稳定性。
二、HAL 库版 STM32 SMBUS 驱动(以 STM32F103 为例)
SMBUS 基于 I2C 外设实现,HAL 库下只需将 I2C 配置为 SMBUS 兼容时序(100KHz、开漏输出、7 位地址)即可。以下是完整驱动,兼容 STM32F1/F4/F7/H7 系列(仅 CubeMX 配置略有差异)。
前置准备(CubeMX 基础配置)
先通过 STM32CubeMX 完成基础配置:
-
选择对应 STM32 型号,配置系统时钟(如 72MHz)。
-
找到 I2C 外设(如 I2C1),模式选择
SMBus Host(或 I2C,SMBUS 兼容)。 -
配置 SCL/SDA 引脚(如 PB6/PB7):模式为
GPIO_Output_OD(开漏输出)、上拉使能。 -
配置 I2C 参数:时钟频率 100KHz、地址位 7 位、应答使能、占空比 2:1。
-
生成代码(选择 HAL 库,MDK-ARM 工程)。
-
头文件(smbus_hal.h)
cpp
#ifndef __SMBUS_HAL_H
#define __SMBUS_HAL_H
#include "main.h" // 包含CubeMX生成的核心头文件
#include "i2c.h" // I2C HAL库头文件
// 硬件宏定义(与CubeMX配置一致)
#define SMBUS_HANDLE hi2c1 // CubeMX生成的I2C句柄(如hi2c1/hi2c2)
#define SMBUS_SLAVE_ADDR 0xA0 // 从设备地址(7位地址左移1位,按需修改)
// 函数声明
uint8_t SMBUS_WriteByte(uint8_t slave_addr, uint8_t reg_addr, uint8_t data); // 单字节写
uint8_t SMBUS_ReadByte(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data); // 单字节读
uint8_t SMBUS_WriteBytes(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t len); // 多字节写
uint8_t SMBUS_ReadBytes(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t len); // 多字节读
#endif /* __SMBUS_HAL_H */
- 源文件(smbus_hal.c)
cpp
#include "smbus_hal.h"
/**
* @brief SMBUS单字节写操作(HAL库版)
* @param slave_addr: 从设备地址(7位左移1位)
* @param reg_addr: 寄存器地址
* @param data: 待写入数据
* @retval 0:成功,其他:HAL库错误码(如1=超时、2=参数错误)
*/
uint8_t SMBUS_WriteByte(uint8_t slave_addr, uint8_t reg_addr, uint8_t data)
{
HAL_StatusTypeDef status;
// 检查参数有效性
if (slave_addr == 0 || reg_addr > 0xFF) return 2;
// HAL库I2C写:地址+寄存器+数据(SMBUS核心流程)
status = HAL_I2C_Mem_Write(&SMBUS_HANDLE, slave_addr, reg_addr,
I2C_MEMADD_SIZE_8BIT, &data, 1, 100); // 100ms超时
return (status == HAL_OK) ? 0 : 1; // 0成功,1超时/失败
}
/**
* @brief SMBUS单字节读操作(HAL库版)
* @param slave_addr: 从设备地址
* @param reg_addr: 寄存器地址
* @param data: 存储读取数据的指针
* @retval 0:成功,其他:错误码
*/
uint8_t SMBUS_ReadByte(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data)
{
HAL_StatusTypeDef status;
// 检查参数有效性
if (data == NULL || slave_addr == 0 || reg_addr > 0xFF) return 2;
// HAL库I2C读:地址+寄存器+读取数据
status = HAL_I2C_Mem_Read(&SMBUS_HANDLE, slave_addr, reg_addr,
I2C_MEMADD_SIZE_8BIT, data, 1, 100); // 100ms超时
return (status == HAL_OK) ? 0 : 1;
}
/**
* @brief SMBUS多字节写操作
* @param slave_addr: 从设备地址
* @param reg_addr: 起始寄存器地址
* @param data: 数据缓冲区
* @param len: 数据长度
* @retval 0:成功,其他:错误码
*/
uint8_t SMBUS_WriteBytes(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t len)
{
HAL_StatusTypeDef status;
if (data == NULL || len == 0 || slave_addr == 0) return 2;
status = HAL_I2C_Mem_Write(&SMBUS_HANDLE, slave_addr, reg_addr,
I2C_MEMADD_SIZE_8BIT, data, len, 100);
return (status == HAL_OK) ? 0 : 1;
}
/**
* @brief SMBUS多字节读操作
* @param slave_addr: 从设备地址
* @param reg_addr: 起始寄存器地址
* @param data: 接收缓冲区
* @param len: 读取长度
* @retval 0:成功,其他:错误码
*/
uint8_t SMBUS_ReadBytes(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data, uint8_t len)
{
HAL_StatusTypeDef status;
if (data == NULL || len == 0 || slave_addr == 0) return 2;
status = HAL_I2C_Mem_Read(&SMBUS_HANDLE, slave_addr, reg_addr,
I2C_MEMADD_SIZE_8BIT, data, len, 100);
return (status == HAL_OK) ? 0 : 1;
}
- 测试示例(main.c 中调用)
cpp
#include "smbus_hal.h"
int main(void)
{
/* 1. CubeMX生成的初始化代码(无需修改) */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init(); // I2C/SMBUS初始化
/* 2. SMBUS测试 */
uint8_t write_data = 0x66;
uint8_t read_data;
uint8_t write_buf[3] = {0x01, 0x02, 0x03};
uint8_t read_buf[3];
uint8_t ret;
// 单字节写
ret = SMBUS_WriteByte(SMBUS_SLAVE_ADDR, 0x01, write_data);
if (ret == 0) {
// 写成功
}
// 单字节读
ret = SMBUS_ReadByte(SMBUS_SLAVE_ADDR, 0x01, &read_data);
if (ret == 0) {
// 读成功,read_data即为读取的值
}
// 多字节写
ret = SMBUS_WriteBytes(SMBUS_SLAVE_ADDR, 0x02, write_buf, 3);
// 多字节读
ret = SMBUS_ReadBytes(SMBUS_SLAVE_ADDR, 0x02, read_buf, 3);
/* 3. 主循环 */
while (1)
{
// 循环执行
HAL_Delay(1000);
}
}
三、关键说明(HAL 库版 vs 标准库版)
-
核心 API 差异
HAL 库封装了底层 I2C 操作,直接使用
HAL_I2C_Mem_Write/Read即可实现 SMBUS 读写,无需手动处理起始 / 停止信号、应答等(由 HAL 库自动完成)。 -
错误处理
驱动中返回 0 表示成功,1 表示超时 / 通信失败,2 表示参数错误,便于调试。
-
兼容性
若使用 STM32F4/F7/H7,仅需修改
SMBUS_HANDLE(如 hi2c2),核心函数完全通用。 -
硬件要求
SCL/SDA 引脚必须为开漏输出 + 上拉(CubeMX 中配置为 GPIO_Output_OD),否则 SMBUS 通信会异常。
总结
-
SMBUS 主要用于硬件监控(电压 / 温度)、电池管理、低速外设通信等场景,核心是 "稳定" 而非 "高速"。
-
HAL 库版 SMBUS 驱动基于
HAL_I2C_Mem_Write/Read封装,无需手动处理总线时序,比标准库更简洁。 -
使用前需通过 CubeMX 配置 I2C 为 SMBUS 兼容模式(100KHz、7 位地址、开漏引脚),驱动函数直接调用即可。
如果需要适配特定从设备(如电池管理芯片、传感器),只需修改SMBUS_SLAVE_ADDR和寄存器地址,核心读写逻辑无需调整。