分享

i2c

 卓奇虫虫 2014-02-19

[概述]

之前在介绍I2C子系统时,提到过使用i2c-dev.c文件在应用程序中实现我们的I2C从设备驱动。不过,它实现的是一个虚拟,临时的i2c_client,随着设备文件的打开而产生,并随着设备文件的关闭而撤销。I2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主题是”i2c_driver成员函数+字符设备驱动”。

[i2c-dev.c源码分析]

I2c-dev初始化函数主要做了注册名为”i2c”的字符设备文件和”i2c-dev”的类。

I2c-dev.c中实现的i2cdev_read和i2cdev_write函数不具有太强的通用性,只适合下面这种单开始信号情况:


而不适合多开始信号的情况:


所以我们经常会使用i2cdev_ioctl函数的I2C_RDWR,在分析i2cdev_ioctl函数之前,我们需要了解一个结构体:

  1. /* This is the structure as used in theI2C_RDWR ioctl call */  
  2. struct i2c_rdwr_ioctl_data {  
  3.          structi2c_msg __user *msgs;         /* pointersto i2c_msgs */  
  4.          __u32nmsgs;                    /* number ofi2c_msgs */  
  5. };  

Msgs     表示单个开始信号传递的数据;

Nmsgs     表示有多少个msgs,比如上图,单开始信号时,nmsgs等于1;多开始信号时,nmsgs等于2

  1. struct i2c_msg {  
  2.          __u16addr;     /* slave address                         */  
  3.          __u16flags;  /* 默认为写入 */  
  4. #define I2C_M_TEN                  0x0010     /*this is a ten bit chip address */   
  5. #define I2C_M_RD           0x0001     /* readdata, from slave to master */   
  6. #define I2C_M_NOSTART                  0x4000     /* if I2C_FUNC_PROTOCOL_MANGLING */   
  7. #define I2C_M_REV_DIR_ADDR     0x2000     /*if I2C_FUNC_PROTOCOL_MANGLING */   
  8. #define I2C_M_IGNORE_NAK          0x1000     /*if I2C_FUNC_PROTOCOL_MANGLING */   
  9. #define I2C_M_NO_RD_ACK           0x0800     /* if I2C_FUNC_PROTOCOL_MANGLING */   
  10. #define I2C_M_RECV_LEN               0x0400     /* length will be first received byte */   
  11.          __u16len;                  /* msg length                              */  
  12.          __u8*buf;                 /* pointer to msgdata                       */  
  13. };  

