【无标题】

Rockchip DVP 视频采集入门笔记

面向初学者整理。代码基于orangepi 内核源码 kernel-5.10(RK3588 CIF 驱动),示例以 OV5640

读完后你应该能回答:

  1. 摄像头的数据是怎么从 sensor 走到 /dev/video0 的?
  2. BT.601 / BT.656 / BT.1120 到底差在哪?
  3. 那张 "EAV/SAV、3/2/1/0" 的时序图在说什么?

0. 一句话理解

传感器(sensor)负责把光变成数字像素,并按某种"约定"把像素+同步信息从一排并行线上吐出来;SoC 的 CIF 负责按这个约定去采样,把有效像素搬进内存,成为一帧图像。

传感器不写内存、不产生中断,它只负责"按时序输出像素"。


1. 整条链路

复制代码
镜头 → Sensor(光电转换 + 片内 ISP)→ DVP 并口线 → RK3588 CIF → DMA → DDR → /dev/video0
                                            ↑
                                     PCLK / 数据线 / (同步线)
角色 负责 不负责
Sensor 曝光、成像、白平衡、格式转换、按行/场时序输出像素 不分配内存、不做 DMA
CIF(rkcif) 用 PCLK 采样数据、识别行/场边界、裁剪、写 DDR、发中断 不决定曝光、不产生像素
用户态 S_FMT 设格式、REQBUFS 申请缓冲、DQBUF 取图 ---

对应驱动目录:


2. 三种"同步方式"(整个笔记最核心的概念)

视频是一行一行传的,接收端必须知道"这一行从哪开始、这一帧从哪开始"。业界有三种做法:

方式 同步信息在哪 代表 用到的线
外同步 单独的 HSYNC / VSYNC / HREF 引脚 BT.601 并口、RAW 并口 PCLK + 数据 + 2 根同步线
内嵌同步 塞在数据流里的特殊字节(SAV/EAV) BT.656、BT.1120 PCLK + 数据(省掉同步线)
包(Packet) 数据包头部的 data type MIPI CSI-2 差分对

一个类比:

  • 外同步:旁边有个人喊"开始!",你跟着数像素。喊的人迟到一点,你就会数错。
  • 内嵌同步:每行开头都印着"第 N 行开始",你只需要找到这行字,永远不数错。
  • 包:像快递,每个包裹上贴着面单(格式、尺寸、通道),自己描述自己。

关键结论:BT.656/1120 并没有改变像素内容,只是把同步信息编码进了数据流。
⚠️ 注意:MIPI 不属于 DVP。 上表列出 MIPI 只是为了对比"同步机制",两者是物理层和协议都完全不同的两套接口:

  • DVP = 并行单端总线:PCLK + 8/16 根数据线 +(可选)HREF/VSYNC,1.8V/3.3V 单端信号;
  • MIPI CSI-2 = 串行差分总线:1~4 对 Data Lane + 1 对 Clock Lane,LVDS 差分,数据以"包"为单位,包头自带格式类型和虚拟通道。

rkcif 驱动同时支持这两条输入通路,所以 DVP_FOR 的输入模式枚举里有 INPUT_MODE_MIPI------但它只表示"DVP 通路不接,数据从 MIPI 前端进来",不代表 MIPI 是 DVP 的一种模式 。格式表里带 dvp_fmt_val 的才是 DVP,只有 csi_fmt_val 的(如 RGB888/RGB565)走 MIPI。


3. 读懂 "EAV/SAV" 时序图

3.1 一行数据的结构

复制代码
1 digital line = EAV | 行消隐 (Digital line blanking) | SAV | 有效像素 (Active video)
                  ↑ H=1                                ↑ H=0

SAV(Start of Active Video)和 EAV(End of Active Video)都是 4 个字节:

复制代码
FF  00  00  XY

XY 字节的位定义:

位 含义
bit7 固定为 1(用来识别这是同步码)
bit6 F:场标志,0 = 第 1 场,1 = 第 2 场
bit5 V:场消隐标志,0 = 有效行,1 = 场消隐行
bit4 H:0 = SAV,1 = EAV
bit3:0 保护位(由 F/V/H 计算,用于校验)

