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

本文介绍如何在SOC I2C控制器不稳定或不足时,利用GPIO实现I2C控制器功能。通过Linux内核提供的平台驱动,仅需配置平台设备描述即可启用。文章详细解析了所需的平台数据设置,并给出了具体的实现代码示例。

实战派 ESP32-S3,双模无线开发板

ESP32-S3 原生支持 ESP-IDF,WiFi + 蓝牙一次搞定

当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子目录

实战派 ESP32-S3,双模无线开发板

ESP32-S3 原生支持 ESP-IDF,WiFi + 蓝牙一次搞定

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值