RK平台I2C开发:从硬件原理到实战排查

电子说

1.4w人已加入

描述

 

 

在嵌入式开发中,I2C 总线是连接外设的 桥梁”—— 小到传感器、EEPROM,大到 LCD 驱动器、音频芯片,都离不开它的控制。而瑞芯微(Rockchip)系列芯片作为主流嵌入式方案,其 I2C 控制器的开发是很多工程师的必备技能。

 

 

今天这篇文章,我们将从I2C 硬件原理和数据帧 讲起,再结合官方《ROCKCHIP I2C 开发指南》,梳理 RK 平台 I2C 开发的全流程、关键配置和常见问题,帮你快速上手!

 

 

一、先搞懂基础:I2C 硬件原理与数据帧

 

在动手开发前,必须先掌握 I2C 的 底层逻辑”—— 硬件结构和数据传输规则,否则后续排查问题会寸步难行。

 

 

1. I2C 硬件原理:两条线搞定通信

 

I2C 总线最核心的特点是 简单:仅需 SDA(串行数据线) SCL(串行时钟线) 两条线,就能实现多主多从的通信(RK 平台仅支持主模式)。

 

 

关键硬件细节:

 

总线拓扑:所有设备的 SDA 连在一起,SCL 连在一起;每个设备有唯一地址,主设备(如 RK 芯片)通过地址识别从设备(如传感器)。

 

 

上拉电阻SDA 和 SCL 必须外接上拉电阻(通常 4.7kΩ~10kΩ),因为 I2C 设备的引脚是 开漏输出—— 只能拉低电平,无法主动输出高电平,需通过上拉电阻将总线拉到高电平(Vcc 通常为 3.3V)。

 

 

�� RK 文档中提到:改变上拉电阻大小可调节 I2C 总线的上拉强度,本质是通过电阻值影响总线的上升沿时间(后面会讲上升沿的重要性)。

 

 

主从关系:主设备负责生成 SCL 时钟、发起通信(发送起始 停止信号);从设备被动响应,根据主设备发送的地址匹配自身。

 

 

2. I2C 数据帧:通信的 语言规则

 

I2C 数据以 ” 为单位传输,每帧包含固定的结构,主从设备必须遵循这套规则才能正常通信。完整帧结构如下(以常用的 位寻址为例):

 

 

  •  
[起始信号] → [地址字节] → [ACK/NACK] → [数据字节1] → [ACK/NACK] → ... → [数据字节n] → [ACK/NACK] → [停止信号]

各部分详解:

 

起始信号(S:主设备拉低 SDA(此时 SCL 为高电平),表示通信开始。

 

 

地址字节位,前 位是从设备地址,第 位是 读写位”——0 表示 从写数据表示 从读数据

若用 10 位寻址,地址字节分两部分:第一字节是固定前缀 11110 + 10 位地址的高 位,第二字节是 10 位地址的低 位。

 

 

ACK/NACK:每传输 1 字节后,接收方需反馈 位:

 

 

ACK(应答):接收方拉低 SDA(表示已接收);

 

 

NACK(非应答):接收方不拉低 SDA(表示未接收或结束传输)。

 

 

数据字节位,可连续传输(RK 控制器一次最多传 32 字节)。

 

 

停止信号(P:主设备拉高 SDA(此时 SCL 为高电平),表示通信结束。

I2C总线

二、RK 平台 I2C 开发核心:控制器、驱动与流程

 

掌握基础后,我们聚焦 RK 平台的具体开发 —— 先了解控制器功能,再区分驱动差异,最后梳理开发流程。

 

 

1. RK I2C 控制器:支持哪些能力?

 

RK 系列芯片的 I2C 控制器兼容性强、配置灵活,核心功能如下(文档 V2.2.0 版本):

 

 

兼容I2C 协议 SMBus 协议(常见外设均支持);

 

 

仅支持主模式RK 芯片作为主设备,控制外部从设备);

 

 

时钟频率:软件可编程,最高支持1000kbpsFast-mode Plus,部分芯片默认支持 400kbpsFast-mode);

 

 

