linux下I2C驱动

默认情况下,I2C以从模式工作。接口在生成起始位后,会自动由从模式切换到主模式,并在仲裁丢失或生成停止位时从主模式切换为主模式。

在主模式下,I2C接口会启动数据传输并生成时钟信号。

任何能够进行发送和接收的设备都可以成为主设备。一个主控制器能控制信号的传输和时钟频率,当然在任何时间点上只能有一个主控制器。

空闲状态:I2C总线的SDA和SCL两条信号线同时处于高电平。

进行数据传输时,在SCL呈现高电平期间,SDA上电平必须保持稳定,低电平为数据0,高电平为数据1.

I2C总线上所有数据都是以8字节传送的,发送器每发送一个字节,就在时钟脉冲期间释放数据线,由接收器反馈一个应答信号。

I2C核心:I2C提供了I2C总线驱动和设备驱动的注册和注销方法,I2C通信方法(algorithm) 上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码

I2C总线:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,其可直接集成在CPU内部。

一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生的特定的访问周期。

master_xfer();用于产生i2c访问周期需要的start,stop,ack信号.

i2c_scan_static_borad_info()为整个I2C子系统的核心,会去遍历一个由I2C设备组成的双向循环链表,并完成所有I2C从设备的i2c_client的注册。

i2c从设备主要完成三大任务

系统初始化时添加i2c_board_info为结构的i2c从设备的信息

在i2c从设备驱动里使用i2c_adapter提供的算法实现i2c通信

将i2c从设备特有数据结构挂载到i2c_client->driver_data


i2c_adapter对应i2c总线控制器

 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.
  */
 struct i2c_adapter {
         struct module *owner;
         unsigned int class;               /* classes to allow probing for */
         const struct i2c_algorithm *algo; /* the algorithm to access the bus */
         void *algo_data;
 
         /* data fields that are valid for all devices   */
         struct rt_mutex bus_lock;
 
         int timeout;                    /* in jiffies */
         int retries;
         struct device dev;              /* the adapter device */
 
         int nr;
         char name[48];
         struct completion dev_released;
 
         struct mutex userspace_clients_lock;
         struct list_head userspace_clients;
 };
i2c_client对应真实的i2c物理设备 

 /**
  * struct i2c_client - represent an I2C slave device
  * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
  *      I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
  * @addr: Address used on the I2C bus connected to the parent adapter.
  * @name: Indicates the type of the device, usually a chip name that's
  *      generic enough to hide second-sourcing and compatible revisions.
  * @adapter: manages the bus segment hosting this I2C device
  * @driver: device's driver, hence pointer to access routines
  * @dev: Driver model device node for the slave.
  * @irq: indicates the IRQ generated by this device (if any)
  * @detected: member of an i2c_driver.clients list or i2c-core's
  *      userspace_devices list
  *
  * An i2c_client identifies a single device (i.e. chip) connected to an
  * i2c bus. The behaviour exposed to Linux is defined by the driver
  * managing the device.
  */
 struct i2c_client {
         unsigned short flags;           /* div., see below              */
         unsigned short addr;            /* chip address - NOTE: 7bit    */
                                         /* addresses are stored in the  */
                                         /* _LOWER_ 7 bits               */
         char name[I2C_NAME_SIZE];
         struct i2c_adapter *adapter;    /* the adapter we sit on        */
         struct i2c_driver *driver;      /* and our access routines      */
         struct device dev;              /* the device structure         */
         int irq;                        /* irq issued by device         */
         struct list_head detected;
 };

i2c_driver和i2c_client发生关联的时刻在i2c_driver的i2c_attach_adapter()函数运行时。

当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。

说起Linux下的子系统,I2C子系统已经算是比较简单的框架了。

  首先相关代码在driver/i2c下

.built-in.o.cmd       .i2c-dev.o.cmd        algos/                i2c-boardinfo.c       i2c-core.h            i2c-dev.o     muxes/.i2c-boardinfo.o.cmd  Kconfig               built-in.o            i2c-boardinfo.o       i2c-core.o            i2c-mux.c           .i2c-core.o.cmd       Makefile              busses/               i2c-core.c            i2c-dev.c             i2c-smbus.c    

