linux设备平台驱动之I2C驱动
i2c硬件特点和读写流程
- 协议
- 双线双向串行传输,数据线
SDA
,时钟线SCL
;SDA
大端传输,每次传输一个字节(8bit) - 读写寄存器地址
addr
分为固定部分和可编程部分,需要查询芯片datasheet
- 读写时序
- 开始传输(
SCL=1
,SDA
由高变低) - 结束传输(
SCL=1
,SDA
由低变高) - 传输应答: master发送完8bit data之后就会在第九个clock等待
ACK
,收到SDA=0
,否则SDA=1
- 数据传输(
SCL=1
,SDA
稳定,无跳变) - 数据改变(
SCL=0
,SDA
改变传输的bit)
- 开始传输(
- 双线双向串行传输,数据线
- 读写流程
- 写
- master发起开始传输信号
- master发出i2c addr(7bit)和W操作(1bit),等待
ACK
- slave发送
ACK
- master发送reg add(8bit),等到
ACK
- slave发送
ACK
- master发送reg data(8bit),等到
ACK
- slave发送
ACK
- master发起结束传输信号
- 读
- master发起开始传输信号
- master发出i2c addr(7bit)和W操作(1bit),等待
ACK
- slave发送
ACK
- master发送reg add(8bit),等到
ACK
- slave发送
ACK
- master发起结束传输信号
- master发起开始传输信号
- master发出i2c addr(7bit)和R操作(1bit),等待
ACK
- slave发送
ACK
- slave发送reg data(8bit),等到
ACK
- master发起
ACK
- 写
编写I2C客户端驱动程序需要的主体步骤
- 1.申明驱动程序支持的设备ID,可以使用
i2c_device_id
声明- 如果支持设备树,可以使用
of_device_id
static const struct i2c_device_id myi2c_ids[] = { {"myi2c", 0}, {}, }; //对于设备树的open firmware(OF)匹配机制,使用of_device_id static const struct of_device_id of_myi2c_id[] = { {.compatible = "buff,myi2c",}, {}, };
- 如果支持设备树,可以使用
- 2.调用
MODULE_DEVICE_TABLE(i2c, myi2c_ids)
向I2C内核设备注册设备列表- 如果支持设备树,则必须使用
MODULE_DEVICE_TABLE(of, of_lm3643_id)
MODULE_DEVICE_TABLE(i2c, myi2c_ids); MODULE_DEVICE_TABLE(of, of_myi2c_id);
- 如果支持设备树,则必须使用
- 3.按照实际需求编写
probe
和remove
函数static int myi2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { xxx } static int myi2c_remove(struct i2c_client *client) { xxx }
- 4.声明并且填充
struct i2c_driver
, 用已经创建的id数组(lm3643_ids
)设置结构体中的id_table
,填充其他变量struct i2c_driver my_i2c_driver = { .driver = { .name = MYI2C_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_myi2c_id), }, .probe = myi2c_probe, .remove = myi2c_remove, .id_table = myi2c_ids, };
- 5.使用填充完毕的
struct i2c_driver
调用module_i2c_driver
函数module_i2c_driver(&my_i2c_driver);
- 或者使用传统驱动初始化函数
static int __init myi2c_init(void) { xxx //核心函数 i2c_add_driver(&my_i2c_driver); xxx } static void __exit myi2c_exit(void) { xxx i2c_del_driver(&my_i2c_driver); xxx } module_init(myi2c_init); //i2c_add_driver module_exit(myi2c_exit); //i2c_del_driver
i2c客户端驱动读写接口实现
依据上文中的读写流程,I2C client读写存在区别
struct myi2c_dev_reg {
u32 dev_id; /*!< Device ID */
u8 addr; /*!< Register DRAM address */
u32 data; /*!< Register Value */
};
int myi2c_read_data(struct i2c_client *client, struct myi2c_dev_reg *reg)
{
unsigned char buf[2];
struct i2c_msg msg[2];
int rval = 0;
//smbus可以使用i2c总线协议进行信号传输,反之不行
if (client->adapter->algo->master_xfer != NULL) {
msg[0].addr = client->addr;
msg[0].buf = ®->addr;
msg[0].len = sizeof(reg->addr);
msg[0].flags = 0;
msg[1].addr = client->addr;
msg[1].buf = buf;
msg[1].len = sizeof(buf);
msg[1].flags = I2C_M_RD;
rval = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ARRAY_SIZE(msg) == rval) {
rval = buf[1];
reg->data = (rval << 8) | buf[0];
}
} else if (client->adapter->algo->smbus_xfer != NULL) {
rval = i2c_smbus_read_word_data(client, reg->addr);
} else {
rval = -EOPNOTSUPP;
}
return rval;
}
int myi2c_write_data(struct i2c_client *client, struct myi2c_dev_reg *reg)
{
struct i2c_msg msg;
char msg_count = 1;
unsigned char buf[3];
int rval = 0;
if (client->adapter->algo->master_xfer != NULL) {
//i2c一次只能传8bit的数据,所以只能按照8bit的长度截
buf[0] = reg->addr;
buf[1] = reg->data & 0xff;
buf[2] = (reg->data >> 8) & 0xff;
msg.addr = client->addr;
msg.buf = buf;
msg.len = sizeof(buf);
msg.flags = 0;
rval = i2c_transfer(client->adapter, &msg, msg_count);
if (msg_count == rval) {
rval = 0;
}
} else if (client->adapter->algo->smbus_xfer != NULL) {
rval = i2c_smbus_write_word_data(client, reg->addr, reg->data);
} else {
rval = -EOPNOTSUPP;
}
return rval;
}