55 linux内核里基于GPIO口的I2C控制器驱动

来源:互联网 发布:php posix getpid 编辑:程序博客网 时间:2024/05/18 12:42

当SOC里的I2C控制器不稳定,或I2C控制器不够用时,我们可以基于GPIO实现I2C控制器的功能.

在linux内核里已提供了相应的代码,是一个平台驱动,只需写平台设备描述相关资源即可.

    make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-    Device Drivers  --->         <*> I2C support  --->             <*>   I2C device interface            I2C Hardware Bus support  --->                  <*> GPIO-based bitbanging I2C //基于GPIO实现的I2C控制器驱动

驱动源码在”drivers/i2c/busses/i2c-gpio.c”里

static struct platform_driver i2c_gpio_driver = {    .driver     = {        .name   = "i2c-gpio", //匹配名为"i2c-gpio"的平台设备        .owner  = THIS_MODULE,        .of_match_table = of_match_ptr(i2c_gpio_dt_ids),    },      .probe      = i2c_gpio_probe,    .remove     = __devexit_p(i2c_gpio_remove),};//通过阅读平台驱动里的probe函数可以得知平台设备需要提供的参数static int __devinit i2c_gpio_probe(struct platform_device *pdev){    struct i2c_gpio_private_data *priv;    struct i2c_gpio_platform_data *pdata;    struct i2c_algo_bit_data *bit_data;    struct i2c_adapter *adap;    int ret;    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);    if (!priv)        return -ENOMEM;    adap = &priv->adap;    bit_data = &priv->bit_data;    pdata = &priv->pdata;    if (pdev->dev.of_node) { //此内核没用到设备树,这里不成立        ret = of_i2c_gpio_probe(pdev->dev.of_node, pdata);        if (ret)            return ret;    } else {        if (!pdev->dev.platform_data) //可以通过平台设备的platform_data成员提供struct i2c_gpio_platform_data类型的数据            return -ENXIO;        memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));    }    ....    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 */    ...    adap->nr = (pdev->id != -1) ? pdev->id : 0; //i2c_adapter对象的号使用平台设备的id    ret = i2c_bit_add_numbered_bus(adap);struct i2c_gpio_platform_data {    unsigned int    sda_pin; // SDA脚的IO口    unsigned int    scl_pin; // SCL脚的IO口    int     udelay; //通过此成员确定SCL的时钟频率, SCL frequency is (500 / udelay) kHz    int     timeout; //多久没收到应答后认为传输失败    unsigned int    sda_is_open_drain:1; //是否有上拉, 没有则设1     unsigned int    scl_is_open_drain:1;    unsigned int    scl_is_output_only:1; //时钟信号是否只由主机提供};

/////////////////////////////////////////////////

把PA9(SDA), PA10(SCL)这两个IO口实现I2C控制器的接口

myi2c9.c

#include <linux/init.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/i2c-gpio.h>#include <mach/gpio.h>struct i2c_gpio_platform_data pdata = {    .sda_pin = GPIOA(9),    .scl_pin = GPIOA(10),    .udelay =  2, // 250Khz    .timeout = 0, //使用默认值,此内核里是100ms};struct platform_device mypdev = {    .name = "i2c-gpio",    .id = 9, // i2c_adapter对象的号就是9    .dev = {        .platform_data = &pdata,     },};module_driver(mypdev, platform_device_register, platform_device_unregister);MODULE_LICENSE("GPL");

加模块后,应在”/dev”目录下有i2c-9设备文件
在”/sys/bus/i2c/devices”目录下有i2c-9子目录

原创粉丝点击