使用i2cdev_ioctl函数的I2C_RDWR指令会调用到i2cdev_ioctl_rdrw函数:

  1. static noinline inti2cdev_ioctl_rdrw(struct i2c_client *client,  
  2.                    unsignedlong arg)  
  3. {  
  4.          structi2c_rdwr_ioctl_data rdwr_arg;  
  5.          structi2c_msg *rdwr_pa;  
  6.          u8__user **data_ptrs;  
  7.          inti, res;  
  8.    
  9.          if(copy_from_user(&rdwr_arg,  
  10.                                (struct i2c_rdwr_ioctl_data __user *)arg,  
  11.                                sizeof(rdwr_arg)))  
  12.                    return-EFAULT;  
  13.    
  14.          /*Put an arbitrary limit on the number of messages that can 
  15.           * be sent at once */  
  16.          if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)  
  17.                    return-EINVAL;  
  18.    
  19.          rdwr_pa= kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);  
  20.          if(!rdwr_pa)  
  21.                    return-ENOMEM;  
  22.    
  23.          if(copy_from_user(rdwr_pa, rdwr_arg.msgs,  
  24.                                rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {  
  25.                    kfree(rdwr_pa);  
  26.                    return-EFAULT;  
  27.          }  
  28.    
  29.          data_ptrs= kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);  
  30.          if(data_ptrs == NULL) {  
  31.                    kfree(rdwr_pa);  
  32.                    return-ENOMEM;  
  33.          }  
  34.    
  35.          res= 0;  
  36.          for(i = 0; i < rdwr_arg.nmsgs; i++) {  
  37.                    /*Limit the size of the message to a sane amount; 
  38.                     * and don't let length change either. */  
  39.                    if((rdwr_pa[i].len > 8192) ||  
  40.                        (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {  
  41.                             res= -EINVAL;  
  42.                             break;  
  43.                    }  
  44.                    data_ptrs[i]= (u8 __user *)rdwr_pa[i].buf;  
  45.                    rdwr_pa[i].buf= memdup_user(data_ptrs[i], rdwr_pa[i].len);  
  46.                    if(IS_ERR(rdwr_pa[i].buf)) {  
  47.                             res= PTR_ERR(rdwr_pa[i].buf);  
  48.                             break;  
  49.                    }  
  50.          }  
  51.          if(res < 0) {  
  52.                    intj;  
  53.                    for(j = 0; j < i; ++j)  
  54.                             kfree(rdwr_pa[j].buf);  
  55.                    kfree(data_ptrs);  
  56.                    kfree(rdwr_pa);  
  57.                    returnres;  
  58.          }  
  59.    
  60.          res= i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);  
  61.          while(i-- > 0) {  
  62.                    if(res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {  
  63.                             if(copy_to_user(data_ptrs[i], rdwr_pa[i].buf,  
  64.                                                 rdwr_pa[i].len))  
  65.                                      res= -EFAULT;  
  66.                    }  
  67.                    kfree(rdwr_pa[i].buf);  
  68.          }  
  69.          kfree(data_ptrs);  
  70.          kfree(rdwr_pa);  
  71.          returnres;  
  72. }  

咋一看,还挺复杂,其实主要做了一件事情:把用户空间传递过来的i2c_rdwr_ioctl_data数据进行错误检查,www. 然后调用i2c_transfer函数与适配器进行通信,如果是接收数据,代码会将访问到的数据传回i2c_rdwr_ioctl_data的buf中。I2c_transfer最终会调用到I2C适配器具体实现的master_xfer函数来与硬件进行通信。

[eeprom实例]

预备知识

使用的mini2440开发板,eeprom的地址为0x50,实验完成一个数据的读写,先看下读写时序

AT24C08任意地址字节写的时序:


AT24C08任意地址字节写的时序:


下面的代码可以按照上面的两个图来阅读:

  1. #include <stdio.h>   
  2. #include <linux/types.h>   
  3. #include <fcntl.h>   
  4. #include <unistd.h>   
  5. #include <stdlib.h>   
  6. #include <sys/types.h>   
  7. #include <sys/ioctl.h>   
  8. #include <errno.h>   
  9. #include <assert.h>   
  10. #include <string.h>   
  11. #include <linux/i2c.h>   
  12. #include <linux/i2c-dev.h>   
  13.    
  14. int main()  
  15. {  
  16.          intfd, ret;  
  17.          unsignedchar rdwr_addr = 0x42;   /* e2prom 读写地址 */  
  18.          unsignedchar device_addr = 0x50; /* e2prom 设备地址 */  
  19.          unsignedchar data = 0x12;  /* 向e2prom写的数据 */  
  20.          structi2c_rdwr_ioctl_data e2prom_data;  
  21.    
  22.          fd= open("/dev/i2c/0", O_RDWR);  
  23.          if(fd < 0) {  
  24.                    perror("openerror");  
  25.                    exit(1);  
  26.          }  
  27.    
  28.          e2prom_data.msgs= (struct i2c_msg *)malloc(e2prom_data.nmsgs * \  
  29.                                                sizeof(structi2c_msg));  
  30.          if(e2prom_data.msgs == NULL) {  
  31.                    perror("mallocerror");  
  32.                    exit(1);  
  33.          }  
  34.    
  35.          ioctl(fd,I2C_TIMEOUT, 1); /* 设置超时 */  
  36.          ioctl(fd,I2C_RETRIES, 2); /* 设置重试次数 */  
  37.    
  38.           
  39.          /*向e2prom的rdwr_addr地址写入数据data*/  
  40.          e2prom_data.nmsgs= 1;  
  41.          e2prom_data.msgs[0].len= 2;  
  42.          e2prom_data.msgs[0].addr= device_addr;  
  43.          e2prom_data.msgs[0].flags= 0;     /* write */  
  44.    
  45.           
  46.          e2prom_data.msgs[0].buf= (unsigned char *)malloc(2);  
  47.          e2prom_data.msgs[0].buf[0]= rdwr_addr;    /* write address */  
  48.          e2prom_data.msgs[0].buf[1]= data;      /* write data */  
  49.    
  50.          ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);  
  51.          if(ret < 0) {  
  52.                    perror("writedata error");  
  53.                    exit(1);  
  54.          }  
  55.          printf("writedata: %d to address: %#x\n", data, rdwr_addr);  
  56.          data= 0;  /* be zero*/  
  57.    
  58.    
  59.          /*从e2prom的rdwr_addr地址读取数据存入buf*/  
  60.          e2prom_data.nmsgs= 2;  
  61.          e2prom_data.msgs[0].len= 1;  
  62.          e2prom_data.msgs[0].addr= device_addr;  
  63. //      e2prom_data.msgs[0].flags= 0;     /* write */   
  64.          e2prom_data.msgs[0].buf= &rdwr_addr;  
  65.    
  66.          e2prom_data.msgs[1].len= 1;  
  67.          e2prom_data.msgs[1].addr= device_addr;  
  68.          e2prom_data.msgs[1].flags= 1;     /* read */  
  69.          e2prom_data.msgs[1].buf= &data;  
  70.    
  71.          ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);  
  72.          if(ret < 0) {  
  73.                    perror("readerror");  
  74.                    exit(1);  
  75.          }  
  76.          printf("read  data: %d from address: %#x\n", data,rdwr_addr);  
  77.           
  78.          free(e2prom_data.msgs);  
  79.          close(fd);  
  80.    
  81.          return0;  
  82. }  

在mini2440开发板上已经实验成功。

linux

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多