Linux 内核中的 I2C 子系统 提供了对 I2C(Inter-Integrated Circuit)总线的完整支持,包括 I2C 控制器驱动(Adapter)、设备驱动(Client Driver) 和 用户空间访问接口。
1. I2C 子系统架构
Linux I2C 子系统采用分层设计,主要分为以下部分:
层级 | 功能 |
---|---|
I2C Core | 核心层,提供注册/注销 Adapter 和 Client 的 API |
I2C Bus | 管理 I2C 设备与驱动的匹配(基于 i2c_device_id 或 of_device_id ) |
I2C Adapter | I2C 控制器驱动(如 i2c-imx 、i2c-s3c2410 ) |
I2C Client | I2C 设备驱动(如 lm75 温度传感器驱动) |
用户空间接口 | 通过 /dev/i2c-* 或 sysfs 访问 I2C 设备 |
2. I2C 核心数据结构
(1) struct i2c_adapter
代表一个 I2C 控制器(如 SoC 的 I2C 接口),关键字段:
struct i2c_adapter { struct device dev; // 关联的设备 const struct i2c_algorithm *algo; // 通信方法(如 master_xfer) int nr; // 适配器编号(如 i2c-0) char name[48]; // 适配器名称(如 "i2c-imx") struct i2c_bus_recovery_info *bus_recovery_info; // 总线恢复 };
(2) struct i2c_client
代表一个 I2C 设备,关键字段:
struct i2c_client { unsigned short addr; // 7-bit 设备地址(如 0x48) char name[I2C_NAME_SIZE]; // 设备名称(如 "lm75") struct i2c_adapter *adapter; // 所属的 I2C 控制器 struct device dev; // 设备模型 };
(3) struct i2c_driver
I2C 设备驱动,关键方法:
struct i2c_driver { int (*probe)(struct i2c_client *); // 设备探测 int (*remove)(struct i2c_client *); // 设备移除 const struct of_device_id *of_match_table; // 设备树匹配表 const struct i2c_device_id *id_table; // 设备 ID 表 };
3. I2C 设备驱动开发
(1) 设备树(Device Tree)描述
&i2c1 { status = "okay"; clock-frequency = <100000>; // 100kHz lm75: temperature-sensor@48 { compatible = "national,lm75"; reg = <0x48>; // I2C 地址 }; };
(2) 驱动代码示例
#include <linux/i2c.h> #include <linux/module.h> static int lm75_probe(struct i2c_client *client) { u8 reg_val[2]; int temp; // 读取温度(LM75 的温度寄存器是 0x00) i2c_smbus_read_i2c_block_data(client, 0x00, 2, reg_val); temp = (reg_val[0] << 8) | reg_val[1]; temp >>= 7; // LM75 温度数据格式 printk("Temperature: %d°C\n", temp); return 0; } static const struct of_device_id lm75_of_match[] = { { .compatible = "national,lm75" }, { } }; MODULE_DEVICE_TABLE(of, lm75_of_match); static struct i2c_driver lm75_driver = { .driver = { .name = "lm75", .of_match_table = lm75_of_match, }, .probe = lm75_probe, }; module_i2c_driver(lm75_driver);
4. 用户空间访问 I2C
(1) 通过 sysfs
访问
# 查看所有 I2C 适配器 ls /sys/bus/i2c/devices/ # 扫描 I2C-1 总线上的设备 i2cdetect -y 1 # 读取 I2C-1 上地址 0x48 的寄存器 0x00 i2cget -y 1 0x48 0x00
(2) 通过 /dev/i2c-*
使用 ioctl
#include <linux/i2c-dev.h> #include <fcntl.h> int fd = open("/dev/i2c-1", O_RDWR); ioctl(fd, I2C_SLAVE, 0x48); // 设置从机地址 uint8_t reg = 0x00; uint8_t buf[2]; write(fd, ®, 1); // 发送要读取的寄存器 read(fd, buf, 2); // 读取 2 字节数据 close(fd);
5. I2C 子系统调试
(1) 内核配置
确保启用:
CONFIG_I2C=y CONFIG_I2C_CHARDEV=y # 提供 /dev/i2c-* 接口 CONFIG_I2C_DEBUG_CORE=y # 调试支持
(2) 动态调试
# 启用 I2C 核心调试信息 echo 8 > /proc/sys/kernel/printk dmesg | grep i2c
(3) 逻辑分析仪
-
使用 Saleae Logic 或 PulseView 抓取 I2C 波形,检查 SCL/SDA 信号。
6. 常见问题
(1) 设备未被检测到
-
检查
i2cdetect
是否能发现设备。 -
确认设备树(
compatible
和reg
)是否正确。
(2) 通信失败
-
检查
clock-frequency
是否匹配设备要求。 -
测量 SCL/SDA 电压(标准 I2C 是 3.3V/5V)。
(3) 驱动加载但未绑定设备
-
检查
of_match_table
或id_table
是否匹配。
7. 典型应用场景
场景 | 示例驱动 |
---|---|
温度传感器 | lm75 、tmp102 |
RTC 时钟 | ds1307 、pcf8563 |
EEPROM | at24 |
触摸屏 | ft5x06 |
PMIC 电源管理 | max77650 |
总结
-
I2C Core 提供核心 API(注册 Adapter/Client)。
-
I2C Adapter 对应 SoC 的 I2C 控制器。
-
I2C Client 代表具体的 I2C 设备。
-
可通过 设备树 或 静态声明 注册设备。
-
用户空间可通过
i2c-tools
或/dev/i2c-*
访问设备。