博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux I2C协议
阅读量:6380 次
发布时间:2019-06-23

本文共 25602 字,大约阅读时间需要 85 分钟。

hot3.png

简介

I2C 总线仅仅使用 SCL 、 SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用。因此, I2C 总线被非常广泛地应用在 EEPROM 、实时钟、小型 LCD 等设备与 CPU 的接口中。

Linux I2C GPIO驱动是在没有专用I2C芯片的情况下,用GPIO口来模拟I2C总线时序,完成Linux与I2C设备的通信过程。用两根GPIO,分别模拟SDA和SCL。它与使用i2c芯片的驱动有所不同的是传输算法的实现,GPIO模拟i2c驱动中有自己的一套传输算法。GPIO模拟I2C是要占用CPU资源的,而用I2C芯片是不占CPU资源的。使用i2c子系统,而不使用普通字符设备,有以下好处:

  1. 使用Linux I2C子系统,不需要去过于详细了解I2C操作。
  2. 编写驱动可移植性强。
  3. 可以使用内核资源,当面对复杂I2C器件,工作量相对少得多。

I2C工作原理

I2C总线标准的两根传输线,SDA是数据线,Scl是时钟线,当SCL为高,SDA由高-à低时,发送启动信息,发送9个脉冲,1-7 是地址,8是读写控制位,9是ACK应答位,所以挂在I2C上的被控设备都接受所发送的信息,并把接收到的7位地址与自己的地址进行比较,如果相同ACK 就会反馈应答。当SCL为低,SDA由低-à高,则发送停止信号。

架构

Linux的I2C构架分为三个部分

1、I2C core框架

提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器 驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码,为系统中每个I2C总线增加相应的读写方法。

I2C core框架具体实现在/drivers/i2c目录下的i2c-core.c和i2c-dev.c

2、I2C总线驱动

定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结 构进行描述。 经过I2C总线驱动的的代码,可以为我们控制I2C产生开始位、停止位、读写周期以及从设备的读写、产生ACK等。

I2C总线驱动具体实现在/drivers/i2c目录下busses文件夹。例如:Linux I2C GPIO总线驱动为i2c_gpio.c. I2C总线算法在/drivers/i2c目录下algos文件夹。例如:Linux I2C GPIO总线驱动算法实现在i2c_algo_bit.c.

3、I2C 设备驱动

是对具体I2C硬件驱动的实现。I2C 设备驱动通过I2C适配器与CPU通信。其中主要包含i2c_driver和 i2c_client数据结构,i2c_driver结构对应一套具体的驱动方法,例如:probe、remove、suspend等,需要自己申明。 i2c_client数据结构由内核根据具体的设备注册信息自动生成,设备驱动根据硬件具体情况填充。具体使用下面介绍。

I2C 设备驱动具体实现放在在/drivers/i2c目录下chips文件夹。

设备注册

下面以GPIO模拟i2c总线的驱动为例,来介绍设备注册,对于使用i2c芯片的驱动都是大同小异,主要在传输算法上的区别。首先make menuconfig把i2c-gpio选上,让它能编进内核。设备注册包括两种设备的注册,i2c-gpio总线和i2c设备驱动。

1、 i2c-gpio总线注册 /drivers/i2c/busses/i2c_gpio.c是i2c-gpio总线驱动源码。在这里可以看到i2c-gpio的注册

