☰
RK3588平台MIPI摄像头驱动开发实战:从OV13855设备树到V4L2链路
2026/10/20 6:57:32 网站建设 项目流程

很多人在香橙派5上跑过官方摄像头demo,一条命令下去画面就出来了,但真要自己从零把一颗MIPI摄像头驱动起来,往往第一步就卡住。我见过不少朋友拿着OV13855模组,把网上设备树抄过来、驱动编译进去,结果i2cdetect一扫全是“--”,查了半天才发现是复位脚电平写反了。这篇文章就以RK3588S平台的香橙派5为载体,围绕OV13855这颗sensor,把MIPI摄像头驱动的底层链路完整拆一遍。标题里写的是“RK3588”,这里先说明一下:香橙派5实际用的是RK3588S,属于同代平台,MIPI和ISP资源与RK3588基本同源,社区习惯上混称RK3588,读起来更顺。文章会覆盖硬件链路、驱动框架、设备树编写、上层验证方法,以及最容易被忽略的复位时序和带宽计算,适合已经会刷内核、但对摄像头驱动还只有“能用”水平的开发者。

1. 为什么点亮一颗OV13855不等于搞懂摄像头驱动

1.1 多半人都会掉进去的“demo陷阱”

官方demo能跑通,最容易被误解的一点是:你以为你已经掌握了摄像头驱动的全部,实际上你只是拿到了一整套已经配好的默认值。设备树是厂家写好的,驱动是内核里编好的,pipeline是脚本自动配置的,你做的事情本质上是“把一个编译好的系统烧进去,然后按了一下快门”。

真正的驱动工作从你换一颗非官方sensor、换一个CSI接口、或者改一块底板才开始。这时候你会突然面对一堆完全没有头绪的错误:I2C无应答、media-ctl列出拓扑却无法link、streamon报错、画面全绿、帧率掉到个位数。每一个问题背后都对应着驱动链路里某一层没配对,而这些层在官方demo里恰恰是不可见的。

我的一位朋友就是典型例子。他拿到一块OV13855模组,照着某厂商内核补丁改设备树,花了两个晚上,最后发现模组上的PWDN引脚是低有效,他把GPIO_ACTIVE_HIGH写成了ACTIVE_LOW。这种错误对官方模组不会发生,因为官方设备树早就帮你调好了。所以本文的存在意义,就是把这些隐层全部摊开。

1.2 三层角色与一条完整链路:sensor、DPHY、CSI Host怎么配合

MIPI CSI摄像头链路可以拆成三个角色,理解这三个角色,后面所有排查都有方向。

Sensor(传感器)是数据源头。OV13855把光信号转成电信号,再通过ADC变成数字Bayer RAW数据,最后按MIPI CSI-2协议把像素数据串行发出去。sensor不负责图像处理,它输出的是RAW10格式,也就是每个像素10bit的Bayer原始数据,必须经过ISP才能变成可看的RGB画面。

DPHY(物理层收发器)负责信号的电平转换和差分传输。MIPI不是简单的并行总线,而是在一对差分线上高速串行传输数据,DPHY就是专门干这个的物理层器件。它在SoC内部,驱动里对应的是phy驱动,负责根据你要跑的速率配置寄存器。

CSI Host(接收端控制器)位于SoC内部,负责按MIPI协议解析从DPHY进来的字节流,把像素按行、按帧重新组装,然后通过DMA写入内存。在RK3588平台上,这个角色由MIPI CSI2 Host承担,但它通常不会直接把数据丢给用户,而是先交给ISP处理。

所以一条完整链路是:OV13855 -> MIPI D-PHY -> CSI Host -> RKISP -> 内存 -> /dev/videoX。用户看到的视频节点往往是ISP的输出,不是sensor直出的节点。这也是很多人用v4l2-ctl打开设备后format怪异的原因——你面对的不是sensor,而是ISP。

1.3 这篇文章到底要讲什么

经过上面铺垫,你可以看到“MIPI摄像头驱动”不是一个单独的文件,而是一套从硬件到软件的协作链条。本文作为系列第一篇,重点解决四个问题:硬件链路怎么摸底、内核驱动骨架里每个模块干什么、设备树怎么写才真正匹配、从启动到出第一帧画面要过哪些关卡。后面几篇再深入ISP 3A、RAW格式处理和多摄同步。

内容会以香橙派5 + OV13855为具体例子,但方法可以平移到其他RK3588/RK3588S板卡和大部分主流sensor上。只要你手里有原理图、sensor datasheet和内核源码,这套排查思路就能直接复用。

