21 Camera驱动开发
Camera驱动开发
Camera基础知识
CMOS Sensor
也常被简称为Sensor,它就是个光电转换器件,把光信号转换成电信号
特点:
- 只是一个裸芯片(die)
- 输出通常是 Raw Bayer 数据(RGGB/BGGR 等排列)
- 不含镜头、ISP、电路板等辅助模块
- 常见规格:分辨率(MP)、尺寸(1/2.3”)、像素尺寸、bit-depth(10/12-bit)
相机模组
将Sensor、镜头、PCB、外壳等封装到一起的摄像头硬件模块,接口可以直接接主控
组成部分:
- CMOS Sensor → 输出 Raw 或 ISP 后的数据
- 镜头系统 → 聚焦光线到传感器
- PCB 电路板 → 控制信号、时钟、I2C/CSI/USB 接口
- ISP(可选)→ 有些模块自带 ISP,输出 NV12 / RGB / MJPEG
- 封装/外壳 → 方便安装、保护
网上各种博客对于相机模组的常见叫法比如IMX215,实际指的是模组的sensor
相机
相机是对相机模组的进一步封装,相机模组一般要搭配主控才能使用,而相机可以理解为:内部主控 + 相机模组,相机一般会自带ISP处理,能够直接向后级主控输出标准格式的像素
常见接口
上面提到相机和相机模组实际上也是有区别的,所以他们在硬件上的数据接口一般也不同
相机模组
MIPI CSI
MIPI CSI(Camera Serial Interface)是移动行业处理器接口联盟(MIPI Alliance)制定的摄像头串行接口标准协议栈,目前许多相机模组都用了该协议栈,它的物理层接口D-PHY通常由2部分组成:
PHY通常负责数据的转换,它一般是个独立的硬件模块,比如以太网里的PHY模块
CSI Transmiter:主机处理器与摄像头模块之间的高速串行接口(传输图像数据)
数据通道(Data Lanes)
数量:1、2 或 4 对差分线(最常见的是 4 Lane,即 4 对差分线)
作用:传输图像数据(像素数据、同步信号等)
命名:通常标记为
D0+/D0-、D1+/D1-、D2+/D2-、D3+/D3-带宽:每对 Lane 的带宽取决于速率(如 1.5 Gbps per Lane),4 Lane 可提供更高吞吐量(适用于高分辨率传感器)
时钟通道(Clock Lane)
数量:1 对差分线(必需)
作用:提供同步时钟信号(与数据 Lane 同步)
命名:通常标记为
CLK+/CLK-
CCI Slave:摄像头控制接口
I2C:用于传感器配置(如设置分辨率、曝光时间等),通常需要:
SCL(I2C 时钟)、SDA(I2C 数据)
GPIO 控制信号(可选):
传感器复位(
RESET)电源使能(
PWDN)
帧同步(
FSYNC)
注意,CSI是个协议栈(类似TCP/IP),并不是像IIC那样的一个具体的接口,它也分为多层,上面提到的他在硬件上有这几种引脚,实际上是由该协议的PHY Layer决定的
实际上MIPI协议栈定义了几种通用物理层接口:D-PHY、C-PHY、M-PHY来传递数据,不管是用来传递摄像头、显示器还是别的什么的数据,物理层面都可以用这几种接口
实例分析:

