4、设备树配置:DTS节点编写、时钟与电源属性、GPIO与复位时序、MIPI DPHY参数配置
设备树(Device Tree)这东西,说白了就是告诉内核“你的硬件长什么样”。Sensor驱动能不能跑起来,一半的功夫都在DTS配置上。我见过太多人花好几天调代码,最后发现是DTS里一个GPIO极性写反了——嗯,这种坑我自己也踩过。
这一节我们聚焦Sensor驱动最关键的几个DTS配置点:节点怎么写、时钟和电源怎么给、GPIO和复位时序怎么控制、MIPI DPHY参数怎么设。你把这些搞清楚了,Sensor在硬件层面就算“站稳”了。
4.1 Sensor DTS节点结构
一个标准的Sensor节点,通常挂在I2C总线下。我个人习惯把节点名写成
// 基本节点骨架
&i2c2 {
status = "okay";
clock-frequency = <400000>; // 400KHz快速模式
ov5640: camera@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
// ... 其他属性
};
};
这里要注意几点:
- compatible:必须和驱动里的of_match_table完全一致,大小写都不能错。
- reg:Sensor的I2C地址,通常是7位地址左移1位后的值。比如OV5640的7位地址是0x3C,写0x3C就行。
- status:默认写"okay",如果硬件没焊或者调试阶段想屏蔽,改成"disabled"。
status-gpios属性,用来指示Sensor的电源好信号。虽然这不是标准属性,但驱动里可以自己解析,调试时特别有用。
4.2 时钟与电源属性
Sensor需要外部时钟源(MCLK/XCLK)和稳定的供电。这两个配不对,Sensor要么不初始化,要么图像有纹波。
4.2.1 时钟配置
时钟属性通常用clocks和clock-names来描述。Sensor的主时钟频率一般在6MHz到27MHz之间,具体看Sensor手册。
clocks = <&clkc CLK_SENSOR>;
clock-names = "xvclk";
clock-frequency = <24000000>; // 24MHz
为什么时钟频率这么重要?我遇到过一个问题:Sensor输出帧率不对,图像一卡一卡的。查了半天,发现是时钟源实际输出23.8MHz,但Sensor内部PLL是按24MHz算的,导致帧同步偏差。后来加了assigned-clock-rates强制锁定频率才解决。
| 属性 | 说明 | 常见值 |
|---|---|---|
clocks |
时钟源句柄 | &clkc CLK_SENSOR |
clock-names |
时钟名称,驱动通过它获取时钟 | "xvclk", "mclk" |
clock-frequency |
期望的时钟频率(Hz) | 24000000 |
assigned-clock-rates |
强制设定时钟频率 | <24000000> |
4.2.2 电源配置
Sensor通常需要多路电源:模拟电压(AVDD)、数字电压(DVDD)、IO电压(IOVDD)。电压值必须精确,偏差超过±5%就可能出问题。
avdd-supply = <®_avdd>;
dvdd-supply = <®_dvdd>;
iovdd-supply = <®_iovdd>;
regulator_set_load和regulator_enable手动控制顺序,不要依赖DTS的默认行为。
4.3 GPIO与复位时序
GPIO控制是Sensor驱动里最容易出bug的地方。复位时序、电源使能、中断检测,每个GPIO的极性、延迟、顺序都要精确。
4.3.1 GPIO属性定义
reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
power-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>;
standby-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>;
这里GPIO_ACTIVE_LOW和GPIO_ACTIVE_HIGH决定了驱动里怎么操作。我曾经犯过一个错:把复位引脚设成GPIO_ACTIVE_HIGH,但Sensor手册写的是低电平复位。结果每次复位都变成解除复位,Sensor一直处于复位状态——图像全黑,我还以为是Sensor坏了。
4.3.2 复位时序要求
复位时序通常包含以下几个阶段:
- 上电稳定:电源稳定后,等待至少1ms(有些Sensor需要10ms)。
- 复位拉低:将复位引脚拉低,保持至少1ms。
- 复位释放:将复位引脚拉高,等待Sensor内部初始化完成(通常5-20ms)。
- I2C通信:复位释放后才能开始I2C读写。
// 驱动中的复位时序示例
gpiod_set_value(sensor->reset_gpio, 0); // 拉低复位
usleep_range(2000, 3000); // 保持2ms
gpiod_set_value(sensor->reset_gpio, 1); // 释放复位
usleep_range(10000, 15000); // 等待10ms初始化
4.4 MIPI DPHY参数配置
MIPI DPHY参数是Sensor和SoC之间高速数据传输的关键。配错了,图像会有条纹、花屏,甚至完全没数据。
4.4.1 DPHY基本参数
| 参数 | 说明 | 典型值 |
|---|---|---|
data-lanes |
使用的数据通道数 | 1, 2, 4 |
clock-lanes |
时钟通道索引 | 0 |
hs-clk-rate |
高速时钟频率(Hz) | 336000000 |
lp-clk-rate |
低功耗时钟频率(Hz) | 10000000 |
port {
sensor_ep: endpoint {
remote-endpoint = <&csi_ep>;
data-lanes = <1 2>; // 使用2条数据通道
clock-lanes = <0>; // 时钟在lane 0
hs-clk-rate = <336000000>; // 336MHz高速时钟
link-frequencies = /bits/ 64 <336000000>;
};
};
4.4.2 时钟频率计算
高速时钟频率怎么算?公式其实不复杂:
hs_clk = (pixel_width × pixel_rate × bits_per_pixel) / (2 × data_lanes)
举个例子:1080p@30fps,RGB888(24bit),使用2条lane:
- 像素时钟 = 1920 × 1080 × 30 ≈ 62.2MHz
- hs_clk = (62.2 × 24) / (2 × 2) ≈ 373MHz
嗯,这里要注意,实际值还要考虑消隐区(blanking)的开销。我一般会在计算值上加10%-15%的余量。
link-frequencies属性必须用/bits/ 64修饰,因为频率值可能超过32位范围。我见过有人写成32位,结果内核解析出来的值完全不对,图像全是噪声。
4.5 完整DTS节点示例
把上面所有内容整合起来,一个完整的Sensor DTS节点大概长这样:
&i2c2 {
status = "okay";
clock-frequency = <400000>;
imx219: camera@10 {
compatible = "sony,imx219";
reg = <0x10>;
// 时钟
clocks = <&clkc CLK_SENSOR>;
clock-names = "xvclk";
clock-frequency = <24000000>;
assigned-clock-rates = <24000000>;
// 电源
avdd-supply = <®_avdd_2v8>;
dvdd-supply = <®_dvdd_1v2>;
iovdd-supply = <®_iovdd_1v8>;
// GPIO
reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
power-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>;
// MIPI DPHY
port {
imx219_ep: endpoint {
remote-endpoint = <&csi_ep>;
data-lanes = <1 2>;
clock-lanes = <0>;
hs-clk-rate = <336000000>;
link-frequencies = /bits/ 64 <336000000>;
};
};
};
};