Note

This is the documentation for the latest development branch and may refer to features that are not available in released versions. If you are looking for the documentation for a specific release, use the drop-down menu on the left and select the desired version.

I2C Usage Instructions#

Overview#

The K230 SoC integrates 5 Synopsys DesignWare I2C controllers, with details as follows:

I2C Controller

Base Address

Default Clock Frequency

Default State

I2C0

0x91405000

100kHz

disabled

I2C1

0x91406000

400kHz

disabled

I2C2

0x91407000

400kHz

disabled

I2C3

0x91408000

400kHz

disabled

I2C4

0x91409000

400kHz

disabled

Note: All I2C controllers are disabled by default and must be enabled in the device tree before use.

Device Tree Configuration#

Device Tree Alias Configuration#

Define I2C bus aliases in the aliases node of the device tree, making it easy for the kernel and other device tree files to reference them.

CanMV-K230 Development Board Example:

aliases {
    serial3 = &uart3;
    i2c0 = &i2c4;   // Corresponds to /dev/i2c-0
    i2c1 = &i2c3;   // Corresponds to /dev/i2c-1
    mmc0 = &mmc_sd0;
    mmc1 = &mmc_sd1;
};

Description:

  • The alias names (i2c0, i2c1) are defined by the kernel or board-level configuration

  • The aliases point to specific I2C controller nodes (&i2c4, &i2c3)

  • The kernel creates corresponding numbered I2C buses based on the aliases, and device nodes can reference them via &i2c0

  • This approach improves the portability of the device tree, eliminating the need to modify all references when board-level configuration changes

Enabling the I2C Controller#

&i2c2 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2_pins>;
    clock-frequency = <400000>;  // 400kHz Fast Mode
};

Configuring Pin Multiplexing#

&iomux {
    i2c2_pins: i2c2_pins {
        pins = K230_IO11, K230_IO12;
        function = "alt3";
        bias-pull-up;
    };
};

Adding I2C Device Nodes#

Add device nodes under the enabled I2C bus:

&i2c2 {
    status = "okay";

    // Example: Add 24C02 EEPROM
    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
    };

    // Example: Add TMP102 temperature sensor
    tmp102@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
    };
};

CanMV-K230 Complete Configuration Example#

Reference file: arch/riscv/boot/dts/canaan/k230-canmv-01studio.dts

// Initialize aliases
aliases {
    serial3 = &uart3;
    i2c0 = &i2c4;
    i2c1 = &i2c3;
    mmc0 = &mmc_sd0;
    mmc1 = &mmc_sd1;
};

// Pin configuration
&iomux {
    i2c2_pins: i2c2_pins {
        pins = K230_IO11, K230_IO12;
        function = "alt3";
    };
};

// Enable I2C3 (used for LT9611 HDMI bridge)
&i2c3 {
    status = "okay";

    lt9611: hdmi-bridge@3b {
        compatible = "lontium,lt9611";
        reg = <0x3b>;
        reset-gpios = <&gpio0_ports 22 GPIO_ACTIVE_HIGH>;
        interrupt-parent = <&gpio0_ports>;
        interrupts = <23 IRQ_TYPE_EDGE_FALLING>;
    };
};

// Enable I2C4
&i2c4 {
    status = "okay";
};

Userspace Operations#

List I2C Buses#

i2cdetect -l

Example output:

[root@canaan ~]# i2cdetect -l
i2c-1 i2c        Synopsys DesignWare I2C adapter  I2C adapter
i2c-2 i2c        Synopsys DesignWare I2C adapter  I2C adapter
i2c-0 i2c        Synopsys DesignWare I2C adapter  I2C adapter

Scan I2C Devices#

i2cdetect -y -r -a 2

Example output:

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

Read Register#

# Read a single byte: from the device at address 0x50 on I2C bus 2, read data at offset 0x00
i2cget -y 2 0x50 0x00

# Read byte data from the specified register (b = byte)
i2cget -y 2 0x50 0x01 b

# Read word data (w = word, 16-bit)
i2cget -y 2 0x50 0x00 w

Write Register#

