• 4412--I2C驱动


     Linux I2C驱动主要分为

          1.总线(主机)驱动(I2C BUS Driver)

          2.设备(外设)驱动(I2C Clients Driver)

    Linux主机驱动和外设驱动分离思想
          外设驱动→API→主机驱动→板级逻辑--具体的i2c设备(camera,ts,eeprom等等)
          主机驱动:根据控制器硬件手册,操作具体的寄存器,产生波形。(单片机工程师肯定有强烈的冲动去配置i2c寄存器,产生波形!)。

    linux应用工程师:屏蔽了驱动和硬件(只看到了read、write、socket等接口)。
    linux驱动程师:屏蔽硬件!提供标准的主机驱动(已经通过主机驱动屏蔽掉了,原厂的工程师已经做好了),驱动工程师需要完成“外设驱动”

    IIC相关的重要的数据结构

    struct i2c_msg;             //I2C消息数据结构  
    struct i2c_algorithm;       //算法驱动数据结构  
    struct i2c_adapter;         //I2C适配器数据结构  
    struct i2c_client;          //I2C客户数据结构  
    struct i2c_driver;          //I2C设备驱动数据结构  
    struct i2c_board_info;      //描述板载I2C设备的信息  

    1.  当总线(plat_form)上注册了对应的IIC从设备时,如果可以匹配成功(驱动名称和plat_form的名字相同),则调用probe初始化设备,注册字符设备,提供接口给应用程序

    struct i2c_driver
    {
        char name[32]; //驱动的名称
        int id;//驱动的ID
        unsigned int flags; /* div,see below */
        int (*attach_adapter(struct i2c_adapter *);
        int("detach_ch_client)(struct i2c_client *);
        int(*command)(struct i2c_client *client,unsigned int cmd,void *arg);
        void (*inc_use)(strtuct i2c_client *client );
        void (*dec_use)(struct i2c_client *client );
    }

    2.IIC发送或者接收一次数据都以数据包    struct i2c_msg 封装

    struct i2c_msg {
             __u16 addr; // slave address
             __u16 flags;
            #define I2C_M_TEN                          0x0010       //10bit地址
            #define I2C_M_RD                           0x0001  //读取数据标志,清零表示写
            #define I2C_M_NOSTART                 x4000         //不发送起始位
            #define I2C_M_REV_DIR_ADDR       0x2000       // 反转读写标志
            #define I2C_M_IGNORE_NAK           x1000    //忽略I2C器件的ack和nack信号
            #define I2C_M_NO_RD_ACK            0x0800       //读操作时不去ACK
            #define I2C_M_RECV_LEN               0x0400       //length will be first received byte
             __u16 len; // msg length
             __u8 *buf; // pointer to msg data
    };             

    3. I2c_adapter 结构标识一个物理I2C总线(适配器),总线通过算法结构访问到适配器。I2C总线上对I2C slave设备的具体操作是在适配器驱动中完成的。适配器驱动作为platform_driver而注册,在probe()到驱动设备后,向总线声明并被添加:

            i2c_add_numbered_adapter(&i2c->adap);    

    I2c-dev驱动是系统自带的一个通用客户驱动,它不是针对某一个I2C设备(即没有自己驱动设备id-table),它可以使得用户空间的程序通过i2c-tools访问总线上的i2c/SMBUS设备。

    struct i2c_adapter {  
        struct module *owner;  
        unsigned int id;  
        unsigned int class;             /* 适配器支持的类型 */  
        const struct i2c_algorithm *algo; /* 该适配器的通信函数 */  
        void *algo_data;  
        /* --- administration stuff. */  
        int (*client_register)(struct i2c_client *) __deprecated;  
        int (*client_unregister)(struct i2c_client *) __deprecated;  
        /* data fields that are valid for all devices  */  
        u8 level;                /* nesting level for lockdep */  
        struct mutex bus_lock;  
        struct mutex clist_lock;  
        int timeout;            /*超时时间段设定10 */  
        int retries;  //通信重复次数限定
    
        /*以下是内嵌的标准device  其中dev->type标识该设备是一个adapter,其值为                *      i2c_adapter_type
         */
        struct device dev;      /* the adapter device */  
        int nr;  // 适配器编号也是Bus编号,第几条IIC总线 
        struct list_head clients;     /* DEPRECATED */  
        char name[48];  
        struct completion dev_released;  
    };      

    4.一个struct i2c_client结构体代表IIC总线上的一个从设备

    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 client所在IIC bUS对应的adapter      */  
         struct i2c_driver *driver;  /* and our access routines   client使用的驱动  */  
         struct device dev;            /* the device structure          */  
         int irq;                     /* irq issued by device          */  
         struct list_head list;          /* DEPRECATED */  
         struct list_head detected;  
         struct completion released;  
    };  

    一. 内核函数接口:(API)。主机驱动提供给外设驱动的函数接口。

    注册i2c设备:i2c_board_info

    驱动注册和卸载函数以及结构体:i2c_del_driver/i2c_add_driver,i2c_driver

    添加客户驱动:static inline int i2c_add_driver(struct i2c_driver *driver)
    删除客户驱动:extern void i2c_del_driver(struct i2c_driver *);   

    读写函数和结构体:i2c_transfer,i2c_msg 

      这几个函数是放之四海而皆准!

      IIC相关的重要数据结构

      

     

      外设驱动:针对具体的外部器件的代码。
      摄像头以及声卡中i2c用来配置外部设备(声卡和摄像头)→地址和配置的内容都不一样!
      板级逻辑:描述主机和外部设备是怎么连接的(IIC协议的内容)。

    二.设备-i2c设备注册以及设备注册之后的查询方法
      查询i2c设备地址:ls /sys/bus/i2c/devices/
      怎么和原理图以及外部设备对应:3-0038→I2C_3_SCL(外部设备的地址addr:datasheet中查0x38)
      查询i2c设备名称:cat /sys/bus/i2c/devices/3-0038/name

      menuconfig中去掉触摸的驱动
        Device Drivers --->
        Input device support --->
        Touchscreens --->
        FT5X0X based touchscreens(去掉)

      添加i2c设备:i2c_devs3[]中添加
          {
            I2C_BOARD_INFO("i2c_test", 0x70>>1),  // 1.设备名称 (一定要和驱动名称一致才会进入probe)2.从机地址
          },
        cat /sys/bus/i2c/devices/3-0038/name结果是i2c_test


    三.驱动-i2c驱动注册和卸载

      i2c设备初始化完成-进入probe函数。
      i2c_del_driver/i2c_add_driver,i2c_driver

    module_init和late_initcall:module_init先运行,late_initcall后运行

    4.驱动-i2c数据的传输(9.7寸或者7寸屏幕)
      i2c_transfer,i2c_msg


    5.Liux-i2c利用字符驱动完成应用层对i2c的读和写(9.7寸或者7寸屏幕)

    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/i2c.h>
    #include <linux/input.h>
    #include <linux/delay.h>
    #include <linux/slab.h>
    #include <linux/gpio.h>
    #include <linux/platform_device.h>
    #ifdef CONFIG_HAS_EARLYSUSPEND
    #include <linux/earlysuspend.h>
    #endif
    #include <linux/regulator/consumer.h>
    #include <mach/gpio.h>
    #include <plat/gpio-cfg.h>
    #include <asm/uaccess.h> 
    #include <linux/miscdevice.h>
    
    static struct i2c_client *this_client;
    
    static int i2c_tes_read_reg(struct i2c_client *client,u8 addr, u8 *pdata) {
        u8 buf1[4] = { 0 };
        u8 buf2[4] = { 0 };
        struct i2c_msg msgs[] = {
            {
                .addr    = client->addr,    //0x38
                .flags    = 0,    //
                .len    = 1,    //要写的数据的长度
                .buf    = buf1,
            },
            {
                .addr    = client->addr,
                .flags    = I2C_M_RD,
                .len    = 1,
                .buf    = buf2,
            },
        };
        int ret;
        buf1[0] = addr;
        ret = i2c_transfer(client->adapter, msgs, 2);
        if (ret < 0) {
            pr_err("read reg (0x%02x) error, %d
    ", addr, ret);
        } else {
            *pdata = buf2[0];
        }
        return ret;
    }
    static int i2c_tes_read_fw_reg(struct i2c_client *client,unsigned char *val)
    {
        int ret;
        *val = 0xff;
        ret = i2c_tes_read_reg(client,0xa6, val);
        printk("ts reg 0xa6 val is %d
    ",*val);
        return ret;
    }
    static int i2c_open_func(struct file *filp)
    {
        return 0;
    }
    
    static int i2c_release_func(struct file *filp)
    {
        return 0;
    }
    
    static ssize_t i2c_read_func(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
    {
        int ret;
        u8 reg_data;
        ret = copy_from_user(&reg_data,buffer,1);  //目的是从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0
        struct i2c_msg msgs[] = {    //IIC发送或者接收一次数据都以数据包    struct i2c_msg 封装
            {
                .addr    = this_client->addr,    //从机地址 0x38
                .flags    = 0,    //
                .len    = 1,    //要写的数据的长度
                .buf    = &reg_data,
            },
            {
                .addr    = this_client->addr,
                .flags    = I2C_M_RD,
                .len    = 1,
                .buf    = &reg_data,
            },
        };
        // 参数:1.适配器结构体的指针 2.消息数组的头指针  3.消息的数量。
        ret = i2c_transfer(this_client->adapter, msgs, 2); 
        if (ret < 0) {
            pr_err("read reg error!
    ");
        }
        ret = copy_to_user(buffer,&reg_data,1);
        return ret;
    }
    
    static ssize_t i2c_write_func(struct file *filp, char __user *buffer, size_t count, loff_t *ppos){
        int ret;
        u8 buf[2];
        struct i2c_msg msgs[1];
        
        ret = copy_from_user(&buf,buffer,2);
        
        msgs[0].addr    = this_client->addr;    //0x38
        msgs[0].flags    = 0;    //
        msgs[0].len    = 2;    //第一个是要写的寄存器地址,第二个是要写的内容
        msgs[0].buf    = buf;
    
        ret = i2c_transfer(this_client->adapter, msgs, 1);
        if (ret < 0) {
            pr_err("write reg 0x%02x error!
    ",buf[0]);
        }
        ret = copy_to_user(buffer,buf,1);
        
        return ret;
    }
    
    
    static struct file_operations i2c_ops = {      //  设备驱动的结构体
        .owner     = THIS_MODULE,
        .open     = i2c_open_func,
        .release= i2c_release_func,
        .write  = i2c_write_func,
        .read     = i2c_read_func,
    };
    
    
    static struct miscdevice i2c_dev = {
        .minor    = MISC_DYNAMIC_MINOR,
        .fops    = &i2c_ops,
        .name    = "i2c_control",
    };
    
    static int i2c_test_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
        unsigned char val;
        printk("==%s:
    ", __FUNCTION__);
        
        i2c_tes_read_fw_reg(client,&val);
        
        this_client = client;
        
        misc_register(&i2c_dev);
        
        return 0;
    }
    
    static int __devexit i2c_test_remove(struct i2c_client *client)
    {
        i2c_set_clientdata(client, NULL);
        misc_deregister(&i2c_dev);
        printk("==%s:
    ", __FUNCTION__);
        return 0;
    }
    
    static const struct i2c_device_id i2c_test_id[] = {
        { "i2c_test", 0 },
        { }
    };
    
    static struct i2c_driver i2c_test_driver = { //驱动结构体
        .probe        = i2c_test_probe,
        .remove        = __devexit_p(i2c_test_remove),
        .id_table    = i2c_test_id,
        .driver    = {
            .name    = "i2c_test",
            .owner    = THIS_MODULE,
        },
    };
    
    static void i2c_io_init(void)   // 相关管脚的初始化
    {
        int ret;
        ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN");
        if (ret) {
            printk(KERN_ERR "failed to request TP1_EN for "
                    "I2C control
    ");
            //return err;
        }
        gpio_direction_output(EXYNOS4_GPL0(2), 1);
        s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT);
        gpio_free(EXYNOS4_GPL0(2));
        mdelay(5);
        
        ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3");
        if (ret) {
            gpio_free(EXYNOS4_GPX0(3));
            ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3");
        if(ret)
        {
            printk("ft5xox: Failed to request GPX0_3 
    ");
        }
        }
        gpio_direction_output(EXYNOS4_GPX0(3), 0);
        mdelay(200);
        gpio_direction_output(EXYNOS4_GPX0(3), 1);
        s3c_gpio_cfgpin(EXYNOS4_GPX0(3), S3C_GPIO_OUTPUT);
        gpio_free(EXYNOS4_GPX0(3));
        msleep(300);    
    }
    
    static int __init i2c_test_init(void)
    {
        printk("==%s:
    ", __FUNCTION__);
        i2c_io_init();  //iic对应的gpio引脚初始化
        printk("==%s:
    ", __FUNCTION__);
        return i2c_add_driver(&i2c_test_driver); //  添加iic设备驱动
    }
    static void __exit i2c_test_exit(void)
    {
        printk("==%s:
    ", __FUNCTION__);
        i2c_del_driver(&i2c_test_driver);  // 删除驱动
    }
    
    late_initcall(i2c_test_init);
    module_exit(i2c_test_exit);
    
    MODULE_AUTHOR("xunwei_rty");
    MODULE_DESCRIPTION("TsI2CTest");
    MODULE_LICENSE("GPL");
    驱动部分
    #include <stdio.h>
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    
    int main(int argc,char **argv)
    {
        int fd,ret;
        char *i2c_device = "/dev/i2c_control";
        unsigned char buffer[2];
        
        printf("open %s!
    ",i2c_device);
        if((fd = open(i2c_device,O_RDWR|O_NDELAY))<0)
            printf("APP open %s failed",i2c_device);
        else{
            printf("APP open %s success!
    ",i2c_device);
        }
        
    //读一个数据0xa6
        buffer[0] = 0xa6;
        ret = read(fd,buffer,1);
        if(ret<0)
            printf("i2c read failed!
    ");
        else{
            printf("i2c read reg 0xa6 data is 0x%02x!
    ",buffer[0]);
        }
        
    //01先从0x00读出一个数据,02写一个数据到0x00,03再读出来对比
        //01
        buffer[0] = 0x00;
        read(fd,buffer,1);
        printf("i2c read reg 0x00 data is 0x%02x!
    ",buffer[0]);
        //02
        buffer[0] = 0x00;
        buffer[1] = 0x40;
        ret = write(fd,buffer,2);
        if(ret<0){
            printf("i2c write failed!
    ");
            goto exit;
        }
        //03
        buffer[0] = 0x00;
        read(fd,buffer,1);
        printf("i2c read reg 0x00 data is 0x%02x!
    ",buffer[0]);
        
        close(fd);
        
    exit:
        close(fd);
        return -1;
    }
  • 相关阅读:
    Android杂谈ubuntu系统下adb连接小米2
    Android UI设计ListView的页脚(footer)的使用
    Android杂谈关于Android的nodpi,xhdpi,hdpi,mdpi,ldpi
    ”该证书已被签发机构吊销“错误解决方案
    Android杂谈RelativeLayout中的baseline是什么?
    ubuntu下git更改默认编辑器
    Android UI设计ListView Item的OnItemLongClickListener同时监听两种事件
    Android UI设计ListView的item选中效果
    Ubuntu下ssh服务器文件操作命令
    ubuntu下emacs的配置(cedit,ecb)
  • 原文地址:https://www.cnblogs.com/hkyst/p/7762316.html
Copyright © 2020-2023  润新知