简介:面向瑞芯微RK3288平台与Android系统的OV13850摄像头MIPI驱动源码包,主要服务于嵌入式驱动开发、相机模组移植及图像传感器调试人员,可用于解决OV13850在Linux/Android上的驱动加载和图像采集问题。压缩包内共5个文件,包含2个C源码、1个mk、1个头文件及1个xml,整体仅104KB,结构精简且层次分明。其中OV13850_MIPI.c实现核心MIPI驱动逻辑,OV13850_tables.c提供sensor寄存器配置表,Android.mk负责驱动模块编译,头文件定义私有接口,xml则用于校准信息配置。目前已吸引655人学习浏览,适合希望快速上手RK3288摄像头驱动开发的读者。通过该源码包,可直观了解OV13850驱动的文件组织方式、初始化流程和寄存器配置思路,为二次移植或调试其他MIPI摄像头提供有效参考。
1. OV13850 驱动包到底是什么:一个 13MP sensor 的 Linux 驱动不是编译完就能用的
拿到一个名为 OV13850.tar.gz 的驱动包时,大多数人的第一反应是解压、make、insmod,然后打开 /dev/video0 就能看到画面。但在嵌入式 Linux 的 camera 开发里,这个流程最多只走通了三分之一。OV13850 是一颗 13MP 的 RAW 输出 sensor,它通过 MIPI CSI-2 接口把 Bayer 数据交给 SoC 的 ISP,Linux 侧对应的驱动是典型的 V4L2 subdev 驱动。
这颗 sensor 的驱动代码量不大,真正决定能不能出图的,反而不是 C 代码本身,而是设备树里怎么挂节点、MCLK 给多少频率、三路供电时序对不对、MIPI lane 数跟接收端是否一致。这篇文章就围绕这个 tar.gz 展开:先讲驱动结构,再给一条从解包到实拍验证的最小路径,最后把移植时最容易翻车的黑屏、花屏、偏色、休眠唤醒问题单独拉出来讲。适合手里正好有这颗 sensor、正在被黑屏或花屏困扰的开发者。
2. 从 V4L2 subdev 到寄存器表:OV13850 驱动的分层结构与关键机制
2.1 subdev 注册与 media pipeline:驱动在内核里的挂载位置
OV13850 驱动在内核里不是独立的字符设备,它注册成一个 i2c client 驱动,同时实现 v4l2_subdev 的 ops。这意味着这颗 sensor 不直接对用户态暴露 /dev/videoX,它只是 media pipeline 里的一个源节点。真正的视频节点由接收 MIPI 数据的 SoC 侧 controller 提供,sensor 通过 subdev ops 把自己的格式、帧率、曝光、增益暴露给上层。
这个设计的实际价值是解耦。如果把 sensor 驱动写成独立字符设备,每次换 SoC 都要重写用户态接口,代码基本无法复用;走 V4L2 subdev 后,sensor 只需要负责“被 i2c 控制、输出 MIPI 数据”,至于数据进了哪个 ISP、以什么格式交给应用,由 media controller 在运行时把链路串起来。我一般会在拿到驱动包后先搜 v4l2_subdev 结构体,确认它实现了哪些 ops。
s_power 处理电源域开关,set_fmt 上报分辨率与 mbus_code,s_stream 启动或停止输出,s_frame_interval 设置帧率,set_ctrl 处理曝光和增益。其中容易被忽略的是 s_power 和运行时 PM。很多供应商的驱动只写了 set_fmt 和 s_stream,电源管理完全依赖设备树里供电常开,这种驱动在调试时看不出问题,一进休眠再唤醒就黑屏,后面的避坑章节专门讲这个。
驱动里还有一个容易被忽略的部分是 subdev pad。OV13850 输出 RAW10,通常只有一个 source pad,但有些供应商的驱动会额外注册 internal pad,用来挂载 Bayer 重排或测试图案生成。你在 media-ctl 里看到 sensor 节点有几个 pad,决定了下游 pipeline 怎么连。如果驱动内部 pad 数量和应用层预期不一致,media-ctl 建链时会报 no link found。
2.2 寄存器初始化序列:预览、拍照、录像三套配置怎么组织
OV13850 驱动源码里体量最大的部分不是逻辑代码,而是寄存器表。这颗 sensor 的输出分辨率可以跳到 4208x3120、2104x1560、1280x960 等多档,每档对应一组分辨率相关的 timing 寄存器;系统又要区分 preview、capture、video 三种模式,所以常见做法是把寄存器表组织成三层。
第一层是公共初始化序列,包括软件复位、时钟分频、MIPI 输出使能、Bayer 顺序选择。这套序列只要上电后写一次,和当前用哪档分辨率无关。第二层是分辨率相关参数,包括曝光行数、输出尺寸、blanking、PLL 分频比,切换分辨率时必须整套更新,不能只改长宽。第三层是模式相关 tweak,比如 preview 可能关闭了某些噪声处理,capture 则打开多帧合成,这部分往往以数组形式放在某个 mode_info 结构体里。
OV 系 sensor 的寄存器风格比较统一。0x0100 是 stream on/off 开关,0x0103 是软件复位,曝光一般从 0x3500 开始连续三个字节,增益在 0x3503 到 0x3504。这类寄存器是芯片内部固件在解析,不是 Linux 驱动自己定义的,所以写错地址不会报错,只会表现为参数不生效。调试时先找 0x0100 是在哪个函数里被写 0 和写 1,就能定位到 s_stream 的开关逻辑。
切模式时最容易翻车的是寄存器数组之间的依赖关系。有些供应商的驱动只给了每个模式的完整序列,没有给增量,你在 capture 序列里看到一条写 0x0100 的指令,它可能要求在 streaming 关闭状态下执行。我始终坚持一条原则:先 s_stream(0) 停流,再写目标模式序列,最后 s_stream(1) 启流。不要在 stream 开启状态下直接切寄存器,否则出来的第一帧经常是撕裂的,而且这种撕裂用肉眼很难判断是 sensor 问题还是 ISP 问题。
2.3 电源时序与 MCLK:为什么 sensor 不出图先查这四路
OV13850 需要三路供电加一路主时钟。我常用的配置是 AVDD 2.8V、DOVDD 1.8V、DVDD 1.2V,MCLK 24MHz。这四路不是随便拉起来就行,上电顺序有硬性要求,一般按 DOVDD 先上、AVDD 跟上、DVDD 最后,MCLK 在电源稳定后提供,reset 引脚全程保持低电平,等三路电和时钟都稳定后再拉高释放。
很多平台把这四路交给了 PMIC 的固定 rail,驱动里通过 regulator 接口获取。但模组厂可能已经把 DOVDD 和 AVDD 在某块板上短接,这种情况下你按顺序控制反而会引入压差。我建议拿到模组先看原理图,确认是不是共点供电。如果是,就把 regulator 节点共用一个 rail,不要让驱动分别开关,否则先关一路再关另一路的瞬间可能出现反向偏压,严重时直接损坏 sensor。
| 参数 | 典型值 | 说明 |
|---|---|---|
| AVDD | 2.8V | 模拟电源,电流需求较大,电压跌落会导致黑屏 |
| DOVDD | 1.8V | IO 电源,i2c 上拉通常由这一路提供 |
| DVDD | 1.2V | 数字内核电源,对纹波敏感 |
| MCLK | 24MHz | 输入主时钟,来自 SoC 的 camera 时钟输出 |
| RESET | 低电平有效 | 上电期间保持低,稳定后拉高 |
| PWDN | 高电平有效 | 拉高进入 power down,正常工作时拉低 |
时序这一块用软件很难精确测量,我的排查手段是示波器挂 reset 和 DOVDD,对比上电到 reset 释放的延迟。驱动里一般会有 msleep(20) 之类等待,如果模组上电慢,20ms 不够就表现为“冷启动黑屏、热复位正常”。此时把上电到 reset 释放的延时提到 50ms 以上,基本能解决。MCLK 频率则直接用示波器看周期,24MHz 对应约 41.7ns 一个周期,差太多就要查 SoC 侧时钟树配置。
3. 拿到 OV13850.tar.gz 后如何落地:解包、改设备树、编译与加载
3.1 解包与目录结构识别:先分清驱动源码、固件和文档
我拿到任何驱动包的第一条命令永远是列清单而不是直接解压:
tar -tzf OV13850.tar.gz这条命令只打印包内文件路径,不落地任何文件,适合先看清包里有没有夹带其他内容,也能判断驱动目录层级。常见包里会有 ov13850.c、ov13850.h、一个 dts 片段说明、一个 README,偶尔还有 sensor 内部固件 bin 文件,后者通常需要单独烧录到模组的 OTP,跟内核驱动无关。
| 文件类型 | 用途 | 是否需要改动 |
|---|---|---|
| ov13850.c | 驱动主源码 | 调试寄存器序列时需要 |
| ov13850.h | 寄存器地址宏定义 | 一般不改 |
| README.txt | 模组手册摘要 | 只读,确认地址和电源 |
| ov13850.dtsi | 设备树模板 | 按实际平台适配 |
| sensor_firmware.bin | 厂商固件 | 烧录到 OTP,与内核无关 |
确认清单后执行解压:
tar -xzf OV13850.tar.gz解压后先看 README。供应商给的 README 里最有用的是三行信息:i2c 地址、MCLK 频率、支持的 RAW 格式。这三个参数直接决定设备树怎么写、MIPI 接收端怎么配。如果 README 缺失,就从 ov13850.c 里搜 reg 的赋值、搜 mclk 的 clk_set_rate、搜 mbus_code 的枚举值。
这一步的产出是“我确认了这颗 sensor 以什么身份挂在系统里”。我见过有人在没确认 i2c 地址的情况下直接改 dts,白费了半天。地址这个东西不是固定的,同一型号在不同模组厂手里可能是 0x36 也可能是 0x20,取决于 ID 引脚绑定的电平,必须以实际模组为准。dts 写错地址后 i2c 通信不会报错,只是设备一直 probe 失败。
3.2 设备树节点配置:地址、时钟、电源、reset 引脚一一对应
设备树是驱动和硬件之间的契约。OV13850 的典型节点长这样:
&i2c3 { status = "okay"; ov13850: sensor@36 { compatible = "ov13850"; reg = <0x36>; pinctrl-names = "default"; pinctrl-0 = <&ov13850_mclk_pins>; reset-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; clocks = <&clk_mipi_csi_out>; clock-frequency = <24000000>; avdd-supply = <®_2v8>; dovdd-supply = <®_1v8>; dvdd-supply = <®_1v2>; }; };compatible 必须和驱动源码里的 of_match_table 完全一致,这里写通用的 sensor 名,实际以源码为准。reg 是 i2c 地址,pinctrl 是 MCLK 引脚的复用状态,clock-frequency 决定驱动里 clk_set_rate 的目标值。注意 MCLK 不是越高越好,OV13850 的典型值是 24MHz,你给它配 27MHz,sensor 内部的 PLL 会按错误的基准算输出时序,出来的图像要么黑要么花。
GPIO 的极性尤其容易写反。reset-gpios 通常用 GPIO_ACTIVE_LOW,意思是低电平时芯片处于复位;pwdn 通常高电平有效,拉高进入 power down。如果你的驱动里读的是 gpiod_set_value,极性标错会导致逻辑完全反过来,现象就是 i2c 能通但不出 MIPI 数据,而且 dmesg 里没有任何错误信息。
供电节点要确认和原理图一致。reg_2v8 代表 AVDD 的 regulator,reg_1v8 代表 DOVDD,reg_1v2 代表 DVDD。如果模组是三路电共用一个电源芯片,那三个 supply 可以指向同一个节点,但这只在模组内部已经处理好隔离时才安全,否则建议保持独立 rail 并做时序控制。
3.3 编译进内核与模块加载验证的完整命令
调试期我习惯先把驱动编进内核而不是做成模块,因为模块加载失败时 insmod 的错误信息往往被系统日志吞掉,排查起来反而不方便。在内核目录下打开驱动选项:
cd linux-xxx make menuconfig # Device Drivers -> Multimedia support -> Media drivers -> # Sensor drivers -> OV13850 sensor support make -j$(nproc)menuconfig 里这一项可能默认是 M 或 N,按空格切成 * 编进内核。如果你不想重编整个内核,可以单独编模块:
make -C /path/to/kernel M=/path/to/ov13850 modules编完后把 .ko 放到目标板文件系统,再执行加载:
modprobe ov13850 i2cdetect -y 3i2cdetect 输出里 0x36 位置出现设备编号,说明 i2c 通信已经建立。如果显示 UU,说明有驱动占用了这个地址,属于正常。如果没有任何反应,回到 4.1 的三种原因排查。
模块加载只是第一步,真正让 sensor 出数还要把 media pipeline 跑起来。加载后立即查看 /sys/class/video4linux 下有没有新增 video 节点,以及 /dev/media0 的拓扑里有没有 OV13850 这个实体。实体出现说明 probe 成功;没出现就去看 dmesg,重点搜 i2c transfer error 和 probe fail 关键字。
4. OV13850 驱动移植避坑:常见花屏、黑屏与通道错误排查
4.1 黑屏无中断:i2c 通信失败的三种原因
现象是应用层抓帧一直超时,sensor 中断根本不产生,dmesg 里反复出现 i2c transfer error。原因逃不出三个:地址不对、供电没上、时钟没给。
先查地址。i2cdetect 扫一遍,如果实际地址是 0x20 而 dts 写 0x36,寄存器读写全部落到错误地址,sensor 完全没有响应。解决是把 dts 的 reg 改成实际扫到的地址,再重新 probe。注意有的 i2c 控制器默认拉起 10bit addressing,需要确保 sensor 节点没有误配成 i2c 10bit 模式。
再查供电。用万用表量 AVDD、DOVDD、DVDD 三路,很多平台在系统 suspend 后会把没被 pm_runtime 管理的 rail 直接关掉。解决是确认驱动里对 regulator 获取了引用,并且 s_power 回调里调用 regulator_enable 和 regulator_disable。如果驱动根本没写电源回调,要在设备树里保证这三路电是常开域。
最后查 MCLK。用示波器点在 MCLK 焊盘上,如果时钟根本没起来,驱动里 clk_prepare_enable 可能失败,也可能引脚被 pinctrl 复用成 GPIO,导致时钟信号被拉死。解决是检查 pinctrl 状态和 clock 树的 enable 计数。我遇到过 pinctrl 默认状态和 sleep 状态不一致,驱动 probe 时是时钟功能,系统睡眠后引脚被 sleep 态切成 GPIO,唤醒后没有切回来。
4.2 出图但花屏:MIPI 时钟、lane 数与 RAW 格式不匹配
现象是 sensor 有数据、能抓到帧,但画面是满屏斜条或者灰绿条纹。这类问题变量最多,按概率排序是 MIPI lane 数不一致、RAW 位宽不一致、虚拟通道错位。
OV13850 常见配置是 2-lane 或 4-lane MIPI。SoC 接收端配置的 lane 数必须与驱动写进 sensor 的一致性。如果 sensor 端设了 4 lane,接收端只收了 2 lane,第二条 lane 的数据全部丢失,图像必然错位。解决是确认 dts 里>media-ctl -d /dev/media0 -r media-ctl -d /dev/media0 -l "'ov13850 3-0036':0->'mipi_receiver':0[1]" media-ctl -d /dev/media0 -V "'ov13850 3-0036':0 [fmt:SRGGB10_1X10/1280x960]"
第一行清空所有链路,第二行建立 sensor 到接收端的 link,第三行设置源端格式。这里 3-0036 是 bus 号 3、i2c 地址 0x36 的组合,名称以 media-ctl -p 打印的实体名为准。格式名 SRGGB10_1X10 表示 RAW10 的 SRGGB Bayer 数据。
然后抓帧:
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=960,pixelformat=RG10 \ --stream-mmap --stream-count=1 --stream-to=frame.raw如果命令成功且 frame.raw 大小等于 1280 乘 960 乘 2 字节,说明 MIPI 数据和 ISP 接收都通了。有的驱动上报 packed 格式,文件大小会是 1280 乘 960 乘 10 除以 8,约 1.5MB,两种都算正常。接下来把 raw 以 16bit 深度、RGGB 排列导入图像软件,能看见一个偏暗的灰度图,就说明 sensor 真的在出图。
5.2 曝光与增益的手动干预与 group hold
出图后的调优集中在曝光和增益。OV 系 sensor 的曝光从 0x3500 开始连续三个字节,增益在 0x3503 之后。手动调曝光的常见做法是直接 i2c 写:
i2cset -y 3 0x36 0x3500 0x00 i2cset -y 3 0x36 0x3501 0x08 i2cset -y 3 0x36 0x3502 0x7a这三条把曝光写到 0x087a,也就是 2170 行的曝光时间。注意曝光和增益的更新受 group hold 机制控制,需要先把 group hold 寄存器置 1,写完所有目标寄存器后再置 0,让参数在同一帧边界生效,否则画面会出现亮度跳变。OV 系通常用 0x3208 做 group hold,具体地址以驱动源码为准。
我自己的习惯是先在驱动表上把曝光固定到中等值,再单独调增益,确认两者独立有效后再交给 3A 算法。有一次我花了两个晚上排查亮度跳变,最后发现是 stream on 状态下边写曝光边抓帧,参数被拆到了两帧里。从那之后我所有曝光增益调整都先设置 group hold,再等一帧、再验证亮度。OV13850 这套驱动说透了就是“电源时序 + 寄存器表 + media pipeline”三样东西,寄存器表是对的、时序是稳的、链路是通的,画面自然就出来了。希望这篇能帮你少走一点弯路,祝顺利出图。
本文还有配套的精品资源,点击获取