- MIPI DPHY提供了4 Lane的RX接口,由Sensor提供Clock,并通过四条数据Lane输入图像数据
- DPHY与CSI-2 Host Contrller之间通过PPI(PHY-Protocol Interface)相连,该接口包括了控制, 数据,时钟等多条信号
- CSI-2 Host Contrller通过PPI接口收到数据后进行解析,完成后通过IDI(Image Data Interface)或者 IPI(Image Pixel Interface)输出到SoC的其他模块(VICAP或ISP,rk3568是送至VICAP模块)
- ISP将处理过的图片输出到MP主通道或SP自身通道,SP一般用来预览图片,SP图片的最大分辨率比 MP低
DVP
DVP(Digital Video Port,摄像头数据并口传输协议)
- 一种并口协议,提供8-bit或10-bit并行传输数据线、HSYNC(Horizontal sync)行同步线、VSYNC(Vertical sync)帧同步线和PCLK(Pixel Clock)时钟同步线
相机
- USB
- GigE Vison
- Camera Link
- CXP
疑问:
1.CSI摄像头是通用的吗(引脚数量和排列是一样的吗)
- 不是,各家CSI摄像头的引脚顺序和数量都是它们自己决定的,即使2Lane也可能引出10几个引脚(大部分都接地罢了…)
- 一般CSI摄像头都是和开发板绑定的,因为CSI接口一般用FPC排线,无法像杜邦线那样自己调整顺序。网上卖的CSI摄像头都会说适配xxx开发板,如果是自己设计的开发板要想用这些摄像头的话,CSI接口引脚顺序不能随便决定
ISP
ISP(Image Signal Processor),即图像信号处理器,用于处理图像信号传感器输出的原始图像信号。 它在相机系统中占有核心主导的地位,是构成相机的重要设备
瑞芯微rk3568平台的ISP2.1 处理图像数据的基本流程如下:

一般抓图的顺序:
- 摄像头的初始化(输出格式、分辨率、输出速率)
- 使能摄像头接入主控板卡中的物理通道
- 使能主控板卡中的ISP(图像信号处理模块)、并让ISP知道当前有效接入的摄像头是哪一个(因为可以多个接入,但只能一个有效)
- 告诉ISP输进来的数据如何处理(颜色空间转换、缩放、裁剪、旋转等)、经由那个通道输出到内存/显存(MP主通道、SP自身通道)
- 输出到内存
V4L2驱动框架
v4l2_device
作用:v4l2_device可以理解为一个V4L2设备的顶层抽象容器,它把一个摄像头或者视频采集卡整体抽象出来,是一个逻辑上的“总控”。他会管理底下的多个子设备,比如一个摄像头设备,就会包含sensor、ISP、I2C控制器等多个子设备
特点:
- 驱动里通常先注册
v4l2_device,再往里面挂video_device和v4l2_subdev - 主要负责管理、统一资源(例如注销时统一清理)
1 | struct v4l2_device { |
video_device
作用:将v4l2_device以字符设备的形式暴露给应用层,让应用层能通过IO操作与V4L2设备进行交互
特点:
- 它有个
cdev的成员变量,所以/dev/videoX节点实际由该类产生 - 一个
v4l2_device下面可以有多个video_device节点,例如:/dev/video0→ 原始采集数据/dev/video1→ ISP 输出
1 | // v4l2-dev.h |
v4l2_subdev
作用:表示一个子设备,一般是整个图像pipeline中的独立的一个模块,比如:
- 摄像头 Sensor (OV5640, IMX219)
- ISP外设
- 视频编解码器芯片
- HDMI 接收器
特点:
- 不直接暴露
/dev/videoX节点(一般不会被应用层直接open) - 通过 sub-device API(
v4l2_subdev_ops)和上层驱动交互 - 常常通过 I²C 或 SPI 注册进来(sensor 驱动就是这种)
- 在 Media Controller 框架里,每个 subdev 会变成一个 entity,和别的 entity 组成 pipeline
举例:在 CSI 摄像头中
ov5640驱动注册为一个v4l2_subdev- CSI 控制器驱动(
video_device)会通过 subdev API 来调用 sensor 的设置,比如分辨率、帧率、曝光
1 | struct v4l2_subdev { |
subdev的
probe中一般用v4l2_async_register_subdev将设备异步注册到V4L2的异步管理链表中,因为probe函数的执行顺序难以通过代码确定,为了满足不同设备的依赖关系,所以引入了异步注册的机制:当某个subdev被异步注册时,V4L2会扫描 notifier 的 subdev list,检查依赖是否满足,如果满足则会把该subdev注册给依赖它的设备
v4l2_subdev 就是摄像头模块中的某个具体功能块,可以认为 subdev 是 “插件化模块”,让驱动更易扩展,每个 subdev 提供一套 ops,比如:
core_ops:控制开关、电源管理video_ops:视频流输入输出pad_ops:媒体 pad 链接
1 | struct v4l2_subdev_ops { |
vb2_queue
定义:是 V4L2 框架中视频帧缓冲管理的核心结构体,是videobuf2(VB2)框架的核心数据结构。主要负责视频帧缓冲区(buffer)的分配、队列管理、映射和DMA交互,是连接驱动层与应用层 mmap/read/streaming 操作的关键桥梁,V4L2的各种数据相关的ioctl,底层都会操作vb2_queue
1 | struct vb2_queue { |
3个关键的回调接口
| 接口结构体 | 作用 | 示例 |
|---|---|---|
vb2_ops |
队列级操作(流控制、队列管理) | queue_setup, start_streaming, buf_queue |
vb2_mem_ops |
内存操作(分配、映射、释放) | vb2_dma_contig_memops, vb2_vmalloc_memops |
vb2_buf_ops |
针对单个 buffer 的回调.主要用来实现应用层(v4l2_buf)和驱动层(vb2_buffer)的数据转换 |
通常默认使用框架实现 |
vb2_ops一般每个多媒体设备都要自己实现,而其他2个,一般使用内核实现的

vb2_buffer
定义:保存单个视频缓冲区的信息,应用层通过ioctl申请的v4l2_buffer,在内核中实际上对应vb2_buffer结构体,他们可以互相转换。vb2_queue中维护了一个vb2_buffer数组代表所有缓冲区,并通过2个链表进行管理
1 | struct vb2_buffer { |
vb2_plane
定义:描述 “每一帧视频缓冲区中的一个平面(Plane)” 的结构体,每个 vb2_buffer 都由一个或多个 vb2_plane 组成,它是帧数据的最小单位。在用户空间中,对应v4l2_plane结构体,2者可以相互转换
为什么一帧数据会被分成多个plane
有些像素格式(比如 RGB24)把所有颜色分量存在一个内存块中,这种格式叫单平面格式。而像 YUV420、NV12 等格式则会将亮度(Y)和色度(UV)分开存放,这就需要多平面格式
1 | struct vb2_plane { |
初始化流程
1 | ┌──────────────────────────────┐ |
系统调用流程
1.普通的系统调用(非ioctl)
1 | 应用层调用系统调用 |
2.ioctl
1 | 应用层调用ioctl |
V4L2的ioctl可以分为2类:
INFO_FL_STD:绑定到设备驱动实现的函数,相关的操作每个驱动可能有不同的实现INFO_FL_FUNC:绑定到V4L2核心层实现的函数,相关操作可能需要参数验证、格式转换,把这部分公用的逻辑放到核心层实现
参考链接
Media Controller驱动框架
作用:他是V4L2的pipeline管理层,把多媒体硬件系统抽象成一个可编程的图,来描述硬件之间的拓扑关系。图中的每个节点是一个功能模块(entity),边是模块间的连接(link)
早期的 V4L2 设备(比如 USB 摄像头)结构简单,数据流只有一条直线,所以一个 /dev/videoX 就能完成采集、控制:
1 | Sensor → Video Node (/dev/video0) |
但后来嵌入式 SoC 多媒体系统变复杂了,比如:
1 | Sensor → MIPI-CSI Receiver → ISP → Scaler → Video Node (/dev/video0) |
这些模块可能:
- 属于不同驱动
- 通过不同总线(I2C、CSI、MMDC)通信
- 可以被多个上层设备共享
- 能通过软件动态切换通路(例如多摄像头切换、双路输出)
传统 V4L2 模型(单个 /dev/videoX)已经没法表示这么复杂的“模块互联”关系,所以引入了Media Controller驱动框架
| 结构体 | 描述 |
|---|---|
media_device |
表示整个媒体设备(如一颗 SoC 的摄像头系统) |
media_entity |
表示功能单元(sensor、CSI、ISP、video node 等) |
media_pad |
entity 的输入输出端口 |
media_link |
pad 与 pad 之间的连接关系 |
与V4L2的关系:
| 层次 | 框架 | 作用 |
|---|---|---|
| 上层 | Media Controller | 描述模块拓扑与连接关系 |
| 中层 | V4L2 Core | 管理视频设备与 subdev |
| 底层 | 具体驱动 | 驱动 sensor、ISP、CSI 等模块 |
Jetson Nano平台下的多媒体pipeline拓扑示意图:
media-ctl -p
Device topology
- entity 1: nvcsi--2 (2 pads, 0 link)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev0
pad0: Sink
pad1: Source
- entity 4: nvcsi--1 (2 pads, 2 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev1
pad0: Sink
<- "imx219 8-0010":0 [ENABLED]
pad1: Source
-> "vi-output, imx219 8-0010":0 [ENABLED]
- entity 7: imx219 8-0010 (1 pad, 1 link)
type V4L2 subdev subtype Sensor flags 0
device node name /dev/v4l-subdev2
pad0: Source
[fmt:SRGGB10_1X10/3264x2464 field:none colorspace:srgb]
-> "nvcsi--1":0 [ENABLED]
- entity 9: vi-output, imx219 8-0010 (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video0
pad0: Sink
<- "nvcsi--1":1 [ENABLED]
media_device
定义:它是多媒体设备的全局抽象对象,用于统一管理摄像头、解码器、编码器、ISP 等多模块之间的连接关系(运行时的数据流的管理)
1 | struct media_device { |
media_entity
在 Media Controller 框架中,每一个可以处理或传输媒体数据的模块(如 sensor、CSI、ISP、video node 等)都被抽象为一个 media_entity,可以理解为它是pipeline中的一个节点
1 | struct media_entity { |
每个media_entity都有若干个pad(数据传输接口),pad之间通过link链接
1 | struct media_pad { |
面试问题
1.sensor驱动的作用是什么
- sensor的驱动主要负责通过D-PHY的CCI对sensor的控制,比如硬件上开启sensor的数据输出、对sensor进行一些分辨率的配置什么的,不负责软件上将sensor的数据传输给下游。模组输出的数据的直接传给了CSI控制器的D-PHY,之后会触发它的中断来完成数据的读取
2.数据是如何在不同子设备之间传输的
- 之前有个误区,以为数据是通过media框架描述出的pipeline在不同模块之间传输的,实际上不是的。虽然media框架通过
media_entity、media_pad、media_link等结构体构建了一个有向图,它描述的只是子设备之间的拓扑(连接)关系,用于实现应用层的一些控制的自动传输,比如我要开启数据传输,需要同步启动多个子设备,通过构建pipeline V4L2就知道该如何控制了 - 数据的传输主要通过DDR,开启数据流后,RAW数据首先被CSI控制器读取,处理后就直接放到DDR,下游硬件直接从DDR读取
3.v4l2_subdev和media_entity的区别是什么
v4l2_subdev主要用于封装对子设备逻辑管理、回调调用,主设备只知道有哪些subdev但不知道他们之间的拓扑关系,比如主机只能知道有ISP、VI、sensor这几个硬件,但是不知道数据应该从sensor输出,经过ISP再到VImedia_entity主要靠media_pad和media_link描述子设备之间的拓扑关系
4.如何调试
- 拓扑关系:使用
media-ctl查看 - 查看V4L2设备的具体能力:使用
v4l2-ctl查看
