4、瑞芯微I2C控制器概述:RK系列I2C控制器特性、寄存器基地址、时钟树与频率配置
好,咱们进入正题。这一章我打算把瑞芯微I2C控制器的“家底”给你翻一翻。你想想看,要调试一个外设,连它长什么样、住哪儿、靠什么吃饭都不知道,那肯定不行。所以,咱们得先搞清楚三件事:特性(它能干啥)、基地址(它住哪儿)、时钟树(它怎么跑起来)。
4.1 RK系列I2C控制器特性
瑞芯微的I2C控制器,说白了就是一个兼容I²C总线标准的硬件模块。我这些年经手的RK芯片,从RK3288到RK3588,核心逻辑基本一致,但细节上各有千秋。
它的主要特性,我列个表给你看,这样更直观:
| 特性项 | 说明 |
|---|---|
| 协议标准 | 兼容 I²C 总线标准,支持标准模式(100kHz)和快速模式(400kHz) |
| 工作模式 | 支持主模式(Master)和从模式(Slave),但咱们调试传感器、EEPROM这些,基本只用主模式 |
| 数据宽度 | 7位和10位设备地址都支持,不过7位地址用得最多 |
| 中断支持 | 有独立的中断线,可以配置中断触发条件,比如传输完成、接收FIFO非空等 |
| FIFO缓冲 | 内置发送和接收FIFO,深度一般是32字节。这个设计很实用,能减少CPU干预 |
| DMA支持 | 部分型号支持DMA传输,适合大数据量搬运,比如从摄像头模组读配置 |
| 滤波功能 | 内置数字滤波器,可以抑制总线上的毛刺信号 |
4.2 寄存器基地址
寄存器基地址,就是控制器在内存空间里的“门牌号”。你只有找到这个门牌号,才能通过指针去读写它的寄存器。
不同型号的RK芯片,I2C控制器的数量不一样,基地址也不同。我以最常见的RK3568为例,它的I2C控制器基地址如下:
| I2C控制器 | 基地址 | 对应总线 |
|---|---|---|
| I2C0 | 0xFE5C0000 | 常用于PMIC(电源管理芯片) |
| I2C1 | 0xFE5D0000 | 常用于摄像头、传感器 |
| I2C2 | 0xFE5E0000 | 常用于HDMI、显示接口 |
| I2C3 | 0xFE5F0000 | 常用于扩展GPIO、音频Codec |
| I2C4 | 0xFE600000 | 常用于触摸屏、TP |
| I2C5 | 0xFE610000 | 常用于其他低速外设 |
在Linux设备树里,这些基地址会通过reg属性来描述。比如:
i2c1: i2c@fe5d0000 {
compatible = "rockchip,rk3568-i2c";
reg = <0x0 0xfe5d0000 0x0 0x1000>;
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_I2C1>, <&cru PCLK_I2C1>;
clock-names = "i2c", "pclk";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
看到没?reg属性里的0xfe5d0000就是基地址,0x1000是地址空间大小,一般是4KB。
4.3 时钟树与频率配置
时钟,是I2C控制器的“心脏”。没有时钟,它就是个死模块。瑞芯微的时钟树比较复杂,但咱们只需要关注跟I2C相关的两条线:
- PCLK(外设时钟):用于控制器内部寄存器访问,一般来自PCLK总线,频率固定。
- CLK(I2C工作时钟):用于产生SCL时钟信号,这个频率决定了I2C总线的传输速率。
我画了一张图,帮你理清这个关系:
从图里你能看到,CLK经过CRU分频后,进入I2C控制器。控制器内部还有一个时钟分频寄存器(I2C_CCR),它会再次对CLK进行分频,最终产生SCL信号。
SCL频率的计算公式很简单:
SCL频率 = CLK频率 / ( (CCR_DIV + 1) * 2 )
其中CCR_DIV就是I2C_CCR寄存器里的分频值。举个例子,如果CLK频率是100MHz,你想让SCL跑在400kHz(快速模式),那么:
400000 = 100000000 / ( (CCR_DIV + 1) * 2 )
=> CCR_DIV + 1 = 100000000 / (400000 * 2) = 125
=> CCR_DIV = 124
所以,你把I2C_CCR寄存器设为124,就能得到400kHz的SCL时钟。
4.4 频率配置实战要点
在实际项目中,频率配置通常通过设备树来完成。你会在I2C控制器的节点里看到clock-frequency属性:
&i2c1 {
status = "okay";
clock-frequency = <400000>; // 目标SCL频率:400kHz
// ... 其他属性
};
驱动在初始化时,会读取这个属性,然后根据CLK频率自动计算CCR_DIV的值。但这里有个坑:驱动不一定能拿到准确的CLK频率。如果CLK来自一个可动态调整的PLL,那驱动读到的频率可能跟实际运行频率不一致。
我的建议是:
- 在调试阶段,通过
clk_get_rate函数打印出CLK的实际频率,确认一下。 - 如果发现SCL频率不准,可以手动在驱动里固定CCR_DIV的值,绕过自动计算。
嗯,这一章的内容就这些。记住,特性、基地址、时钟树,这三样是I2C调试的“地基”。地基打不牢,后面全是空中楼阁。