I2C 驱动
一、Linux I2C 子系统架构
应用(ioctl/read/write)
↓
i2c-dev(/dev/i2c-N)→ 用户态驱动
↓
i2c-core(i2c.h)
↓
i2c_adapter(adapter)→ 总线驱动(如 i2c-imx.c、stm32f7-i2c.c)
↓
i2c_client → 设备驱动(如 mysensor.c)
↓
I2C 总线硬件
二、关键结构
1. i2c_adapter(总线控制器)
struct i2c_adapter {
struct module *owner;
const struct i2c_algorithm *algo;
struct device dev;
int nr;
char name[48];
// ...
};
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size, union i2c_smbus_data *data);
u32 (*functionality)(struct i2c_adapter *adap);
};
2. i2c_client(设备实例)
struct i2c_client {
unsigned short addr; // 7-bit 地址
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter;
struct device dev;
struct i2c_driver *driver;
// ...
};
3. i2c_driver
struct i2c_driver {
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
const struct i2c_device_id *id_table;
const struct of_device_id *of_match_table;
struct device_driver driver;
};
4. i2c_msg
struct i2c_msg {
__u16 addr; // 7-bit 地址
__u16 flags;
#define I2C_M_RD 0x01
#define I2C_M_TEN 0x10
#define I2C_M_DMA_SAFE 0x20
#define I2C_M_RECV_LEN 0x40
#define I2C_M_NO_RD_ACK 0x0800
__u16 len;
__u8 *buf;
};
三、设备驱动示例
1. 设备树
&i2c1 {
clock-frequency = <100000>;
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>;
pagesize = <8>;
size = <256>;
address-width = <8>;
};
mysensor@68 {
compatible = "myvendor,mysensor";
reg = <0x68>;
interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
};
};
2. 驱动代码
#include <linux/i2c.h>
#include <linux/module.h>
struct my_priv {
struct i2c_client *client;
struct mutex lock;
};
static int my_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct my_priv *priv;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) return -ENOMEM;
priv->client = client;
mutex_init(&priv->lock);
i2c_set_clientdata(client, priv);
// 探测设备(读 ID 寄存器)
u8 id;
int ret = i2c_smbus_read_byte_data(client, 0x00);
if (ret < 0) return ret;
id = ret;
if (id != 0xAB) return -ENODEV;
dev_info(&client->dev, "found mysensor id=0x%02x\n", id);
return 0;
}
static int my_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id my_id[] = {
{ "mysensor", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, my_id);
static const struct of_device_id my_of[] = {
{ .compatible = "myvendor,mysensor" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of);
static struct i2c_driver my_driver = {
.driver = {
.name = "mysensor",
.of_match_table = my_of,
},
.probe = my_probe,
.remove = my_remove,
.id_table = my_id,
};
module_i2c_driver(my_driver);
MODULE_LICENSE("GPL");
四、I2C 通信 API
1. 标准 I2C
int i2c_master_send(struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(struct i2c_client *client, char *buf, int count);
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
// 多消息示例(带寄存器地址的读)
u8 reg = 0x10, val;
struct i2c_msg msg[2] = {
{ .addr = client->addr, .len = 1, .buf = ® }, // 写寄存器地址
{ .addr = client->addr, .len = 1, .buf = &val, .flags = I2C_M_RD }, // 读
};
i2c_transfer(client->adapter, msg, 2);
2. SMBus
i2c_smbus_read_byte(client);
i2c_smbus_write_byte(client, value);
i2c_smbus_read_byte_data(client, reg);
i2c_smbus_write_byte_data(client, reg, value);
i2c_smbus_read_word_data(client, reg);
i2c_smbus_read_i2c_block_data(client, reg, count, values);
i2c_smbus_write_i2c_block_data(client, reg, count, values);
3. 内联读写辅助宏
#include <linux/i2c.h>
static inline int i2c_read_reg(struct i2c_client *c, u8 reg, u8 *val)
{
struct i2c_msg msg[2] = {
{ .addr = c->addr, .len = 1, .buf = ® },
{ .addr = c->addr, .len = 1, .buf = val, .flags = I2C_M_RD },
};
return i2c_transfer(c->adapter, msg, 2) == 2 ? 0 : -EIO;
}
static inline int i2c_write_reg(struct i2c_client *c, u8 reg, u8 val)
{
u8 buf[2] = { reg, val };
struct i2c_msg msg = { .addr = c->addr, .len = 2, .buf = buf };
return i2c_transfer(c->adapter, &msg, 1) == 1 ? 0 : -EIO;
}
五、I2C 适配器(总线驱动)API
总线驱动实现 master_xfer:
static int my_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
// 启动 I2C 控制器,等待完成
// 实现可能用中断、DMA 等
}
static u32 my_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_I2C_BLOCK;
}
static const struct i2c_algorithm my_algo = {
.master_xfer = my_xfer,
.functionality = my_func,
};
static int my_probe(struct platform_device *pdev)
{
struct i2c_adapter *adap = &my_i2c->adap;
adap->algo = &my_algo;
adap->dev.parent = &pdev->dev;
adap->nr = pdev->id;
i2c_add_adapter(adap);
// ...
}
六、调试工具
# 扫描总线
i2cdetect -l # 列出适配器
i2cdetect -y 1 # 扫描 /dev/i2c-1
i2cdetect -y -r 1 # 快速扫描
# 读写
i2cget -y 1 0x50 0x10 # 读寄存器
i2cset -y 1 0x50 0x10 0x55 # 写寄存器
i2cdump -y 1 0x50 # dump 所有寄存器
i2ctransfer -y 1 w2@0x50 0x10 0xAB r4
# 内核调试
cat /sys/bus/i2c/devices/1-0050/
dmesg | grep i2c
七、用户态驱动(i2c-dev)
int fd = open("/dev/i2c-1", O_RDWR);
ioctl(fd, I2C_SLAVE, 0x50); // 从设备地址
i2c_smbus_read_byte_data(fd, 0x10);
适合:原型、调试、不想写内核模块时。
八、踩坑清单
| 现象 | 原因 |
|---|---|
| No such device | 地址错 / 设备未上电 |
| IO error | 总线被锁 / 接线错 / 上拉电阻 |
| 数据错 | 速率太高 / 信号干扰 |
| 中断不触发 | GPIO 没配中断功能 |