I2C子系统之驱动SSD1306 OLED

理解I2C设备驱动框架,主要围绕四个结构体去分析就容易了。

struct i2c_algorithm:提供I2C协议的实现的操作,如:master_xfer实现数据收发的最基本方法。

struct i2c_adapter:每一个i2c_adapter都代表一个I2C物理接口,一个cpu可以有多个I2C接口(i2c_adapter),i2c_algorithm就是为i2c_adapter提供I2C协议的实现。每增加一个i2c接口,即是向i2c_core.c注册一个i2c_adapter

struct i2c_driver:代表着一类I2C从机设备的驱动,比如:at24cxx的驱动,不同类型的I2C从机需要注册不同的i2c_driver,如:ssd1306的驱动不同于at24cxx的驱动。每增加一个类型的I2C从机设备,都要向i2c_core.c注册一个i2c_driver

struct i2c_client:代表具体的某一个I2C从机设备,如:at24cxx系列的设备,有at24c01,at24c02等,每增加一个at24cxx设备,都要注册一个i2c_client。只有I2C从机设备被探测到,i2c_client才会被注册。

这四者的关系可以分为:i2c_algorithm和i2c_adapter一起驱动I2C总线,i2c_driver和i2c_client一起实现设备驱动。

注:linux目前只支持I2C主机模式。本文引用内核源码中i2c-algo-bit.c和i2c-gpio.c文件来讲解, i2c_driver由驱动开发者根据特定的设备提供,这里引用作者提供的ssd1306.c。i2c-algo-bit.c和i2c-gpio.c共同实现IO模拟I2C。

i2c-algo-bit.c提供了一个i2c_algorithm,i2c-gpio.c提供了一个i2c_adapter。

i2c-algo-bit.c通过以下代码绑定到i2c-gpio.c

i2c-algo-bit.c

static const struct i2c_algorithm i2c_bit_algo = {
    .master_xfer    = bit_xfer,
    .functionality  = bit_func,
};

static int i2c_bit_prepare_bus(struct i2c_adapter *adap)
{
    ... ...
    adap->algo = &i2c_bit_algo;
    ... ...
    return 0;
}

int i2c_bit_add_bus(struct i2c_adapter *adap)
{
    ... ...
    err = i2c_bit_prepare_bus(adap);
    ... ...
    return i2c_add_adapter(adap);
}

i2c-gpio.c

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;
    ... ...
    pdata = pdev->dev.platform_data;
    ... ...
    i2c_bit_add_bus(adap);
    ... ...
}

这里就注册了一个i2c_adapter。

要驱动ssd1306,因此对应地要提供一个i2c_driver,与i2c_adapter建立关系。

ssd1306.c

static struct i2c_driver ssd1306_driver = {
    .driver = {
            .name  = "ssd1306",
        },
        .id    = I2C_DRIVERID_I2CDEV,
        .attach_adapter = ssd1306_attach_adapter,
        .detach_client  = ssd1306_detach_client,
};

static int ssd1306_module_init(void)
{
    i2c_add_driver(&ssd1306_driver);
    return 0;
}

i2c_driver和i2c_adapter是怎样建立关系的呢?

I2c_core.c负责桥接i2c_driver和i2c_adapter建立关系,在i2c_driver和i2c_adapter注册的时候,两者都会调用driver->attach_adapter(adapter)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    ... ...
    driver->attach_adapter(adapter);
    ... ...
    return 0;
}

static int i2c_register_adapter(struct i2c_adapter *adap)
{
    ... ...
    driver->attach_adapter(adap);
    ... ...
}

driver->attach_adapter(adapter)实际上调用

1 static int ssd1306_attach_adapter(struct i2c_adapter *adapter) 2 { 3 return i2c_probe(adapter, &addr_data, ssd1306_detect); 4 }

I2c_probe()函数的作用就是,探测是否存在ssd1306这个设备,是怎样探测的呢?就是通过发送从机地址到ssd1306,如果ssd1306返回应答信号,就认为探测到了。

int i2c_probe(struct i2c_adapter *adapter,
          struct i2c_client_address_data *address_data,
          int (*found_proc) (struct i2c_adapter *, int, int))
{
    ... ...
    i2c_probe_address(adapter,
                    address_data->probe[i + 1],
                    -1, found_proc);
    ... ...
}

代码太多,简化函数调用关系如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/7523527a0e17da2d479d416348ae0566.html