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
disabledby 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 configurationThe 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
&i2c0This 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)
