Complete Communications Engineering

I2C operations on Linux systems are handled by the Linux kernel, and usually these operations are hidden from user space behind a kernel-level device driver.  However, there are cases where user applications may need to generate their own I2C commands.  This could be useful if the software needs more control over a peripheral than the Linux driver supports, or during development to test out different register settings on a peripheral device.  User space applications can interact with the I2C bus through /dev nodes in the filesystem and ioctl calls.  The necessary definitions are in <linux/i2c.h> and <linux/i2c-dev.h>.

To access the I2C bus from a user application, the first step is to open one of the i2c /dev nodes using the standard Linux open function.  These nodes usually have a name like ‘i2c-1’ where the number is an I2C bus index.  Once the file is opened, ioctl commands can be used to generate messages on the I2C bus.  The available ioctl commands are defined in <linux/i2c-dev.h>.  When the I2C bus is no longer needed, call the close function on the file handle.  The following code examples demonstrate reading and writing chip registers on the I2C bus from a user application:

Write an I2C chip register from a user application:

void reg_write(int i2c_fd, uint8_t device_address,

                        uint8_t reg_index, uint8_t new_reg_value)

{

    struct i2c_msg msg;

    struct i2c_rdwr_ioctl_data data;

    uint8_t buf[2] = {reg_index, new_reg_value};

 

    msg.addr = (__u16)device_address;

    msg.flags = 0;

    msg.len = 2;

    msg.buf = buf;

 

    data.msgs = &msg;

    data.nmsgs = 1;

 

    ioctl(i2c_fd, I2C_RDWR, &data);

}

Read an I2C chip register from a user application:

uint8_t reg_read(int i2c_fd, uint8_t device_address, uint8_t reg_index)

{

    struct i2c_msg msg[2];

    struct i2c_rdwr_ioctl_data data;

    uint8_t reg_value;

 

    msg[0].addr = (__u16)device_address;

    msg[0].flags = 0;

    msg[0].len = 1;

    msg[0].buf = &reg_index;

 

    msg[1].addr = (__u16)device_address;

    msg[1].flags = I2C_M_RD;

    msg[1].len = 1;

    msg[1].buf = &reg_value;

 

    data.msgs = msg;

    data.nmsgs = 2;

 

    ioctl(i2c_fd, I2C_RDWR, &data);

 

    return reg_value;

}