rk3288 i2c-tools使用

i2c-tools源码下载

  • Linux驱动程序: drivers/i2c/i2c-dev.c
  • I2C-Tools-4.2: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
  • AP3216C:在git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git,在10.1篇,第十六章,I2C编程。

APP访问硬件肯定是需要驱动程序的, 对于I2C设备,内核提供了驱动程序drivers/i2c/i2c-dev.c,通过它可以直接使用下面I2C控制器驱动程序来访问I2C设备。 框架如下:
rk3288 i2c-tools使用_第1张图片i2c-tools是一套好用的工具,也是一套示例代码。

编译i2c-tools:

# IMX6ULL
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=$PATH:/home/book/Documents/100ask_firefly-rk3288/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin

# 修改I2C-Tools的Makefile指定交叉编译工具链
CC ?= gcc
AR ?= ar
STRIP ?= strip
# 改为(指定交叉编译工具链前缀, 去掉问号):
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip
# 在Makefile中,“?=”在第一次设置变量时才会起效果,如果之前设置过该变量,则不会起效果。
make
# 执行make时,是动态链接,需要把libi2c.so也放到单板上
# 想静态链接的话,执行:make USE_STATIC_LIB=1 

# 把tools目录下的可执行文件放到/usr/bin目录下
# 把lib下的/usr/lib/libi2c.so.0.1.1复制到/usr/lib下
# 然后再/usr/lib目录下执行 ln -s libi2c.so.0.1.1 libi2c.so.0

i2c-tools用法

i2cdetect使用

# 列出当前的I2C Adapter(或称为I2C Bus、I2C Controller)
root@npi:~# ./i2cdetect -l
i2c-1	i2c       	21a4000.i2c                     	I2C adapter
i2c-4	i2c       	i2c-0-mux (chan_id 0)           	I2C adapter
i2c-0	i2c       	21a0000.i2c                     	I2C adapter

# 打印某个I2C Adapter的Functionalities, I2CBUS为0、1、2等整数
root@npi:~# ./i2cdetect -F 0     
Functionalities implemented by /dev/i2c-0:
I2C                              yes
SMBus Quick Command              yes
SMBus Send Byte                  yes
SMBus Receive Byte               yes
SMBus Write Byte                 yes
SMBus Read Byte                  yes
SMBus Write Word                 yes
SMBus Read Word                  yes
SMBus Process Call               yes
SMBus Block Write                yes
SMBus Block Read                 yes
SMBus Block Process Call         no
SMBus PEC                        yes
I2C Block Write                  yes
I2C Block Read                   yes

# 看看有哪些I2C设备, I2CBUS为0、1、2等整数
# --表示没有该地址对应的设备, 
# UU表示有该设备并且它已经有驱动程序,
# 数值表示有该设备但是没有对应的设备驱动
root@npi:~# ./i2cdetect -y -a 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

i2cget使用

root@npi:~# ./i2cget 
Usage: i2cget [-f] [-y] [-a] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
  I2CBUS is an integer or an I2C bus name
  ADDRESS is an integer (0x08 - 0x77, or 0x00 - 0x7f if -a is given)
  MODE is one of:
    b (read byte data, default)
    w (read word data)
    c (write byte/read byte)
    Append p for SMBus PEC
# 使用示例
# 读一个字节: I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
i2cget -f -y I2CBUS CHIP-ADDRESS
# 读某个地址上的一个字节:CHIP-ADDRESS表示设备地址, DATA-ADDRESS: 芯片上寄存器地址
# MODE:有2个取值, b-使用`SMBus Read Byte`先发出DATA-ADDRESS, 再读一个字节, 中间无P信号
#                c-先write byte, 在read byte,中间有P信号
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE
# 读某个地址上的2个字节,同上,MODE:w-表示先发出DATA-ADDRESS,再读2个字节
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE

i2cset使用

