zoukankan      html  css  js  c++  java
  • I2C子系统驱动框架及应用 (转)

    I2C子系统驱动框架:     应用程序层(app层) ——————————————————————————————————– i2c driver层: 从设备驱动层(TS  Sensor等) 1. 需要和应用层交互(fops  cdev) 2. 封装数据,但是不知道数据如何写入到硬件,需要调用adapter层的相关函数去写 ——————————————————————————————————– i2c core:维护i2c bus, 包括i2c driver和i2c client链表 1. 实现i2c client和i2c driver的匹配 ——————————————————————————————————– i2c adapter层: i2c控制器层,初始化i2c控制器,实现i2c时序 1. 将数据写入或读取从设备 2. 不知道具体数据(i2c driver提供的数据)是什么,但知道具体如何操作(读/写)从设备         这一层是具体的厂商实现的,比如三星:driver/i2c/busser/i2c-s3c2410.c

    框架中,i2c core是由Linux内核实现的(i2c-core.c),i2c adapter是由具体的芯片厂商实现的,比如三星的芯片adapter实现都在driver/i2c/busser/i2c-s3c2410.c。所以这连个部分需要编译到uImage中(make menuconfig -> device driver -> <*> i2c support -> i2c hardware Bus support -> S3C2410 I2C driver)。 如果在/sys/bus/i2c/devices/i2c-0/1/2                表示有i2c-adapter 存在

    在总结的时候看到有其他博友整理的框图非常好,我就借过来给大家分享! i2c子系统框架

    重要结构体之间的关系

    从i2c驱动架构图中可以看出,linux内核对i2c架构抽象了一个叫核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器),core层不但为上面的设备驱动提供封装后的内核注册函数,而且还为下面的硬件事件提供注册接口(也就是i2c总线注册接口i2c_add_register),可以说core层起到了承上启下的作用。

    相关的重要结构体和函数: 1. i2c_client 每一个i2c从设备都需要用一个i2c_client结构体来描述,i2c_client对应真实的i2c物理设备device,但是i2c_client不是我们自己写程序去创建的,而是通过以下常用的方式自动创建的(这个地方不做详细说明,以介绍总体框架为主): platform创建: 1. 注册i2c_board_info 2. 获取对应的adapter,然后i2c_new_device devicetree创建: 3. 通过设备树的一个节点去描述一个从设备,设备树在解析的时候会自动创建client

    struct i2c_client {
        unsigned short flags;        //标志位 (读写)
        unsigned short addr;         //7位的设备地址(低7位)
        char name[I2C_NAME_SIZE];    //设备的名字,用来和i2c_driver匹配
        struct i2c_adapter *adapter; //依附的适配器(adapter),适配器指明所属的总线(i2c0/1/2_bus)
        struct device dev;           //继承的设备结构体
        int irq;                     //设备申请的中断号
        struct list_head detected;   //已经被发现的设备链表
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2. i2c_driver driver是指向从设备的驱动程序,由我们自己去实现并通过i2c_add_register注册到i2c的bus中,和i2c clinet进行匹配,匹配成功则调用probe函数。

    struct i2c_driver {
        int (*probe)(struct i2c_client *, const struct i2c_device_id *); //设备匹配成功调用的函数
        int (*remove)(struct i2c_client *);                              //设备移除之后调用的函数
        struct device_driver driver;                                     //设备驱动结构体
        const struct i2c_device_id *id_table;   //设备的ID表,匹配用platform创建的client
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. i2c_adapter i2c总线适配器其实就是一个i2c总线控制器,本质上是一个物理设备,主要用来完成i2c总线控制器相关的数据通信 由芯片厂商去实现的。

    struct i2c_adapter {
        struct module *owner;
        unsigned int class;               //允许匹配的设备的类型
        const struct i2c_algorithm *algo; //指向适配器的驱动程序,实现发送数据的算法
        struct device dev;                //指向适配器的设备结构体
        char name[48];                    //适配器的名字
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4. i2c_algorithm  i2c算法,适配器对应的驱动程序,每一个适配器对应一个驱动程序,用来描述适配器和设备之间的通信方法 由芯片厂商去实现的。

    struct i2c_algorithm {
        //传输函数指针,指向实现IIC总线通信协议的函数
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);        
    };
    • 1
    • 2
    • 3
    • 4

    5. i2c_msg  把要发送的数据封装成msg结构体(比如16个字节进行拆分)

    struct i2c_msg {
        __u16 addr;     /* slave address  */
        __u16 flags;    /* 1 - 读  0 - 写 */
        __u16 len;      /* msg length     */
        __u8 *buf;      /* 要发送的数据   */
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6. i2c_add_register 注册一个i2c_driver结构体,通过name或者id_tables或者of_match_table去匹配一个i2c_client,如果匹配成功,则会调用i2c_driver结构体里面的probe函数,并将对应的i2c_client结构体传过来。 #define  i2c_add_driver(driver)  i2c_register_driver(THIS_MODULE, driver) int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

    7. i2c_transfer  负责通过对应的i2c总线对依附于这个adapter的从机设备(i2c_client)进行读写数据(双向的)。其中要读写的数据要封装成为一个i2c_msg结构体,根据msg的flags标志位是0还是1来决定是读还是写。其实i2c_transfer是对master_xfer的封装。

    /**
     * i2c_transfer - execute a single or combined I2C message
     * @adap: Handle to I2C bus
     * @msgs: One or more messages to execute before STOP is issued to
     *  terminate the operation; each message begins with a START.
     * @num: Number of messages to be executed.
     *
     * Returns negative errno, else the number of messages executed.
     *
     * Note that there is no requirement that each message be sent to
     * the same slave address, although that is the most common model.
     */
    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    eg.
    /* i2c_msg指明要操作的从机地址,方向,缓冲区 */
    struct i2c_msg msg[] = {
        {client->addr, 0, 1, &txbuf},    //0表示写,向往从机写要操作的寄存器的地址
        {client->addr, 1, 1, &rxbuf},    //读数据
    };
    
    /* 通过i2c_transfer函数操作msg */
    ret = i2c_transfer(client->adapter, msg, 2);    //执行2条msg
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这几个重要结构体之间的关系: a – i2c_adapter与i2c_algorithm i2c_adapter对应与物理上的一个适配器,而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的(i2c_algorithm中的又是更下层与硬件相关的代码提供)通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。 i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start、stop、ack信号,以i2c_msg为单位发送和接收通信数据。 i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体

    b –i2c_driver和i2c_client     i2c_driver对应一套驱动方法 i2c_client对应真实的i2c物理设备device,每个i2c设备都需要一个i2c_client来描述 i2c_driver与i2c_client的关系是一对多。一个i2c_driver上可以支持多个同等类型的i2c_client.

    c – i2c_adapter和i2c_client i2c_adapter和i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表

    下面给出一个i2c子系统实例代码(用设备树实现): 主机 -  三星的某款cpu 从机 - mpu6050三轴加速度传感器

    设备树描述: 当设备树被内核解析后会生成一个依附于i2c-0这个adapter的i2c_client

    @i2c-0 {//表示这个i2c_client所依附的adapter是i2c-0
        //对应i2c_client的name = "invensense,mpu6050"
        compatible = "invensense,mpu6050";
        //对应i2c_client的addr = 0x69  -- 从机设备的地址
        reg = <0x69>;
        //对应i2c_client的irq
        interrupts = <70>;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    driver代码:

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/i2c.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <asm/uaccess.h>
    #include "mpu6050.h"
    
    MODULE_LICENSE("GPL");
    
    #define SMPLRT_DIV      0x19
    #define CONFIG          0x1A
    #define GYRO_CONFIG     0x1B
    #define ACCEL_CONFIG    0x1C
    #define TEMP_OUT_H      0x41
    #define TEMP_OUT_L      0x42
    #define PWR_MGMT_1      0x6B
    
    int MAJOR = 255;
    int MINOR = 0;
    
    struct mpu6050_device {
        struct cdev cdev;
        dev_t devno;
        struct i2c_client * client;
    }mpu6050_dev;
    
    /* 读取mpu6050中一个字节的数据,将读取的数据的地址返回 */
    static int mpu6050_read_byte(struct i2c_client * client, unsigned char reg_add)
    {
        int ret;
    
        /* 要读取的那个寄存器的地址 */
        char txbuf = reg_add;
    
        /* 用来接收读到的数据 */
        char rxbuf[1];
    
        /* i2c_msg指明要操作的从机地址,方向,缓冲区 */
        struct i2c_msg msg[] = {
            {client->addr, 0, 1, &txbuf},       //0表示写,向往从机写要操作的寄存器的地址
            {client->addr, I2C_M_RD, 1, rxbuf}, //读数据
        };
    
        /* 通过i2c_transfer函数操作msg */
        ret = i2c_transfer(client->adapter, msg, 2);    //执行2条msg
        if (ret < 0)
        {
            printk("i2c_transfer read err
    ");
            return -1;
        }
    
        return rxbuf[0];
    }
    
    static int mpu6050_write_byte(struct i2c_client * client, unsigned char reg_addr, unsigned char data)
    {
        int ret;
    
        /* 要写的那个寄存器的地址和要写的数据 */
        char txbuf[] = {reg_addr, data};
    
        /* 1个msg,写两次 */
        struct i2c_msg msg[] = {
            {client->addr, 0, 2, txbuf}
        };
    
        ret = i2c_transfer(client->adapter, msg, 1);
        if (ret < 0)
        {
            printk("i2c_transfer write err
    ");
            return -1;
        }
    
        return 0;
    }
    
    static int mpu6050_open(struct inode * inodep, struct file * filep)
    {
        printk("%s called
    ", __func__);
    
        mpu6050_write_byte(mpu6050_dev.client, PWR_MGMT_1, 0x00);
        mpu6050_write_byte(mpu6050_dev.client, SMPLRT_DIV, 0x07);
        mpu6050_write_byte(mpu6050_dev.client, CONFIG, 0x06);
        mpu6050_write_byte(mpu6050_dev.client, GYRO_CONFIG, 0xF8);
        mpu6050_write_byte(mpu6050_dev.client, ACCEL_CONFIG, 0x19);
    
        return 0;
    }
    
    static int mpu6050_release(struct inode * inodep, struct file * filep)
    {
        printk("%s called
    ", __func__);
    
        return 0;
    }
    
    void get_temp(union mpu6050_data * data)
    {
        data->temp = mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_L);
        data->temp |= mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_H) << 8;
    }
    
    static long mpu6050_ioctl(struct file * filep, unsigned int cmd, unsigned long arg)
    {
        union mpu6050_data data;
    
        switch (cmd)
        {
            case GET_TEMP:
                get_temp(&data);
                break;
            default:
                break;
        }
    
        if (copy_to_user((unsigned int *)arg, &data, sizeof(data)))
            return -1;
    
        return 0;
    }
    
    struct file_operations mpu6050_fops = {
        .owner = THIS_MODULE,
        .open  = mpu6050_open,
        .release = mpu6050_release,
        .unlocked_ioctl = mpu6050_ioctl,
    };
    
    /* 匹配函数,设备树中的mpu6050结点对应转换为一个client结构体 */
    static int mpu6050_probe(struct i2c_client * client, const struct i2c_device_id * id)
    {
        int ret;
        printk("mpu6050 match ok!
    ");
    
        mpu6050_dev.client = client;
    
        /* 注册设备号 */
        mpu6050_dev.devno = MKDEV(MAJOR, MINOR);
        ret = register_chrdev_region(mpu6050_dev.devno, 1, "mpu6050");  
        if (ret < 0)
            goto err1;
    
        cdev_init(&mpu6050_dev.cdev, &mpu6050_fops);
        mpu6050_dev.cdev.owner = THIS_MODULE;
        ret = cdev_add(&mpu6050_dev.cdev, mpu6050_dev.devno, 1);
        if (ret < 0)
            goto err2;
    
        return 0;
    
    err2:
        unregister_chrdev_region(mpu6050_dev.devno, 1);
    err1:
        return -1;
    }
    
    static int mpu6050_remove(struct i2c_client * client)
    {
        printk("mpu6050 removed!
    ");
    
        cdev_del(&mpu6050_dev.cdev);
        unregister_chrdev_region(mpu6050_dev.devno, 1);
    
        return 0;
    }
    
    /* 用来匹配mpu6050的设备树 */
    static struct of_device_id mpu6050_of_match[] = {
        {.compatible = "invensense,mpu6050"},
        {},
    };
    
    struct i2c_driver mpu6050_driver = {
        .driver = {
            .name = "mpu6050",
            .owner = THIS_MODULE,
            .of_match_table = of_match_ptr(mpu6050_of_match),
        },
        .probe = mpu6050_probe,
        .remove = mpu6050_remove,
    };
    
    static int mpu6050_init(void)
    {
        printk("%s called
    ", __func__);
    
        i2c_add_driver(&mpu6050_driver);
    
        return 0;
    }
    
    static void mpu6050_exit(void)
    {
        printk("%s called
    ", __func__);
    
        i2c_del_driver(&mpu6050_driver);
    
        return ;
    }
    
    module_init(mpu6050_init);
    module_exit(mpu6050_exit);
    
    http://blog.csdn.net/hanp_linux/article/details/72832158
  • 相关阅读:
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 390 消除游戏
    Java实现 LeetCode 390 消除游戏
  • 原文地址:https://www.cnblogs.com/xihong2014/p/7729431.html
Copyright © 2011-2022 走看看