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\n", 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\n", data.byte);
    if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                         I2C_SMBUS_BYTE_DATA,&data))
        return -1;
    else{
//        printf("read_byte: %#x\n", 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\n", data.byte);
    int ret = i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                            I2C_SMBUS_BYTE_DATA, &data);
//    printf("write_byte: %#x\n", 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\n", 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\n");
    }

    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\n",
            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] = '\0';
    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\n",
                i2cbus, i2cbus, strerror(ENOENT));
        } else {
            fprintf(stderr, "Error: Could not open file "
                "`%s': %s\n", filename, strerror(errno));
            if (errno == EACCES)
                fprintf(stderr, "Run as root?\n");
        }
    }

    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\n", filename);
        return -1;
    }

    //掩碼,可以單獨設置某一位而不影響其他位
    if (vmask) {
        int oldvalue;

        oldvalue = i2c_smbus_read_byte_data(file, daddr);
    printf("write and read\n");
        if (oldvalue < 0) {
            fprintf(stderr, "Error: Failed to read old value\n");
            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\n");
        close(file);
        return -1;
    }

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

    //if (res < 0) {
    //    printf("Warning - readback failed\n");
    //} else
    //if (res != value) {
    //    printf("Warning - data mismatch - wrote 0x%0*x, read back 0x%0*x\n",2,value,2, res);
    //} else {
        //printf("Value 0x%0*x written, readback matched\n",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\n");
        return -1;
    }

    *read_data = res;
    //printf("read data -- 0x%x\n",*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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM