注意

这是最新开发分支配套的文档,可能包含已发布版本中尚未提供的功能。如果您要查看特定版本的文档,请使用左侧的下拉菜单并选择所需要的版本。

I2C 使用说明#

概述#

K230 SoC 集成了 5 个 Synopsys DesignWare I2C 控制器,具体信息如下:

I2C 控制器

基地址

默认时钟频率

默认状态

I2C0

0x91405000

100kHz

disabled

I2C1

0x91406000

400kHz

disabled

I2C2

0x91407000

400kHz

disabled

I2C3

0x91408000

400kHz

disabled

I2C4

0x91409000

400kHz

disabled

注意:所有 I2C 控制器默认状态均为 disabled,需在设备树中启用后方可使用。

设备树配置#

设备树别名配置#

在设备树的 aliases 节点中定义 I2C 总线别名,便于内核和其他设备树文件引用。

CanMV-K230 开发板示例:

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

说明:

  • 别名名称(i2c0i2c1)由内核或板级配置定义

  • 别名指向具体的 I2C 控制器节点(&i2c4&i2c3

  • 内核会根据别名创建对应编号的 I2C 总线,设备节点可通过 &i2c0 引用

  • 此方式提高了设备树的可移植性,板级配置更改时无需修改所有引用

启用 I2C 控制器#

&i2c2 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2_pins>;
    clock-frequency = <400000>;  // 400kHz 快速模式
};

配置引脚复用#

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

添加 I2C 设备节点#

在启用的 I2C 总线下添加设备节点:

&i2c2 {
    status = "okay";

    // 示例:添加 24C02 EEPROM
    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
    };

    // 示例:添加温度传感器 TMP102
    tmp102@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
    };
};

CanMV-K230 完整配置示例#

参考文件:arch/riscv/boot/dts/canaan/k230-canmv-01studio.dts

// 初始化别名
aliases {
    serial3 = &uart3;
    i2c0 = &i2c4;
    i2c1 = &i2c3;
    mmc0 = &mmc_sd0;
    mmc1 = &mmc_sd1;
};

// 引脚配置
&iomux {
    i2c2_pins: i2c2_pins {
        pins = K230_IO11, K230_IO12;
        function = "alt3";
    };
};

// 启用 I2C3(用于 LT9611 HDMI 桥接)
&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>;
    };
};

// 启用 I2C4
&i2c4 {
    status = "okay";
};

用户空间操作#

列出 I2C 总线#

i2cdetect -l

输出示例:

[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

扫描 I2C 设备#

i2cdetect -y -r -a 2

输出示例:

     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: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

读取寄存器#

# 读取单个字节:从 I2C 总线 2 上地址为 0x50 的设备,读取偏移量为 0x00 的数据
i2cget -y 2 0x50 0x00

# 读取指定寄存器的字节数据(b = byte)
i2cget -y 2 0x50 0x01 b

# 读取字数据(w = word,16 位)
i2cget -y 2 0x50 0x00 w

写入寄存器#

# 写入单个字节
i2cset -y 2 0x50 0x00 0x55

# 写入字数据(w = word)
i2cset -y 2 0x50 0x00 0x1234 w

设备文件接口#

I2C 设备在 /dev/ 中以以下形式出现:

ls -l /dev/i2c-*

输出示例:

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

编程示例#

C 语言示例#

#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 示例#

使用 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)

不使用库扫描设备#

#!/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)

相关文件#

  • 设备树文件: arch/riscv/boot/dts/canaan/k230.dtsi

  • 平台驱动: drivers/i2c/busses/i2c-designware-platdrv.c

  • 核心驱动: drivers/i2c/busses/i2c-designware-core.h

  • Kconfig: drivers/i2c/busses/Kconfig

评论列表
条评论
登录