提示:FF 00 00 这个前导序列在有效像素里是不允许出现 的。所以规范要求把数据里的 0x00/0xFF 分别裁剪成 0x01/0xFE,OV5640 的注释里也写了这一点(ov5640.c 约 2030 行)。

3.2 图上的数字 0~7 是怎么来的

把 (F, V, H) 当二进制读,正好得到 0~7:

行类型 F V H 值
场 1 有效行 0 0 0 / 1 SAV=0 ,EAV=1
场 1 场消隐行 0 1 0 / 1 SAV=2 ,EAV=3
场 2 有效行 1 0 0 / 1 SAV=4 ,EAV=5
场 2 场消隐行 1 1 0 / 1 SAV=6 ,EAV=7

所以图里那些 3/2/1/0、7/6/5/4 就是 EAV/SAV 的 XY 值,不是像素值。

3.3 隔行(Interlaced)与逐行(Progressive)

复制代码
隔行(图 37-2):1 frame = Field 1 + Field 2
  ┌──────────── 一帧 ────────────┐
  │ Field1 (F=0) │ Field2 (F=1)  │   ← 两场在时间上分开,各占一半行数
  │ 奇/偶行交替   │ 另一组行       │
  └──────────────────────────────┘

逐行(图 37-3):1 frame = 1 field,F 恒为 0
  图像一次传完,没有 F 位翻转

驱动侧为什么特别关心这个:

  • V4L2_FIELD_NONE(逐行)→ 走 TRANSMIT_PROGRESS,一行就是内存里一行;
  • V4L2_FIELD_INTERLACED(隔行)→ 走 TRANSMIT_INTERFACE,并且两场要写进同一个 buffer。

先纠正一个常见误解:1080i 不是"分辨率减半"。1080i 的 1080 行是两场合计,每场 540 行(一场偶数行、一场奇数行),拼起来仍是完整 1080 行;只是运动画面的"瞬时垂直分辨率"约等于 540 线。

3.3.1 代码逐行解析:两场如何"织"成一帧

以下代码在 rkcif_csi_channel_init() 里(capture.c L2717),以 1920×1080i、YUV422(每像素 2 字节) 为例:

变量 初值 含义
channel->width 1920 图像宽度(像素)
channel->height 1080 图像高度(行)
channel->virtual_width ALIGN(width × 每像素字节数, 8) DDR 里一行的跨度(stride),单位字节
c 复制代码
if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) {
    channel->virtual_width *= 2;   /* 3840 → 7680 */
    channel->height /= 2;          /* 1080 → 540  */
}
  • height /= 2 :一个场只有 540 行,写入通道控制寄存器 width | (height << 16)(L2893-2894);

  • virtual_width *= 2 :同一个场相邻两行之间空出一行的位置 留给另一个场。这个值写进 4 个 "virtual line width" 寄存器(L2896-2903),分别对应帧 0/帧 1 的 Y/UV。

    一个场的行跨度变成 7680:
    |<-------------- 7680 字节 -------------->|
    |<-- 本场这一行 3840 字节 -->|<- 空 3840 ->|

buffer 总大小没有变 :540 行 × 7680 = 4,147,200 = 3840 × 1080。跨度翻倍、行数减半,乘积不变。

两个场的 DMA 起始地址(L2032-2040):

c 复制代码
rkcif_write_register(dev, frm1_addr_y,
        next_buf->buff_addr[RKCIF_PLANE_Y] + (channel->virtual_width / 2));
  • frm0(场 1)地址 = buffer 基址 B
  • frm1(场 2)地址 = B + virtual_width/2 = B + 7680/2 = B + 3840

注意 virtual_width / 2 恰好等于一行有效数据的字节数(3840),不是"半帧"。两个场都按 7680 跨度逐行递增:

复制代码
场1:B+0,      B+7680,      B+15360,     B+23040, ...
场2:B+3840,   B+11520,     B+19200,     B+26880, ...

合起来看内存地址:

复制代码
偏移 0      ← 场1 第 0 行   (逻辑第 0 行)
偏移 3840   ← 场2 第 0 行   (逻辑第 1 行)
偏移 7680   ← 场1 第 1 行   (逻辑第 2 行)
偏移 11520  ← 场2 第 1 行   (逻辑第 3 行)
...