寻址模式:支持位地址 10 位地址(覆盖绝大多数外设);

 

 

数据传输:一次中断 / 轮询最多传输 32 字节,效率较高。

 

 

2. 关键注意:RK 平台的两种 I2C 驱动

 

RK 平台的 I2C 驱动因内核版本不同分为两类,配置方式差异较大,必须区分清楚:

 

 

驱动文件

 

 

适用内核版本

 

 

配置方式

 

 

最高频率

 

 

i2c-rk3x.c

 

 

Linux 4.19+(如 RK356XRV1126

 

 

设备树(DTS)配置

 

 

1000kbps

 

 

i2c-rockchip.c

 

 

Linux 3.10 内核

 

 

代码中配置i2c_msg 结构体

 

 

1000kbps

 

 

已支持所有 RK 芯片 所有内核版本,实际开发时需先确认所用内核版本,再选择对应驱动。

 

 

3. RK I2C 开发流程:三种传输模式

 

RK I2C 控制器的核心是 传输模式,不同场景对应不同模式,本质是通过配置寄存器(如 I2C_CONI2C_CLKDIV)实现。

 

 

模式 1:只发送模式(Transmit onlyI2C_CON[1:0] = 2'b00

 

适用于主设备向从设备写数据(如配置传感器参数),步骤如下:

 

 

1.配置I2C_CLKDIV:设置 SCL 时钟频率(如 400kbps);

 

 

2.配置I2C_CON:选择只发送模式,并发送 起始信号(S

 

 

3.I2C_TXDATA0~TXDATA7 写入要发送的数据;

 

 

4.配置I2C_MTXCNT:设置发送数据的字节数;

 

 

5.等待发送完成中断I2C_IPD[2]);

 

 

6.若有更多数据,重复步骤 3~5;若无,配置 I2C_CON 发送停止信号(P,结束传输。

 

 

模式 2:混合模式(Mix modeI2C_CON[1:0] = 2'b01/11

 

适用于先写后读(如先发送寄存器地址,再读取该寄存器的值),步骤如下:

 

 

1.配置I2C_CLKDIV:设置 SCL 频率;

 

 

2.配置I2C_CON:选择混合模式,发送起始信号;

 

 

3.配置I2C_MRXADDR I2C_MRXRADDR:设置从设备地址和读取地址;

 

 

4.配置I2C_MRXCNT:设置要读取的数据字节数;

 

 

5.等待接收完成中断I2C_IPD[3]);

 

 

6.若有更多数据,重复步骤 3~5;若无,发送停止信号,结束传输。

 

 

模式 3:只接收模式(Receive onlyI2C_CON[1:0] = 2'b10

 

适用于主设备从从设备读数据(如读取传感器采集的数值),步骤如下:

 

 

1.配置I2C_CLKDIV:设置 SCL 频率;

 

 

2.配置I2C_CON:选择只接收模式,发送起始信号;

 

 

3.配置I2C_MRXCNT:设置接收数据的字节数;

 

 

4.等待接收完成中断I2C_IPD[3]);

 

 

5.若有更多数据,重复步骤 3~4;若无,发送停止信号,结束传输。

 

 

4. 驱动参数配置:关键是 时钟频率

 

I2C 通信能否稳定,核心是 时钟频率配置”—— 需符合 I2C 协议对 上升沿时间(Tr” 和 下降沿时间(Tf” 的要求,否则会出现通信失败。

 

 

第一步:明确协议时序要求

 

I2C 协议对不同模式的时序有严格规定(文档中表格整理):

 

 

参数

 

 

标准模式(100kbps

 

 

快速模式(400kbps

 

 

高速模式(1000kbps

 

 

单位

 

 

SCL 频率

 

 

≤100

 

 

≤400

 

 

≤1000

 

 

kHz

 

 

上升沿 Tr

 

 

≤1000

 

 

≤300

 

 

≤120

 

 

ns

 

 

下降沿 Tf

 

 

≤300

 

 

≤300

 

 

≤300

 

 

ns

 

 


注:Tr 和 Tf 需用示波器测量,若超过最大值,需调整上拉电阻(如减小电阻值缩短上升沿)。

 

 

第二步:两种驱动的配置示例

 

i2c-rk3x.cDTS 配置)

 

 

配置在设备树中,关键参数是clock-frequency(时钟频率)、i2c-scl-rising-time-nsSCL 上升沿时间)。

 

 

示例(配置 I2C1 为 400kbps):

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
&i2c1 {    status = "okay";    i2c-scl-rising-time-ns = <265>;  // 示波器实测上升沿 265ns    i2c-scl-falling-time-ns = <11>;  // 下降沿通常不变,可默认    clock-frequency = <400000>;      // 400kbps(Fast-mode)        // 挂载从设备(如 ES8316 音频芯片)    es8316: es8316@10 {        compatible = "everest,es8316";        reg = <0x10>;  // 从设备地址 0x10        // 其他外设参数...    };};

i2c-rockchip.c(代码配置)

 

 

在代码中配置i2c_msg 结构体的scl_rate 成员,示例(配置 200kbps):

 

 

  •  
  •  
  •  
  •  
  •  
  •  
struct i2c_msg xfer_msg;xfer_msg[0].addr = client->addr;  // 从设备地址xfer_msg[0].len = num;            // 数据长度xfer_msg[0].flags = client->flags;// 读写标志(0=写,1=读)xfer_msg[0].buf = buf;            // 数据缓存xfer_msg[0].scl_rate = 200 * 1000; // 200kbps 时钟频率

5. RK I2C 如何使用?内核态 用户态 工具

 

1)内核态使用(推荐,适合产品化)

 

RK I2C 内核态开发遵循 Linux 标准 I2C 接口,参考内核文档 Documentation/i2c/writing-clients,核心是:

 

 

注册 I2C 客户端驱动(i2c_driver);

 

 

使用i2c_master_send()(写数据)和i2c_master_recv()(读数据)接口。

 

 

2)用户态使用(适合调试)

 

通过/dev/i2c-%d 设备节点直接访问,步骤:

 

 

1.打开设备节点:int fd = open("/dev/i2c-1", O_RDWR);

 

 

2.设置从设备地址:ioctl(fd, I2C_SLAVE, 0x10);0x10 为从设备地址);

 

 

3.读写数据:用read()/write() 函数直接读写。

 

 

��参考内核文档Documentation/i2c/dev-interface

 

 

3I2C 工具(快速调试必备)

 

I2C-tools 是开源工具集,需交叉编译后使用,支持命令行调试:

 

 

下载地址

 

 

https://www.kernel.org/pub/software/utils/i2c-tools/

 

 

git clone git://git.kernel.org/pub/scm/utils/i2c-tools/i2c-tools.git

 

 

核心工具

 

 

i2cdetect:扫描 I2C 总线及挂载的设备(如 i2cdetect -y 1 扫描总线 1);

 

 

i2cdump:读取从设备所有寄存器的值(如i2cdump -f -y 1 0x10);

 

 

i2cget:读取单个寄存器(如i2cget -y 1 0x10 0x01 读地址 0x10 的 0x01 寄存器);

 

 

i2cset:写入单个寄存器(如i2cset -y 1 0x10 0x01 0x55  0x55 到 0x01 寄存器)。

 

 

4GPIO 模拟 I2C(不推荐)

 

RK 内核支持用 GPIO 模拟 I2C,但效率低,仅适合临时调试。配置示例(DTS):

 

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
i2c@4 {    compatible = "i2c-gpio";    gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>, // SDA 引脚            <&gpio5 8 GPIO_ACTIVE_HIGH>; // SCL 引脚    i2c-gpio,delay-us = <2>; // 约 100kbps 频率    #address-cells = <1>;    #size-cells = <0>;    status = "okay";        // 挂载从设备(如 GT9xx 触摸屏)    gt9xx: gt9xx@14 {        compatible = "goodix,gt9xx";        reg = <0x14>;        // 其他外设参数...    };};

三、RK I2C 常见问题:从错误码到波形 Debug

 

开发中难免遇到问题,文档中整理了两类驱动的常见错误,我们按错误类型” 分类梳理,方便排查。

 

 

1. NACK 错误:从设备无应答

 

现象:

 

i2c-rk3x.c:调用传输接口返回-6-ENXIO

 

 

i2c-rockchip.c:调用传输接口返回-11-EAGAIN

 

 

原因与解决:

 

1.I2C 地址错误:确认从设备地址(如文档中 ES8316 是 0x10GT9xx 是 0x14),注意地址是否需要左移(位地址通常需左移 位,加读写位);

 

 

2.从设备未正常工作:检查从设备供电(如是否上电)、上电时序是否正确;

 

 

3.时序不匹配:从设备需要停止信号(P),但主设备发送了重复起始信号(Sr),需修改传输流程;

 

 

4.总线干扰:用示波器测量,若实际是 ACK 波形却报 NACK,需排查外部干扰(如布线是否靠近强电)。

 

 

2. 超时错误:日志提示 “timeout”

 

根据日志中ipd 的值,对应不同问题:

 

 

1)日志:timeout, ipd: 0x00, state: 1

 

问题:I2C 控制器异常,无法发送起始信号;

 

 

原因:

 

 

a.SCL/SDA 引脚复用错误(IOMUX 配置错);

 

 

b.上拉电压不对(如 3.3V 上拉变成 1.8V);

 

 

c.引脚被外设拉低(电压异常);

 

 

d.I2C 时钟未开启或时钟源过小;

 

 

e.同时配置了CON_START CON_STOP 位(寄存器配置冲突)。

 

 

2)日志:timeout, ipd: 0x10, state: 1

 

问题:控制器正常,但 CPU 无法响应 I2C 中断;

 

 

原因:

 

 

a.CPU0 被阻塞(RK I2C 中断默认在 CPU0,用 cat /proc/interrupts 查看);

 

 

b.I2C 中断位被关闭(检查中断使能寄存器)。

 

 

3)日志:timeout, ipd: 0x80, state: 1 scl was hold by slave

 

问题:SCL 被从设备拉低(总线卡死);

 

 

排查方法:

 

 

a.排除法:若外设少,逐个断开外设,复现问题定位元凶

 

 

b.硬件检测:在 SCL 总线串入电阻(如 220Ω,约上拉电阻的 1/20),测量电阻两端压差 —— 电压更低的一端对应拉低 SDA/SCL 的设备;

 

 

c.波形验证:用示波器抓取波形,对比不同从设备的低电平,与故障时的低电平匹配的即为问题设备。

 

 

3. 终极 Debug:抓取 I2C 波形

 

若以上方法无法解决,最有效的方式是抓取故障时的 I2C 波形

 

 

1.在代码中卡住 CPU”:在出错位置加 while(1),避免发起新的 I2C 任务;

 

 

2.用示波器测量 SDA 和 SCL 引脚:观察是否有起始信号、地址字节、ACK 信号;

 

 

3.对比协议要求:若波形缺失(如无起始信号),检查控制器配置;若有 NACK,检查从设备地址或状态。

 

 

四、RK I2C 开发知识脑图

 

最后,用一张脑图总结全文核心,方便大家收藏回顾:

 

 

I2C总线

写在最后

 

RK 平台的 I2C 开发,核心是 理解协议 区分驱动 重视时序”—— 只要掌握了硬件原理和数据帧规则,再结合官方文档配置驱动、排查问题,就能快速搞定绝大多数场景。

 

 

如果大家在开发中遇到具体问题,欢迎在评论区交流;也可以收藏本文,遇到问题时对照脑图和排查步骤,效率会更高!

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分