目录
1 概述
本文主要描述了i2c的使用以及分析了大部分i2c子系统源码和实现原理,本文以读者对i2c硬件原理已掌握为基础来描述,需要读者理解基础的i2c通信过程。
2 I2c子系统框架
从分层角度上看,i2c子系统大致分为设备驱动层client、i2c核心层和i2c适配器层,如下图大致描述了整个i2c应用和内核框架的关系逻辑,从上到下,用户可通过底层提供的总线设备或者外设设备来访问挂载在总线上的i2c设备。
i2c子系统向驱动层提供了i2c client,每一个i2c设备将被实现成一个client,设备驱动在拿到i2c_client后,即可通过该对象来读写i2c数据访问i2c设备。I2c核心层向下也提供了i2c适配器层,每一个硬件i2c都被实现成一个i2c adapter,主要负责向i2c核心层提供硬件操作接口
3 I2C的使用流程
本节讲i2c在驱动层如何被调用使用,没有特殊说明,均认为i2c作为master,后面章节将介绍i2c作为slave的用法。
3.1 在驱动里使用
在i2c的驱动应用中,比较常见的是先在设备树里的i2c节点挂在硬件上挂在到该i2c总线的i2c设备,例如一个sensor的节点:
&i2c2 {
status = "okay";
...
ov5695: ov5695@36 {
compatible = "ovti,ov5695";
reg = <0x36>;
avdd-supply = <&vcc2v8_dvp>;
clocks = <&cru SCLK_CIF_OUT>;
clock-names = "xvclk";
dvdd-supply = <&vcc1v5_dvp>;
dovdd-supply = <&vcc1v8_dvp>;
pinctrl-names = "default";
pinctrl-0 = <&cif_clkout_m0 &mipi_pdn>;
reset-gpios = <&gpio2 RK_PB6 GPIO_ACTIVE_LOW>;
...
};
};
然后在sensor的驱动里,调用i2c_register_driver将自己定义好的struct i2c_driver传入该接口,i2c总线将会调用我们自定义好的probe函数,让设备驱动程序加载起来,有时也会用i2c注册宏module_i2c_driver来做。
static struct i2c_driver ov5695_i2c_driver = {
.driver = {
.name = "ov5695",
.pm = &ov5695_pm_ops,
.of_match_table = of_match_ptr(ov5695_of_match),
},
.probe = ov5695_probe,
.remove = ov5695_remove,
};
module_i2c_driver(ov5695_i2c_driver);
----------------------或者-----------------------
static int mpu6050_driver_init(void)
{
i2c_add_driver(&mpu6050_driver);
return 0;
}
static void mpu6050_driver_exit(void)
{
i2c_del_driver(&mpu6050_driver);
}
module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
之后将进入probe函数,传入的i2c_client结构体,用于i2c数据收发
static int ov5695_probe(struct i2c_client *client)
{
}
static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len, u32 *val)
{
struct i2c_msg msgs[2];
...
int ret;
if (len > 4)
return -EINVAL;
data_be_p = (u8 *)&data_be;
/* Write register address */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = (u8 *)®_addr_be;
/* Read data from register */
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = len;
msgs[1].buf = &data_be_p[4 - len];
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
...
return 0;
}
3.2 在应用层使用
I2c子系统同时提供了每个i2c总线的设备,应用可以直接打开i2c总线设备,传入从机地址来进行通信
int fd = open("/dev/i2c-0", O_RDWR);
int i2c_write(uint8_t slave, uint8_t reg, uint8_t * data, int len)
{
unsigned char buf[1024];
struct i2c_rdwr_ioctl_data i2c_data;
struct i2c_msg i2c_msg;
i2c_data.nmsgs = 1;
i2c_data.msgs = &i2c_msg ;
ioctl(_fd, I2C_TIMEOUT, 1);
ioctl(_fd, I2C_RETRIES, 2);
memset(buf, 0, 1024);
buf[0] = reg;
memcpy(&buf[1], buf, len);
i2c_data.msgs[0].addr = slave;
i2c_data.msgs[0].flags = 0;
i2c_data.msgs[0].buf = &buf[0];
i2c_data.msgs[0].len = len+1;
int ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);
...
return 0;
}
int i2c_read(int8_t slave, int8_t reg, uint8_t * data, int len)
unsigned char buf[2];
struct i2c_rdwr_ioctl_data i2c_data;
struct i2c_msg i2c_msg[2] ;
i2c_data.nmsgs = 2;
i2c_data.msgs = &i2c_msg[0] ;
ioctl(_fd, I2C_TIMEOUT, 1);
ioctl(_fd, I2C_RETRIES, 2);
buf[0] = reg ;
i2c_data.msgs[0].addr = slave;
i2c_data.msgs[0].flags = 0;
i2c_data.msgs[0].buf = &buf[0];
i2c_data.msgs[0].len = 1;
i2c_data.msgs[1].addr = slave;
i2c_data.msgs[1].flags = 1;
i2c_data.msgs[1].buf = data;
i2c_data.msgs[1].len = le