场 1 占偶数行、场 2 占奇数行,正好填满彼此空出的位置 ------这就是"按行交织写成一整帧"。所以用户态拿到的已经是完整 1080 行,不需要自己拼;sequence /= 2(L7436-7437)让应用看到的帧率是 25/30fps,V4L2_FIELD_INTERLACED 标记则提示"这是织出来的帧,运动可能有梳状"。

DVP 通道的差异 :上面这段属于 MIPI/CSI 通道。DVP 走另一套寄存器(L4881-4909),stride 直接来自用户态 bytesperline,高度来自 pixm.height(DVP_VIR_LINE_WIDTH / DVP_SET_SIZE);换 buffer 时的第二场偏移在 L2224-2230,同样是 + virtual_width/2。调试 DVP 隔行源(如 NVP6158)时,按整帧大小申请 buffer,再 hexdump 看奇偶行是否来自两个场即可确认。

3.3.2 隔行帧怎么显示

三种主流做法:

方案 做法 优点 缺点
A. Weave(直接当逐行帧) 把已交织的 buffer 直接送 VO/RGA/编码器,按 1080p 输出 零成本,静止画面清晰度最高 运动物体边缘有梳状/锯齿
B. Bob(只取一场) 只取 540 行,纵向放大 2 倍 运动无梳状,最简单 垂直分辨率减半,静态发虚
C. 去隔行(Deinterlace) 用相邻场/帧插值生成逐行帧 清晰度和流畅度兼顾 需要缓存一场 + 额外算力

方案 C 的常见实现:

手段 说明
硬件 RK3588 的 VOP 可直接输出 1080i 隔行时序(由 HDMI 端电视去隔行);RGA 可做简单 weave/bob 或双场混合
GStreamer deinterlace 元素(内部 yadif/bwdif,运动自适应)
FFmpeg -vf yadif / bwdif
自研 场间插值 + 运动检测,静止区域 weave、运动区域插值

两个必须注意的坑:

  1. 场序(Top/Bottom Field First) :先采的场与"奇/偶行"约定搞反,weave 后整帧会上下错一行,表现为轻微上下抖动或细密锯齿。V4L2 用 V4L2_FIELD_INTERLACED_TB / _BT 表示。
  2. 帧率语义 :sequence 已除以 2,上层看到的是 25/30fps(不是 50/60),做时间戳同步时别按场率算。

3.4 多通道复用(图的最后一行)

BT.656 的 SAV/EAV 里还能带 ID 位 ,因此一条并口可以并排传 2 路或 4 路视频(车载 AHD 常见)。CIF 用 MULTI_ID_MODE_1/2/4 分流,见 regs.h L628-638。


4. DVP 的物理连线

复制代码
                 ┌──────────── I2C (SCL/SDA,配寄存器) ────────────┐
                 │                                                │
  ┌──────────┐   │        ┌──────────────────────────────┐        │
  │  Sensor  │◄──┴───────►│ SoC I2C                      │        │
  │          │            └──────────────────────────────┘        │
  │          │── XCLK/MCLK ◄────── cif_clkout(可选,SoC 供 24MHz)│
  │          │── PCLK ───────────► cif_clkin   (采样时钟)        │
  │          │── D[7:0] ──────────► cif_d0~d7  (BT656 只用这 8 根)│
  │          │── D[15:8] ─────────► cif_d8~d15 (BT1120 追加)     │
  │          │── HREF ────────────► cif_href   (601/RAW 用)      │
  │          │── VSYNC ───────────► cif_vsync  (601/RAW 用)      │
  │          │◄─ RESETB/PWDN/电源 ── GPIO/regulator                 │
  └──────────┘                                                     │
信号 方向 作用
XCLK / MCLK SoC → Sensor 传感器 PLL 的参考时钟,典型 24MHz
PCLK Sensor → SoC 像素采样时钟(源同步,SoC 不输出)
D7:0 Sensor → SoC 8 位数据总线
D15:8 Sensor → SoC 高 8 位,BT.1120 用
HREF Sensor → SoC 行有效(这行数据有效)
VSYNC Sensor → SoC 帧/场开始
SCL/SDA 双向 I2C/SCCB 配寄存器

