1. RK3568设备树中的I2C总线配置
第一次接触RK3568设备树中的I2C配置时,我也是一头雾水。但当我把它和STM32的I2C初始化对比后,发现两者本质上是相通的。我们先看STM32的I2C初始化代码:
I2C_HandleTypeDef hi2c1; hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c1);这段代码定义了I2C实例、时钟速度、占空比、地址模式等参数。而在RK3568设备树中,I2C的配置是这样的:
i2c1: i2c@fe5a0000 { compatible = "rockchip,rk3568-i2c", "snps,designware-i2c"; reg = <0x0 0xfe5a0000 0x0 0x100>; interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru CLK_I2C1>, <&cru PCLK_I2C1>; clock-names = "i2c", "pclk"; pinctrl-names = "default"; pinctrl-0 = <&i2c1_xfer>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; };设备树中的配置包含了寄存器地址、中断号、时钟、引脚复用等硬件相关信息。与STM32相比,RK3568把这些硬件信息都抽象到了设备树中,而驱动代码则变得通用化。
实际项目中,我们需要修改的主要是以下几个部分:
- 启用I2C控制器:将status从"disabled"改为"okay"
- 配置引脚复用:确保pinctrl-0指向正确的引脚组
- 添加设备节点:在I2C控制器下添加具体的设备节点
比如要添加一个AT24C02 EEPROM设备,可以这样配置:
&i2c1 { status = "okay"; pinctrl-0 = <&i2c1_xfer>; eeprom@50 { compatible = "atmel,24c02"; reg = <0x50>; pagesize = <8>; }; };这里有几个关键点需要注意:
- compatible属性必须与驱动匹配
- reg属性指定设备地址(0x50)
- pagesize是设备特定参数
2. CAN总线配置详解
CAN总线的配置比I2C稍微复杂一些,因为涉及CAN控制器和收发器的配合。我们还是先看STM32的CAN初始化:
CAN_HandleTypeDef hcan; hcan.Instance = CAN1; hcan.Init.Prescaler = 16; hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_13TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = DISABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = DISABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; HAL_CAN_Init(&hcan);而在RK3568设备树中,CAN控制器的配置如下:
can0: can@fe570000 { compatible = "rockchip,rk3568-can"; reg = <0x0 0xfe570000 0x0 0x1000>; interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru CLK_CAN0>, <&cru PCLK_CAN0>; clock-names = "baudclk", "apb_pclk"; resets = <&cru SRST_CAN0>, <&cru SRST_P_CAN0>; reset-names = "can", "can-apb"; pinctrl-names = "default"; pinctrl-0 = <&can0_pins>; status = "disabled"; };实际使用中,我们需要关注以下几个配置项:
- 时钟配置:确保时钟频率与所需波特率匹配
- 引脚复用:正确配置CAN_TX和CAN_RX引脚
- 收发器使能:通常需要控制收发器的STB引脚
一个完整的CAN节点配置示例:
&can0 { status = "okay"; pinctrl-0 = <&can0_pins>; assigned-clocks = <&cru CLK_CAN0>; assigned-clock-rates = <50000000>; // 50MHz }; &pinctrl { can0_pins: can0-pins { rockchip,pins = /* can0_rx */ <1 RK_PB1 2 &pcfg_pull_none>, /* can0_tx */ <1 RK_PB2 2 &pcfg_pull_none>; }; };CAN总线的波特率计算比较特殊,需要在驱动中配置。设备树只提供时钟基准,实际波特率在驱动初始化时计算设置。
3. I2C与CAN配置的常见问题
在实际项目中,I2C和CAN的配置经常会遇到各种问题。根据我的经验,最常见的有以下几种:
I2C设备无响应
- 检查设备地址是否正确(注意7位/8位地址区别)
- 确认上拉电阻是否合适(通常4.7kΩ)
- 用示波器检查SCL/SDA波形是否正常
CAN总线通信失败
- 确认终端电阻是否匹配(通常120Ω)
- 检查波特率配置是否正确
- 验证收发器供电是否正常
引脚复用冲突
- 确保没有其他功能占用相同引脚
- 检查pinctrl配置是否正确
时钟问题
- I2C时钟频率是否在设备支持范围内
- CAN时钟基准是否满足波特率需求
对于调试,我建议采用以下步骤:
- 首先确认硬件连接正确
- 检查设备树配置是否被正确加载
cat /proc/device-tree/i2c@fe5a0000/status - 查看内核日志是否有错误信息
dmesg | grep i2c - 使用工具验证总线通信
- I2C可以使用i2c-tools
i2cdetect -y 1 - CAN可以使用can-utils
candump can0
- I2C可以使用i2c-tools
4. 进阶配置技巧
掌握了基础配置后,下面分享几个进阶技巧:
I2C多设备管理 当总线上有多个设备时,需要注意:
- 确保设备地址不冲突
- 考虑总线负载(电容效应)
- 必要时使用I2C多路复用器
示例配置:
&i2c1 { status = "okay"; sensor@48 { compatible = "ti,tmp112"; reg = <0x48>; }; eeprom@50 { compatible = "atmel,24c02"; reg = <0x50>; }; };CAN FD配置 RK3568支持CAN FD,配置时需要:
- 设置更高的时钟频率
- 配置数据段波特率
- 使用兼容的收发器
示例配置:
&can0 { status = "okay"; assigned-clocks = <&cru CLK_CAN0>; assigned-clock-rates = <100000000>; // 100MHz rockchip,can-fd = <1>; };电源管理 对于低功耗应用,可以配置自动休眠:
&i2c1 { rockchip,sleep-mode = <1>; wakeup-source; };DMA配置 高性能应用可以启用DMA:
&i2c1 { dmas = <&dmac0 10>, <&dmac0 11>; dma-names = "tx", "rx"; };
这些配置都需要驱动支持,在修改前务必确认内核版本和驱动能力。