linux 内核GPIO 模拟 I2C
2013-06-27 17:36
567 查看
转载地址:http://wenku.baidu.com/view/015c8549c850ad02df804105.html
一、应用背景
在许多情况下,我们并没有足够的I2C总线,本文主在介绍
如何利用Linux内核中的i2c-gpio模块,利用2条GPIO线模拟i2c总线,并挂载设备
二、思路
先通过对i2c-gpio所定义的结构体初始化(包括初始化i2c的2条线,频率、timeout等)并将i2c-gpio模块编译进内核,
实现用GPIO_X,GPIO_Y 2条GPIO线注册新的i2c总线。此时这个模块对i2c设备是透明的,及挂在这2条GPIO线的i2c设
备可以直接使用Linux内核通用的i2c设备注册,传输和注销等方法。
三、步骤:
1、首先,确认在注册i2c-gpio模块前,所要用到的2条GPIO口是没有被系统其它地方所调用的。
在每个系统平台启动时,都会打开一系列的设备,他们通常实现在arch/目录下相应的平台子目录中的例如setup.c,devices.c文件中,
在这里我们进行i2c总线的注册以及设备的挂载。
(1) i2c-gpio定义的结构在include/linux/i2c-gpio.h中,代码如下所示:
#ifndef _LINUX_I2C_GPIO_H
#define _LINUX_I2C_GPIO_H
/**
* * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
* * @sda_pin: GPIO pin ID to use for SDA
* * @scl_pin: GPIO pin ID to use for SCL
* * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
* * @timeout: clock stretching timeout in jiffies. If the slave keeps
* * SCL low for longer than this, the transfer will time out.
* * @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
* * isn't actively driven high when setting the output value high.
* * gpio_get_value() must return the actual pin state even if the
* * pin is configured as an output.
* * @scl_is_open_drain: SCL is set up as open drain. Same requirements
* * as for sda_is_open_drain apply.
* * @scl_is_output_only: SCL output drivers cannot be turned off.
* */
struct i2c_gpio_platform_data {
unsigned int sda_pin;
unsigned int scl_pin;
int udelay;
int timeout;
unsigned int sda_is_open_drain:1;
unsigned int scl_is_open_drain:1;
unsigned int scl_is_output_only:1;
};
#endif /* _LINUX_I2C_GPIO_H */
注:其中sda_pin和scl_pin分别是i2c总线的数据线和时钟线,在i2c-gpio中会通过gpio_request函数对这2个口进行申请,
(2) udelay和timeout如果不设初值,i2c-gpio中会自动将其设为默认值。
if (pdata->udelay)
bit_data->udelay = pdata->udelay;
else if (pdata->scl_is_output_only)
bit_data->udelay = 50; /* 10 kHz */
else
bit_data->udelay = 5; /* 100 kHz */
if (pdata->timeout)
bit_data->timeout = pdata->timeout;
else
bit_data->timeout = HZ / 10; /* 100 ms */
bit_data->data = pdata;
2、
(1) 初始化这个结构体后再将其装入platform_device结构体,方便注册。代码:
static struct platform_device i2c1_gpio_device = {
.name = "i2c-gpio",
.id = 1,
.dev = {
.platform_data = &i2c2_gpio_data,
},
};
(2) 注册i2c_gpio设备,将i2c设备挂入我们注册的总线
i2c_register_board_info(0, gps1_i2c0_devs, ARRAY_SIZE(gps1_i2c0_devs));
#if defined(CONFIG_I2C_GPIO)
platform_device_register(&i2c1_gpio_device);
platform_device_register(&i2c2_gpio_device);
platform_device_register(&i2c3_gpio_device);
i2c_register_board_info(1, gps1_gpio_i2c1_devs, ARRAY_SIZE(gps1_gpio_i2c1_devs));
i2c_register_board_info(2, gps1_gpio_i2c2_devs, ARRAY_SIZE(gps1_gpio_i2c2_devs));
i2c_register_board_info(3, gps1_gpio_i2c3_devs, ARRAY_SIZE(gps1_gpio_i2c3_devs));
#endif
注:此时,我们就可以在i2c设备的驱动程序中通过遍历所在i2c总线,得到其所在的地址i2c_device_addr
(3) 在i2c驱动中,需要注册一个i2c_driver的结构体,例如,ssd253x.c 代码如下
static const struct i2c_device_id ssd253x_ts_id[] = {
{ "ssd253x-ts", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssd253x_ts_id);
static struct i2c_driver ssd253x_ts_driver = {
.driver = {
.name = "ssd253x-ts",
},
.probe = ssd253x_ts_probe,
.remove = ssd253x_ts_remove,
.suspend = ssd253x_ts_suspend,
.resume = ssd253x_ts_resume,
.id_table = ssd253x_ts_id,
};
static int __init ssd253x_ts_init(void)
{
......
ret=i2c_add_driver(&ssd253x_ts_driver);
......
}
注:在init函数中调用i2c_add_driver,此时将会对所在i2c总线进行遍历并得到该设备的适配器等信息,
主要目的即是使驱动得到自己的i2c_client,在这个i2c_client中,已经有了该i2c设备的地址等信息,
我们在驱动中定义一个新的i2c_client全局变量,
把得到的这个i2c_client传给这个全局变量,以便进行后面的i2c操作
四、总结
直接用GPIO口模拟I2C时序和利用内核模块i2c-gpio虚拟i2c总线的区别
1. 用GPIO口模拟I2C时序不需要在系统启动时注册I2C总线,只需要在I2C设备驱动中单独实现。
用i2c-gpio模块虚拟i2c总线需要在系统启动时注册新的I2C总线,并将i2c设备挂载到新的i2c总线,涉及的范围较广
// CPIO口模拟I2C时序
i2c_register_board_info(0, gps1_i2c0_devs, ARRAY_SIZE(gps1_i2c0_devs));
// 用i2c-gpio模块虚拟i2c总线
#if defined(CONFIG_I2C_GPIO)
platform_device_register(&i2c1_gpio_device);
platform_device_register(&i2c2_gpio_device);
platform_device_register(&i2c3_gpio_device);
i2c_register_board_info(1, gps1_gpio_i2c1_devs, ARRAY_SIZE(gps1_gpio_i2c1_devs));
i2c_register_board_info(2, gps1_gpio_i2c2_devs, ARRAY_SIZE(gps1_gpio_i2c2_devs));
i2c_register_board_info(3, gps1_gpio_i2c3_devs, ARRAY_SIZE(gps1_gpio_i2c3_devs));
#endif
2. 用GPIO口模拟I2C时序,代码操作较繁琐,且不方便挂载多个i2c设备
用i2c-gpio模块可以完全模拟i2c总线,可以挂载多个设备
3. 在i2c读写操作时
(1)用GPIO口模拟I2C时序需要每次根据读/写操作发送器件地址<<1+1/0,然后再发送寄存器地址。
(2)用i2c-gpio模块相当于直接在i2c总线上操作,在系统启动挂载i2c设备时已经告诉了i2c总线它的地址,在该设备自己的驱动中,
只需要通过i2c_add_driver操作即可以得到其地址等诸多信息,读写操作只需要发送寄存器地址即可
一、应用背景
在许多情况下,我们并没有足够的I2C总线,本文主在介绍
如何利用Linux内核中的i2c-gpio模块,利用2条GPIO线模拟i2c总线,并挂载设备
二、思路
先通过对i2c-gpio所定义的结构体初始化(包括初始化i2c的2条线,频率、timeout等)并将i2c-gpio模块编译进内核,
实现用GPIO_X,GPIO_Y 2条GPIO线注册新的i2c总线。此时这个模块对i2c设备是透明的,及挂在这2条GPIO线的i2c设
备可以直接使用Linux内核通用的i2c设备注册,传输和注销等方法。
三、步骤:
1、首先,确认在注册i2c-gpio模块前,所要用到的2条GPIO口是没有被系统其它地方所调用的。
在每个系统平台启动时,都会打开一系列的设备,他们通常实现在arch/目录下相应的平台子目录中的例如setup.c,devices.c文件中,
在这里我们进行i2c总线的注册以及设备的挂载。
(1) i2c-gpio定义的结构在include/linux/i2c-gpio.h中,代码如下所示:
#ifndef _LINUX_I2C_GPIO_H
#define _LINUX_I2C_GPIO_H
/**
* * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
* * @sda_pin: GPIO pin ID to use for SDA
* * @scl_pin: GPIO pin ID to use for SCL
* * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
* * @timeout: clock stretching timeout in jiffies. If the slave keeps
* * SCL low for longer than this, the transfer will time out.
* * @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
* * isn't actively driven high when setting the output value high.
* * gpio_get_value() must return the actual pin state even if the
* * pin is configured as an output.
* * @scl_is_open_drain: SCL is set up as open drain. Same requirements
* * as for sda_is_open_drain apply.
* * @scl_is_output_only: SCL output drivers cannot be turned off.
* */
struct i2c_gpio_platform_data {
unsigned int sda_pin;
unsigned int scl_pin;
int udelay;
int timeout;
unsigned int sda_is_open_drain:1;
unsigned int scl_is_open_drain:1;
unsigned int scl_is_output_only:1;
};
#endif /* _LINUX_I2C_GPIO_H */
注:其中sda_pin和scl_pin分别是i2c总线的数据线和时钟线,在i2c-gpio中会通过gpio_request函数对这2个口进行申请,
(2) udelay和timeout如果不设初值,i2c-gpio中会自动将其设为默认值。
if (pdata->udelay)
bit_data->udelay = pdata->udelay;
else if (pdata->scl_is_output_only)
bit_data->udelay = 50; /* 10 kHz */
else
bit_data->udelay = 5; /* 100 kHz */
if (pdata->timeout)
bit_data->timeout = pdata->timeout;
else
bit_data->timeout = HZ / 10; /* 100 ms */
bit_data->data = pdata;
2、
(1) 初始化这个结构体后再将其装入platform_device结构体,方便注册。代码:
static struct platform_device i2c1_gpio_device = {
.name = "i2c-gpio",
.id = 1,
.dev = {
.platform_data = &i2c2_gpio_data,
},
};
(2) 注册i2c_gpio设备,将i2c设备挂入我们注册的总线
i2c_register_board_info(0, gps1_i2c0_devs, ARRAY_SIZE(gps1_i2c0_devs));
#if defined(CONFIG_I2C_GPIO)
platform_device_register(&i2c1_gpio_device);
platform_device_register(&i2c2_gpio_device);
platform_device_register(&i2c3_gpio_device);
i2c_register_board_info(1, gps1_gpio_i2c1_devs, ARRAY_SIZE(gps1_gpio_i2c1_devs));
i2c_register_board_info(2, gps1_gpio_i2c2_devs, ARRAY_SIZE(gps1_gpio_i2c2_devs));
i2c_register_board_info(3, gps1_gpio_i2c3_devs, ARRAY_SIZE(gps1_gpio_i2c3_devs));
#endif
注:此时,我们就可以在i2c设备的驱动程序中通过遍历所在i2c总线,得到其所在的地址i2c_device_addr
(3) 在i2c驱动中,需要注册一个i2c_driver的结构体,例如,ssd253x.c 代码如下
static const struct i2c_device_id ssd253x_ts_id[] = {
{ "ssd253x-ts", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssd253x_ts_id);
static struct i2c_driver ssd253x_ts_driver = {
.driver = {
.name = "ssd253x-ts",
},
.probe = ssd253x_ts_probe,
.remove = ssd253x_ts_remove,
.suspend = ssd253x_ts_suspend,
.resume = ssd253x_ts_resume,
.id_table = ssd253x_ts_id,
};
static int __init ssd253x_ts_init(void)
{
......
ret=i2c_add_driver(&ssd253x_ts_driver);
......
}
注:在init函数中调用i2c_add_driver,此时将会对所在i2c总线进行遍历并得到该设备的适配器等信息,
主要目的即是使驱动得到自己的i2c_client,在这个i2c_client中,已经有了该i2c设备的地址等信息,
我们在驱动中定义一个新的i2c_client全局变量,
把得到的这个i2c_client传给这个全局变量,以便进行后面的i2c操作
四、总结
直接用GPIO口模拟I2C时序和利用内核模块i2c-gpio虚拟i2c总线的区别
1. 用GPIO口模拟I2C时序不需要在系统启动时注册I2C总线,只需要在I2C设备驱动中单独实现。
用i2c-gpio模块虚拟i2c总线需要在系统启动时注册新的I2C总线,并将i2c设备挂载到新的i2c总线,涉及的范围较广
// CPIO口模拟I2C时序
i2c_register_board_info(0, gps1_i2c0_devs, ARRAY_SIZE(gps1_i2c0_devs));
// 用i2c-gpio模块虚拟i2c总线
#if defined(CONFIG_I2C_GPIO)
platform_device_register(&i2c1_gpio_device);
platform_device_register(&i2c2_gpio_device);
platform_device_register(&i2c3_gpio_device);
i2c_register_board_info(1, gps1_gpio_i2c1_devs, ARRAY_SIZE(gps1_gpio_i2c1_devs));
i2c_register_board_info(2, gps1_gpio_i2c2_devs, ARRAY_SIZE(gps1_gpio_i2c2_devs));
i2c_register_board_info(3, gps1_gpio_i2c3_devs, ARRAY_SIZE(gps1_gpio_i2c3_devs));
#endif
2. 用GPIO口模拟I2C时序,代码操作较繁琐,且不方便挂载多个i2c设备
用i2c-gpio模块可以完全模拟i2c总线,可以挂载多个设备
3. 在i2c读写操作时
(1)用GPIO口模拟I2C时序需要每次根据读/写操作发送器件地址<<1+1/0,然后再发送寄存器地址。
(2)用i2c-gpio模块相当于直接在i2c总线上操作,在系统启动挂载i2c设备时已经告诉了i2c总线它的地址,在该设备自己的驱动中,
只需要通过i2c_add_driver操作即可以得到其地址等诸多信息,读写操作只需要发送寄存器地址即可
相关文章推荐
- Linux 内核gpio模拟I2C
- ARM linux 内核gpio模拟I2C
- ARM Linux 内核gpio模拟I2C
- linux 内核gpio模拟spi (3线24bit模式)
- linux gpio模拟i2c的使用/用GPIO模拟I2C总线-2 .
- Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线
- linux平台上GPIO模拟I2C
- linux2.4 GPIO模拟实现I2C数据传输-DS1302
- linux平台上GPIO模拟I2C
- linux平台上GPIO模拟I2C
- 我的内核学习笔记12:linux i2c-gpio驱动应用实例
- Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线
- linux i2c-gpio 模拟i2c时序出现oops错误
- Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线
- linux gpio模拟i2c的使用/用GPIO模拟I2C总线-1
- linux gpio模拟i2c的使用/用GPIO模拟I2C总线-2
- Linux I2C子系统分析之(一) ----- 用GPIO模拟I2C总线
- MTK GPIO 模拟I2C
- Davinci DM6446 Linux 内核分析—— gpio.c .