一、 Linux I²C 驱动模型简介
1. 三大核心结构
结构体 | 作用 |
---|---|
i2c_adapter | 表示一条 I²C 总线(硬件或虚拟) |
i2c_client | 表示挂在总线上的一个 I²C 设备 |
i2c_driver | 表示驱动程序,用于识别并驱动 i2c_client |
二、 I²C 驱动与设备匹配机制
方式一:通过设备树匹配
✅ 设备树节点
&i2c0 {
ap3216c@1e {
compatible = "lite-on,ap3216c";
reg = <0x1e>;
};
};
✅ 驱动中匹配方式
static const struct of_device_id of_match_ids_ap3216c[] = {
{ .compatible = "lite-on,ap3216c" },
{ }
};
static struct i2c_driver i2c_ap3216c_driver = {
.driver = {
.name = "ap3216c", // 也可用于调试信息显示
.of_match_table = of_match_ids_ap3216c,
},
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.id_table = ap3216c_ids,
};
⚠️ 驱动中的
.name
不直接参与匹配,但用于日志和 fallback 名称识别。(会在某些兜底机制(fallback)中参与匹配)
方式二:手动注册 i2c_client
适用于平台不支持设备树或快速调试场景。
static struct i2c_board_info board_info = {
I2C_BOARD_INFO("ap3216c", 0x1e),
};
struct i2c_adapter *adapter = i2c_get_adapter(0);
struct i2c_client *client = i2c_new_device(adapter, &board_info);
匹配依赖 i2c_device_id
:
static const struct i2c_device_id ap3216c_ids[] = {
{ "ap3216c", 0 },
{ }
};
三、 GPIO 模拟 I²C(Bit-Banging)
1. 原理:用 GPIO 手动控制 SDA/SCL 电平
- SDA、SCL 都是 GPIO
- 使用
udelay()
控制时序 - 可封装
start()
/stop()
/send_byte()
/recv_byte()
等函数
2. 优点与缺点
优点 | 缺点 |
---|---|
不依赖硬件控制器 | 占用 CPU |
简单、通用 | 时序受限,效率低 |
3. 示例函数框架(伪代码)
void i2c_start(void) {
sda_high(); scl_high(); udelay(5);
sda_low(); udelay(5);
scl_low(); udelay(5);
}
void i2c_stop(void) {
sda_low(); scl_high(); udelay(5);
sda_high(); udelay(5);
}
4. 利用 Linux i2c-gpio
框架
DTS 配置
i2c_gpio: i2c-gpio {
compatible = "i2c-gpio";
gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>, <&gpio1 11 GPIO_ACTIVE_HIGH>; // SDA, SCL
i2c-gpio,delay-us = <5>;
};
自动创建 /dev/i2c-X
节点
modprobe i2c-gpio-custom bus0=0,10,11
四、 软件模拟 I²C 总线(虚拟 I²C)
✅ 1. 本质:注册一个自定义的 i2c_adapter
,告诉内核“这是条 I²C 总线”
g_adapter = kzalloc(...);
g_adapter->algo = &i2c_bus_virtual_algo;
i2c_add_adapter(g_adapter); // 内核正式登记这条总线
✅ 2. 定义通信算法 i2c_algorithm
const struct i2c_algorithm i2c_bus_virtual_algo = {
.master_xfer = i2c_bus_virtual_master_xfer, // 收发数据的函数
.functionality = i2c_bus_virtual_func,
};
这告诉内核这条“虚拟 I²C 总线”怎么通信、支持哪些功能。
✅ 3. 配合设备树创建总线和子设备
i2c_bus_virtual@0 {
compatible = "100ask,i2c-bus-virtual";
#address-cells = <1>;
#size-cells = <0>;
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
};
};
✅ 4. 驱动行为流程总结
步骤 | 描述 |
---|---|
1 | 设备树中匹配 compatible |
2 | platform 驱动注册触发 probe |
3 | 在 probe 中创建 i2c_adapter 并注册 |
4 | 内核根据设备树中挂载的子节点创建 i2c_client |
5 | i2c_driver 匹配到 i2c_client ,调用对应 probe |
6 | 数据读写通过 master_xfer 模拟转发到软件逻辑中 |
✅ 五、统一总结一句话:
只要你实现了并注册了
i2c_adapter
,无论底层是硬件、GPIO、甚至纯软件模拟,Linux 内核就认为你提供了一条合法的 I²C 总线。
小结对比表:各种 I²C 实现方式
实现方式 | 控制方式 | 是否真实硬件 | 是否注册 i2c_adapter | 场景推荐 |
---|---|---|---|---|
硬件 I²C | SoC 控制器 | 是 | 是 | 正式产品 |
GPIO 模拟 I²C | 控制 GPIO | 否 | 是(通过 i2c-gpio ) | 没有硬件 I²C |
软件虚拟 I²C | 纯软件逻辑 | 否 | 是 | 测试、模拟器、教学 |