zoukankan      html  css  js  c++  java
  • Linux驱动之IIC总线

    <作用>

    电子设备中有很多IIC设备之间需要进行相互通信,这样就产生了IIC总线,常用来实现设备之间的数据通信。
     
    <IIC总线结构>
    IIC总线只有两条线,一条是串行数据线(SDA),另外一条是串行时钟线(SCL).
    注:每一个连接到总线上的设备都有一个唯一的地址可以访问(这一点有点像USB设备)
     
    <IIC总线信号类型>
    a:开始信号(S):当SCL信号为高电平,SDA的电平由高电平变为低电平表示开始传输数据。
    b:结束信号(P):当SCL信号为高电平,SDA的电平由低电平变为高电平表示结束传输数据。
    c:相应信号(ACK):从机接收到8位数据后,在第9个时钟周期,拉低SDA电平,表示接受到数据,这个信号称为应答信号。
     
    <IIC数据传输方式>
    主机(IIC控制器)主要通过SDA向从机发送数据,当总线处于空闲的时候SDA和SCL都处于高电平。
    a:当主机检测到总线处于空闲状态时,主机发送给开始信号(S)
    b:主机发出8位数据,8位数据中,前7位中表示从机地址,第8位表示数据的传输方向。
    c:和地址匹配的从机发出相应信号。
    d:从机传输一系列响应序列
    e:主机接收到这些数据后,发出结束信号P,该次数据传输完成。
     
    <IIC控制器结构图>
    a:主要涉及的寄存器
    IICCON:控制寄存器
    IICSTAT:状态寄存器
    IICADD:地址寄存器
     
    IICCDS:接收和发送的数据存储寄存器
     
    <linux 驱动之IIC>
    a:背景
    对于IIC设备的控制,但是IIC设备太多了,为了很好的管理这些设备(不同等的设备名,管理各种设备,不同设备的地址,不同设备对应的驱动),Linux内核开发了如下结构体:
    b:IIC设备(i2c_client)
    struct i2c_client {
    unsigned short flags;
    unsigned short addr;
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter; //通过该指针可以找到适配器,然后通过适配器找到                       //适配器算法中的发送消息和接受函数完成同IIC设备                          //的通信              
    struct i2c_driver *driver;
    struct device dev; //用于建立设备模型
    int irq;
    struct list_head detected;
    };
     
    c:IIC驱动(i2c_driver)
    struct i2c_driver {
    unsigned int class;
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;
    int (*detach_adapter)(struct i2c_adapter *) __deprecated;
    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 *);
    void (*alert)(struct i2c_client *, unsigned int data);
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
    struct device_driver driver;//用于建立设备模型
    const struct i2c_device_id *id_table;//是一个数组,保存该驱动支持的所有的 的                               //设备信息 
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;
    struct list_head clients;
    };
    d:IIC适配器(i2c_adapter)//其本质表示适配器这个设备
    -----i2c_adapter is the structure used to identify a physical i2c bus along with the access algorithms necessary to access it.内核中可以有很多总线适配器,其中内核中有一个静态指针数组adapters记录了所有已经注册的总线适配器。
     
    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设备
    };
    d:IIC适配器驱动(i2c_algorithm)
     
    /*
     * The following structs are for those who like to implement new bus drivers:
     * i2c_algorithm is the interface to a class of hardware solutions which can
     * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
     * to name two of the most common.
     */
    struct i2c_algorithm {
    /* If an adapter algorithm can't do I2C-level access, set master_xfer
      to NULL. If an adapter algorithm can do SMBus access, set
      smbus_xfer. If set to NULL, the SMBus protocol is simulated
      using common I2C messages */
    /* master_xfer should return the number of messages successfully
      processed, or a negative value on error */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
      int num);
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
      unsigned short flags, char read_write,
      u8 command, int size, union i2c_smbus_data *data);
     
    /* To determine what the adapter supports */
    u32 (*functionality) (struct i2c_adapter *);
    };
    e:注意:i2c_driver和i2c_client属于设备层,i2c_adapter和i2c_algorithm属于总线层。他们之间的关系如图所示:
    f:IIC设备之间的通信方式
    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 */
    };
    填该结构体,用于同IIC设备通信。
     
    <IIC设备驱动流程>
    a:流程图
     
    (1)xxx_init()和i2c_add_driver()实现对IIC子系统的初始化,这项工作由内核完成。
    (2)xxx_attach_adapter()用来将IIC设备驱动和适配器挂接在一起,这样才能IIC设备驱动才能利用适配器驱动控制适配器和IIC设备通信。(注意:在这两步中间需要根据具体适配器实现相应的驱动,以及相应struct algorithm{}的实现)
    (3)i2c_probe()函数是在检测到IIC设备后调用的函数,进一步实现IIC设备的各数据结构的初始化。包括下面的函数都为了实现相应的功能。
     
    b:IIC子系统初始化
    (1)初始化函数
    (2)卸载函数
     
    c:适配器驱动程序
    (1)IIC适配器内核结构
    注意:
    "struct i2c_adapter adap"
    这是内核提供的IIC适配器的结构体,特定类型的适配器结构体需要在这个结构上进行扩充(面向对象的思想)。
    "void __iomem  *regs"
    指向IIC适配器的控制寄存器地址,注意这个地址是物理地址
    "struct resource *ioarea"
    指向适配器申请的资源,这里应该是内存资源,先进行申请,然后进行映射
    "struct i2c_msg *msg"
    用来实现IIC设备驱动于设备之间的通信
     
    2)适配器加载函数
    (3)适配器卸载函数
    注意:这个适配器驱动跟字符设备驱动很相似,因为这里不需要同用户空间进行交互,所以这里没有相应的fops,但是有相应的类似fops——struct i2c_algorithm{}。和字符设备驱动相似,
    重点就是要实现这个类似fops,struct i2c_algorithm{}中的相应的函数通过操作"void __iomem  *regs"中的相应的寄存器。
     
    <IIC适配器驱动模块的加载与卸载>
    a:这里将适配器驱动定性为平台设备
    加载函数
    b:卸载函数
    c:注意其中的"s3c2410_i2c_driver"
    d:对于一个平台设备来"s3c2410_i2c_probe()"函数是很重要的
    (1)"s3c2410_i2c_probe()"中的"s3c2410_i2c_init()"函数
     
    e:与"s3c2410_i2c_probe()"相反的函数是s3c2410_i2c_remove()。
     
    <IIC设备驱动实例>
    a:设备文件操作函数集
    static struct file_operations eep_fops = {
    .owner = THIS_MODULE,
    .llseek = eep_llseek,
    .read = eep_read,
    .ioctl = eep_ioctl,
    .open = eep_open,
    .release = eep_release,
    .write = eep_write,
    };
    static dev_t dev_number; /* Allotted Device Number */
    static struct class *eep_class; /* Device class */
    /* Per-device client data structure for each
    * memory bank supported by the driver
    */
     
    b:IIC设备结构体
    struct eep_bank {
    struct i2c_client *client; /* I2C client for this bank */
    unsigned int addr; /* Slave address of this bank */
    unsigned short current_pointer; /* File pointer */
    int bank_number; /* Actual memory bank number */
    /* ... */ /* Spinlocks, data cache for slow devices,.. */
    };
    #define NUM_BANKS 2 /* Two supported banks */
    #define BANK_SIZE 2048 /* Size of each bank */
    struct ee_bank *ee_bank_list; /* List of private data
    structures, one per bank */
    c:初始化函数
    int __init
    eep_init(void)
    {
    int err, i;
    /* Allocate the per-device data structure, ee_bank */
    ee_bank_list = kmalloc(sizeof(struct ee_bank)*NUM_BANKS, GFP_KERNEL);
    memset(ee_bank_list, 0, sizeof(struct ee_bank)*NUM_BANKS);
    /* Register and create the /dev interfaces to access the EEPROM
    banks. Refer back to Chapter 5, "Character Drivers" for more details */
    if (alloc_chrdev_region(&dev_number, 0,
    NUM_BANKS, "eep") < 0) {
    printk(KERN_DEBUG "Can't register device ");
    return -1;
    }
    eep_class = class_create(THIS_MODULE, DEVICE_NAME);
    for (i=0; i < NUM_BANKS;i++) {
    /* Connect the file operations with cdev */
    cdev_init(&ee_bank[i].cdev, &ee_fops);
    /* Connect the major/minor number to the cdev */
    if (cdev_add(&ee_bank[i].cdev, (dev_number + i), 1)) {
    printk("Bad kmalloc ");
    return 1;
    }
    device_create(eep_class, NULL, MKDEV (MAJOR) (dev_number),i),
    "eeprom%d", i);
    }
    /* Inform the I2C core about our existence. See the section
    "Probing the Device" for the definition of eep_driver */
    err = i2c_add_driver(&eep_driver);
    if (err) {
    printk("Registering I2C driver failed, errno is %d ", err);
    return err;
    }
    printk("EEPROM Driver Initialized. ");
    return 0;
    }
    (1) eep_driver结构体如下所示:
    eep_init()在设备初始化的时调用i2c_add_driver()将注册eep_probe()
    static struct i2c_driver eep_driver =
    {
    .driver = {
    .name = "EEP", /* Name */
    },
    .id = I2C_DRIVERID_EEP, //设备标志符I2C_DRIVERID_EEP对于每个设备应该是唯一的
    .attach_adapter = eep_probe, /* Probe Method */
    .detach_client = eep_detach, /* Detach Method */
    };
    (2)当IIC核心调用表明主机适配器已经存在的客户驱动程序的probe()方法时,其会发过来调用i2c_probe(),该函数的参数是驱动程序所关联的设备地址以及具体的探测函数attach()。
     
    #include <linux/i2c.h>
    /* The EEPROM has two memory banks having addresses SLAVE_ADDR1
    * and SLAVE_ADDR2, respectively
    */
    static unsigned short normal_i2c[] = {
    SLAVE_ADDR1, SLAVE_ADDR2, I2C_CLIENT_END
    };
    static struct i2c_client_address_data addr_data = {
    .normal_i2c = normal_i2c,
    .probe = ignore,
    .ignore = ignore,
    .forces = ignore,
    };

    static int eep_probe(struct i2c_adapter *adapter)
    {
    /* The callback function eep_attach(), is shown in Listing 8.5 */
    return i2c_probe(adapter, &addr_data, eep_attach);
    }

    int eep_attach(struct i2c_adapter *adapter, int address, int kind)
    {
    static struct i2c_client *eep_client;
    eep_client = kmalloc(sizeof(*eep_client), GFP_KERNEL);
    eep_client->driver = &eep_driver; /* Registered in Listing 8.2 */
    eep_client->addr = address; /* Detected Address */
    eep_client->adapter = adapter; /* Host Adapter */
    eep_client->flags = 0;
    strlcpy(eep_client->name, "eep", I2C_NAME_SIZE);
    /* Populate fields in the associated per-device data structure */
    /* ... */
    /* Attach */
    i2c_attach_client(new_client);
    }  
     
    <整个程序框架>
     
     
  • 相关阅读:
    OpenVINO Model Server的服务化部署——step3(django服务构建)
    (5)名称空间 namespace 和 using 声明
    (4)#include 指令
    (3)注释
    (2)简单的程序
    (1)Hello World
    javaScript 错误学习 -- throw、try 、catch和 finally
    js 如何在数字前面自动补零,生成序列号、单据号
    vs2015项目运行出现“无法启动IIS Express Web服务器”,如何解决
    Sql Server 2008 如何将数据表导出Excel文件?
  • 原文地址:https://www.cnblogs.com/big-devil/p/8589675.html
Copyright © 2011-2022 走看看