I2C 驱动开发
1. 概述
1.1 功能简介
I2C(Inter-Integrated Circuit)是一种两线式串行通信协议,由 SDA(数据线)和 SCL(时钟线)组成。RK3576 内置多路 I2C 控制器,支持标准模式(100kHz)、快速模式(400kHz)等速率,广泛用于连接传感器、EEPROM、PMIC 等低速外设。M4-R2 底板 40PIN 引脚提供 I2C 接口(Pin3/Pin5 为 I2C8,Pin27/Pin28 为 I2C0),开发者可外接 I2C 从设备。
1.2 应用场景
- 传感器读取:温湿度传感器、加速度计、陀螺仪等
- EEPROM 存储:读写小容量非易失性存储器
- PMIC 通信:与电源管理芯片通信,配置电压轨
- 触摸屏控制器:连接 I2C 接口的触摸屏 IC
- 音频编解码器:配置 Codec 芯片的寄存器
1.3 工作原理
I2C 总线采用主从架构,支持多主机。通信过程包括:起始信号 → 从机地址 + 读写位 → 应答 → 数据传输 → 停止信号。RK3576 的 I2C 控制器支持 7 位寻址模式,内置 FIFO 缓冲区,支持中断与轮询方式传输。
详见 RK 官方文档:[《Rockchip_Developer_Guide_Linux_Software_CN.pdf》]
2. 硬件准备
2.1 硬件清单
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3576BQ 核心板 + M4-R2 底板 | 1 | |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| I2C 从设备 | AT24C02 EEPROM / TMP102 温度传感器 | 1 | 地址 0x50 / 0x48 |
| 上拉电阻 | 4.7kΩ | 2 | SDA、SCL 各一个 |
| 杜邦线 | 母对母 | 若干 |
2.2 引脚连接
以 M4-R2 底板 40PIN 的 I2C8 接口(Pin3/Pin5)为例:
| 主控板引脚 | 外设引脚 | 功能说明 |
|---|---|---|
| I2C8_SDA(40PIN Pin3) | EEPROM SDA | I2C 数据线 |
| I2C8_SCL(40PIN Pin5) | EEPROM SCL | I2C 时钟线 |
| 3.3V | EEPROM VCC | 电源 |
| GND(40PIN Pin9) | EEPROM GND | 共地 |
40PIN 的 Pin3/Pin5 为 I2C8 接口,Pin27/Pin28 为 I2C0 接口(S-01)。实际操作时应选择未被其他功能占用的 I2C 总线,详细引脚位置见 40PIN 引脚定义。
2.3 硬件确认
# 确认 I2C 总线是否存在
ls /dev/i2c-*
# 扫描 I2C 总线上的设备
i2cdetect -y 0
# 输出示例:
# 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: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --3. 软件准备
3.1 内核配置
确认以下内核配置已启用:
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_RK3X=y
CONFIG_OF=y
CONFIG_OF_I2C=y
CONFIG_I2C_RK3X是 Rockchip I2C 控制器的通用驱动,适用于 RK3576。
3.2 设备树
RK3576 的 I2C 控制器在设备树中定义(以下为 RK 平台通用结构示例):
i2c8: i2c@fe5cc000 {
compatible = "rockchip,rk3576-i2c", "rockchip,rk3399-i2c";
reg = <0x0 0xfe5cc000 0x0 0x1000>;
clocks = <&cru CLK_I2C8>, <&cru PCLK_I2C8>;
clock-names = "i2c", "pclk";
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&i2c8m0_xfer>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};以上寄存器基地址、中断号与时钟标识为 RK 平台通用结构示例,具体数值以实际发布的 RK3576 SDK 设备树为准(待板上确认)。
4. 操作步骤
4.1 查看 I2C 总线信息
# 查看 I2C 适配器信息
cat /sys/class/i2c-adapter/i2c-0/name
# 输出:rk3x-i2c
# 查看 I2C 设备列表
ls /sys/bus/i2c/devices/
# 输出:0-0050 i2c-0 i2c-1 i2c-2 ...4.2 读写 I2C 设备
# 读取 AT24C02 EEPROM 地址 0x00 的数据
i2cget -y 0 0x50 0x00
# 向 AT24C02 EEPROM 地址 0x00 写入数据 0xAA
i2cset -y 0 0x50 0x00 0xAA
# 转储 AT24C02 前 16 字节
i2cdump -y 0 0x50
# 扫描 I2C 总线
i2cdetect -y 04.3 设备树中挂载 I2C 从设备
&i2c8 {
status = "okay";
clock-frequency = <400000>; // 400kHz (Fast Mode)
// 挂载 AT24C02 EEPROM
eeprom: eeprom@50 {
compatible = "atmel,at24";
reg = <0x50>;
pagesize = <8>;
};
// 挂载 TMP102 温度传感器
temp_sensor: temperature-sensor@48 {
compatible = "ti,tmp102";
reg = <0x48>;
interrupt-parent = <&gpio3>;
interrupts = <RK_PB1 IRQ_TYPE_LEVEL_LOW>;
};
};4.4 通过 sysfs 查看 I2C 信息
# 查看 I2C 适配器详情
cat /sys/class/i2c-adapter/i2c-0/device/0-0050/name
# 输出:eeprom
# 查看 I2C 驱动绑定
ls /sys/bus/i2c/drivers/at24/5. 代码详解
5.1 内核中 I2C 驱动的注册
// RK3576 I2C 控制器驱动(简化)
// drivers/i2c/busses/i2c-rk3x.c
static struct platform_driver rk3x_i2c_driver = {
.probe = rk3x_i2c_probe,
.remove = rk3x_i2c_remove,
.driver = {
.name = "rk3x-i2c",
.of_match_table = rk3x_i2c_match,
},
};
static const struct of_device_id rk3x_i2c_match[] = {
{ .compatible = "rockchip,rk3576-i2c" },
{ /* sentinel */ }
};5.2 用户空间 I2C 操作的内核调用链
用户空间 i2cget -y 0 0x50 0x00
→ i2cdev_open()
→ i2c_transfer()
→ rk3x_i2c_xfer()
→ 设置 I2C 寄存器,发起传输6. 常见问题
6.1 I2C 总线无响应
现象: i2cdetect 扫描不到任何设备
解决方案:
# 检查 I2C 总线是否正常
cat /sys/kernel/debug/i2c/i2c-0
# 确认设备树中 I2C 节点状态
# 具体节点路径以实际 DTB 反编译结果为准
dtc -I dtb -O dts /sys/firmware/fdt | grep -A3 "i2c@"
# 应输出 "okay"检查硬件连接(SDA、SCL 上拉电阻是否正确)、从机地址是否正确。
6.2 I2C 传输超时
现象: 读写 I2C 设备时提示 timeout
解决方案:
# 查看 I2C 错误日志
dmesg | grep -i i2c检查时钟频率是否过高、从设备是否支持当前速率、总线是否被其他设备占用。
6.3 I2C 地址冲突
现象: 两个设备响应同一个地址
解决方案: 确认每个 I2C 从设备的地址引脚配置(A0/A1/A2),确保地址唯一。使用 i2cdetect 逐个扫描确认。