4.1 RK3588 的实际引脚

见 rk3588s-pinctrl.dtsi L131-189:

pin group 引脚 用途
cif_clk GPIO4_B4 cif_clkout(给 sensor 的时钟)
cif_dvp_clk GPIO4_B0 / B2 / B3 cif_clkin / cif_href / cif_vsync
cif_dvp_bus8 GPIO4_A0~A7 D0~D7
cif_dvp_bus16 GPIO3_C4~D3 D8~D15

注意 cif_dvp_bus16 只包含高 8 位,所以 BT.1120 要同时引用 bus8 + bus16。

DTS 写法(参考 NVP6158 的板级文件):

dts 复制代码
pinctrl-0 = <&cif_clk &cif_dvp_clk &cif_dvp_bus8 &cif_dvp_bus16>;
/* endpoint */
bus-width = <16>;

4.2 MIPI CSI-2 的物理连线(对比参考,不属于 DVP)

MIPI 与 DVP 是两套独立接口(见第 2 节的提示)。这里放它的连接与 DTS 配置,只是为了对照理解 rkcif 为什么同时支持两条通路。

复制代码
        Sensor(如 OV13850 / IMX219)                    RK3588 CSI D-PHY
        ┌───────────────┐                              ┌──────────────┐
        │  CLK+ ────────┼──── 100Ω 差分对 ─────────────►│ CSI_CLK+     │
        │  CLK- ────────┼─────────────────────────────►│ CSI_CLK-     │
        │  D0+  ────────┼──── Lane 0 ─────────────────►│ CSI_D0+      │
        │  D0-  ────────┼─────────────────────────────►│ CSI_D0-      │
        │  D1+  ────────┼──── Lane 1 ─────────────────►│ CSI_D1+      │
        │  D1-  ────────┼─────────────────────────────►│ CSI_D1-      │
        │  D2/D3 ...    │  (4-lane 时追加 2 对)        │              │
        │  SCL/SDA ◄───►│──── I2C ────────────────────►│ I2C          │
        │  MCLK ◄───────┼──── 24MHz(模块自带晶振则可不接)           │
        │  RESETB/PWDN ◄┼──── GPIO ───────────────────►│ GPIO         │
        │  AVDD/DVDD/DOVDD/GND                        │              │
        └───────────────┘                              └──────────────┘

高速部分只有两类线:

线 数量 说明
Clock Lane(CLK+/CLK−) 1 对 差分时钟,DDR
Data Lane(D0±...D3±) 1~4 对 可配置 1/2/4 lane
I2C(SCL/SDA) 2 根 低速单端,配寄存器
MCLK 1 根 sensor 主时钟(模组自带晶振时可不接)
RESETB / PWDN 各 1 根 复位、掉电
电源 2~4 路 AVDD / DOVDD(1.8V) / DVDD + GND

2-lane 只需 6 根高速线(时钟 2 + 数据 4),16 位 DVP 却要 17 根(PCLK + 16 数据 + 同步),这是 MIPI 的核心优势。

  • 完整链路是 sensor → csi2_dphy0 → mipi2_csi2 → rkcif_mipi_lvds2 →(可选)rkisp。

完整源码见 rk3588-orangepi-5-plus-camera1.dtsi L7-155。

驱动里的对应关系:

DTS / 概念 驱动里
data-lanes V4L2_MBUS_CSI2_DPHY + lane 数解析
link-frequencies link_freq 控制项,sensor 按 lane 速率算 pixel_rate(OV13850 驱动内部固定 300MHz,DTS 可不写)
像素格式 MEDIA_BUS_FMT_SBGGR10_1X10、UYVY8_2X8 等(1X16 = 每时钟 1 个 16bit 采样)
CSI 包类型 CIF 的 get_data_type() 把 mbus code 映射成 data type(RAW10=0x2B、RGB565=0x22,见 capture.c L601-645)

与 DVP 的直观对比:

