Skip to main content

I2C

1. I2C Subsystem

In the Linux operating system, the I2C subsystem is a critical driver framework for managing and controlling various external devices connected through the I2C bus. More detailed information about the I2C subsystem can be found in the <Linux kernel source>/Documentation/i2c directory. Key components of the I2C subsystem include:

  1. sysfs Interface: The I2C subsystem provides a user-space interface through the sysfs filesystem, allowing users to access and configure information related to I2C devices. The /sys/bus/i2c/devices directory is used to manage and configure the attributes and status of I2C devices. Users can read and write sysfs files to obtain device information or control devices.
  2. I2C Device Nodes: In the /dev directory, character device nodes like /dev/i2c-3 are typically created. These nodes allow users to communicate with specific I2C devices or I2C adapters in user space. Through these nodes, users can send and receive data to operate I2C devices.

2. I2C Testing (Shell)

2.1 Pinout

Luckfox Omni3576 Pinout:

2.2 View Devices

In the /sys/bus/i2c/devices directory, each I2C device has its own folder. The names of these folders typically include the I2C bus and device number, such as /sys/bus/i2c/devices/i2c-0, which represents a device on I2C bus 0. To view the I2C buses present in the system, you can use the following command:

root@luckfox:/home/luckfox# ls /sys/bus/i2c/devices/
1-0023 2-0010 2-004e 2-0051 4-0037 5-0037 5-005d i2c-0 i2c-1 i2c-10 i2c-11 i2c-2 i2c-4 i2c-5

2.3 I2C Testing

  1. View devices on the i2c-0 interface.

    i2cdetect -a -y 0

    root@luckfox:/home/luckfox# i2cdetect -a -y 0
    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- 3d -- --
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
  2. Read the values of all registers of a specific device.

    i2cdump  -f -y 0 0x68
  3. Read a specific register value of a particular I2C device. For example, read register 0x01 from the device at address 0x3d.

    i2cget -f -y 0 0x68 0x01

3. I2C Communication (Python Program)

  1. 完整代码:

    import smbus
    import time

    # I2C Bus (choose 0 or 1 based on your device)
    I2C_BUS = 0

    # Slave device address
    I2C_ADDR = 0x3d

    # Register address (choose according to the register you need)
    REG_ADDR = 0x00 # For example, read the first register's data

    def i2c_read():
    # Create SMBus instance
    bus = smbus.SMBus(I2C_BUS)

    try:
    # Read the register value from the slave device
    data = bus.read_byte_data(I2C_ADDR, REG_ADDR)
    print(f"Data read from device 0x{I2C_ADDR:02x}: 0x{data:02x}")
    except Exception as e:
    print(f"Error reading from device: {e}")
    finally:
    bus.close()

    if __name__ == "__main__":
    i2c_read()
  2. Run the Program:

    root@luckfox:/home/luckfox# python3 i2c_test.py 
    Data read from device 0x3d: 0x41

4. I2C Communication (C Program)

  1. Complete Code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <linux/i2c-dev.h>
    #include <stdint.h>

    #define I2C_BUS "/dev/i2c-0" // I2C Bus (choose i2c-0 or i2c-1 based on your device)
    #define I2C_ADDR 0x3d // Slave device address

    int i2c_read(int file, uint8_t reg_addr) {
    uint8_t buf[1] = {0};

    // Write register address
    if (write(file, &reg_addr, 1) != 1) {
    perror("Failed to write register address");
    return -1;
    }

    // Read data from the slave device
    if (read(file, buf, 1) != 1) {
    perror("Failed to read from device");
    return -1;
    }

    printf("Data read from device 0x%02x: 0x%02x\n", I2C_ADDR, buf[0]);
    return buf[0];
    }

    int main() {
    int file;

    // Open the I2C bus
    if ((file = open(I2C_BUS, O_RDWR)) < 0) {
    perror("Failed to open I2C bus");
    return 1;
    }

    // Set the slave device address
    if (ioctl(file, I2C_SLAVE, I2C_ADDR) < 0) {
    perror("Failed to set I2C address");
    close(file);
    return 1;
    }

    // Read data from register 0x00
    i2c_read(file, 0x00);

    // Close the I2C device
    close(file);

    return 0;
    }
  2. Compile and Run the Program:

    gcc i2c_test.c  -o i2c_test
    ./i2c_test

5. Device Tree Overview

  1. The I2C DTS source file has been defined in kernel-6.10/arch/arm64/boot/dts/rockchip/rk3576.dtsi, and we can call it directly.

    i2c0: i2c@27300000 {
    compatible = "rockchip,rk3576-i2c", "rockchip,rk3399-i2c";
    reg = <0x0 0x27300000 0x0 0x1000>;
    clocks = <&cru CLK_I2C0>, <&cru PCLK_I2C0>;
    clock-names = "i2c", "pclk";
    interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0m0_xfer>;
    resets = <&cru SRST_I2C0>, <&cru SRST_P_I2C0>;
    reset-names = "i2c", "apb";
    #address-cells = <1>;
    #size-cells = <0>;
    status = "disabled";
    };
  2. The device file path is located inkernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576.dts,The following snippet enablesi2c0:

    &i2c0 {
    status= "okay";
    pinctrl-0 = <&i2c0m1_xfer>;
    clock-frequency = <400000>;
    };

    &pinctrl {
    i2c0_demo: i2c0_demo {
    rockchip,pins =<0 RK_PC1 9 &pcfg_pull_up>,
    <0 RK_PC2 9 &pcfg_pull_up>;
    };
    };