2. 香橙派5与RK3588的MIPI链路摸底

2.1 先确认你的板子用的是RK3588还是RK3588S

网上搜“RK3588 香橙派5”会得到两种说法,原因在于香橙派5的核心SoC是RK3588S。RK3588S是RK3588的精简版,主要砍掉了一些对单板电脑不重要的IO接口,但同时保留了完整的MIPI CSI和ISP资源。从摄像头驱动角度,两者基本可以等同对待,但引脚编号、电源域定义、设备树节点名可能不一样,所以拿到板子第一件事就是去官网下原理图PDF,搜“CSI”“MIPI”“CAMERA”三个关键词,确定你的摄像头FPC座到底接在哪个CSI Host、哪个DPHY上。

这个方法看着笨,却是我见过最能节省时间的一步。很多人在网上抄设备树,抄的却是另一个型号板子的节点,I2C总线编号不同导致sensor挂不上,debug半天才发现走错了总线。

建议整理一张硬件清单:

项目确认要点
SoC型号RK3588还是RK3588S,取决于具体板卡
CSI物理接口FPC连接器位置、lane数量、是否有供电控制开关
对应的CSI Host查原理图上FPC座丝印附近的主控引脚编号
I2C总线sensor挂在I2C几号总线,原理图会有标注
可用GPIO复位、掉电、MCLK、中断脚分别连到主控哪个GPIO
供电方案AVDD/DVDD/DOVDD分别由哪个regulator提供,是否可控

2.2 OV13855的传感器参数:驱动里几乎所有数字都来自这里

OV13855是一颗1300万像素级别的Bayer RAW输出sensor,常见于RK平台的摄像头模组。它的关键参数决定了设备树、驱动配置和带宽需求,整理如下:

参数典型值
有效像素4208 x 3120,13MP级别
输出格式RAW10 Bayer,常见BGGR/SGBRG排列
MIPI接口支持1/2/4 lane,D-PHY
数据率上限每lane约1Gbps级别,视工作模式而定(不同驱动实现有差异)
I2C地址常见读写地址0x36/0x37(7bit地址0x1B),以上拉电阻配置为准
供电AVDD 2.8V,DOVDD 1.8V,DVDD 1.2V
外部时钟参考设计通常为24MHz,以模组规格书为准
控制引脚PWDN(掉电)、RESET(复位),电平有效极性见模组规格书

驱动代码里的分辨率表、register list、lane配置,几乎全部来自datasheet里的timing chapter。你不需要把整本datasheet看完,但必须能从模组规格书里找到它支持的最高分辨率和对应帧率,否则后续带宽计算没法做。

2.3 用带宽公式判断lane数:为什么默认配4 lane而不是2 lane

MIPI带宽需求可以这样估算:

带宽需求 = 水平总数 x 垂直总数 x 帧率 x 每像素bit数

注意这里的水平/垂直总数不是分辨率,而是包含blanking的整帧尺寸。以4K30为例粗略算一下:假设水平总数约4400、垂直总数约3150,每像素RAW10是10bit,则带宽约为:

4400 x 3150 x 30 x 10 ≈ 4.15Gbps

如果只用2 lane,每lane要跑到约2.1Gbps,已经逼近常见D-PHY的高速率档位,哪怕勉强跑到也几乎没有裕量。用4 lane的话,每lane只需要约1.04Gbps,落在D-PHY一个相对稳定、兼容性最好的速率区间。

所以BSP和官方模组默认4 lane不是随意选的,是算过带宽后的必然选择。这个计算还直接关系到设备树里link-frequencies那个值,后面第四、六章会反复用到。

3. V4L2 subdev、DPHY与CSI Host:驱动骨架里的三个协作者

3.1 sensor驱动是发端,但不直接出视频节点

很多第一次接触摄像头驱动的朋友会问:OV13855的驱动编译进去后,为什么/dev/videoX没有多一个节点?原因是sensor驱动注册的是V4L2 subdev,不是video device。

在Linux V4L2框架里,摄像头链路被拆成多个“子设备”,每个子设备负责一段功能,最终由链路末端的采集器(比如ISP或CSI Host驱动)导出video节点。sensor subdev通过v4l2_async_register_subdev()注册,等待CSI Host或ISP驱动通过v4l2_async_nf_init()初始化的notifier来绑定。绑定成功后,sensor和host之间建立了一条media link,用户态才能见到完整的pipeline。