root@npi:~# ./i2cset 
Usage: i2cset [-f] [-y] [-m MASK] [-r] [-a] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]
  I2CBUS is an integer or an I2C bus name
  ADDRESS is an integer (0x08 - 0x77, or 0x00 - 0x7f if -a is given)
  MODE is one of:
    c (byte, no value)
    b (byte data, default)
    w (word data)
    i (I2C block data)
    s (SMBus block data)
    Append p for SMBus PEC
#  写一个字节: I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址;DATA-ADDRESS就是要写的数据
# value是8位数值,b表示一个byte
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE [b]
# value是16位数值,w表示一个word
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE w
# value是多个数值,s是smbus的多个字节写入
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE1 ... VALUEN s
# value是多个数值,i是i2c的多个字节写入
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE1 ... VALUEN i

i2ctransfer使用

root@npi:~# ./i2ctransfer 
Usage: i2ctransfer [-f] [-y] [-v] [-V] [-a] I2CBUS DESC [DATA] [DESC [DATA]]...
  I2CBUS is an integer or an I2C bus name
  DESC describes the transfer in the form: {r|w}LENGTH[@address]
    1) read/write-flag 2) LENGTH (range 0-65535, or '?')
    3) I2C address (use last one if omitted)
  DATA are LENGTH bytes for a write message. They can be shortened by a suffix:
    = (keep value constant until LENGTH)
    + (increase value by 1 until LENGTH)
    - (decrease value by 1 until LENGTH)
    p (use pseudo random generator until LENGTH with value as seed)

Example (bus 0, read 8 byte at offset 0x64 from EEPROM at 0x50):
  # i2ctransfer 0 w1@0x50 0x64 r8
Example (same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0):
  # i2ctransfer 0 w17@0x50 0x42 0xff-
i2ctransfer -f -y 2 w2@0x40 0x00 0x06 r1
其中参数为2表示i2c-2,w2表示写2个字节,0x40是i2c设备地址(7位地址),0x00 0x06是数据,r1表示读取一个字节

使用i2c控制AP3216C

AP3216C是红外、光强、距离三合一的传感器,以读出光强、距离值为例,步骤如下:

  • 复位:往寄存器0写入0x4
  • 使能:往寄存器0写入0x3
  • 读光强:读寄存器0xC、0xD得到2字节的光强
  • 读距离:读寄存器0xE、0xF得到2字节的距离值

使用SMBus协议

root@npi:~# ./i2cset -f -y 0 0x1e 0 0x3
root@npi:~# ./i2cset -f -y 0 0x1e 0 0x4
# 读取光强值
root@npi:~# ./i2cget -f -y 0 0x1e 0xc w
0x0017
# 用手电照射后,读取光强值
root@npi:~# ./i2cget -f -y 0 0x1e 0xc w
0x15fe
# 读取距离值
root@npi:~# ./i2cget -f -y 0 0x1e 0xe w
0x0f02
# 用手捂住读取距离值
root@npi:~# ./i2cget -f -y 0 0x1e 0xe w
0xbf8f

使用I2C协议

i2ctransfer -f -y 0 w2@0x1e 0 0x4
i2ctransfer -f -y 0 w2@0x1e 0 0x3
i2ctransfer -f -y 0 w1@0x1e 0xc r2
i2ctransfer -f -y 0 w1@0x1e 0xe r2

App控制I2C设备

  • 指定I2C控制器
    • i2c-dev.c为每个I2C控制器都生成一个设备节点:/dev/i2c-0、/dev/i2c-1
    • open某个/dev/i2c-x节点,就能访问该I2C控制器下的设备
  • 指定I2C设备
    • 通过ioctl指定I2C设备地址
    • ioctl(file, I2C_SLAVE, address),如果设备已经再用了就会失败
    • ioctl(file, I2C_SLAVE_FORCE, address),强制指定设备地址访问
  • 传输数据
    • 普通I2C方式:ioctl(file, I2C_RDWR, &rdwr)
    • SMBus方式:ioctl(file, I2C_SMBUS, &args)

i2c-tools源码分析

阅读i2ctransfer.c源码,这个是使用普通的i2c方式读写数据
首先需要打开i2c设备,使用open_i2c_dev函数