DVP MIPI CSI-2
信号 单端并行 差分串行
走线 PCLK + 8/16 数据 + 2 同步 时钟 1 对 + 数据 1~4 对
板级限制 短、难等长、易受干扰 可较长、抗干扰、FPC 友好
速率上限 约 100~150MHz PCLK 每 lane 1~2.5Gbps,多 lane 叠加
同步 时钟沿 + HREF/VSYNC 或 SAV/EAV 包边界 + 包头类型/虚拟通道

一句话:MIPI 用"更少的线、更高的单线速率、差分抗干扰"取代 DVP 的"多根并行线 + 显式同步",代价是两端都要有 D-PHY SerDes 和包协议处理。


5. 五种数据格式

5.1 BT.601:外同步的 YUV422

数据流(8bit,UYVY 顺序):

复制代码
PCLK   _/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_
HREF   ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\_________
D[7:0] <U0><Y0><V0><Y1><U2><Y2><V2><Y3>   0x10 0x80 ...
       └── 像素0 ──┘└── 像素1 ──┘         └── 行消隐 ──┘
  • 每 2 个像素 4 个字节(色度共享)→ 16 bit/像素;
  • 8bit 总线:2 个 PCLK 传一组 U Y V Y;16bit 总线:1 个 PCLK 传 1 像素;
  • 消隐期间线上市固定电平(8bit:Y=0x10,C=0x80);
  • 没有 SAV/EAV,同步全靠 HREF/VSYNC。

代码对应:

c 复制代码
{
    .mbus_code   = MEDIA_BUS_FMT_UYVY8_2X8,
    .dvp_fmt_val = INPUT_MODE_YUV | YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY,
    .fmt_type    = CIF_FMT_TYPE_YUV,
}

5.2 BT.656 / BT.1120:内嵌同步的 YUV422

BT.1120 传的同样是 YCbCr 4:2:2(YUV422),只是接口形式和位宽与 BT.656 不同。

复制代码
BT.1120(16 位,4:2:2)------ 一个 PCLK 传一个像素
PCLK   _/‾\_/‾\_/‾\_
Y[7:0] <--- Y0 ---><--- Y1 ---><--- Y2 --->
C[7:0] <--- Cb0 ---><--- Cr0 ---><--- Cb2 --->
                    └─ 两个像素共享一组 Cb/Cr ─┘

对比 BT.656(8 位,Y/C 复用在同 8 根线上,2 个 PCLK 才凑一对像素):

复制代码
D[7:0] <U0><Y0><V0><Y1>   ← UYVY,2 个 PCLK 传 2 个像素

两者的像素语义完全一样,差别只在"包装":

BT.656 BT.1120
数据线 8 位(Y/C 复用) 16 位(Y、C 分开)或 20 位(10bit 分量)
像素率 PCLK ≈ 2× 像素率 PCLK ≈ 1× 像素率(可双边沿再翻倍)
典型分辨率 SD 720×576 / 720×480 HD 1080i / 1080p
同步 EAV/SAV 内嵌 EAV/SAV 内嵌(同样 FF 00 00 XY)
色度 4:2:2 4:2:2(另有 4:4:4 双链路扩展,工程上少见)

注意:BT.1120 标准名义上是 10 bit 分量 (20 位总线),但绝大多数 SoC 实现用 8 bit 截断的 16 位总线,Rockchip CIF 属于后者。

代码对应:

  • 输入模式宏:INPUT_BT1120_YUV422,名字就写明了 YUV422,见 regs.h L652
  • 选择逻辑:capture.c L4285-4298(双边沿 + 指定 field → 走 BT1120)
  • YC 交换位:BT1120_YC_SWAP(regs.h L627 / L648),用于交换高/低 8 位中的 Y 和 C,见 capture.c L4830
  • 格式仍归为 YUV 类:fmt_type = CIF_FMT_TYPE_YUV,写内存时按 YUV422 的 bpp 计算,不做 RGB 转换
  • 发送端实例:RK628 的 BT.1120 发送端固定用 MEDIA_BUS_FMT_UYVY8_2X8(rk628_bt1120_v4l2.c L1243、L1912),2X8 即"每时钟 2 个 8bit 采样"(Y 和 C 各 8 位),正好对应 16 位 BT.1120
  • 该驱动还有 SW_YUV2VYU_SWP 寄存器(L655-662)用来调整 UYVY ↔ VYUY 顺序

