LV001-Sensor时钟与MIPI接口配置
所谓"配置 sensor",最终只落到三件事:给它一个正确的输入时钟、定好时序参数(帧率与曝光上限)、把 MIPI 链路的 lane 数与速率对齐。这三件事彼此咬合,改动任意一项都会牵连另外两项。
本文先讲清它们的定义与换算关系,再给出从目标帧率反推配置的步骤,最后用本板的 OV8858 把数值验证一遍。
文中源码与设备树引用 kernel-6.1 仓库,主要涉及 drivers/media/i2c/ov8858.c、arch/arm64/boot/dts/rockchip/rk3568-lubancat-csi2.dtsi 与 arch/arm64/boot/dts/rockchip/rk356x.dtsi;sensor 侧的时序与 PLL 以 OV8858COB_(rev 2A)_DS_2.0_Chippack.pdf 数据手册为准。
一、 硬件环境
本文以如下软硬件环境为例,下文所有参数均取自该组合。
| 项目 | 信息 |
|---|---|
| 开发板 | 野火鲁班猫 2(LubanCat 2),SoC 为 RK3568 |
| 摄像头接口 | MIPI CSI-2,经 csi2_dphy0;overlay 形如 rk356x-lubancat-csi*-ov8858-*.dts |
| 摄像头模组 | 野火 EBF410127 |
| Sensor | OmniVision OV8858,COB 封装,8MP(3264x2448) |
| 模组代号 | HS5885-BNSM1018-V01(设备树 rockchip,camera-module-name) |
| I2C 地址 | 0x36 |
| 供电 | DOVDD 1.8 V、AVDD 2.8 V、DVDD 1.2 V |
| 对焦 | DW9714 VCM(lens-focus = <&dw9714>) |
| 数据手册 | OV8858COB_(rev 2A)_DS_2.0_Chippack.pdf;驱动读寄存器 0x302a 自动区分 R1A / R2A 并选用对应寄存器表 |
上表中,模组适配范围见模块资料说明(野火 OV8858 适配鲁班猫 RK356X 系列);模组型号取自原理图 EBF410127v1_SCH_20230426.pdf;I2C 地址、三路供电与对焦器件定义在设备树节点 ov8858@36 内。
二、 链路参数与换算关系
1. 链路上要配的四类参数
从"sensor 出一帧图"这个目标出发,需要确定的参数只有四类,它们的来源各不相同:
| 参数 | 作用 | 由谁决定 | 配在哪 |
|---|---|---|---|
| xvclk | sensor 的输入参考时钟 | 板级晶体 / 主控时钟输出 | 设备树 clocks |
| HTS / VTS | 行、帧总长,决定帧率与曝光上限 | sensor 厂商 setting(第三节 4) | 驱动模式表 + 寄存器表 |
| link frequency | MIPI 时钟 lane 频率,决定带宽 | sensor 内部 PLL | 寄存器表(驱动常量上报) |
| lane 数 | 数据通道数量 | 硬件走线 + SoC D-PHY 能力 | 设备树 data-lanes |
后三类必须彼此自洽:帧率、带宽、lane 数只要有一项对不上,就会丢帧、花屏或干脆不出图。第 3 小节给出的公式,就是判断它们是否自洽的依据。
2. 三类时钟的分工
“时钟”这个词在这个场景里同时指三样东西,混着说最容易出错:
- xvclk(外部输入时钟):主控提供给 sensor 的参考时钟,sensor 内部所有 PLL 都以它为基准倍频。
- 像素时钟(pixel clock / pixel_rate):sensor 内部的读出节奏,决定一行的时长和一帧的时长,对上层表现为
V4L2_CID_PIXEL_RATE。 - MIPI 时钟(link frequency):D-PHY 时钟 lane 的频率;数据 lane 采用 DDR(双沿)传输,因此每条 lane 的数据率为
link_freq × 2。
三者是"基准 → 倍频 → 分头使用"的关系:xvclk 错了,后两个必然全错。
这三样东西在不同资料里的叫法并不统一,对照如下:
| 常见叫法 | 本文所用名字 |
|---|---|
| MCLK / XCLK / XVCLK / INCK | xvclk |
| PCLK / pixel clock | 像素时钟(pixel_rate) |
| D-PHY clock / MIPI clock | MIPI 时钟(link frequency) |
别名有坑,用之前先认器件。 pclk 在 DVP / 并口输出的 sensor 上是一根真实的引脚,一个周期送出一个像素;本板走 MIPI,没有这根引脚,它只以内部读出节奏的形式存在,对上层就是 pixel_rate。mclk 更含糊:同一份板级设备树里,音频 codec 节点也写作 clock-names = "mclk"(那是 I2S 主时钟),摄像头节点旁还有 rockchip,vcm-mclk(对焦马达时钟)。看到这两个缩写,先确认它挂在哪个节点、属于哪个器件,再谈频率。
3. 核心换算公式
每条 lane 数据率 = link_freq × 2 (DDR 双沿)
总带宽 = link_freq × 2 × lanes
pixel_rate = link_freq × 2 × lanes ÷ 位宽
帧率 = pixel_rate ÷ (HTS × VTS)驱动的注释直接写了第 3 个公式:
// drivers/media/i2c/ov8858.c
/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
ov8858->pixel_rate = MIPI_FREQ * 2U * ov8858->lane_num / 10U;由这组公式可以正推、也可以反推:
- 正推(拿到参数后算能力):已知 link_freq、lane 数、位宽,算出 pixel_rate,再除以
HTS × VTS得到帧率。 - 反推(定目标后定参数):先按目标分辨率与帧率得到需要的 pixel_rate,再保证
link_freq × 2 × lanes ≥ pixel_rate × 位宽。反推步骤见第五节。
三、 时序参数:消隐、帧率与曝光
1. 一帧与一行的构成
sensor 不是连续不断地输出像素,行与行之间、帧与帧之间必须留出空白时间(blanking),供内部复位与读出使用。于是有了两个"总长"参数:
HTS(水平总长)= 有效宽度 + HBLANK
VTS(垂直总长)= 有效高度 + VBLANK真正决定帧率的是这两个总长,而不是有效区域:帧率 = pixel_rate ÷ (HTS × VTS)。有效区域之外的部分同样消耗时钟周期。
2. 驱动如何暴露这些参数
驱动把 HTS、VTS 的默认值写在模式表里,再换算成两个 V4L2 控件暴露给用户:
| 控件 | 计算公式 | 是否可调 |
|---|---|---|
| HBLANK | hts_def - width | 只读 |
| VBLANK | vts_def - height | 可调,上限 OV8858_VTS_MAX - height |
// drivers/media/i2c/ov8858.c
h_blank = mode->hts_def - mode->width;
__v4l2_ctrl_modify_range(ov8858->hblank, h_blank,
h_blank, 1, h_blank);
vblank_def = mode->vts_def - mode->height;
__v4l2_ctrl_modify_range(ov8858->vblank, vblank_def,
OV8858_VTS_MAX - mode->height,
1, vblank_def);3. 调 VTS 的连带影响
从"帧率 = pixel_rate ÷ (HTS × VTS)"看,调小 VTS 就能提帧率。但曝光上限被 VTS 直接绑定:
// drivers/media/i2c/ov8858.c
exposure_max = mode->vts_def - 4;【注意】
VTS 调小后帧率上升,最大曝光时间同步缩短,低光下画面会变暗。因此"提帧率"与"保低光"是一对矛盾,不能只看帧率一项指标。
4. HTS / VTS 的数值从哪来
手册只定义寄存器,不给模式表。 HTS / VTS 在手册里有两处定义:
| 位置 | 内容 |
|---|---|
| 表 4-2 image windowing control functions(PDF 第 61 页) | 0x380C/0x380D TIMING_HTS = Horizontal total size;0x380E/0x380F TIMING_VTS = Vertical total size,默认值 0x074C / 0x0A74 |
| 表 6-7 timing control registers(PDF 第 115 页) | 同两项,描述 Total horizontal / vertical timing size,同样给出默认值 |
手册给的是寄存器地址、默认值和"总长"这个含义,没有"某分辨率 + 某帧率该填多少"的对照表;它给的默认值(0x074C = 1868、0x0A74 = 2676)也不是本板在用的值。
4.1 来源一:厂商 setting 一并下发
实际数值随厂商 setting 一起下发。 这份驱动是 Rockchip BSP(Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.),寄存器表里直接留着 OV setting 文件的版本戳:
// drivers/media/i2c/ov8858.c
static const struct regval ov8858_global_regs_r2a_2lane[] = {
// MIPI=720Mbps, SysClk=144Mhz,Dac Clock=360Mhz.
// v00_01_00 (05/29/2014) : initial setting
// AM19 : 3617 <- 0xC0
// AM20 : change FWC_6K_EN to be default 0x3618=0x5aAM19 / AM20 是 Application Note 的改动记录,v00_01_00 (05/29/2014) 是 setting 文件的版本号。也就是说,HTS / VTS 与 PLL 系数(0x0302 pll1_multi、0x0303 pll1_divm、0x0304 pll1_div_mipi…)同属一套 setting:厂商按目标分辨率与帧率算好后,由 SoC BSP 打包进驱动。所以这两个值不需要单独向厂家索要——只有目标模式不在 setting 里时,才需要找厂商补一套。
同一组值在驱动里写了两遍,可以互相印证:
| 模式 | 寄存器表写入 | 模式表声明 |
|---|---|---|
| 3264x2448 | 0x380c/0x380d = 0x0794、0x380e/0x380f = 0x09aa | hts_def = 0x0794 * 2、vts_def = 0x09aa |
| 1632x1224 | 0x380c/0x380d = 0x0788、0x380e/0x380f = 0x04dc | hts_def = 0x0788、vts_def = 0x04dc |
这两对值具体落在哪个数组里。 以本板在用的全分辨率 2 lane 为例,0x0794 / 0x09aa 就是 ov8858_3264x2448_regs_r2a_2lane[] 中紧邻的两对寄存器(R1A 的同名表写法一致),该数组定义在 drivers/media/i2c/ov8858.c 第 1381~1397 行:
// drivers/media/i2c/ov8858.c
static const struct regval ov8858_3264x2448_regs_r2a_2lane[] = {
{0x0100, 0x00},
{0x3501, 0x9a},// exposure M
{0x3502, 0x20},// exposure L
{0x3778, 0x1a},//
{0x3808, 0x0c},// x output size H
{0x3809, 0xc0},// x output size L
{0x380a, 0x09},// y output size H
{0x380b, 0x90},// y output size L
{0x380c, 0x07},// HTS H
{0x380d, 0x94},// HTS L
{0x380e, 0x09},// VTS H
{0x380f, 0xaa},// VTS L
{0x3814, 0x01},// x odd inc
{0x3821, 0x46},// mirror on, bin off
{0x382a, 0x01},// y odd inc
{0x3830, 0x06},这段邻接关系能说明这几个值的来历:0x3808~0x380b 是 x/y 输出尺寸(写入 3264x2448),0x3814 / 0x3821 / 0x382a 是抽点与 binning 开关,夹在中间的 0x380c / 0x380d 与 0x380e / 0x380f 就是 HTS 与 VTS 本身。一张寄存器表把“输出尺寸 → 时序 → 抽点 → 曝光增益”整块下发,HTS / VTS 只是其中一项,因此它必然与同表的尺寸、抽点值成组出现,不能单独替换——想换一组 HTS / VTS,等于换掉整张表,也就是换一套 setting。
单位与 ×2 约定。 HTS / VTS 都以 sensor 内部 SysClk 计数(这套 setting 的注释写明 SysClk=144Mhz),所以 帧率 = SysClk ÷ (HTS × VTS)。代入本板两套 setting:
全分辨率 : 2 × 0x0794 × 0x09aa × 15 = 2 × 1940 × 2474 × 15 ≈ 143.99e6 ✓ 144 MHz
2×2 binning: 2 × 0x0788 × 0x04dc × 30 = 2 × 1928 × 1244 × 30 ≈ 143.9e6 ✓ 144 MHz两种模式的 HTS 寄存器值都要 ×2 才对得上 SysClk。这就是全分辨率模式写 hts_def = 0x0794 * 2 的原因,也是第五节第 3 小节那条【注意】所说"两种模式写法不一致"的根源。
换到 4 lane 时,口径要跟着 pixel_rate 一起变。 上面的 ×2 算法以 2 lane 为前提:pixel_rate 本身随 lane 数翻倍(MIPI_FREQ × 2 × lane_num ÷ 10),lane 数为 4 时即 360e6 × 2 × 4 ÷ 10 = 288e6。用这个值核对 4 lane 的全分辨率表(驱动里 4 lane 只提供全分辨率模式):3880 × 2474 × 30 = 288e6,与 R1A 4 lane 表的 vts_def = 0x09aa 吻合——setting 注释那行 SysClk=144Mhz 是沿用文件头文字,4 lane 时不能照它推算。R2A 4 lane 表的 vts_def = 0x0a00(2560)比自洽值略大,3880 × 2560 × 30 ≈ 298e6,理论帧率约 29 fps(标称 30 fps);本板走 2 lane,不受此影响。
顺带解释了 HBLANK 为什么是只读的:HTS 随 setting 一起定死,改它相当于改厂商时序,所以驱动只把 VBLANK(即 VTS)放开给用户。
4.2 来源二:由已知参数反推
目标模式不在现成 setting 里时,只能自行反推。反推依赖的是第二节那条乘积关系——HTS × VTS = pixel_rate ÷ 目标帧率 是定值,所以行、帧消隐只要已知其中一个,另一个就能唯一求出。两个方向都成立,区别在于能不能落地配置。
4.2.1 已知行消隐,求帧消隐
HTS = width + HBLANK
VTS = pixel_rate ÷ (HTS × 目标帧率)
VBLANK = VTS − height以本板全分辨率代入:HTS = 3264 + 616 = 3880,VTS = 144e6 ÷ (3880 × 15) ≈ 2474,VBLANK = 2474 − 2448 = 26,与 setting 里的 0x09aa 一致。这条路径可以落地:帧消隐就是驱动暴露的 V4L2_CID_VBLANK,算完即可写入。
结果必须过两道校验:
(1)VTS 不得小于有效高度,否则目标帧率无解;同时受 VTS ≤ OV8858_VTS_MAX(0x7fff)限制,且 VTS 本身就是曝光上限的来源(VTS − 4)。仍以本板为例,若目标改为 20 fps,VTS = 144e6 ÷ (3880 × 20) ≈ 1856 < 2448,无解——这与帧率上限 pixel_rate ÷ (HTS × height) ≈ 15.2 fps 是同一件事。
(2)行消隐的计数口径要与 4.1 的 ×2 约定一致。binning 模式即为此例:若照驱动的 hts_def = 0x0788 = 1928 取 HBLANK = 296,算得 VTS = 144e6 ÷ (1928 × 30) ≈ 2490,与 setting 的 1244 相差一倍;换成 ×2 后的 HTS = 3856,VTS ≈ 1245 即吻合。
4.2.2 已知帧消隐,求行消隐
同一组关系反过来用:
VTS = height + VBLANK
HTS = pixel_rate ÷ (VTS × 目标帧率)
HBLANK = HTS − width本板全分辨率代入:VTS = 2448 + 26 = 2474,HTS = 144e6 ÷ (2474 × 15) ≈ 3880,HBLANK = 3880 − 3264 = 616,与 setting 一致。
解同样唯一,但这条路径只用于核算,不用于配置:
行消隐没有写入通道。 帧消隐是可调项(
V4L2_CID_VBLANK,驱动写寄存器0x380e/0x380f);行消隐只是 setting 的导出值,驱动以mode->hts_def - mode->width给出一个只读的 HBLANK(第三节第 2 小节),改它等于换掉整张寄存器表。反推值未必落在 setting 的整数上。
pixel_rate由link_freq取整而来,除回去会有零点几行的偏差。仍以 binning 模式为例:VTS = 1224 + 20 = 1244,HTS = 144e6 ÷ (1244 × 30) ≈ 3858,比 setting 的 3856 多 2 行,只够判断"是不是同一套参数",不能当配置值写回。越界即无解。
HTS不能小于width + 最小行消隐(sensor 内部读出要求),否则这组"帧消隐 + 帧率"本身不可达。
因此这个方向的实际用途是核对:拿到一段已知的"帧率 + VTS"记录,反查 HTS 是否等于 setting 里的值,用来判断 pixel_rate 与 setting 是否自洽——也就是 4.1 的 ×2 约定与第五节第 3 小节【注意】要处理的问题。
两个方向中只有 4.2.1 能落到配置上,4.2.2 用于核算,这也解释了这类场合通常给出的是行消隐。完整的反推步骤见第五节第 1 小节。
四、 MIPI 接口:速率与 lane 数
1. link frequency 与带宽
MIPI 侧的配置目标只有一个:带宽要够。判据由第二节的公式变形得到:
link_freq ≥ pixel_rate × 位宽 ÷ (2 × lanes)本板取 link_freq = 360 MHz(即每条 lane 720 Mbps),驱动以常量写死并通过 V4L2_CID_LINK_FREQ 上报;设备树里没有 link-frequencies 节点时,SoC 侧就以驱动上报的值为准去配 D-PHY。
// drivers/media/i2c/ov8858.c
#define MIPI_FREQ 360000000U
#define OV8858_XVCLK_FREQ 24000000
#define OV8858_LANES 2
#define OV8858_BITS_PER_SAMPLE 102. 2 线与 4 线的取舍
"N 线 MIPI"指使用了 N 对差分数据 lane,另加 1 对时钟 lane。lane 越多,同样 link_freq 下带宽越高;反过来,lane 减半就必须提高 link_freq 或降低帧率来补。
设备树端点的 data-lanes 数组元素个数就是 lane 数,clock-lanes 指明时钟用哪条物理 lane:
// arch/arm64/boot/dts/rockchip/rk3568-lubancat-csi2.dtsi
port {
/* MIPI CSI-2 bus endpoint */
ov8858_out: endpoint {
remote-endpoint = <&dphy0_in_ov8858>;
clock-lanes = <0>;
data-lanes = <1 2>;
}
};驱动读的正是这个数组的长度:
// drivers/media/i2c/ov8858.c
rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0);
if (rval <= 0) {
dev_warn(dev, " Get mipi lane num failed!\n");
return -1;
}
ov8858->lane_num = rval;sensor 侧则用寄存器 0x3018 声明 lane 数,4 lane 时写 0x72、2 lane 时写 0x32;驱动为两种 lane 数各准备了一整套寄存器表,切换 lane 数等于换表:
// drivers/media/i2c/ov8858.c
{0x3018, 0x32}, // MIPI 2 lane
{0x3020, 0x93}, // Clock switch output normal, pclk_div =/1
{0x3022, 0x01}, // pd_mipi enable when rst_sync
{0x3031, 0x0a}, // MIPI 10-bit mode3. SoC 侧约束(RK3568 D-PHY)
lane 数不是想给多少就给多少,SoC 侧有硬约束。RK3568 只有 1 个 CSI2 D-PHY、4 条物理 lane,可整用也可拆成两组 2 lane:
// arch/arm64/boot/dts/rockchip/rk356x.dtsi
/*
* csi2_dphy0: used for csi2 dphy full mode,
is mutually exclusive with
csi2_dphy1 and csi2_dphy2
* csi2_dphy1: used for csi2 dphy split mode,
physical lanes use lane0 and lane1,
can be used with csi2_dphy2 parallel
* csi2_dphy2: used for csi2 dphy split mode,
physical lanes use lane2 and lane3,
can be used with csi2_dphy1 parallel
*/LubanCat 用 csi2_dphy0,配 2 条数据 lane。ISP 侧的 CSI2RX 支持 x1 / x2 / x4 lane,见《Rockchip_RK3568_TRM_Part2_V1.1-20210301.pdf》12.1。
五、 配置落地:OV8858 实例
1. 从目标帧率反推配置
把前面的公式串成一条可执行的路径:
(1)确定输出分辨率与目标帧率。
(2)查厂商模式表,取该模式给出的 HTS 与 VTS。
(3)核对 pixel_rate = HTS × VTS × 帧率,确认该模式确实能跑出目标帧率。
(4)由 pixel_rate、位宽、lane 数算出所需 link_freq,并确认不超过 D-PHY 与 CSI2RX 上限。
(5)选定对应的寄存器表(2 lane 与 4 lane 各一套,内含 PLL 系数与 HTS / VTS),写入设备树与驱动。
(6)反算校验:由 link_freq 推出的 pixel_rate,是否与 HTS × VTS × 帧率 吻合。
第 3 步与第 6 步是同一件事的两个方向,务必双向对齐——只算单边是配置出错的主要来源。
2. 本板取值与依据
| 参数 | 取值 | 依据 |
|---|---|---|
| xvclk | 24 MHz | 板上晶振;OV8858 寄存器表按 24 MHz 反推 |
| data-lanes | 2 | 设备树 data-lanes = <1 2>,且必须匹配所选寄存器表 |
| 每条 lane 数据率 | 720 Mbps | link_freq 360 MHz × 2(DDR) |
| pixel_rate | 144 Mpixel/s | 360 × 2 × 2 ÷ 10 |
| 输出格式 | RAW10 | 10bit |
驱动还会主动校验 xvclk,不匹配时告警:
// drivers/media/i2c/ov8858.c
ret = clk_set_rate(ov8858->xvclk, OV8858_XVCLK_FREQ);
if (ret < 0)
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
if (clk_get_rate(ov8858->xvclk) != OV8858_XVCLK_FREQ)
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");3. 两种输出模式:全分辨率与 2×2 binning
本节先说明 binning 的原理与后果,再对照驱动里切换模式的寄存器,最后给出本板的模式选择与帧率上限。
3.1 binning 的定义与四个后果
手册 §3.2 subsampling(PDF 第 58 页)给了定义:
With binning mode ON, the voltage levels of adjacent pixels (of the same color) are averaged before being sent to the ADC.
即在送进 ADC 之前,先把相邻的同色像素电压做平均。OV8858 支持的是 2×2 binning(手册图 3-2),把 2×2 个同色像素合并成 1 个输出像素。由此带来四个直接后果:
分辨率减半、视场不变:3264x2448 → 1632x1224,仍是整幅成像面,不是裁掉一半——手册的原话是 while maintaining the field of view。
暗光表现更好:参与合并的同色像素变多,等效感光面积变大,信噪比随之改善。
帧率上限翻倍:每帧要读出的行数减半,VTS 从 2474 降到 1244,15 fps 也就变成了 30 fps。
MIPI 需求减半:输出像素总量从约 120 Mpx/s(3264 × 2448 × 15)降到约 60 Mpx/s(1632 × 1224 × 30),所以两种模式沿用同一个
link_freq = 360 MHz都有富余。
binning 在手册里通常是和裁剪配合使用的提帧率手段:表 2-1(PDF 第 24 页)列的 720p 1408x792 90fps = cropping + 2x binning、VGA 640x480 120fps = cropping + 4x binning 都是这个用法。本板用的是「整幅 2×2 binning、不裁剪」,所以视场不变。
3.2 模式切换涉及的寄存器
对比 ov8858_1632x1224_regs_* 与 ov8858_3264x2448_regs_*(R1A 与 R2A 的 2 lane 表写法一致;binning 只有 2 lane 模式,4 lane 模式只提供全分辨率):
| 寄存器 | 全分辨率 | 2×2 binning | 手册定义 |
|---|---|---|---|
0x3814 X_ODD_INC | 0x01 | 0x03 | 水平抽点:1 = 不抽,3 = 2× 合并 |
0x382a Y_ODD_INC | 0x01 | 0x03 | 垂直抽点:同上 |
0x3821 TIMING_FORMAT2 | 0x46(bin off) | 0x67(bin on) | bit[5] = Vertical binning(手册表 3-1) |
偶数抽点 0x3815 / 0x382b 两套表都保持 0x01(由 2 lane 全局表写入),所以水平与垂直各按 2× 合并,合起来就是 2×2。
3.3 本板的模式选择与帧率上限
两种模式靠换 overlay 切换,属性值仍是 rockchip,ov8858-capture-mode:
rk356x-lubancat-csi{0,1}-ov8858-3264x2448-15fps-overlay.dts:不写该属性,驱动读失败会打一条Get capture_mode failed!警告并回落到 0,即 3264x2448;rk356x-lubancat-csi{0,1}-ov8858-1632x1224-30fps-overlay.dts:写入rockchip,ov8858-capture-mode = <1>,即 1632x1224。
也就是说,用全分辨率那条 overlay 时开机看到那条警告是预期行为,不是配置故障。
| 模式 | 输出尺寸 | HTS | VTS | 帧率上限 |
|---|---|---|---|---|
| 全分辨率 | 3264x2448 | 0x0794 × 2 = 3880 | 0x09aa = 2474 | 15 fps |
| 2×2 binning | 1632x1224 | 0x0788 = 1928 | 0x04dc = 1244 | 30 fps |
用第 1 节的路径校验全分辨率模式:3880 × 2474 × 15 ≈ 143.99e6,与由 link_freq 推出的 144 Mpixel/s 吻合。
【注意】
全分辨率模式的 hts_def 取了寄存器值的 2 倍(0x0794 * 2),而 1632x1224 模式直接使用寄存器值(0x0788)。若两种模式都按 ×2 约定,则都落在 144 Mpixel/s 上,与由 link_freq 推出的 pixel_rate 自洽;按驱动现值计算,binning 模式只有约 72 Mpixel/s,并会影响上报的 HBLANK。做带宽核算时建议以 link_freq 反推的 pixel_rate 为准。
4. 常见配置错误与排查
4.1 参数错配的现象
四类参数里,xvclk 是基准,另外三类都是它的后果;错配的表现随参数与偏离方向而不同:
| 参数 | 偏低 | 偏高 |
|---|---|---|
| xvclk | 内部 PLL 倍频基准变小,MIPI 与像素时钟整体下移,常见花屏、错行 | 同样整体上移;与按 24 MHz 算的寄存器表不匹配时,颜色与时序全乱 |
| link frequency | 带宽不足:丢行、撕裂、DVP 侧 FIFO 溢出,帧率达不到标称 | 超出 sensor PLL 或 SoC D-PHY 能力:D-PHY 锁不住、直接枚举不到 sensor;若只是 sensor 侧写错而上报值不实,表现为周期性错行 |
| pixel_rate | 非独立可配量,由 link_freq × 2 × lanes ÷ 位宽 推出;上报偏低会让上层按错误的像素率算超时,帧率显示与实际不符 | 上报偏高属“账面通过、实际带不动”,表现为丢帧与帧率抖动 |
| 消隐 | HTS / HBLANK 过小:行内复位与读出时间不足,出现周期性噪点、竖条、颜色异常;VTS / VBLANK 过小:帧率标称上升,但曝光上限(VTS − 4)被压低,暗光发黑,再小则 VTS < height 无解 | HTS 过大:帧率被乘积拉低;VTS 过大:帧率低于预期、拖影变长,超过 OV8858_VTS_MAX(0x7fff)会被控件范围直接拒绝 |
这里有个不对称要记住:xvclk 是“基准”,link frequency 与 pixel_rate 都是它的后果;而消隐里 HTS 只读、VTS 可调,现场真正能改的只有 VBLANK 一个。
落到本板的具体错误,常见的有这几种:
- lane 数与寄存器表不匹配:设备树写 4 lane 却使用 2 lane 的寄存器表,出图异常。
- link_freq 低于带宽需求:丢行、丢帧;若高于 D-PHY 与 CSI2RX 的能力,则直接识别不到。
- xvclk 不等于 24 MHz:驱动告警
xvclk mismatched,实际速率整体偏移。 - VTS 调得过小:帧率上去了,曝光上限(
VTS - 4)被压低,暗光发黑,机理见第三节第 3 小节。 - csi2_dphy0 与 dphy1 / dphy2 同时启用:设备树注释已说明 full 与 split 互斥。
4.2 排查路径
(1)先读驱动日志定参数,多数错配到这一步就能现身:
| 日志 | 含义 |
|---|---|
lane_num(2) pixel_rate(144000000) | 实际取到的 lane 数与换算出的像素率 |
ov8858_...: on: 0, 3264x2448@15 | 实际选中的模式与帧率 |
xvclk mismatched, modes are based on 24MHz | 输入时钟不等于 24 MHz,所有参数整体偏移 |
Get mipi lane num failed! | 设备树 data-lanes 读失败,lane 数取不到 |
Get capture_mode failed! | 未指定 capture-mode,回落到 0,即全分辨率 |
(2)量上报值,再对手算值。用 v4l2-ctl --list-ctrls 读出 LINK_FREQ、PIXEL_RATE、HBLANK(应恰为 hts_def − width)、VBLANK(范围上限应为 0x7fff − height),与第二节的公式逐项对照;对不上就说明计数口径或所选寄存器表有问题。
(3)确认链路是否真的搭起来。media-ctl -p -d /dev/media0 看 pipeline 是否连通,v4l2-ctl --list-formats-ext 能否列出目标模式。
(4)按现象归类收窄:
| 现象 | 优先怀疑 | 验证方式 |
|---|---|---|
| 完全不出图 / 枚举不到 sensor | lane 数与走线不符、link_freq 超出 D-PHY 能力、csi2_dphy0 与 dphy1 / dphy2 同时启用 | 比对 lane_num 日志与设备树 data-lanes、clock-lanes 映射 |
| 出图但错行、花屏、颜色异常 | link_freq 或位宽口径错、data-lanes 与寄存器表不匹配 | 核对 PLL 系数所在寄存器表与 MIPI_FREQ 常量 |
| 帧率不对 | VTS 或 pixel_rate | 算 pixel_rate ÷ (HTS × VTS),与 --list-formats-ext 的标称值和实测对比 |
| 暗光发黑、亮度上不去 | VTS 太小,曝光上限被压低 | 看 VBLANK 当前值与曝光控件的上限 |
| 画面有周期性噪点、条纹 | HBLANK 不足 | 比对只读 HBLANK 与 setting 里的 hts_def − width |
(5)双向验算闭合。正推(参数 → 帧率)与反推(帧率 → 参数)必须同时成立,只算单边是配置出错的主要来源,见第五节第 1 小节第 3、6 步。
(6)最后看 SoC 侧硬约束。D-PHY 的 lane 数与单 lane 速率上限以 TRM 为准(第四节第 3 小节);本板 720 Mbps/lane 余量充足,瓶颈通常不在 D-PHY,而在设备树映射与寄存器表的选择。
前三步都是只读操作、不动代码,多数错配到第二步就能看出来。
本文档由 markdowncli 技能辅助生成