static struct platform_driver i2c_gpio_driver = {       .driver = {              .name = "i2c-gpio",           //驱动名字              .owner = THIS_MODULE,       },       .probe = i2c_gpio_probe,       .remove = __devexit_p(i2c_gpio_remove),}; static int __init i2c_gpio_init(void){       int ret;       ret = platform_driver_register(&i2c_gpio_driver);//注册成平台设备       if (ret)              printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);       return ret;}module_init(i2c_gpio_init);

platform是linux虚 拟的总线,称为platform总线,相应的设备称为platform_device,相应的驱动称为platform_driver。我们知道i2c总 线也对应一个设备,在这里就是对应的i2c_adapte结构,这在后面会有详细介绍。在这里可以看到它将i2c总线驱动注册成平台设备驱动 platform_driver_register(&i2c_gpio_driver) 把i2c_gpio设备注册为平台设备,需要在mach_xxx的板级文件(devices.c)中添加i2c-gpio需要用到的资源定义,即将i2c总线设备封装成平台设备,下面首先定义总线占用的系统资源

static struct i2c_gpio_platform_data i2c3_data = {        .sda_pin = CONFIG_SDA_PIN;        .scl_pin = CONFIG_SCL_PIN; //设置需要用到的gpio引脚             .udelay = 0,  //设置I2C工作频率,如果没有默认值为50             .timeout = 0, //设置I2C工作超时,如果没有默认值为10};

由于i2c_gpio驱动需要注册到platform总线上面,还需要在mach_xxx的板级文件中添加i2c-gpio的platform_device结构。

static struct platform_device i2c3_device = {    .name       = "i2c-gpio", //必须和i2c-gpio驱动的名字相同    .id         = 2,          //总线ID号    .dev = {        .platform_data  = &i2c3_data,    },};

注册i2c-gpio驱动前要有一个GPIO的设置过程,设置过程如下:

{//SDAPnx_gpio_set_mode(GPIO_F8,GPIO_MODE_MUX1)Pnx_gpio_set_direction(GPIO_F8,GPIO_DIR_OUTPUT)//SCLPnx_gpio_set_mode(GPIO_F7,GPIO_MODE_MUX1)Pnx_gpio_set_direction(GPIO_F7,GPIO_DIR_OUTPUT) };

最后把i2c-gpio设备注册进platform总线。 platform_device_register(&i2c3_device);

2、把i2c设备驱动注册到i2c-gpio总线 例如:设备驱动源码在/drivers/i2c/chips/lis35de.c,其注册到i2c总线需要的 做法如下。首先定义设备ID:

static const struct i2c_device_id lis35de_id[] = {                     { "lis35de", 0 },//设备名和设备是有数据长度                     { }};

然后声明i2c_driver结构

static struct i2c_driver st_lis35de_driver = {               .probe     = st_lis35de_probe,               .remove        = st_lis35de_remove,               .suspend   = st_lis35de_suspend,               .resume        = st_lis35de_resume,//上面4个函数根据具体情况取舍               .id_table = lis35de_id,               .driver        = {                     .name    = "lis35de",  //驱动名字                },};

最后调用static inline int i2c_add_driver(struct i2c_driver *driver)注册lis35de驱动到I2C总线,如下

static int __init st_lis35de_init(void){         return i2c_add_driver(&st_lis35de_driver);//注册st_lis35de_driver};module_init(st_lis35de_init);

但是到目前还不知道注册到那根I2C总线,现在把lis35de设备驱动添加到我们想要的i2c-gpio总线上。使用内核提供的函数i2c_register_board_info,在mach_xxx的板级文件中把设备信息注册到需要注册的I2C总线上面。

int __init i2c_register_board_info(int busnum,//设备需要注册到的总线ID         struct i2c_board_info const *info,//设备信息包括设备名,地址等unsigned len)

例如:把lis35de驱动注册到i2c-gpio总线,总线ID为2。

static struct i2c_board_info i2c_devices_lis35de[] = {    {        I2C_BOARD_INFO("lis35de", 0x1C), //设备名和地址    },};i2c_register_board_info(2,i2c_devices_lis35de,ARRAY_SIZE(i2c_devices_lis35de));

arch/arm/mach-pnx67xx/board_pnx67xx_wavex.c中unsigned int pnx_modem_gpio_reserved[]

下注释掉GPIO_F7,GPIO_F8,防止内核认为F8,F7已经使用过了,至此已经把i2c- gpio总线注册到系统,把设备驱动注册到i2c-gpio总线。

前面说了那么多,是不是有点乱了,这里我们在来理一下

i2c总线驱动

1、在那个devices.c文件中,声明平台设备占用的系统资源,然后定义一个平台设备,并注册这个平台设备到平台总线上

2、在i2c-gpio.c文件中,声明该驱动支持的设备列表,然后定义一个平台驱动结构,并注册这个平台驱动到平台总线上

i2c设备驱动

1、同样在devices.c文件下,在对应总线的设备列表中声明一个i2c设备结构,然后通过i2c_register_board_info()函数,将这个设备列表注册到i2c总线上

2、在lis35de.c文件中,声明支持的i2c设备列表和一个i2c设备驱动结构体i2c_driver,然后将其注册到i2c总线上

注意:这里不管是设备还是驱动先注册到总线上,他们都会自动请求匹配总线上的所有驱动或设备。

I2C关键数据结构和详细注册流程

上面的描述都是i2c系统的框架,具体的数据结构注册流程下面会详细介绍。

关键数据结构

在i2c.h头文件中定义了i2c_adapter、i2c_algorithm、i2c_driver和i2c_client 4个比较关键的数据结构。

1、i2c_algorithm对应一套通信方法。

用来实现具体的收发算法,此数据结构非常重要,通过其中的收发函数会调用具体的硬件收发操作,对于i2c-gpio总线的通信方法实现在/drivers/i2c目录下algos文件夹i2c_algo_bit.c。

struct i2c_algorithm {    int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);     //i2c传输函数指针    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data * data);    //smbus传输函数指针u32 (*functionality) (struct i2c_adapter *); //返回适配器支持功能};

i2c_adapter

用来定义总线上的每一个adapter(适配器),每一个adapter都需要i2c_algorithm中提供的通信函数来控制适配器的访问周期,因此 在i2c_adapter中包含i2c_algorithm指针。i2c_algorithm的关键函数master_xfer用于产生I2C访问信号, 以i2c_msg为单位。

struct i2c_adapter {struct module *owner;  //所属模块unsigned int id;      //algorithm类型,定义在i2c-id.h以I2C_ALGO_开始unsigned int class;       /* classes to allow probing for */const struct i2c_algorithm *algo;void *algo_data;   //algorithm数据 int (*client_register)(struct i2c_client *); //client注册时调用int (*client_unregister)(struct i2c_client *); /* data fields that are valid for all devices   */u8 level;           /* nesting level for lockdep */struct mutex bus_lock;struct mutex clist_lock; int timeout;            /* in jiffies */int retries;struct device dev;      /* 适配器设备 */ int nr;struct list_head clients;   /* DEPRECATED */char name[48];struct completion dev_released;}; struct i2c_msg {__u16 addr; /* 从机地址         */__u16 flags;#define I2C_M_TEN        0x0010  /* this is a ten bit chip address */#define I2C_M_RD     0x0001  /* read data, from slave to master */#define I2C_M_NOSTART        0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_REV_DIR_ADDR   0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_IGNORE_NAK 0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_NO_RD_ACK      0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_RECV_LEN       0x0400  /* length will be first received byte */__u16 len;      /* msg length               */__u8 *buf;      /* pointer to msg data          */};

i2c_driver结构体

struct i2c_driver {    int id;    unsigned int class;    int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter    int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapterint (*detach_client)(struct i2c_client *) __deprecated; //脱离i2c_ client     int (*probe)(struct i2c_client *, const struct i2c_device_id *);    int (*remove)(struct i2c_client *);    void (*shutdown)(struct i2c_client *);    int (*suspend)(struct i2c_client *, pm_message_t mesg);    int (*resume)(struct i2c_client *);     int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);    //类似ioctl    struct device_driver driver;    const struct i2c_device_id *id_table;    int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);    /* Device detection callback for automatic device creation */    const struct i2c_client_address_data *address_data;    struct list_head clients;};4)i2c_client结构体。struct i2c_client {    unsigned short flags;       /* 标志 */    unsigned short addr;        /* 低7位为芯片地址 */                       char name[I2C_NAME_SIZE]; //设备名    struct i2c_adapter *adapter;    /*依附i2c_adapter   */    struct i2c_driver *driver;  /*依附i2c_ driver   */    struct device dev;      /* the device structure     */    int irq;            /* irq issued by device     */    struct list_head list;      /* DEPRECATED */    struct list_head detected;    struct completion released;};

详细注册流程

在平台设备驱动注册时,i2c-gpio总线被注册后,第一个执行的函数是i2c_gpio_probe:在这里主要做了3件事,首先构筑了一个 i2c_adapter,然后申请gpio,最后关键调用i2c_bit_add_bus。虽然i2c总线被注册成平台设备,但这里主要讲解i2c总线部 分内容,对平台设备和驱动的注册过程不做详述,感兴趣的可以查阅平台驱动部分内容。

static int __devinit i2c_gpio_probe(struct platform_device *pdev){       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;       ret = gpio_request(pdata->sda_pin, "sda");  if (ret)              goto err_request_sda;       ret = gpio_request(pdata->scl_pin, "scl");       if (ret)              goto err_request_scl;       if (pdata->sda_is_open_drain) {              //如果集电极开路              gpio_direction_output(pdata->sda_pin, 1);//设定方向为输出              bit_data->setsda = i2c_gpio_setsda_val;//设定setsda实现函数       } else {              gpio_direction_input(pdata->sda_pin);//如果集电极不是开路,那么设定方向为输入              bit_data->setsda = i2c_gpio_setsda_dir;//设定setsda的实现函数       }       if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {//集电极开路并仅做输出              gpio_direction_output(pdata->scl_pin, 1);//设定方向              bit_data->setscl = i2c_gpio_setscl_val; //设定setscl的实现函数       } else {        //集电极不是开路时候              gpio_direction_input(pdata->scl_pin);              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;     //让bit_data与platform_data相关联       adap->owner = THIS_MODULE;       snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);       adap->algo_data = bit_data;  //algo_data中加入具体的实现方法,被algo中函数调用       adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;       adap->dev.parent = &pdev->dev;       /*        * If "dev->id" is negative we consider it as zero.        * The reason to do so is to avoid sysfs names that only make        * sense when there are multiple adapters.        */       adap->nr = (pdev->id != -1) ? pdev->id : 0;       ret = i2c_bit_add_numbered_bus(adap);  //添加进入总线,在i2c-algo-bit.c中定义       if (ret)              goto err_add_bus;       platform_set_drvdata(pdev, adap);//加定适配器到platform设备中去       return 0;}

此函数是整个i2c-gpio.c的关键,几乎整个文件函数的注册以及algo中相关函数的注册都与他相关。i2c_bit_add_bus:在这里调用i2c_bit_prepare_bus。

int i2c_bit_add_numbered_bus(struct i2c_adapter *adap){       int err;       err = i2c_bit_prepare_bus(adap); //这里就是算法和适配器的关联函数,算法在别处已有实现       if (err)              return err;       return i2c_add_numbered_adapter(adap);}

i2c_bit_prepare_bus:可以看到原来分配到的适配器在这里和i2c_bit_algo算法结合了。

static int i2c_bit_prepare_bus(struct i2c_adapter *adap){       struct i2c_algo_bit_data *bit_adap = adap->algo_data;       if (bit_test) {              int ret = test_bus(bit_adap, adap->name);              if (ret < 0)                     return -ENODEV;       }       /* register new adapter to i2c module... */       adap->algo = &i2c_bit_algo; //没错,就是这里的关联       adap->timeout = 100;     /* default values, should */       adap->retries = 3;   /* be replaced by defines       */       return 0;}

再回头看i2c_bit_add_bus中调用的i2c_add_numbered_adapter(adap):

int i2c_add_adapter(struct i2c_adapter *adapter){       int    id, res = 0;retry:       if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)              return -ENOMEM;       mutex_lock(&core_lock);       /* "above" here means "above or equal to", sigh */       res = idr_get_new_above(&i2c_adapter_idr, adapter,                            __i2c_first_dynamic_bus_num, &id);       mutex_unlock(&core_lock);       if (res < 0) {              if (res == -EAGAIN)                     goto retry;              return res;       }       adapter->nr = id;       return i2c_register_adapter(adapter);}//在i2c_add_numbered_adapter最后调用了i2c_register_adapter(adapter);static int i2c_register_adapter(struct i2c_adapter *adap){       int res = 0, dummy;       /* Can't register until after driver model init */       if (unlikely(WARN_ON(!i2c_bus_type.p)))              return -EAGAIN;       mutex_init(&adap->bus_lock);       mutex_init(&adap->clist_lock);       INIT_LIST_HEAD(&adap->clients);       mutex_lock(&core_lock);       /* Add the adapter to the driver core.        * If the parent pointer is not set up,        * we add this adapter to the host bus.        */       if (adap->dev.parent == NULL) {              adap->dev.parent = &platform_bus;              pr_debug("I2C adapter driver [%s] forgot to specify "                      "physical device\n", adap->name);       }       dev_set_name(&adap->dev, "i2c-%d", adap->nr);       adap->dev.release = &i2c_adapter_dev_release;       adap->dev.class = &i2c_adapter_class;       res = device_register(&adap->dev);       if (res)              goto out_list;       dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);       /* create pre-declared device nodes for new-style drivers */       if (adap->nr < __i2c_first_dynamic_bus_num)              i2c_scan_static_board_info(adap); //静态扫描挂在总线上的所有i2c设备,然后逐一创建一个i2c_client结构       /* Notify drivers */       dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,                             i2c_do_add_adapter);out_unlock:       mutex_unlock(&core_lock);       return res;out_list:       idr_remove(&i2c_adapter_idr, adap->nr);       goto out_unlock;}// 其中的i2c_scan_static_board_info(adap)调用i2c_new_device:static void i2c_scan_static_board_info(struct i2c_adapter *adapter){       struct i2c_devinfo  *devinfo;       mutex_lock(&__i2c_board_lock);       list_for_each_entry(devinfo, &__i2c_board_list, list) {              if (devinfo->busnum == adapter->nr                            && !i2c_new_device(adapter,                                          &devinfo->board_info))  //就是这里,它根据devinfo来创建一个新的i2c_client设备结构                     printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",                            i2c_adapter_id(adapter),                            devinfo->board_info.addr);       }       mutex_unlock(&__i2c_board_lock);}i2c_new_device:struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){       struct i2c_client     *client;       int                  status;       client = kzalloc(sizeof *client, GFP_KERNEL);       if (!client)              return NULL;       client->adapter = adap;       client->dev.platform_data = info->platform_data;       if (info->archdata)              client->dev.archdata = *info->archdata;       client->flags = info->flags;       client->addr = info->addr;       client->irq = info->irq;       strlcpy(client->name, info->type, sizeof(client->name));       /* a new style driver may be bound to this device when we        * return from this function, or any later moment (e.g. maybe        * hotplugging will load the driver module).  and the device        * refcount model is the standard driver model one.        */       status = i2c_attach_client(client);       if (status < 0) {              kfree(client);              client = NULL;       }       return client;}

在i2c_new_device中,内核为我们分配i2c_client,把我们在板级文件中申明的设备名和地址

static struct i2c_board_info i2c_devices[] = {    {        I2C_BOARD_INFO("lis35de", 0x1C), //设备名和地址    },

填充到i2c_client。一个i2c_client就代表着一个位于adapter适配器上,地址为client->addr,使用设备驱动的一个i2c设备。到这里就可以通过 Linux I2C核心提供的不依赖硬件接口的函数了,接受/发送函数等。 也许你会疑惑在创建i2c_client设备时,是根据devinfo来创建的,那么这个devinfo是从那来的?我们在devices.c中不是注册 的是i2c_board_info这个结构吗?没错,在devices.c中的确是注册的i2c_board_info结构,下面我们来看看注册这个结构 数组的这个i2c_regester_board_info()函数

int __initi2c_register_board_info(int busnum,       struct i2c_board_info const *info, unsigned len){       int status;       down_write(&__i2c_board_lock);       /* dynamic bus numbers will be assigned after the last static one */       if (busnum >= __i2c_first_dynamic_bus_num)              __i2c_first_dynamic_bus_num = busnum + 1;       for (status = 0; len; len--, info++) {              struct i2c_devinfo  *devinfo;              devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);              if (!devinfo) {                     pr_debug("i2c-core: can't register boardinfo!\n");                     status = -ENOMEM;                     break;              }              devinfo->busnum = busnum;              devinfo->board_info = *info;              list_add_tail(&devinfo->list, &__i2c_board_list);       }       up_write(&__i2c_board_lock);       return status;}

看到了吧,在这个注册函数里,它创建一个devinfo结构变量,并用总线号和i2c_board_info结构来初始化这个devinfo变量,然后加入一个全局的devinfo链表,来看看这个devinfo结构体的定义:

struct i2c_devinfo {       struct list_head       list;       int                  busnum;       struct i2c_board_info     board_info;};

这个注册函数就是在i2c-boardinfo.c中定义的,它维护一个全局的devinfo链表,在创建i2c_client的时候,就是通过这个devinfo链表来逐一创建的。

使用I2C子系统资源函数操作I2C设备

Linux I2C 核心提供的函数主要有:

1、增加/删除i2c_adapter

Int  i2c_add_adapter(struct i2c_adapter *adap);Int  i2c_del_adapter(struct i2c_adapter *adap);

2、增加/删除i2c_driver

Int  i2c_register_driver(struct module  *owner,struct i2c_driver *driver);Int  i2c_del_driver(struct i2c_driver *driver);

3、i2c_client依附和脱离

Int i2c_attach_client(struct i2c_client *client);Int i2c_detach_client(struct i2c_client *client);

4、 i2c传送\发送\接收

Int i2c_transfer(struct i2c_adaper *adap,struct i2c_msg *msgs,int num);Int i2c_master_send(struct i2c_client *client,const char *buf,int count);Int i2c_master_recv(struct i2c_client *client,const char *buf,int count);

以上三个函数必须首先在设备驱动中的xxx_probe中提供指向i2c_client的指针,注意在提取出i2c_client之前不要使用,否则出现空指针。 具体使用方法:eg:

struct i2c_client *this_client;//申明全局变量;static int __init st_lis35de_probe(struct i2c_client *client, const struct i2c_device_id * devid){       I2c_set_clientdata(client,&lis35de);       Client->addr = 0x1c;    this_client = client;//提取出client,然后使用,这三句要放到acc_init之前。       acc_init();//这个函数最终会调用LIS35DE_init(),把这个函数注释掉,因为这个函数的gpio的设定已经被放到devices.c gpio-i2c注册之前,这里不用再来一遍。} void LIS35DE_IICWrite(u_int8_t RegAdd, u_int8_t Data ,u_int8_t *result){    char buffer[2];     *result = 1;   // buffer[0]=LIS35DE_AddW;//使用i2c_master_send等函数,不再需要传送地址    buffer[0]=RegAdd;    buffer[1]=Data;    if(i2c_master_send(this_client, buffer,2)<0)    {        printk(KERN_ERR "LIS35DE_IICWrite: i2c_master_send error\n");        return;    }     *result = 0;    return;} int8_t LIS35DE_IICRead(u_int8_t RegAdd,u_int8_t *result){     S8 Data;     *result = 1;     char buffer[0];      //buffer[0]=LIS35DE_AddW;	 //使用i2c_master_send等函数,不再需要传送地址      buffer[0]=RegAdd;      //buffer[2]=LIS35DE_AddR; //使用i2c_master_send等函数,不再需要传送地址      if(i2c_master_send(this_client, buffer,1)<0)      {        printk(KERN_ERR "LIS35DE_IICRead: i2c_master_send error\n");        return -1;      }     if( i2c_master_recv(this_client, &Data,1)<0)      {        printk(KERN_ERR "LIS35DE_IICRead: i2c_master_recv error\n");        return -1;      }     *result = 0;      return Data;}//下面是使用i2c_transfer()函数来实现上面函数的例子:static int LIS35DE_RxData(char *rxData, int length){    struct i2c_msg msgs[] = {        {         .addr = this_client->addr,         .flags = 0,         .len = 1,         .buf = rxData,         },        {         .addr = this_client->addr,         .flags = I2C_M_RD,         .len = length,         .buf = rxData,         },    };#if DEBUG    printk(KERN_INFO "%s\n", __FUNCTION__);#endif    if (i2c_transfer(this_client->adapter, msgs, 2) < 0) {        printk(KERN_ERR "LISI2C_RxData: transfer error\n");        return -EIO;    } else        return 0;} static int LIS35DE_TxData(char *txData, int length){    struct i2c_msg msg[] = {        {         .addr = this_client->addr,         .flags = 0,         .len = length,         .buf = txData,         },    };#if DEBUG    printk(KERN_INFO "%s\n", __FUNCTION__);#endif    if (i2c_transfer(this_client->adapter, msg, 1) < 0) {        printk(KERN_ERR "LISI2C_TxData: transfer error\n");        return -EIO;    } else        return 0;}void LIS35DE_IICWrite(u_int8_t RegAdd, u_int8_t Data ,u_int8_t *result){    char buff[2];    *result = 1;//  client->addr = LIS35DE_AddW;    buff[0] = RegAdd;    buff[1] = Data;    if( LIS35DE_TxData(buff, 2) < 0 ) {#if DEBUG        printk(KERN_INFO "%s\n", __FUNCTION__);        printk("# LIS35IIC Write Error #\r\n");#endif        return;    }        *result = 0;} int8_t LIS35DE_IICRead(u_int8_t RegAdd,u_int8_t *result){    S8 Data;    *result = 1;    char buff[2];//  client->addr = LIS35DE_AddR;    buff[0] = RegAdd;    if( LIS35DE_RxData(buff, 1) < 0 ) {#if DEBUG        printk("# LIS35IIC Read Error #\r\n");#endif        return 1;    }    *result = 0;    Data = *buff;    return Data;}

其中有一部分/* */是由代码模拟时序来模拟i2c的。 6、 Gpio模拟i2c总线的通用传输算法 /drivers/i2c/i2c-algo-bit.c

int i2c_bit_add_numbered_bus(struct i2c_adapter *adap){       int err;       err = i2c_bit_prepare_bus(adap);   //加入adaoter类之前的一些操作,包括设定超时和重试,以及设定 i2c_algorithm的具体设定方法。       if (err)              return err;       return i2c_add_numbered_adapter(adap);} static int bit_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgs[], int num)/*参数:具体的适配器        需要传送的数据        数据数据*/static int bit_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgs[], int num){       i2c_start(adap);   //启动总线       for (i = 0; i < num; i++) {              pmsg = &msgs[i];              nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; //检测是否忽略响应              if (!(pmsg->flags & I2C_M_NOSTART)) {                     if (i) {                            i2c_repstart(adap);						   //如果是混合模式,则重新启动传输                     }                     ret = bit_doAddress(i2c_adap, pmsg);                     if ((ret != 0) && !nak_ok) {					  //如果出错了,则出错处理                            goto bailout;                     }              }              if (pmsg->flags & I2C_M_RD) {					 //收数据                } else {                     //发送数据                     /* write bytes from buffer */                     ret = sendbytes(i2c_adap, pmsg);                     if (ret >= 1)                     if (ret < pmsg->len) {                            if (ret >= 0)                                   ret = -EREMOTEIO;                            goto bailout;                     }              }       }       ret = i;bailout:       bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");       i2c_stop(adap);       return ret;} static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg){       while (count > 0) {              retval = i2c_outb(i2c_adap, *temp);  //发送一个字节的数据              /* OK/ACK; or ignored NAK */        /*一个字节一个字节的往后移动*/              if ((retval > 0) || (nak_ok && (retval == 0))) {                     count--;                     temp++;                     wrcount++;}

理清i2c中的个结构体关系

通过上面的讲解,已基本上简单地介绍完i2c驱动的方方面面,或许你还是对这里面的众多结构体之间的联系很迷惑,下面就来分析一下 i2c_driver 、 i2c_client 、 i2c_adapter 和 i2c_algorithm 这 4 个数据结构的作用及其盘根错节的关系。

  1. i2c_adapter 与 i2c_algorithm i2c_adapter 对应于物理上的一个适配器,而 i2c_algorithm 对应一套通信方法。一个 I2C 适配器需要 i2c_algorithm 中提供的通信函数来控制适配器上产生特定的访问周期。缺少 i2c_algorithm 的 i2c_adapter 什么也做不了,因此 i2c_adapter 中包含其使用的 i2c_algorithm 的指针。 i2c_algorithm 中的关键函数 master_xfer() 用于产生 I2C 访问周期需要的信号,以 i2c_msg (即 I2C 消息)为单位。 i2c_msg 结构体也非常关键,代码清单给出了它的定义。
struct i2c_msg {  __u16 addr; /* 设备地址 */  __u16 flags; /* 标志 */  __u16 len;  /* 消息长度 */  __u8 *buf;  /* 消息数据 */};

2、i2c_driver 与 i2c_client

i2c_driver 对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。 i2c_client 对应于真实的物理设备,每个 I2C 设备都需要一个 i2c_client 来描述。 i2c_client 一般被包含在 i2c 字符设备的私有信息结构体中。 i2c_driver 与 i2c_client 发生关联的时刻在 i2c_driver 的 attach_adapter() 函数被运行时。 attach_adapter() 会探测物理设备,当确定一个 client 存在时,把该 client 使用的 i2c_client 数据结构的 adapter 指针指向对应的 i2c_adapter 。 driver 指针指向该 i2c_driver ,并会调用 i2c_adapter 的 client_register() 函数。相反的过程发生在 i2c_driver 的 detach_client() 函数被调用的时候。

3、i2c_adpater 与 i2c_client

i2c_adpater 与 i2c_client 的关系与 I2C 硬件体系中适配器和设备的关系一致,即 i2c_client 依附于 i2c_adpater 。由于一个适配器上可以连接多个 I2C 设备,所以一个 i2c_adpater 也可以被多个 i2c_client 依附, i2c_adpater 中包括依附于它的 i2c_client 的链表。

i2c驱动的编写建议

那么对于一个驱动工程师,如何编写自己的i2c相关的驱动,下面仅提供个参考方案:

1、提供 I2C 适配器的硬件驱动,探测、初始化 I2C 适配器(如申请 I2C 的 I/O 地址和中断号)、驱动 CPU 控制的 I2C 适配器从硬件上产生各种信号以及处理 I2C 中断等。

2、提供 I2C 适配器的 algorithm ,用具体适配器的 xxx_xfer() 函数填充 i2c_algorithm 的 master_xfer 指针,并把 i2c_algorithm 指针赋值给 i2c_adapter 的 algo 指针。

3、实现 I2C 设备驱动与 i2c_driver 接口,用具体设备 yyy 的 yyy_attach_adapter() 函数指针、 yyy_detach_client() 函数指针和 yyy_command() 函数指针的赋值给 i2c_driver 的 attach_adapter 、 detach_adapter 和 detach_client 指针。

4、实现 I2C 设备驱动的文件操作接口,即实现具体设备 yyy 的 yyy_read() 、 yyy_write() 和 yyy_ioctl() 函数等。 上述工作中 1 、 2 属于 I2C 总线驱动, 3 、 4 属于 I2C 设备驱动,做完这些工作,系统会增加两个内核模块。

转载于:https://my.oschina.net/albert2011/blog/858317

你可能感兴趣的文章
浏览器渲染原理及解剖浏览器内部工作原理
查看>>
dubbo连接zookeeper注册中心因为断网导致线程无限等待问题【转】
查看>>
Spring Boot项目配置RabbitMQ集群
查看>>
bash 交互与非交互
查看>>
怎么提高自身技术
查看>>
北京游泳馆
查看>>
cacti安装与配置
查看>>
Mac 安卓模拟器打开 ONS
查看>>
完全卸载Oracle 11g教程
查看>>
Oracle调整表空间大小——ORA-03297: 文件包含在请求的 RESIZE 值以外使用的数据
查看>>
二叉树(一)
查看>>
[Windows Azure]Windows Azure Identity
查看>>
函数的递归
查看>>
JavaScript之将JS代码放在什么位置最合适
查看>>
【“零起点”--百度地图手机SDK】如何使用离线地图?
查看>>
深拷贝与浅拷贝复习
查看>>
各种参数的响应时间
查看>>
SQL Server 索引重建脚本
查看>>
23:LVS客户端配置脚本案例
查看>>
Android播放本地视频
查看>>