zoukankan      html  css  js  c++  java
  • linux 标准i2c接口(一)

    一:I2C设备操作方式:

    1.  应用程序操作法:i2c的设备的驱动可以直接利用linux内核提供的i2c-dev.c文件提供的ioctl函数接口在应用层实现对i2c设备的读写,但是在应用层使用ioctl函数对应用程序员要求较高,需要自行构建msg结构体,必须了解设备的操作流程,时序之类的。

    这方式实现需要用用程序员调用 read, write, ioctl, open, close等linux标准文件接口操作/dev/i2c(X)设备文件。

    2. 驱动程序操作法:i2c设备的驱动也可以通过普通的设备驱动实现,像往常的驱动一样实现,然后在应用层就可以像读取普通文件一样操作,无需再考虑读写时序。其实普通的设备驱动也可以用两种方法实现,

    1)构建字符设备驱动,在open,read,write等函数中直接操作i2c总线的相关寄存器来读写i2c设备,但是这种方法因平台不同,设备不同都要重新写驱动

    2)在设备驱动中调用i2c-core.c提供的i2c_transfer函数来实现和i2c设备的通信,这样只要对不同的设备写不同的驱动就行了。

    二:首先我们来看一看应用程序操作法:

    标准i2c接口提供的标准的设备/dev/i2c,首先我们看看linux下标准i2c的结构。

    在/linux-kernel-3.8/drivers/i2c目录下

            ----Algos/                  一些i2c总线适配器通信的算法,个人感觉是用I/O口模拟实现i2c通信的算法

            ----Busses/               I2C总线驱动的方法,对应于君正4775适配器驱动的文件是i2c-jz4775.c

            ----Chips/                 I2C设备驱动,具体到某个设备,比如at24c08等

            ----I2c-boardinfo.c     定义i2c_register_board_info 注册i2c设备相关信息(其实就是将要注册的i2c设备添加到i2c设备链表)

            ----I2c-core.c           I2C核心文件,用于联系设备驱动和总线驱动,作为一个桥梁,有用的函数i2c_add_addapter

                                           i2c_add_driver,和i2c_transfer函数 (其实就是定义了一些标准的API接口函数)

            ----I2c-dev.c            通用的i2c设备驱动(其实就是生成的标准的/dev/i2c字符设备文件接口)

            ----Kconfig

            ----Makefile

    我们要选择编译/linux-kernel-3.8/drivers/i2c/相关的platform driver和arch/mips/xburst/soc-4775/board//trunk/core/core-misc.c相结合最终生成的固件就会有/dev/i2c0标准接口。

    以下是笔者做qn8025FM驱动时写下的通过标准i2c设备文件实现的读写函数:通过这两个读写函数就可以直接读写i2c设备的寄存器实现想用的功能

    /*
    * Version: V1.0.2.
    * Date: 2014-11-25.
    */

    #include "qn8025_i2c.h"

    /*源头即是  IOCTL 呼叫, 通道为 I2C_SMBUS,
    而 args 则定义了: 读或写, 位置, 长度,  以及回传值的资讯内容.*/
    //ioctl(file, I2C_SMBUS, (i2c_smbus_ioctl_data*)msgset);
    static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
                                         int size, union i2c_smbus_data *data)
    {
        struct i2c_smbus_ioctl_data args;

        args.read_write = read_write;
        args.command = command;
        args.size = size;
        args.data = data;
        int ret=  ioctl(file,I2C_SMBUS,&args);//set Smbus transfer
        //printf("ioctl-read_i2c: %#x ", args.data->byte);
        return ret;
    }

    //向设备发送一个比特
    static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
    {
        return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,(union i2c_smbus_data *)0);
    }
       
    static inline __s32 i2c_smbus_read_byte(int file)
    {
        union i2c_smbus_data data;
        if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
            return -1;
        else
            return 0x0FF & data.byte;
    }

    static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
    {
        return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
                                I2C_SMBUS_BYTE,(union i2c_smbus_data *)0);
    }

    static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
    {
        union i2c_smbus_data data;
        data.byte = 0;
    //    printf("before read_byte: %#x ", data.byte);
        if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                             I2C_SMBUS_BYTE_DATA,&data))
            return -1;
        else{
    //        printf("read_byte: %#x ", data.byte);
            return 0x0FF & data.byte;
            }
    }

    static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command,
                                                  __u8 value)
    {
        union i2c_smbus_data data;
        data.byte = value;
    //    printf("before write_byte: %#x ", data.byte);
        int ret = i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                                I2C_SMBUS_BYTE_DATA, &data);
    //    printf("write_byte: %#x ", data.byte);
        return ret;
    }

    static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
    {
        union i2c_smbus_data data;
        if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                             I2C_SMBUS_WORD_DATA,&data))
            return -1;
        else
            return 0x0FFFF & data.word;
    }

    static inline __s32 i2c_smbus_write_word_data(int file, __u8 command,
                                                  __u16 value)
    {
        union i2c_smbus_data data;
        data.word = value;
        return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                                I2C_SMBUS_WORD_DATA, &data);
    }

    static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
    {
        union i2c_smbus_data data;
        data.word = value;
        if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                             I2C_SMBUS_PROC_CALL,&data))
            return -1;
        else
            return 0x0FFFF & data.word;
    }


    /* Returns the number of read bytes */
    static inline __s32 i2c_smbus_read_block_data(int file, __u8 command,
                                                  __u8 *values)
    {
        union i2c_smbus_data data;
        int i;
        if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                             I2C_SMBUS_BLOCK_DATA,&data))
            return -1;
        else {
            for (i = 1; i <= data.block[0]; i++)
                values[i-1] = data.block[i];
            return data.block[0];
        }
    }

    static inline __s32 i2c_smbus_write_block_data(int file, __u8 command,
                                                   __u8 length, const __u8 *values)
    {
        union i2c_smbus_data data;
        int i;
        if (length > 32)
            length = 32;
        for (i = 1; i <= length; i++)
            data.block[i] = values[i-1];
        data.block[0] = length;
        return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                                I2C_SMBUS_BLOCK_DATA, &data);
    }

    /* Returns the number of read bytes */
    /* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
       ask for less than 32 bytes, your code will only work with kernels
       2.6.23 and later. */
    static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command,
                                                      __u8 length, __u8 *values)
    {
        union i2c_smbus_data data;
        int i;

        if (length > 32)
            length = 32;
        data.block[0] = length;
        if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                             length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
                              I2C_SMBUS_I2C_BLOCK_DATA,&data))
            return -1;
        else {
            for (i = 1; i <= data.block[0]; i++)
                values[i-1] = data.block[i];
            return data.block[0];
        }
    }

    static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
                                                       __u8 length,
                                                       const __u8 *values)
    {
        union i2c_smbus_data data;
        int i;
        if (length > 32)
            length = 32;
        for (i = 1; i <= length; i++)
            data.block[i] = values[i-1];
        data.block[0] = length;
        return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                                I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
    }

    /* Returns the number of read bytes */
    static inline __s32 i2c_smbus_block_process_call(int file, __u8 command,
                                                     __u8 length, __u8 *values)
    {
        union i2c_smbus_data data;
        int i;
        if (length > 32)
            length = 32;
        for (i = 1; i <= length; i++)
            data.block[i] = values[i-1];
        data.block[0] = length;
        if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                             I2C_SMBUS_BLOCK_PROC_CALL,&data))
            return -1;
        else {
            for (i = 1; i <= data.block[0]; i++)
                values[i-1] = data.block[i];
            return data.block[0];
        }
    }

    //检测i2c设备功能
    /*static int check_funcs(int file, int size, int daddress, int pec)
    {
        unsigned long funcs;

        //check adapter functionality
        if (ioctl(file, I2C_FUNCS, &funcs) < 0) {//获取i2c设备支持的功能
            fprintf(stderr, "Error: Could not get the adapter "
                "functionality matrix: %s ", strerror(errno));
            return -1;
        }

        switch (size) {
        case I2C_SMBUS_BYTE:
            if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
                fprintf(stderr, MISSING_FUNC_FMT, "SMBus receive byte");
                return -1;
            }
            if (daddress >= 0
             && !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE)) {
                fprintf(stderr, MISSING_FUNC_FMT, "SMBus send byte");
                return -1;
            }
            break;

        case I2C_SMBUS_BYTE_DATA:
            if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
                fprintf(stderr, MISSING_FUNC_FMT, "SMBus read byte");
                return -1;
            }
            break;

        case I2C_SMBUS_WORD_DATA:
            if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA)) {
                fprintf(stderr, MISSING_FUNC_FMT, "SMBus read word");
                return -1;
            }
            break;
        }
        if (pec
         && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C))) {
            fprintf(stderr, "Warning: Adapter does "
                "not seem to support PEC ");
        }

        return 0;
    }
    */

    //设置i2c从设备地址
    static int set_slave_addr(int file, int address, int force)
    {
        /* With force, let the user read from/write to the registers
           even when a driver is also running */
        if (ioctl(file, force ? I2C_SLAVE_FORCE : I2C_SLAVE, address) < 0) {
            fprintf(stderr,
                "Error: Could not set address to 0x%02x: %s ",
                address, strerror(errno));
            return -errno;
        }

        return 0;
    }

    //打开i2c设备
    static int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
    {
        int file;

        snprintf(filename, size, "/dev/i2c/%d", i2cbus);
        filename[size - 1] = '';
        file = open(filename, O_RDWR);//打开i2c设备
        if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
            sprintf(filename, "/dev/i2c-%d", i2cbus);
            file = open(filename, O_RDWR);
        }

        if (file < 0 && !quiet) {
            if (errno == ENOENT) {
                fprintf(stderr, "Error: Could not open file "
                    "`/dev/i2c-%d' or `/dev/i2c/%d': %s ",
                    i2cbus, i2cbus, strerror(ENOENT));
            } else {
                fprintf(stderr, "Error: Could not open file "
                    "`%s': %s ", filename, strerror(errno));
                if (errno == EACCES)
                    fprintf(stderr, "Run as root? ");
            }
        }

        return file;
    }

    //向i2c设备写数据 (0, 0x58,  , 9300, 0)
    static int i2c_write_data(int i2cbus,int addr,int daddr,int set_val,int vmask)
    {
        int res, file;
        int value;
        char filename[20];

        //打开设备描述符,可以自动寻找合适的设备名
        file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
        if (file < 0 || set_slave_addr(file, addr, 1)) {
        //    perror("open i2c");
            printf("open %s fail ", filename);
            return -1;
        }

        //掩码,可以单独设置某一位而不影响其他位
        if (vmask) {
            int oldvalue;

            oldvalue = i2c_smbus_read_byte_data(file, daddr);
        printf("write and read ");
            if (oldvalue < 0) {
                fprintf(stderr, "Error: Failed to read old value ");
                close(file);
                return -1;
            }

            value = (set_val & vmask) | (oldvalue & ~vmask);
        } else {
            value = set_val;
        }

        res = i2c_smbus_write_byte_data(file, daddr, value);
        if (res < 0) {
            fprintf(stderr, "Error: Write failed ");
            close(file);
            return -1;
        }

        //res = i2c_smbus_read_byte_data(file, daddr);
       
        close(file);

        //if (res < 0) {
        //    printf("Warning - readback failed ");
        //} else
        //if (res != value) {
        //    printf("Warning - data mismatch - wrote 0x%0*x, read back 0x%0*x ",2,value,2, res);
        //} else {
            //printf("Value 0x%0*x written, readback matched ",2, value);
           
            return 0;//读到的值和写入的值一致,才返回成功
        //}

        //return -1;
    }

    //从i2c读取数据
    static int i2c_read_data(int i2cbus,int addr,int daddr,unsigned char *read_data)
    {
        int file,res;
        char filename[20] = {0};
        //打开i2c设备
        file = open_i2c_dev(0, filename, sizeof(filename), 0);
        if (file < 0 || set_slave_addr(file, addr, 1))//设置从设备地址
            return -1;

        res = i2c_smbus_read_byte_data(file, daddr);
        close(file);

        if (res < 0) {
            fprintf(stderr, "Error: Read failed ");
            return -1;
        }

        *read_data = res;
        //printf("read data -- 0x%x ",*read_data);

        return 0;
    }


    /* qn8025 set "set_value" to register "daddr"
    addr: slave address
    daddr: register address
    set_val: the value wiil put into register
    vmask: value maske
    */
    int qn8025_i2c_write_data(int addr, int daddr, int set_val, int vmask)
    {
        return i2c_write_data(0, addr, daddr, set_val, vmask);
    }


    /* qn8025 get "read_data" from register "daddr"
    addr: slave address
    daddr: register address
    set_val: the value wiil get from register
    */
    int qn8025_i2c_read_data(int addr, int daddr, unsigned char *read_data)
    {
        return i2c_read_data(0, addr, daddr, read_data);
    }

  • 相关阅读:
    Mysql备份恢复
    Mysql事务学习笔记
    MongoDB进阶
    MongoDB入门
    Mysql流程解析
    Mysql Explain学习笔记
    面试题
    聚集索引和非聚集索引
    端口号占用
    classpath: 和classpath*:的区别
  • 原文地址:https://www.cnblogs.com/xuyh/p/4959499.html
Copyright © 2011-2022 走看看