sensor驱动的核心回调是s_stream()。打开流时,驱动会干三件事:配置MIPI参数(lane、速率、虚拟通道)、写寄存器让sensor开始输出、通知下游host准备接收。关闭流时反序执行。如果你在调试时用v4l2-ctl--stream-on后再单独关闭某个subdev,会发现链路缺了sensor那一环,这时候视频节点直接报错,这就是很多奇怪错误的来源。

3.2 DPHY和CSI Host:物理层与协议层的分界线

DPHY驱动的职责很纯粹:告诉物理层“用多快的速率跑”。驱动会根据设备树里link-frequencies(比如/bits/ 64 <480000000>)计算分频系数。如果sensor支持多档速率,DPHY驱动甚至会让sensor和CSI Host协商一个双方都能接受的速率。把DPHY驱动想成“变速箱”,sensor是发动机,CSI Host是车轮,三者转速不匹配,车就会抖。

CSI Host驱动的职责则是协议解析和DMA搬运。它从MIPI总线上收字节流,按同步信号恢复出帧边界,然后让DMA把数据写到内存。RK3588上CSI Host会通过media controller把自己的实体和sensor、DPHY、ISP实体串成一条线。一旦某个实体没有link到下一个实体,streamon阶段就会报错“pipeline not configured”之类的问题。