i2c-dev是用户态直接相关联的,里面有相关文件(linux中设备即文件)操作的函数,最后依然会调用到i2c-core(是硬件层与系统调用层的中间缓冲层)的函数(i2c_master_send,i2c_master_recv最终还是调用i2c_transfer),i2c默认的主设备号为89.

 static int __init i2c_dev_init(void)
 {
         int res;
 
         printk(KERN_INFO "i2c /dev entries driver\n");
 
         res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
         if (res)
                 goto out;

         i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
         if (IS_ERR(i2c_dev_class)) {
                 res = PTR_ERR(i2c_dev_class);
                 goto out_unreg_chrdev;
         }
 
         /* Keep track of adapters which will be added or removed later */
         res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
         if (res)
                 goto out_unreg_class;
 
         /* Bind to already existing adapters right away */
         i2c_for_each_dev(NULL, i2cdev_attach_adapter);
 
         return 0;
 
 out_unreg_class:
         class_destroy(i2c_dev_class);
 out_unreg_chrdev:
         unregister_chrdev(I2C_MAJOR, "i2c");
 out:
         printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
         return res;
 }
在i2c-core中最后还是调用我们在busses/下写的i2c控制器模块中的传输函数(回调) ,在xxx_i2c_probe中,初始化一些硬件环境(中断,内存空间,时钟),i2c控制器抽象成i2c_adapter,最后调用i2c_add_numbered_adapter().注册到i2c子系统中。在该函数中调用i2c_register_adapter(),最终还是调用device_register().创建设备节点。其中之前一直很让我困惑的是节点设备的名字,原来在子系统中已经将设备节点的名字封装好,不需要我们在去命名,注意这里的节点设备名字和platform_device的名字是可以不相同的。

如果register成功就调用另一个核心的函数:

          /* create pre-declared device nodes */
          if (adap->nr < __i2c_first_dynamic_bus_num)
                  i2c_scan_static_board_info(adap);

扫描之前静态注册的I2C控制器设备,代码在(arch/xxx_cpu/xxx_sys/i2c_board_info.c)中:

关键函数在:

  static int __init xxx_i2c_board_init(void)
  {
          i2c_register_board_info(0,xxx_i2c_1_board_info,ARRAY_SIZE(xxx_i2c_1_board_info));
          i2c_register_board_info(1,xxx_i2c_2_board_info,ARRAY_SIZE(xxx_i2c_2_board_info));
          i2c_register_board_info(2,xxx_i2c_3_board_info,ARRAY_SIZE(xxx_i2c_3_board_info));
          i2c_register_board_info(3,xxx_i2c_4_board_info,ARRAY_SIZE(xxx_i2c_4_board_info));
          return 0;
  }
如果注册总线号和i2c控制器的number相同则又要创建一个注册新的i2c设备i2c_new_device()//??也不知道为什么


最关键的是是i2c通信过程。主要在xxx_i2c_probe()中dev->adap->algo = &i2c_xxx_algo;

 static struct i2c_algorithm i2c_xxx_algo = {
         .master_xfer    = i2c_xxx_xfer,
         .functionality  = i2c_xxx_func,
 };
最终i2c-core中的i2c_transfer()会调用i2c_xxx_xfer();里面主要对I2C控制器芯片的寄存器操作。

信息的内容被封装成i2c_message;

 struct i2c_msg {
         __u16 addr;     /* slave address                        */
         __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测试程序的编写了,我们使用的是eeprom,因为是支持i2c接口,所以向i2c从设备地址写入若干数值,再从该地址读取数值,如果写入和读出是相同的,那么就代表i2c驱动是对的。


编写具体的i2c驱动

提供i2c适配器的硬件驱动,探测,初始化i2c适配器(申请i2c,I/O地址和中断号),驱动cpu控制的i2c适配器从硬件上产生。

提供i2c控制的algorithm(xxx_tranfer)

实现i2c设备驱动中的i2c_driver接口,只是实现设备与总线的连接

实现i2c设备对应类型的具体驱动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值