三个容易混淆的点:

  1. BT.1120 不是 RGB 接口。RGB 在 Rockchip 上走 MIPI(CSI_WRDDR_TYPE_RGB888),RAW 走 INPUT_MODE_RAW。
  2. 16 位线不代表 16bit 色深,而是 Y 8bit + C 8bit 各占一半,色度仍是每两个像素共享一组,属于 4:2:2。
  3. BT.1120 的 EAV/SAV 内容与 BT.656 一致,只是 Y 和 C 同时送出,所以硬件解析更快、PCLK 更低。

一句话:BT.656 和 BT.1120 是同一套 YUV422 数据的两种包装------656 用 8 位线分时传 Y/C,1120 用 16 位线同时传 Y/C;同步码格式相同。

5.3 RAW Bayer 8 / 10 / 12

RAW 数据里没有任何同步码,所以必须用 HREF + VSYNC。

c 复制代码
{ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,   .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8,  },
{ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10, },
{ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12, },
c 复制代码
cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
             V4L2_MBUS_HSYNC_ACTIVE_HIGH |
             V4L2_MBUS_VSYNC_ACTIVE_HIGH |
             V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
cfg->type  = V4L2_MBUS_PARALLEL;   /* 不是 BT656 */
  • OV5640 也支持 RAW8(L2343-2362),但它只支持 8 位。

5.4 RGB565 / RGB888

  • sensor 端有 :OV5640 支持 RGB565_2X8_LE/BE(L2327-2336),MT9M111 也支持(L560-590)。
  • RK3588 的 CIF 并口没有 RGB 通路 :格式表里 RGB 条目只有 csi_fmt_val,没有 dvp_fmt_val(capture.c L475-482)。
  • 所以在本平台上:接 DVP 摄像头时不要选 RGB565,用 YUV422(BT.601/BT.656)或 RAW。RGB 只能从 MIPI CSI 进来。

6. 关键寄存器 DVP_FOR

DVP_FOR 是 "DVP FOR mat" 寄存器:一个寄存器描述"这一路输入长什么样、怎么解析" 。驱动在 capture.c L4854-4879 把各个位拼好后整体写入。

老款 CIF 的位定义(regs.h L586-642):

位 字段 含义
bit0 VSYNC 极性 VSY_HIGH/LOW_ACTIVE
bit1 HSYNC 极性 HSY_HIGH/LOW_ACTIVE
bit3:2 输入模式 BT601-YUV / BT656 / BT1120 / RAW / JPEG / MIPI
bit6:5 YUV 字节顺序 UYVY / YVYU / VYUY / YUYV
bit7 YUV 422 / 420 YUV_INPUT_422/420
bit12:11 RAW 位宽 8 / 10 / 12
bit13 同步码检测 BT656_DETECT_SAV / ..._SAV_EAV
bit24 BT1120 单/双边沿 BT1120_CLOCK_SINGLE/DOUBLE_EDGES
bit25 逐行 / 隔行 TRANSMIT_PROGRESS / INTERFACE
bit26 YC 交换 BT1120_YC_SWAP
bit28~30 多通道 ID 1/2/4 路、LSB/MSB

RK3588 版本的编码不同(见 regs.h L644-653):输入模式改为 INPUT_BT601_YUV422 / BT656 / BT1120 / SONY_RAW,另有 bit9(逐行/隔行)、bit11(双边沿)、bit12(YC swap)、bit13(SAV 检测)、bit21(高对齐)。

记住: BT.656/1120 时序图里的所有"设置",最终都落在这个寄存器里。


7. 分辨率与时序参数:hact / htot / vact / vtot

以 OV5640 的 VGA 模式为例(ov5640.c L553-583):

c 复制代码
{OV5640_MODE_VGA_640_480, SUBSAMPLING,
 640, 1896, 480, 1080,          /* hact, htot, vact, vtot */
 ov5640_setting_VGA_640_480, ...}
参数 值 含义
hact 640 每行有效像素(画面宽度)
htot 1896 每行总像素时钟数(含行消隐)
vact 480 每帧有效行数(画面高度)
vtot 1080 每帧总行数(含场消隐)
复制代码
一行:|◄──────────────── 1896 PCLK ────────────────►|
      |◄─前肩─►|◄──── hact 640 ────►|◄── 消隐 1256 ──►|