这里要特别提一个常见误区:DPHY不是sensor和CSI Host之间的一堵墙,而是被两者共同管理的物理资源。所以设备树里sensor endpoint只声明>&i2c1 { status = "okay"; ov13855: ov13855@36 { compatible = "ovti,ov13855"; reg = <0x36>; pinctrl-names = "default"; pinctrl-0 = <&ov13855_mclk>; /* MCLK引脚复用 */ clocks = <&cru CLK_REF_MIPI0>; clock-names = "xvclk"; clock-frequency = <24000000>; /* 以模组规格书为准 */ rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; dovdd-supply = <&vcc_1v8>; /* DOVDD 1.8V */ avdd-supply = <&vcc2v8_cam>; /* AVDD 2.8V */ dvdd-supply = <&vcc1v2_cam>; /* DVDD 1.2V */ pwdn-gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_LOW>; /* 极性必须对 */ reset-gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_LOW>; port { ov13855_out: endpoint { remote-endpoint = <&mipidphy0_in>; >&mipi_dcphy0 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&mipi_dcphy0_pins>; };

这段把PHY时钟和引脚复用打开。同时要去看&csi2_host0、&rkcif、&rkisp等节点是否已经status = "okay"。RK3588平台的完整链路经常要同时使能多个节点,删掉任何一个都会导致pipeline不完整。

此外,很多BSP里还要求设置power-domains = <&power RK3588_PD_VI>,保证摄像头相关电源域在休眠唤醒后能正确恢复。这一点极易忽略,但非常重要——不加这个,系统待机后摄像头大概率起不来。

4.3 内核配置与驱动编译落板:别漏了哪些开关

设备树配好后,还要保证驱动代码本身编进了内核。第一步是确认CONFIG_VIDEO_OV13855之类开关打开。不同内核版本里菜单路径略有差异,通常在Device Drivers -> Multimedia support -> Media drivers -> Camera sensor devices下能找到OV13855相关项。如果你用的是厂商BSP,可能还需要打开CONFIG_VIDEO_ROCKCHIP_ISP、CONFIG_VIDEO_ROCKCHIP_CIF、CONFIG_PHY_ROCKCHIP_DPHY_RX这些。

编译时建议用make Image dtbs先编译内核和设备树,确认没有语法错误再烧录。落板后执行dmesg | grep ov13855,能看到类似“ov13855 1-0036: driver probed”才说明匹配成功。如果probe失败,优先检查I2C总线和时钟,不要继续往后配pipeline。

5. 从上电到第一帧画面的完整验证

5.1 先把media pipeline打通:这是比编译更关键的一步

驱动probe成功只是万里长征第一步,真正的坎在media pipeline。RK3588平台上,常见的pipeline实体名称可以通过media-ctl -p看到,不同BSP版本会略有区别。下面是一段典型的配置流程示意(实体名以实际环境为准):

# 查看当前拓扑 media-ctl -p # 清掉所有链接,从干净状态开始 media-ctl -r # 建立sensor到ISP的链路(具体实体名以media-ctl -p实际显示为准) media-ctl -l "'ov13855 1-0036':0 -> 'rkisp-isp0':0 [1]" media-ctl -l "'rkisp-isp0':0 -> 'rkisp_mainpath':0 [1]" # 设置sensor输出格式 media-ctl -f "'ov13855 1-0036':0 [SGBRG10_1X10/2688x1520]"

我见过太多人忽略这条命令,直接跑v4l2-ctl --stream-mmap,结果streamon失败。RK平台的惯例是必须先把media链路显式配好,再操作video设备节点。这条顺序反了,后面全是徒劳。

这里有个小技巧:先media-ctl -r清链接,再逐个建立,出错时能明确知道是哪一环没link上。如果link失败,去核对实体名和pad编号,通常是你写错了总线号或地址。

5.2 用v4l2-ctl抓单帧:先验证链路再说程序

pipeline打通后,先用现成工具抓一帧验证,别急着写程序。命令如下:

# 查看视频节点 v4l2-ctl --list-devices # 设置输出格式并抓取一帧 v4l2-ctl -d /dev/video0 --set-fmt-video=width=2688,height=1520,pixelformat=NV12 \ --stream-mmap --stream-count=1 --stream-to=frame.raw

如果这一帧能出来,说明整个链路从sensor到ISP到内存基本是通的。接下来可以用gst-launch-1.0或者其他预览工具看画面是否正常。这里值得注意的一点是:sensor输出的是10bit RAW,但ISP输出到video节点的通常是NV12、NV16这类YUV格式,所以v4l2-ctl里设置的是NV12而不要试图设置成RAW10。

如果抓帧失败,回到5.1重新核对media链路,再看dmesg里CSI/ISP的错误打印。这一步会非常快地让问题收敛。

5.3 最简V4L2采集代码:自己写一遍才算会

工具验证通过后,建议还是自己写一遍采集代码。V4L2采集的核心流程只有四步:VIDIOC_S_FMT设置格式、VIDIOC_REQBUFS申请buffer、VIDIOC_QBUF/DQBUF轮流投递和取帧、VIDIOC_STREAMON/OFF开关流。下面是最小可用的流程骨架:

int fd = open("/dev/video0", O_RDWR); struct v4l2_format fmt = {0}; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 2688; fmt.fmt.pix.height = 1520; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; ioctl(fd, VIDIOC_S_FMT, &fmt); struct v4l2_requestbuffers req = {0}; req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl(fd, VIDIOC_REQBUFS, &req); /* 逐一mmap、QBUF后 */ int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd, VIDIOC_STREAMON, &type); while (1) { struct v4l2_buffer buf = {0}; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ioctl(fd, VIDIOC_DQBUF, &buf); /* 取出一帧 */ /* 处理 buf.mem + buf.bytesused 里的数据 */ ioctl(fd, VIDIOC_QBUF, &buf); /* 放回buffer */ }

写代码时最容易漏的是VIDIOC_REQBUFS后忘记mmap,或者DQBUF后没有立刻QBUF导致buffer耗尽。另外多buffer时要记录每个index对应的mmap地址,别用固定映射。

如果这段代码跑通了,说明你对V4L2采集层的理解已经过了工具验证的门槛,接下来才能谈驱动里的性能优化和异常处理。

6. 概率性踩坑:完整排查链路

6.1 I2C探测不到设备:按这个顺序一层层测

这是摄像头驱动最常见的启动故障,现象是i2cdetect扫不到地址,或者probe阶段超时。我的排查顺序是固定的,从物理层往协议层走:

  1. 量供电:AVDD/DOVDD/DVDD三路电压是否存在,纹波是否正常。用示波器看比万用表更可靠,因为某些模组上电瞬间需求电流大,电压跌一下就导致I2C不稳定。
  2. 量MCLK:sensor外部时钟是否真的输出。很多板子的MCLK引脚是多功能复用,设备树pinctrl没配对就没有波形。
  3. 检查PWDN和RESET电平:PWDN高有效时,如果被拉高,sensor处于掉电态,I2C不会应答;RESET同理。
  4. 核对I2C总线和地址:设备树里reg和实际模组地址是否一致。很多模组通过SCCB地址选择引脚决定地址,不同模组还不一样。
  5. 检查上拉电阻:I2C速率较高时上拉电阻过大、总线电容大,都会导致数据线翻转慢,出现概率性通信失败。

我遇到最多的情况排序是:PWDN/RESET极性错 > MCLK没使能 > I2C地址错误 > 供电不稳定。所以强烈建议在设备树里把GPIO极性注释清楚,并在上电后先用万用表量过再烧系统。

6.2 花屏、绿屏与错位:问题往往在MIPI协议参数

能出图但画面花,说明链路是通的,问题出在sensor输出的MIPI参数和接收端配置不一致。最常见的几类原因:

  • >

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询