这是一个非常经典且核心的图像传感器(CIS)问题。无论是在古老的 CRT 电视机,还是现代的 CMOS 传感器(如你研究的 IMX415)中,消隐(Blanking) 都是必不可少的。
简单来说,消隐就是**"不传输图像数据的空隙时间"**。
我们可以从 历史原因 、同步机制 、曝光控制 和 硬件处理 四个维度来理解。
1. 历史原因:从 CRT 电子束说起(直观理解)
在早期的 CRT(阴极射线管)显示器中,画面是由一束电子束在荧光屏上"扫描"出来的。
- 水平消隐 (H-Blank) :电子束从左向右扫完一行后,必须快速折返到下一行的起点。在折返的过程中,如果电子束继续发光,屏幕上就会出现一条斜线。因此,必须在折返时"关掉"电子束,这段时间就是水平消隐。
- 垂直消隐 (V-Blank) :当电子束扫完整个屏幕的最底行后,必须快速折返到屏幕的最顶端。这个大折返的时间就是垂直消隐。
虽然现代 CMOS 传感器没有电子束,但这种"扫描"的逻辑被保留在了时序协议中。
2. 同步机制:图像的"标点符号"(协议维度)
对于接收端(比如 Rockchip 的 ISP 或 CPU)来说,它收到的是一串连续的电信号。如果没有消隐,它怎么知道哪里是一行的结束?哪里是一帧的结束?
- H-Blank ≈\approx≈ 逗号:它告诉接收端:"这一行像素传完了,请准备接收下一行。"
- V-Blank ≈\approx≈ 句号:它告诉接收端:"这一帧图像传完了,请准备接收下一帧。"
通过监测 HSYNC(行同步信号) 和 VSYNC(场同步信号),ISP 才能正确地将数据流还原成一张图片,而不是一团乱码。
3. 曝光控制:调节快门的"秘密武器"(驱动维度)
这一点对你写驱动至关重要! 在 CMOS 传感器中,曝光时间与消隐时间有着直接的关系。
- 曝光时间 ≤\le≤ 垂直总行数 (VTS)
- VTS=有效图像高度 (Height)+垂直消隐 (V-Blank)\text{VTS} = \text{有效图像高度 (Height)} + \text{垂直消隐 (V-Blank)}VTS=有效图像高度 (Height)+垂直消隐 (V-Blank)
为什么需要 V-Blank 来控制曝光?
如果你想增加曝光时间(让画面更亮),但图像高度(Height)是固定的(比如 1080p),你不能改变高度,那么唯一的办法就是增加 V-Blank 的时间。
- 增加 V-Blank →\rightarrow→ 增加 VTS\text{VTS}VTS →\rightarrow→ 允许更长的曝光时间 →\rightarrow→ 画面变亮。
- 副作用 :增加 V-Blank 会导致整个帧周期变长 →\rightarrow→ 帧率 (FPS) 下降。
这就是为什么在你的代码中,exposure_max 是由 mode->vts_def - 8 决定的。
4. 硬件处理:给 ISP "喘息"的时间(性能维度)
图像数据的传输量极大。传感器在发送数据之前和之后,内部电路需要进行一些准备工作:
- 电荷复位:传感器在采集完一批像素后,需要时间清理电路,准备下一波采样。
- 数据缓存:ISP 接收到数据后,需要时间将其写入内存(DDR)。如果数据像洪水一样永不停歇地涌入,ISP 的缓存(FIFO)会溢出导致丢帧。
消隐期就给了硬件一个**"喘息"**的机会,确保数据传输的稳定性。
总结对比表
| 维度 | 水平消隐 (H-Blank) | 垂直消隐 (V-Blank) |
|---|---|---|
| 直观比喻 | 每一行的"换行符" | 每一帧的"分页符" |
| 主要作用 | 行同步,告知行结束 | 场同步,告知帧结束 |
| 对驱动的影响 | 影响行总长度 (HTS) | 直接影响最大曝光时间和帧率 (FPS) |
| 硬件意义 | 给行扫描电路留出复位时间 | 给整帧图像处理和存储留出时间 |
回到你的代码:
当你看到 vblank_def = mode->vts_def - mode->height; 时,你其实是在计算这个传感器的"呼吸空间"。如果你在调试时发现画面太暗且无法增加曝光,或者帧率不对,通常就要检查这个 VTS\text{VTS}VTS(有效高度 + V-Blank)的设置。
传感器的尺寸是固定的,增加 V-Blank 的时间是怎么实现的
这是一个非常深刻的问题。要理解这一点,首先需要区分两个完全不同的概念:物理尺寸(Physical Size) 和 时序周期(Timing Cycle)。
结论先行: 增加 V-Blank 并不是增加传感器的物理面积,而是在输出图像数据之后,强行插入一段"空闲时间"。
你可以把它类比为:跑道长度(物理尺寸)是固定的,但运动员跑完一圈后,在起跑线休息的时间(V-Blank)是可以延长或缩短的。
1. 物理尺寸 vs. 时序周期
- 物理尺寸(固定) :IMX415 的像素阵列(比如 4056×30404056 \times 30404056×3040 个像素点)是蚀刻在硅片上的,物理位置永远不变。这决定了图像的最高分辨率。
- 时序周期(可变) :图像传感器并不是瞬间拍完一张照,而是一行一行地扫描 输出。
- 有效高度 (Active Height):传感器输出实际像素数据的行数(例如 1080 行)。
- 垂直消隐 (V-Blank) :在输出完最后一行有效像素后,到开始输出下一帧第一行像素之间的等待时间。
2. 增加 V-Blank 是如何实现的?(内部机制)
在传感器内部,有一个**计数器(Counter)**在控制扫描进度。
- 计数开始 :计数器从
0开始增加。 - 输出阶段 :当计数器在
0到Height-1之间时,传感器将像素数据通过 MIPI 接口发送给 ISP。 - 消隐阶段 :当计数器达到
Height时,传感器停止发送像素数据(此时 MIPI 总线进入空闲状态或发送结束标志)。 - 等待阶段 :计数器继续增加,直到达到一个预设的数值 VTS\text{VTS}VTS(Vertical Total Size)。
- 循环重启 :一旦计数器达到 VTS\text{VTS}VTS,它立刻清零,重新开始下一帧的输出。
那么,增加 V-Blank 就是通过修改寄存器,把 VTS\text{VTS}VTS 的值调大:
VTS=有效高度 (Height)+垂直消隐 (V-Blank)\text{VTS} = \text{有效高度 (Height)} + \text{垂直消隐 (V-Blank)}VTS=有效高度 (Height)+垂直消隐 (V-Blank)
- 如果你设置 VTS=1100\text{VTS} = 1100VTS=1100(假设 Height=1080),那么 V-Blank 就是 20 行的时间。
- 如果你设置 VTS=2000\text{VTS} = 2000VTS=2000,有效高度依然是 1080,但 V-Blank 变成了 920 行的时间。
结果: 物理像素没变,但每一帧之间多了一段很长的"空白等待期"。
3. 为什么要这么做?(与曝光的关系)
这就是你代码中 exposure_max = mode->vts_def - 8 的由来。
在 CMOS 传感器中,曝光时间(积分时间) 是在行扫描过程中完成的。如果我想让曝光时间变得非常长(比如在极暗环境下),曝光时间可能会超过一帧的有效高度。
- 如果 VTS\text{VTS}VTS 较小 :曝光时间被限制在 VTS\text{VTS}VTS 之内。如果你想要 1500 行的曝光,但 VTS\text{VTS}VTS 只有 1100,那么你永远无法实现这么长的曝光。
- 如果增加 VTS\text{VTS}VTS(增加 V-Blank) :你把 VTS\text{VTS}VTS 扩展到 2000。现在,你的曝光时间最高可以设为 1992 行(2000−82000-82000−8)。
代价是: VTS\text{VTS}VTS 越大 →\rightarrow→ 每一帧的总耗时越长 →\rightarrow→ 帧率 (FPS) 随之下降。
总结图解
| 状态 | 图像输出 (Active) | →\rightarrow→ | 垂直消隐 (V-Blank) | →\rightarrow→ | 下一帧开始 |
|---|---|---|---|---|---|
| 短 V-Blank | [ 1080 行 ]\text{[ 1080 行 ]}[ 1080 行 ] | →\rightarrow→ | [ 20 行等待 ]\text{[ 20 行等待 ]}[ 20 行等待 ] | →\rightarrow→ | (快速开始下一帧) |
| 长 V-Blank | [ 1080 行 ]\text{[ 1080 行 ]}[ 1080 行 ] | →\rightarrow→ | [ 920 行等待 ]\text{[ 920 行等待 ]}[ 920 行等待 ] | →\rightarrow→ | (缓慢开始下一帧) |
所以,增加 V-Blank 其实就是:在硬件计数器里增加一个"等待值",让传感器在拍完一张照后,多休息一会儿再拍下一张。
水平消隐怎么实现的
如果说 V-Blank(垂直消隐) 是在两张照片之间留出的"大休息",那么 H-Blank(水平消隐) 就是在每一行像素之间留出的"微休息"。
它的实现原理与 V-Blank 非常相似,但作用在行级别。
1. 核心实现机制:水平总长度 (HTS)
在传感器内部,每一行的读取是由一个水平计数器 (Horizontal Counter) 控制的。
- 物理现实 :传感器的像素阵列是固定的(比如宽度 WWW)。
- 逻辑时序 :传感器并不在读完第 WWW 个像素后立刻跳到下一行,而是会等待一段时间,直到计数器达到一个预设的 HTS (Horizontal Total Size,水平总长度)。
实现公式:
HTS (水平总长度)=Width (有效图像宽度)+H-Blank (水平消隐宽度)\text{HTS (水平总长度)} = \text{Width (有效图像宽度)} + \text{H-Blank (水平消隐宽度)}HTS (水平总长度)=Width (有效图像宽度)+H-Blank (水平消隐宽度)
具体执行流程:
- 有效输出阶段 :传感器从第 1 个像素读到第 WWW 个像素,并通过 MIPI 接口发送出去。
- 进入消隐阶段 :当计数器达到 WWW 时,传感器停止发送像素数据。
- 等待时间:计数器继续增加,但此时 MIPI 总线上没有图像数据(可能会发送一些同步包或进入低功耗状态)。
- 行跳转 :当计数器增加到 HTS\text{HTS}HTS 时,传感器触发一个**"行结束"**信号,并将读取指针跳到下一行的起始位置。
- 重启循环 :计数器清零,开始输出下一行的前 WWW 个像素。
2. 为什么需要 H-Blank?(它在干什么?)
你可能会问:既然为了速度,为什么不直接跳到下一行,非要浪费时间在 H-Blank 上?
A. 模拟电路的"复位"时间 (Reset & Pre-charge)
传感器读取像素是通过电荷转移实现的。读完一行后,行驱动电路(Row Driver)和模数转换器(ADC)需要时间进行电荷复位 和预充电。如果没有任何间隔,下一行的电荷可能会受到上一行残余电荷的影响,导致图像出现**条纹(Banding)**或噪声。
B. 接收端 (ISP) 的处理时间
接收端(如 Rockchip 的 ISP)在接收每一行数据时,需要进行行缓冲(Line Buffer)操作。H-Blank 给 ISP 留出了处理当前行数据、准备接收下一行数据的缓冲时间。
C. MIPI 协议的开销
MIPI CSI-2 协议在每一行数据的开头和结尾都需要发送特定的控制包(Packet),例如:
- SoL (Start of Line):告知 ISP "我要开始传这一行了"。
- EoL (End of Line) :告知 ISP "这一行传完了"。
这些控制包的发送和解析需要时间,这部分时间就包含在 H-Blank 中。
3. H-Blank vs V-Blank 的对比总结
为了让你彻底理清,我把两者放在一起对比:
| 维度 | 水平消隐 (H-Blank) | 垂直消隐 (V-Blank) |
|---|---|---|
| 发生位置 | 每一行像素之间 →\rightarrow→ 每行一次 | 每一帧图像之间 →\rightarrow→ 每帧一次 |
| 控制参数 | HTS=Width+H-Blank\text{HTS} = \text{Width} + \text{H-Blank}HTS=Width+H-Blank | VTS=Height+V-Blank\text{VTS} = \text{Height} + \text{V-Blank}VTS=Height+V-Blank |
| 实现手段 | 修改水平计数器的上限 | 修改垂直计数器的上限 |
| 主要目的 | 行电路复位、行同步、MIPI 行包 | 帧同步、控制最大曝光时间、控制帧率 |
| 对驱动影响 | 通常是固定的(只读),改动会导致同步失败 | 经常被修改,用以调节亮度 ↔\leftrightarrow↔ 帧率 |
| 类比 | 打字机每行写完后的 "回车换行" | 写完一整页后的 "翻页" |
回到你的代码:
c
h_blank = mode->hts_def - mode->width;
imx415->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
h_blank, h_blank, 1, h_blank);
if (imx415->hblank)
imx415->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
为什么这里设为 READ_ONLY(只读)?
因为 H-Blank 涉及到极其精密的硬件时序(纳秒级),一旦被用户随意修改,ISP 可能会无法识别行同步信号,直接导致图像花屏 或驱动崩溃。而 V-Blank 相对宽松,修改它只会改变帧率,不会导致同步失效,所以 V-Blank 是可写的。