一帧:|◄──────────────── 1080 行 ──────────────────►|
      |◄── 场消隐 600 行 ──►|◄──── vact 480 行 ────►|

这 4 个数最终写到 4 个 16 位寄存器(ov5640.c L1094-1106):TIMING_DVPHO / DVPVO / HTS / VTS。

7.1 公式

复制代码
帧率 fps = PCLK / (htot × vtot)
行频     = fps × vtot

驱动的等效像素率(ov5640.c L1548-1556):

c 复制代码
rate = vtot * htot * fps;    /* VGA@60 → 1896×1080×60 ≈ 122.86 MHz */

7.2 为什么 htot 远大于 640?

因为 OV5640 的行时间由内部时钟决定,不能压缩到 640 个时钟 。小分辨率模式是"子采样"(SUBSAMPLING):行周期固定,只输出其中一部分像素。这也解释了为什么几乎所有小分辨率模式都是 htot = 1896,只有 vtot 随帧率变化(984 / 1080)------htot 是硬件下限,vtot 是帧率调节旋钮。

7.3 谁来关心这些数?

参数 Sensor CIF
hact / vact ✅ 决定输出窗口 ✅ 写 DVP_SET_SIZE
htot / vtot ✅ 决定 PCLK、帧率 ❌ 不需要
行消隐长度 隐含在 htot 里 ❌ 601 靠 HREF 跳过;656 靠 SAV 间距跳过

CIF 只写有效宽高(capture.c L4906-4912):

c 复制代码
rkcif_write_register(dev, CIF_REG_DVP_VIR_LINE_WIDTH, val);          /* stride */
rkcif_write_register(dev, CIF_REG_DVP_SET_SIZE,
                     width | (height << 16));                         /* 有效宽高 */

7.4 设置流程

分辨率不写在 DTS 里,而是运行时协商:

复制代码
用户态 v4l2-ctl --set-fmt-video=width=1280,height=720,pixelformat=UYVY
   ↓ S_FMT
sensor subdev:ov5640_set_fmt() → 从 mode 表选最接近的模式 → 写寄存器
   ↓ 格式传到 CIF
rkcif:rkcif_set_fmt() → 记录 width/height/field
   ↓ STREAMON
rkcif_stream_start() → 写 DVP_SET_SIZE / DVP_VIR_LINE_WIDTH / DVP_FOR

调试时先看 media-ctl -p 里 sensor pad 和 CIF pad 的格式是否一致。

注意:BT.656 的 SAV/EAV 不携带宽高信息,所以两端必须显式配置成一致;656 只解决"从哪开始",不解决"搬多少"。


8. 常见问题(FAQ)

Q1:为什么 RGB565 在 DVP 下不支持,而 YUV422 支持?

不是 DVP 协议的限制,而是 Rockchip CIF 的 DVP 硬件解析器没有实现 RGB:DVP_FOR 的输入模式枚举里没有 RGB 选项(regs.h L590-598)。MIPI 能支持是因为 CSI-2 的 data type 里直接定义了 RGB565=0x22、RGB888=0x24(capture.c L628-645),接收端只需按包搬运。

另外从工程上看:RGB565 与 YUV422 同为 16bit/像素,接 RGB565 不省带宽,画质还更低(色度只有 5/6 bit),厂商没动力做。

Q2:BT.601 和 BT.656 的本质区别?

BT.601 BT.656
同步 HSYNC/VSYNC 独立引脚 数据流里的 EAV/SAV
消隐 数据线上是 0x10/0x80 数据线上是同步码
像素内容 4:2:2 YUV 完全相同
抗干扰 依赖同步线时序 同步点绝对 + 保护位校验
多路复用 不支持 支持(ID 位)
隔行场信息 靠 VSYNC 靠 XY 的 F 位

Q3:BT.656/1120 的优势只是为了省引脚吗?

