ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OV13850 Linux驱动移植指南:从V4L2 subdev到出帧避坑

OV13850 Linux驱动移植指南:从V4L2 subdev到出帧避坑 简介这是一套面向Linux/Android摄像头驱动开发者的OV13850传感器驱动源码基于瑞芯微RK3288硬件平台。压缩包共5个文件整体104KB包含两个C源文件、一个头文件、一个Android.mk构建脚本和一个XML校准数据文件。驱动源码涵盖了传感器上电、时钟配置、I2C寄存器写入、MIPI链路建立以及图像数据输出等完整流程并给出了寄存器配置表与私有数据结构定义。已有655人浏览学习适合正在做瑞芯微平台Camera驱动移植或OV13850方案调试的工程师参考。整包结构精简目录区分校准与源文件模块读者既能快速集成到现有Android系统也可对照源码理解驱动初始化、上电时序与图像采集链路其中涉及I2C寄存器操作、MIPI收发配置、图像格式转换等关键实现并配有构建脚本与校准文件适合作为驱动入门和二次开发的基础材料。1. OV13850.tar.gz 是什么一颗 13MP sensor 的 Linux 驱动要怎么落地拿到一个名为 OV13850.tar.gz 的 Linux 驱动包往往意味着板子上那颗 1380 万像素的 MIPI 摄像头模组还没被内核认出来。OV13850 在 RK、全志、NXP 这类带 ISP 的嵌入式平台上很常见驱动要做的不是把 sensor 点亮而是把它接进 V4L2 框架让应用层能打开节点、配置格式、持续拿到帧。这篇笔记按接手这类驱动包的实际顺序展开先弄清 subdev ops 和设备树怎么配再编译出 ko、用 media-ctl 让 sensor 出流最后把常见的上电时序、时钟和 I2C 地址坑提前避开。正在做 linux 驱动开发被这颗 sensor 卡在不出图、花屏或者 probe 失败上的人按这个路径走能少翻几次车。2. OV13850 驱动接入 V4L2subdev 回调、设备树属性和注册顺序很多第一次接触 sensor 驱动的人会下意识想找 file_operations 里的 read/write——那是字符设备驱动框架的思维。OV13850 这类 MIPI sensor 在 Linux 里走的是 V4L2 subdev 模型它不直接暴露 /dev/videoX而是作为一个 media entity 挂在 /dev/media0 上由 ISP 的 video 节点把帧收走。驱动包能不能用先看它有没有把这三件事做对I2C 总线枚举、subdev ops 注册、设备树 GPIO 和时钟绑定。2.1 驱动核心就三部分i2c_driver、v4l2_subdev 与 ops 表翻开解压后的 ov13850.c去掉平台附带的那堆寄存器数组真正干活的骨架一般长这样/* ops 表是上层调用的入口stream on/off 和电源控制都在这里 */ static const struct v4l2_subdev_core_ops ov13850_core_ops { .s_power ov13850_s_power, }; static const struct v4l2_subdev_video_ops ov13850_video_ops { .s_stream ov13850_s_stream, .g_frame_interval ov13850_g_frame_interval, }; static const struct v4l2_subdev_ops ov13850_subdev_ops { .core ov13850_core_ops, .video ov13850_video_ops, };这段代码要连着看懂三层。最外层是一个 i2c_driverprobe 时拿到 struct i2c_client调用 v4l2_i2c_subdev_init 或 v4l2_i2c_subdev_reg 把 subdev 挂到 I2C 总线上。s_power 是上电回调平台在打开 sensor 时先调它里面一般做 GPIO 拉高、延时、写一串初始化寄存器s_stream 才是出流的开关video 节点 stream on 时V4L2 会按媒体拓扑从 sensor 到 ISP 依次调用各 subdev 的 s_stream(true)。所以排查不出图时不要在应用层瞎猜先在 s_stream 里打一条 dev_info 看它到底有没有被调到。ops 表之外驱动里还会有一个 v4l2_ctrl_handler负责曝光、增益、白平衡这些控制项。OV13850 的曝光和增益是写在寄存器里的ctrl 回调的 set 函数最后都会落到 i2c 写寄存器。判断一个驱动包是不是完整就看它是不是同时给了 i2c_driver、subdev ops 和 ctrl handler——很多网上下载的“驱动”只有寄存器表没有 ops 注册那种东西上板子是跑不起来的。2.2 设备树里给 sensor 开的卡片clocks、pwdn、reset 与 lane 数驱动能不能 probe 成功一半决定在设备树上。OV13850 挂在某个 I2C 控制器下面同时需要一路 MCLK、一路 PWDN、一路 RESET。一个最小 dts 节点通常长这样i2c0 { status okay; ov13850: ov1385010 { compatible ovti,ov13850; reg 0x10; clocks cru SCLK_CAM0; clock-names xvclk; assigned-clocks cru SCLK_CAM0; assigned-clock-rates 24000000; pwdn-gpios gpio1 5 GPIO_ACTIVE_LOW; reset-gpios gpio1 6 GPIO_ACTIVE_LOW; pinctrl-names default; pinctrl-0 cif_clk_pin; port { ov13850_out: endpoint { remote-endpoint mipi_csi2_in; >tar xzvf OV13850.tar.gz find . -type f | grep -iE ov13850|dts|readme|README|patch | sort一个能用的包通常包含 drivers/media/i2c/ov13850.c、头文件里面是寄存器表、平台 dts 的 diff以及一份说明文档。先别急着把 .c 拷过去打开文件头看两件东西兼容字符串比如 “ovti,ov13850”以及 include 了哪些内核头文件。v4l2_subdev 的 ops 结构在 4.x 内核改过几次驱动若按较老内核写成 s_power 直接挂在 core ops 上在新内核上可能编译报错或者运行时不回调这种情况要先找平台 SDK 内核版本对应。3.2 内核配置把 ov13850 编成模块还是内建OV13850 在 kernel Kconfig 的主菜单路径一般是Device Drivers → Multimedia support → Media driver types → Camera sensor devices → OV13850 sensor support不少平台把 sensor 菜单放在 I2C camera sensors 下入口名字可能叫 OmniVision OV13850 support。选成模块会生成 ov13850.ko选内建则进 zImage。我一般选模块原因很实际调 dts 时不用反复烧整个镜像改完 dts 后重新编 dtb 即可ko 和 dtb 分开更新翻车半径小。对应 Kconfig 和 Makefile 的写法一般是这样的# drivers/media/i2c/Makefile obj-$(CONFIG_VIDEO_OV13850) ov13850.o编之前确认内核源码里已经加了这段否则 menuconfig 里看不到选项。平台 SDK 目录下执行export ARCHarm64 export CROSS_COMPILEaarch64-linux-gnu- make menuconfig make Image dtbs modules -j$(nproc)ARCH 决定内核和 ko 的体系结构CROSS_COMPILE 是交叉编译工具链前缀这两行错一个编译出的模块加载时就会报 unknown symbol 或者 Invalid module format。make Image 编内核、dtbs 编设备树、modules 编全部 ko-j 并行数按机器内存给。提示如果 ARCH 写成了 arm 而板子是 64 位ko 能编出来但 insmod 会直接拒绝不用怀疑工具链坏了。3.3 把 dts 节点挂到具体板子上一份最小补丁的写法板级 dts 通常在 arch/arm64/boot/dts/rockchip/ 或 vendor 目录里。找到 sensor 要挂的那条 I2C 总线比如 i2c0按第 2.2 节的模板把 ov1385010 节点加进去还要在同一文件的某个 pinctrl 节点确认 MCLK 引脚的复用。平台 SDK 里常给一个 camera 的 dtsi把通用 sensor 配置放那里board dts 里只引用。补完 dts 后重新编 dtbmake dtbs把新 dtb 烧到板的 boot 分区。如果整个流程是 SDK 的编译脚本在管理就找到它里面对应 dtb 的输出目录把 dtb 单独拷贝出来用板子自带工具更新。不烧 dtb 只换 ko多半会发现 dts 里的节点根本没生效。3.4 把 ko 送进 rootfsscp、modinfo 与 dmesg 验证dtb 更新完成后把驱动模块装进板子scp drivers/media/i2c/ov13850.ko rootboard-ip:/lib/modules/$(uname -r)/extra/ ssh rootboard-ip depmod -a modprobe ov13850scp 是板子和主机之间拷文件最简单的方式depmod 重建模块依赖modprobe 按名字加载。加载后立刻看ssh rootboard-ip dmesg | grep ov13850正常会看到“ov13850 0-0010: probing v4l2 subdev”这类日志后面跟着 v4l2_ctrl 注册信息。如果 dmesg 没有任何输出先看 modprobe 有没有找到模块、I2C 地址对不对不要直接进应用层。到这一步驱动已经在内核里活过来了接下来要解决的是让它出帧。4. 让 OV13850 真正出帧media-ctl 拓扑、v4l2-ctl 与格式核对probe 成功不代表能出图很多驱动包就是卡在“注册了但没帧”这一步。原因大多是媒体链路没拉起来或者 subdev 的 pad 格式没有设置。出帧这件事要按顺序做确认节点、建立 link、设置格式、拉流。4.1 先确认设备节点和拓扑dmesg 与 media-ctl -p加载驱动后先确认内核里出现了哪些新设备dmesg | grep -E ov13850|media|csi ls -l /dev/v4l-subdev* /dev/video* /dev/media* media-ctl -d /dev/media0 -pmedia-ctl -p 的输出里会列出每个 entitysensor 一般叫 ov13850 0-0010后面跟着 pad0。这一行要能看到说明 subdev 注册进了拓扑。看不到的话优先级最高的问题是 dts 里节点没贴到对应的 i2c 控制器上或者驱动里 entity name 初始化失败。注意板子上可能不止一个 media devicesensor 走 ISP 的 /dev/media0USB 摄像头是 /dev/media1。用 -d 参数指定别拿默认节点拉错链路。注意如果 ls /dev/media* 没有任何节点先回内核配置开 CONFIG_MEDIA_CONTROLLER子设备 API 没开后面所有命令都白搭。4.2 在 media 拓扑里把 OV13850 接到 ISPlink 与 pad 格式拓扑是静态的还需要把 sensor pad 和 ISP 入口用 link 连起来。我一般用这三条命令media-ctl -d /dev/media0 -r media-ctl -d /dev/media0 -l ov13850 0-0010:0 - mipi csi2:0[1] media-ctl -d /dev/media0 -V ov13850 0-0010:0[fmt:SRGGB10_1X10/1920x1080]第一条 -r 是清除所有 link 设置回到出厂拓扑第二条 -l 建立 sensor pad0 到 CSI 接收端 pad0 的链路[1] 表示 enable。第三条 -V 设置 sensor pad0 的输出格式和分辨率SRGGB10_1X10 对应的就是 OV13850 的 RAW10 Bayer 输出Bayer 顺序是 RGGB。实体名字里的 0-0010 是 i2c 总线号和地址以自己的 media-ctl -p 输出为准平台不同名字略有差异。如果平台 ISP 支持部分 SoC 会把 sensor 配成 UYVY但那是 ISP 后处理的结果对 OV13850 这类 raw sensor别跳级配成 YUYV 去拉流。4.3 用 v4l2-ctl 拉流并核对帧尺寸拓扑和格式设置完在 video 节点上抓帧v4l2-ctl -d /dev/video0 \ --set-fmt-videowidth1920,height1080,pixelformatRG10 v4l2-ctl -d /dev/video0 \ --stream-mmap --stream-count30 \ --stream-to/tmp/ov13850_1080p.raw--set-fmt-video 里的 pixelformat 要写 RG10这是 V4L2 里对 RAW10 四字符码的约定写法在应用层它代表每个像素占两个字节、高位填充。--stream-count30 是抓 30 帧不设置的话会一直跑调试时容易把 emmc 写满。跑完后检查文件大小ls -l /tmp/ov13850_1080p.raw1920x1080 的 RAW10 每帧是 192010802 字节因为没有压缩这是一条很好用的自检公式——文件大小不是这个值说明像素格式或者分辨率没真正生效不要急着怪驱动。我见过有人把 pixelformat 写成 RGGB 或者 GREY命令能过但抓下来的全是空帧多半就是这行写错。4.4 把 RAW 转成人能看的图验证用的最省事路径v4l2-ctl 抓下来的是 sensor 直接吐的 Bayer 数据在 PC 上最省事的方式是用支持 Bayer 的查看器直接打开或者交给 ffmpeg 做 demosaicffmpeg -f rawvideo -pix_fmt bayer_bggr8 \ -s 1920x1080 -i /tmp/ov13850_1080p.raw \ -vf formatrgb24 /tmp/ov13850_1080p.png这里 bayer_bggr8 是按常见 Bayer 顺序写的实际顺序以 OV13850 的配置为准SRGGB 对应的是 bayer_rggb8不同平台 ISP 的输出顺序可能被调整过。如果转出来的图颜色不对但物体轮廓清晰说明 sensor 链路是通的颜色全乱通常是 bayer 顺序或者格式位宽标错图里全是横条纹就要回到 PCLK 和 MIPI lane 的问题上。这节的目的是验证链路不是做 ISP跑通后把中间验证命令固化成一个脚本后面每次改完 dts 都重跑一遍。5. OV13850 驱动移植避坑电源时序、时钟频率、I2C 地址与 MIPI 参数这一章是最容易让整块板子看起来像报废的地方。OV13850 这类 raw sensor 对模拟电源和时钟的敏感度远高于 USB 摄像头probe 失败、花屏、黑屏在多数情况下不是代码逻辑错而是时序和电参数没满足。以下 4 条是这些年反复遇到的坑按现象、原因、解决办法写。5.1 probe 时好时坏首轮开机认到、重启后丢失现象刚烧完系统第一次启动 dmesg 里能看到 ov13850 probing v4l2 subdevreboot 之后同样的 dts 和 ko 却报 read reg error再断电重新上电又好了。原因上电时序没满足。OV13850 对电源的预期顺序大体是 AVDD、DOVDD、DVDD 先建立MCLK 稳定后 PWDN 拉低释放RESET 再拉高完成复位每两步之间要求毫秒级延时。板级 dts 里如果用了 regulator 的 boot-on但 GPIO 控制的 PWDN 提前被驱动 s_power 释放sensor 内部上电复位还没完成I2C 第一次访问自然超时。重启时内核时钟和 regulator 的初始化顺序变了问题就暴露出来。解决把 PWDN 和 RESET 的 GPIO 控制收进驱动而不是在 dts 里用默认状态硬拉。驱动里 s_power(true) 的顺序固定为先把 pwdn 置为有效开启 clocks再延时 10ms然后置无效 pwdn、释放 reset再延时 20ms最后才去写 sensor 寄存器。我用过的一种写法是static int ov13850_s_power(struct v4l2_subdev *sd, int on) { gpiod_set_value_cansleep(ov13850-pwdn, on ? 0 : 1); if (on) { usleep_range(10000, 12000); gpiod_set_value_cansleep(ov13850-reset, on ? 1 : 0); usleep_range(20000, 22000); } return 0; }注意 dts 里 pwdn-gpios 是 GPIO_ACTIVE_LOW所以“置为有效”在逻辑层是 0gpiod_set_value_cansleep 会帮你做硬件电平翻转不要在驱动里再写一次 !。改完后再用 reboot 和 cold boot 两种方式各验证三轮别再只看一次开机日志。5.2 MCLK 给成 27M花屏和条纹来回换花样现象链路能出帧但画面有斜向条纹或者分辨率高了直接没有 PCLK把分辨率降到 VGA 又正常但帧率也不对。原因OV13850 内部的 PLL 配置是按某个输入频率算好的常见是 24MHz 或 27MHz。寄存器表里的 PLL 倍频和分频只对其中一个频率成立如果 dts 的 assigned-clock-rates 给了 27MHz而驱动寄存器表按 24MHz 设计MIPI 的 data rate 就会整体偏移表现为花屏、条纹或带宽不够。VGA 正常是因为低分辨率下时序余量大把问题掩盖了。解决先用 linux 常用命令确认实际时钟到底是多少cat /sys/kernel/debug/clk/clk_summary | grep -i xvclk然后对照寄存器表确认 PLL 是按哪个频率计算的。多数发布包里 README 会写 24M crystal required 之类的话没有 README 就看 s_stream 里写的 pll 寄存器值反推频率。改 dts 里的 assigned-clock-rates 比改寄存器表安全因为时钟源在 SoC 侧改起来最快但要注意确认 I2C 读到的是不是同一个时钟树有些平台 sensor 时钟和 MCLK 引脚不复用改了 timeout 反而起不来。5.3 I2C 地址 0x10 和 0x20 来回打架现象dmesg 一直是 ov13850: i2c read error但 i2cdetect 在总线上明明能看到 0x10 有一个设备且驱动反复 probe 都失败。原因OV13850 的 7 位 I2C 地址是 0x108 位写地址是 0x20读地址是 0x21。设备树 reg 属性按内核规则填 7 位也就是 0x10但有些 SDK 的驱动在 probe 里又手动算了一次地址读到 client-addr 后再 1于是实际发出的事务落在 0x20 或者 0x21I2C 控制器里又不做修正总线事务自然没有 ACK。还有的板级 dts 直接把 reg 写成 0x20内核后面访问时再移位发的地址就变成 0x40在总线上根本不存在。解决一律在 dts 写 reg 0x10驱动里不要再对 client-addr 做算术。I2C core 会在收发时自动完成 7 位到 8 位的转换。写完以后用 i2cdetect -y -r 0 确认总线上 0x10 有设备再在驱动 probe 里打印一条 client-addr看到的值必须是 0x10。如果客户改了模组地址比如 SA0 上拉把地址变成 0x12记得 dts 和驱动两边一起改别只在一边动手。这里还有个迷惑点i2cdetect 显示的是 7 位地址v4l2-subdev 的 entity name 里 0-0010 的 0010 也是 7 位两边对不起来时先统一语境。5.4 MIPI lane 数与 data rate 不匹配高分辨率黑屏低分辨率正常现象1920x1080 还能偶尔出图切到 2560x1440 或者更高就彻底黑屏media-ctl 把格式设完后 dmesg 报 CSI 超时换一块只有 2-lane 的板子同样的 dts 在 4-lane 配置下全黑。原因OV13850 模组在硬件上定义了 MIPI lane 连接常见 4-lane 和 2-lane 两种同时驱动里 link-frequencies 要和 SoC CSI 接收能力匹配。dts 里>for r in 640x480 1280x720 1920x1080 2560x1440; do w${r%x*}; h${r#*x} v4l2-ctl -d /dev/video0 --set-fmt-videowidth$w,height$h,pixelformatRG10 timeout 2 v4l2-ctl -d /dev/video0 --stream-mmap --stream-count5 /dev/null || echo fail at $r done这条脚本要在 rootfs 里放好它比任何测速工具都能更快暴露 lane 和速率问题。如果某个分辨率失败但低一档正常先怀疑 vblank/hblank 配置驱动里不同分辨率往往对应不同的 vts 和 hts这两个值决定 PCLK 和 MIPI 吞吐不能只改 width 和 height。这是我在 lane 问题上最疼的一条血泪经验硬件排线到模组之间经常只接了两对差分光改 dts 没用。6. 验收前调好这 3 个点曝光增益、分辨率切换与稳定性驱动能出帧只算及格能交付还要再过三关。6.1 先验证曝光和增益的链路通不通用 v4l2-ctl 在 subdev 节点上直接打控制命令v4l2-ctl -d /dev/v4l-subdev0 --set-ctrlexposure1000 v4l2-ctl -d /dev/v4l-subdev0 --get-ctrlexposure如果驱动里的 v4l2_ctrl_handler 没有注册 V4L2_CID_EXPOSURE命令会直接报错。参数能设置成功再把镜头对向亮处和暗处观察帧平均亮度是否变化。只出图不能调曝光这个驱动离验收还差一大截。6.2 分辨率切换前先把流停干净切分辨率最常见的失败方式是黑屏。我的习惯是先 stream offsleep 200ms 让 sensor 把残留的帧排空再 set format再 stream on。放在脚本里就是v4l2-ctl --stream-off sleep 0.2 v4l2-ctl --set-fmt-videowidth640,height480,pixelformatRG10 v4l2-ctl --stream-mmap --stream-count5多次切换后仍然黑屏查 s_stream(false) 的实现是不是把寄存器表恢复到了初始状态以及 CSI 接收端有没有在 stream off 时清掉 buffer。6.3 压测 30 分钟用 dmesg 抓瞬时错误交付之前我会让板子跑一轮长时间抓帧同时开 dmesg -w 盯着看dmesg -w | grep ov13850 timeout 1800 v4l2-ctl -d /dev/video0 --stream-mmap --stream-count3000帧计数和文件大小都要连续中途出现 timeout、lost frames 就要回到时钟和 lane 上查而不是在 app 层打补丁。这三个点是我现在拿到任何 sensor 包都先做的验证OV13850 也不例外。链路通了、参数能控、长时间不掉帧再交给应用层做预览或抓拍心里才有底。希望帮到你。本文还有配套的精品资源点击获取
返回列表