linux kernel 中的i2c-gpio使用
在linux中,我们可以自己来使用gpio来模拟i2c,但是万能的linux中其实也已经有了i2c的东东了。
现在看看i2c-gpio.c文件中到底提供了哪些接口
static void i2c_gpio_setsda_dir(void *data, int state) static void i2c_gpio_setsda_val(void *data, int state) static void i2c_gpio_setscl_dir(void *data, int state) static void i2c_gpio_setscl_val(void *data, int state) static int i2c_gpio_getsda(void *data) static int i2c_gpio_getscl(void *data)
一看上面的接口,相当明确,跟普通的gpio模拟i2c所实现的接口一致,是实现的gpio的sda,scl的设置,仅此而已。
在kernel/driver/i2c/algos/i2c-algo-bit.c中是系统中默认提供的gpio模拟i2c的框架代码。
static inline void sdalo(struct i2c_algo_bit_data *adap) static inline void sdahi(struct i2c_algo_bit_data *adap) static inline void scllo(struct i2c_algo_bit_data *adap) static int sclhi(struct i2c_algo_bit_data *adap) static void i2c_start(struct i2c_algo_bit_data *adap) static void i2c_repstart(struct i2c_algo_bit_data *adap) static void i2c_stop(struct i2c_algo_bit_data *adap) static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) static int i2c_inb(struct i2c_adapter *i2c_adap) static int test_bus(struct i2c_algo_bit_data *adap, char *name) static int try_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) static int acknak(struct i2c_adapter *i2c_adap, int is_ack) static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) static int bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) /* -----exported algorithm data: ------------------------------------- */ static const struct i2c_algorithm i2c_bit_algo = { .master_xfer = bit_xfer, .functionality = bit_func, };
这里对于任何一个做过gpio模拟i2c模拟的童鞋来说都没有陌生的东西,不明白的可以看看i2c的协议,比我说的清楚。
这里说下出现的struct i2c_algorithm,其中的master_xfer就是我们调用i2c_master_send/i2c_master_recv的时候最终调用的接口。
下面说说如何使用此i2c-gpio的框架代码
1、 查看board.c文件,先看看系统中的i2c的注册是在哪里注册的,添加如下
static struct i2c_gpio_platform_data i2c_bus_data = { .sda_pin = GPIO103, .scl_pin = GPIO102, .udelay = 20, .timeout = 100, .sda_is_open_drain = 1, .scl_is_open_drain = 1, }; static struct platform_device arch_device_i2c[] = { { .name = "arch-i2c-1", .id = 0, .num_resources = ARRAY_SIZE(arch_resource), .resource = arch_resource, }, { .name = "arch-i2c-2", .id = 1, .num_resources = ARRAY_SIZE(arch_resource), .resource = arch_resource, }, //add { .name = "i2c-gpio", .id = 2, .dev = { .platform_data = &i2c_bus_data, } }, //end add };
这里看到了系统中有了两个i2c了,这里platform_device注册的时候id则使用了2,device name则是和i2c gpio的driver中的name是一致的,即i2c-gpio.c中的name。
2、 在使用gpio模拟i2c的设备中,需要指定使用的i2c的id,这样才可以知道使用的哪一个。这样在初始化时会对所在i2c总线进行遍历并得到该设备的适配器等信息,这样就像使用普通的i2c那样来使用了。
-----------------------------------------------华丽的分割线----------------------------------------------------------------------------
直接用GPIO口模拟I2C时序和利用内核模块i2c-gpio虚拟i2c总线的区别:
1. 用GPIO口模拟I2C时序不需要在系统启动时注册I2C总线,只需要在I2C设备驱动中单独实现。用i2c-gpio模块虚拟i2c总线需要在系统启动时注册新的I2C总线,并将i2c设备挂载到新的i2c总线,涉及的范围较广。
2. 用GPIO口模拟I2C时序,代码操作较繁琐,且不方便挂载多个i2c设备。用i2c-gpio模块可以完全模拟i2c总线,可以挂载多个设备。
3. 在i2c读写操作时,用GPIO口模拟I2C时序需要每次根据读/写操作发送器件地址<<1+1/0,然后再发送寄存器地址。用i2c-gpio模块相当于直接在i2c总线上操作,在系统启动挂载i2c设备时已经告诉了i2c总线它的地址,在该设备自己的驱动中,只需要通过i2c_add_driver操作即可以得到其地址等诸多信息,读写操作只需要发送寄存器地址即可。
Ps:
I2c调试的时候,也要具体再读读代码看看和你的时序是否一致,切记。