省引脚只是附带(只省 2 根)。更重要的是:同步点绝对(不怕消隐长度偏差)、自带保护位、天然支持隔行、支持多路复用、走线少更易等长。代价是两端要有同步码检测状态机、每行多 8 字节开销、数据要裁剪 0x00/0xFF、示波器调试不直观。

Q4:用 BT.601/656 的 sensor 需要设置分辨率吗?

需要,而且两端都要设 。sensor 通过 mode 表设定 hact/vact/htot/vtot 并写寄存器;CIF 通过 DVP_SET_SIZE 设定有效宽高。SAV/EAV 不含宽高,硬件不会自动识别。

Q5:图像出现"斜切"或"上下撕裂"是什么原因?

  • 斜切:CIF 的宽度和 sensor 的有效宽度不一致(多搬/少搬了消隐字节);
  • 撕裂:field 设置不一致(sensor 隔行、CIF 按逐行),或两场写入 buffer 的偏移算错。

Q6:为什么 BT.656 模式下 VSYNC/HREF 不用接?

因为同步信息已经编码进数据流,sensor 在 656 模式下会主动关闭这两根引脚的输出(OV5640:PAD_OUTPUT_ENABLE01 = 0x1f,见 L2087-2088)。


9. 术语表

术语 解释
DVP Digital Video Port,并行单端摄像头接口的统称(PCLK + 数据线 + 可选同步线)
MIPI CSI-2 串行差分摄像头接口(Data/Clock Lane + 包协议),与 DVP 是两套独立接口,不属于 DVP
PCLK Pixel Clock,像素采样时钟
HREF Horizontal Reference,行有效区间
SAV / EAV Start/End of Active Video,内嵌同步码 FF 00 00 XY
F / V / H XY 字节里的场标志 / 场消隐标志 / 行标志
BT.601 数字分量视频采样标准(4:2:2、13.5MHz)
BT.656 8bit 内嵌同步接口,BT.601 数据 + SAV/EAV
BT.1120 16bit 内嵌同步接口,用于高清
hact/htot 有效宽度 / 含消隐的总宽度
vact/vtot 有效行数 / 含消隐的总行数
field 逐行(NONE)或隔行(INTERLACED)
mbus code V4L2 描述总线上像素格式的枚举,如 UYVY8_2X8
subdev 子设备,sensor/桥接芯片在 V4L2 里的抽象

10. 一句话总结

  • BT.656/1120 = 把"同步信号"变成"数据流里的文字",因此接收端更稳、可复用、可隔行。
  • CIF 只做三件事:认同步、按配置的宽高搬像素、写内存。
  • 分辨率不来自同步码,必须两端显式配置成一致。
  • 在本平台上,DVP 只走 YUV422 / RAW / BT656 / BT1120,RGB 走 MIPI。
相关推荐
上海红同电子2 小时前
红同电子线束工厂100问|绝缘护套采购,材质PVC、TPE、PP该如何选型?
数据库·经验分享·职场和发展·产品运营·制造·材质
Go Work3 小时前
npm 卡在 Step 7 七分钟,翻上去四行才看到 E404
经验分享·笔记
小向是个Der3 小时前
P02_你的变量住在哪里_教学文档
经验分享·笔记·stm32·单片机
宝绿特塑业再生科技13 小时前
改性HDPE再生颗粒在户外家具挤出发泡板材中的色差控制与高品质价值重构
经验分享·科技·学习·生活·1024程序员节·户外家具
Regentsoft丽晶软件16 小时前
从 “定时拉取“ 到 “事件驱动“,服装库存实时同步的底层架构怎么设计?
经验分享·php·零售
狮子座的男孩17 小时前
html+css基础:25、3D变换(3D空间与景深/透视点的位置/位移/旋转/缩放/多重变换/背部可见性)
css·经验分享·html·旋转·透视点·缩放/位移·3d变换/空间与景深
狮子座的男孩18 小时前
解决:使用Burp Suite工具实现防重放攻击的过程及遇到的代理等问题
经验分享·web开发·工具·代理·burpsuite·网络开发·防重放攻击
2601_9672127219 小时前
高防护轨道式插座安全结构与工程适配技术说明
大数据·经验分享·安全
速德瑞仪器1 天前
如何精准把控紫外能量,选择合适的UV能量计
经验分享·紫外辐照计