通过 bitbang GPIO来实现i2c总线协议

i2c总线可以通过bitbang两个gpio口来实现, bitbang就是位拉高拉低的意思,英文手册中经常出现的一个词。

drivers/i2c/algos/i2c-algo-bit.c, 就是这个i2c的algos程序,通过bitbang SCL 和SDA两个引脚,来实现I2C总线协议。

1. 实现bitbang,  有两个最基本的操作,一个是设置GPIO的输出(高或者低),另一个就是读取GPIO的状态。

因为实现I2C总线协议,必须要有时间来控制SCL的脉冲宽度(这里用的是udelay),所以需要看GPIO口的拉高或者拉低到底有没有实现,就需要读取GPIO的状态来判断。

这里相关的代码如下:

/* --- setting states on the bus with the right timing: --------------- */

#define setsda(adap,val) adap->setsda(adap->data, val)
#define setscl(adap,val) adap->setscl(adap->data, val)
#define getsda(adap) adap->getsda(adap->data)
#define getscl(adap) adap->getscl(adap->data)
设置sda 和scl的输出,读取sda和scl的状态, 这两个函数的具体实现需要用到i2c busses下的一个代码,这里使用的是通用的drivers/i2c/busses/i2c-gpio.c。
在i2c_gpio_probe()函数中,实现了上面几个函数指针的赋值:

static int __init i2c_gpio_probe(struct platform_device *pdev)
{
        struct i2c_gpio_platform_data *pdata;
        struct i2c_algo_bit_data *bit_data;
        struct i2c_adapter *adap;
        int ret;

        pdata = pdev->dev.platform_data;
        if (!pdata)
                return -ENXIO;

        ret = -ENOMEM;
        adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
        if (!adap)
                goto err_alloc_adap;
        bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);
        if (!bit_data)
                goto err_alloc_bit_data;

        gpio_request_mux(pdata->sda_pin, GPIO_MUX_GPIO);
        gpio_request_mux(pdata->scl_pin, GPIO_MUX_GPIO);

        gpio_set_puen(pdata->sda_pin, 1);
        gpio_set_puen(pdata->scl_pin, 1);

        if (pdata->sda_is_open_drain) {
                mxc_set_gpio_direction(pdata->sda_pin, 0);
                bit_data->setsda = i2c_gpio_setsda_val;
        } else {
                mxc_set_gpio_direction(pdata->sda_pin, 1);
                bit_data->setsda = i2c_gpio_setsda_dir;
        }

        if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
                mxc_set_gpio_direction(pdata->scl_pin, 0);
                bit_data->setscl = i2c_gpio_setscl_val;
        } else {
                mxc_set_gpio_direction(pdata->scl_pin, 1);
                bit_data->setscl = i2c_gpio_setscl_dir;
        }

        if (!pdata->scl_is_output_only)
                bit_data->getscl = i2c_gpio_getscl;
        bit_data->getsda = i2c_gpio_getsda;

        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;

        adap->owner = THIS_MODULE;
        snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
        adap->algo_data = bit_data;
        adap->dev.parent = &pdev->dev;

        ret = i2c_bit_add_bus(adap);
        if (ret)
                goto err_add_bus;

        platform_set_drvdata(pdev, adap);

        dev_info(&pdev->dev, "using pins %x (SDA) and %x (SCL%s)\n",
                 pdata->sda_pin, pdata->scl_pin,
                 pdata->scl_is_output_only
                 ? ", no clock stretching" : "");

        return 0;

err_add_bus:
        mxc_free_gpio(pdata->scl_pin);
err_request_scl:
        mxc_free_gpio(pdata->sda_pin);
err_request_sda:
        kfree(bit_data);
err_alloc_bit_data:
        kfree(adap);
err_alloc_adap:
        return ret;
}
首先是获得GPIO, 把GPIO设成高阻抗状态。

        gpio_request_mux(pdata->sda_pin, GPIO_MUX_GPIO);
        gpio_request_mux(pdata->scl_pin, GPIO_MUX_GPIO);

        gpio_set_puen(pdata->sda_pin, 1);
        gpio_set_puen(pdata->scl_pin, 1);
然后就是开始赋值这四个函数指针:

        if (pdata->sda_is_open_drain) {
                mxc_set_gpio_direction(pdata->sda_pin, 0);
                bit_data->setsda = i2c_gpio_setsda_val;
        } else {
                mxc_set_gpio_direction(pdata->sda_pin, 1);
                bit_data->setsda = i2c_gpio_setsda_dir;
        }

        if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
                mxc_set_gpio_direction(pdata->scl_pin, 0);
                bit_data->setscl = i2c_gpio_setscl_val;
        } else {
                mxc_set_gpio_direction(pdata->scl_pin, 1);
                bit_data->setscl = i2c_gpio_setscl_dir;
        }

        if (!pdata->scl_is_output_only)
                bit_data->getscl = i2c_gpio_getscl;
        bit_data->getsda = i2c_gpio_getsda;
再接下来是设置 udelay 和timeout, 这连个值在bitbang中都会使用到,udelay是用来控制scl的脉冲周期的,timeout是用来控制最长脉冲宽度,比如有时CPU被中断,被别的进程使用,这时bitbang就会有延时,timeout就是用来控制最大的延时的,如果延时超出timeout,就会报错之类的出现,后面会有详细代码说明。

        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 */

最后就是i2c adapter 的赋值,并向i2c core 添加i2c adapter:

        bit_data->data = pdata;

        adap->owner = THIS_MODULE;
        snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
        adap->algo_data = bit_data;
        adap->dev.parent = &pdev->dev;

        ret = i2c_bit_add_bus(adap);
        if (ret)
                goto err_add_bus;

        platform_set_drvdata(pdev, adap);
i2c_bit_add_bus() 中可以先测试I2C bus bitbang功能行不行,如果测试失败,就返回错误。如果成功,就添加adapter到i2c core中。
int i2c_bit_add_bus(struct i2c_adapter *adap)
{
        int err;

        err = i2c_bit_prepare_bus(adap);
        if (err)
                return err;

        return i2c_add_adapter(adap);
}
EXPORT_SYMBOL(i2c_bit_add_bus);
i2c_bit_prepare_bus() 就是用来测试i2c最基本的拉低拉高bitbang功能的,并添加几个adapter数据。

/*
 * registering functions to load algorithms at runtime
 */
static int i2c_bit_prepare_bus(struct i2c_adapter *
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值