# Write a single byte
i2cset -y 2 0x50 0x00 0x55

# Write word data (w = word)
i2cset -y 2 0x50 0x00 0x1234 w

Device File Interface#

I2C devices appear in /dev/ as follows:

ls -l /dev/i2c-*

Example output:

crw-rw---- 1 root i2c 89, 0 Jun 10 10:00 /dev/i2c-0
crw-rw---- 1 root i2c 89, 1 Jun 10 10:00 /dev/i2c-1

Programming Examples#

C Language Example#

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

void print_usage(const char *prog)
{
    printf("Usage: %s [I2C_BUS]\n", prog);
    printf("  -h, --help    Show this help message\n");
    printf("  I2C_BUS       I2C bus device (default: /dev/i2c-0)\n");
}

int main(int argc, char *argv[])
{
    // 解析 -h 参数
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            return 0;
        }
    }

    int file;
    char *filename = "/dev/i2c-0";

    if (argc > 1) {
        filename = argv[1];
    }

    // 打开 I2C 设备
    file = open(filename, O_RDWR);
    if (file < 0) {
        perror("Failed to open i2c device");
        return -1;
    }

    printf("Scanning I2C bus: %s\n", filename);
    printf("Address  Device\n");
    printf("-------  ------\n");
    printf("\n7-bit addresses (0x00-0xff):\n");

    // 扫描所有 0x00 到 0xFF 的地址
    for (int addr = 0x00; addr <= 0xFF; addr++) {
        // 设置从设备地址
        if (ioctl(file, I2C_SLAVE, addr) < 0) {
            continue;
        }

        // 尝试读取 1 个字节
        // 此操作是安全的,不会影响设备状态
        unsigned char buf[1];
        int result = read(file, buf, 1);

        // 如果读取成功,说明设备存在
        if (result == 1) {
            printf("  0x%02X   Present\n", addr);
        }
    }

    printf("\nNote: 0x78-0x7F are reserved addresses.\n");

    close(file);
    return 0;
}

Python Example#

Using Adafruit Blinka busio#


import time
import sys
import board
import busio

def main(bus_num=0):
    """Scan I2C devices on specified bus."""
    # SCL and SDA pins for different I2C buses

    print(f"Hello Blinka! Using I2C bus {bus_num}")

    _, scl, sda = board.pin.i2cPorts[bus_num]
    i2c = busio.I2C(scl, sda)

    while not i2c.try_lock():
        pass

    try:
        devices = i2c.scan()
        print(f"I2C devices found on bus {bus_num}: {[hex(i) for i in devices]}")
    finally:
        i2c.unlock()


if __name__ == "__main__":
    bus = 0
    if len(sys.argv) > 1:
        if sys.argv[1] in ["-h", "--help"]:
            print("Usage: python pi_busio_i2c.py [bus_num]")
            print("  bus_num  I2C bus number (default: 0)")
            print("           0 - I2C0 (SCL/SDA)")
            print("           1 - I2C1 (SCL1/SDA1)")
            print("           2 - I2C2 (SCL2/SDA2)")
            sys.exit(0)
        bus = int(sys.argv[1])

    main(bus)

Scanning Devices Without Libraries#

#!/usr/bin/env python3
import fcntl
import sys

I2C_SLAVE = 0x0703

def i2c_scan(bus_num=0):
    with open(f"/dev/i2c-{bus_num}", "r+b", buffering=0) as f:
        print(f"Scanning I2C bus {bus_num}:")
        print("Address  Device")
        print("-------  ------")
        for addr in range(0x00, 0x100):
            try:
                fcntl.ioctl(f.fileno(), I2C_SLAVE, addr)
                f.read(1)  # 尝试读取一个字节
                print(f"  0x{addr:02X}   Present")
            except:
                pass

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help"]:
        print("Usage: python i2c.py [bus_num]")
        print("  bus_num  I2C bus number (default: 0)")
        print("  -h, --help  Show this help message")
        sys.exit(0)

    bus = 0
    if len(sys.argv) > 1:
        bus = int(sys.argv[1])
    i2c_scan(bus)
Comments list
Comments
Log in