//i2cbusses.c
int open_i2c_dev(int i2cbus, char *filename, size_t size, int quiet)
{
	//...
    len = snprintf(filename, size, "/dev/i2c/%d", i2cbus);		//通过i2cbus号去打开/dev/i2c/x文件
	//...
    file = open(filename, O_RDWR);

    if (file < 0 && (errno == ENOENT || errno == ENOTDIR)) {
        len = snprintf(filename, size, "/dev/i2c-%d", i2cbus);	//如果没打开成功/dev/i2c/x文件,就去打开/dev/i2c-x文件
		//...
        file = open(filename, O_RDWR);
    }
	//...错误处理
    return file;
}

然后设置i2c的从地址,使用set_slave_addr函数,调用了ioctl(file, I2C_SLAVE, address)

//i2cbusses.c
int set_slave_addr(int file, int address, int force)
{
	//ioctl(file, I2C_SLAVE, address)
	//ioctl(file, I2C_SLAVE_FORCE, address)
    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;
}

然后构造传输的内容

	struct i2c_msg msgs[I2C_RDRW_IOCTL_MAX_MSGS];
	msgs[nmsgs].addr = address;
	msgs[nmsgs].flags = flags;
	msgs[nmsgs].len = len;

struct i2c_msg {
    __u16 addr; /* slave address            */
    __u16 flags;
#define I2C_M_RD        0x0001  /* read data, from slave to master */
                    /* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
#define I2C_M_DMA_SAFE      0x0200  /* the buffer of this message is DMA safe */
                    /* makes only sense in kernelspace */
                    /* userspace buffers are copied anyway */
#define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
#define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_NOSTART */
#define I2C_M_STOP      0x8000  /* if I2C_FUNC_PROTOCOL_MANGLING */
    __u16 len;      /* msg length               */
    __u8 *buf;      /* pointer to msg data          */
}; 

调用函数传输i2c数据

	struct i2c_rdwr_ioctl_data rdwr;
	rdwr.msgs = msgs;
    rdwr.nmsgs = nmsgs;
    nmsgs_sent = ioctl(file, I2C_RDWR, &rdwr);

阅读i2cget.c、i2cset.c源码,这个是使用smbus.c方式读写数据
首先需要打开i2c设备,使用open_i2c_dev函数,同上
然后设置i2c的从地址,使用set_slave_addr函数,调用了ioctl(file, I2C_SLAVE, address),同上
使用分装好的smbus函数,在include/i2c/smbus.h中有大量的smbus函数

extern __s32 i2c_smbus_access(int file, char read_write, __u8 command,
                  int size, union i2c_smbus_data *data);

extern __s32 i2c_smbus_write_quick(int file, __u8 value);
extern __s32 i2c_smbus_read_byte(int file);
extern __s32 i2c_smbus_write_byte(int file, __u8 value);
extern __s32 i2c_smbus_read_byte_data(int file, __u8 command);
extern __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value);
extern __s32 i2c_smbus_read_word_data(int file, __u8 command);
extern __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value);
extern __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value);

/* Returns the number of read bytes */
extern __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values);
extern __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length,
                    const __u8 *values);
extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
                       __u8 *values);
extern __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, __u8 length,
                        const __u8 *values);

/* Returns the number of read bytes */
extern __s32 i2c_smbus_block_process_call(int file, __u8 command, __u8 length,
                      __u8 *values);

这些接口都调用了一个函数i2c_smbus_access()

__s32 i2c_smbus_access(int file, char read_write, __u8 command,
               int size, union i2c_smbus_data *data)
{
    struct i2c_smbus_ioctl_data args;
    __s32 err;

    args.read_write = read_write;
    args.command = command;
    args.size = size;
    args.data = data;

    err = ioctl(file, I2C_SMBUS, &args);
    if (err == -1)
        err = -errno;
    return err;
}

参考文档:https://www.kernel.org/doc/html/latest/i2c/dev-interface.html

你可能感兴趣的:(#,I2C,i2c,i2c-tools,linux,kernel,100ask)