☰
RK3288平台OV2710与XC6130驱动移植:设备树配置与排查实战
2026/10/5 4:46:41 网站建设 项目流程

简介:面向RK3288平台,压缩包内含XC6130与OV2710摄像头组合在Android/Linux系统下的驱动适配代码,适用于嵌入式驱动开发、Camera调试及BSP集成场景。包体仅26KB,内容紧凑,共5个文件:2个C源文件实现驱动主体逻辑,1个头文件定义接口,Android.mk负责编译集成,cam_board_rk3288.xml用于板级配置。目前已有140人学习下载。从内部目录看,资源按XC6130、include_priv、source等模块组织,覆盖从底层寄存器操作、i2c通信到board级参数配置的完整链路,可直接参考OV2710 sensor驱动注册、上电时序设置及hal层对接方式。对于需要快速在RK3288上点亮XC6130+OV2710组合的工程师,这套小型驱动包能有效节省排错时间,提供可移植的代码骨架与配置范例,兼具实用性与学习价值。

1. 拿到 xc6130_ov2710_driver.tar.gz 之后先搞清它是什么

一块 RK3288 主板,Android 7.1 固件跑得好好的,点歌机、广告机今天要换摄像模组,OV2710 接上后 /dev/video0 就是不出图。这个问题我遇到过不止一次:不是硬件坏了,而是驱动没有按平台套路放对位置。标题里这个 tar.gz 就是这类方案的典型交付物——RK3288 平台、Android 系统、OV2710 传感器,外加一颗配套芯片 xc6130。xc6130 在多数模组里负责给 OV2710 提供干净的供电和参考时钟,偶尔还兼顾复位和 MIPI 时序;所以光有 ov2710.c 不解决全部问题,得 xc6130 一起改。下面按我自己的移植流程讲一遍:先拆包,再落位,然后配设备树、编译、排查。

2. 把驱动接进 RK3288 内核:解压、落位和 Kconfig

2.1 解包先看目录,别急着跑 install.sh

很多厂商给的 sensor 驱动包,入口是一个 install.sh 或者 auto_build.sh。我一般不会直接去执行这个脚本,因为这类脚本默认把文件拷贝到 SDK 的 kernel 目录,覆盖范围可能远大于我们要的 ov2710.c 和 xc6130.c。一旦它把 dts、config、HAL 一起覆盖掉,后面翻车了都不知道是哪一步动的手。

先把它解压到一个独立目录,看一遍真实结构:

mkdir -p ~/rk_drv_xc6130 && tar -xzf xc6130_ov2710_driver.tar.gz -C ~/rk_drv_xc6130 find ~/rk_drv_xc6130 -maxdepth 3 -type f | sort

这里-C指把压缩包释放到~/rk_drv_xc6130,不污染当前内核源码树;find -maxdepth 3只看前两层文件,足够判断包里是完整 Android 工程还是纯内核补丁。大部分驱动包是后者,常见的会包含ov2710.c、ov2710.h、xc6130.c,以及一个用于参考的.dts片段或 README。

随后打开 README 或 Makefile 看它的编译方式。如果 README 里写了“MIPI 2 lane / 1080P@30fps”这类字样,说明它默认按标准的 OV2710 输出做;如果写了“使用 GPIO0_C1 做复位”这一类平台相关参数,说明它还需要和板子本身的 pinctrl 对齐。

2.2 把源文件放进 kernel/drivers/media/video,并接进 Kconfig/Makefile

RK3288 的 Android 7.1 SDK 里,sensor 类驱动的位置通常在内核的drivers/media/video/,少数定制 BSP 会挪到drivers/media/platform/rockchip/camera/。判断依据很简单:看 SDK 内核中已经存在的 sensor 驱动放哪,就放哪。我这边常用路径是kernel/drivers/media/video/,照它放不会错。

KSRC=~/rk3288_7.1/kernel cd ~/rk_drv_xc6130 cp -a ov2710.c ov2710.h $KSRC/drivers/media/video/ cp -a xc6130.c xc6130.h $KSRC/drivers/media/video/ grep -n "ov2710\|xc6130" $KSRC/drivers/media/video/Makefile grep -n "VIDEO_OV2710\|XC6130" $KSRC/drivers/media/video/Kconfig

第一次执行时两个 grep 多半没有输出,因为 Kconfig 和 Makefile 里还没加这一项。这时不要用“把某文件替换成同名的另一个”这种偷懒方式,因为不同 BSP 会冲突。正确做法是在现有文件末尾追加一个独立的配置块:

cat >> $KSRC/drivers/media/video/Kconfig <<'EOF' config VIDEO_OV2710_XC6130 tristate "OV2710 sensor with XC6130 companion" depends on VIDEO_V4L2 && I2C && ARCH_ROCKCHIP help This is a camera sensor module using OV2710 and XC6130. EOF cat >> $KSRC/drivers/media/video/Makefile <<'EOF' obj-$(CONFIG_VIDEO_OV2710_XC6130) += ov2710_xc6130.o ov2710_xc6130-objs := ov2710.o xc6130.o EOF

这里的逻辑我重点讲一下:ov2710_xc6130-objs把两个 .o 合成一个独立模块,是为了让xc6130.c里的供电和时序函数只服务于 ov2710,两个文件一起编译,避免出现符号被别的 sensor 驱动误用的情况。如果你坚持obj-m += ov2710.o xc6130.o分开编,那么 xc6130 导出的符号会被当作通用 PMU 驱动,后面的 insmod 顺序会变成玄学。

编译前确认配置项已经开启。如果是整体打包进内核,建议直接放y:

cd $KSRC make ARCH=arm rockchip_defconfig grep "VIDEO_OV2710_XC6130" .config

如果 grep 看不到=y,就手动在arch/arm/configs/rockchip_defconfig末尾加一行CONFIG_VIDEO_OV2710_XC6130=y,再重新生成 .config。否则后面make menuconfig会按默认值落成m,Android 的 rootfs 里没有模块加载器,系统启动时根本不加载你的 .ko,图像自然出不来。

2.3 头文件、平台宏和 GPIO 的静态检查

代码落位后先别急着编译。sensor 驱动大多是厂商从小平台搬过来的,里面有大量#include <mach/xxx.h>和gpio_xxx()旧接口。RK3288 的 Android 7.1 内核已经全面切到设备树和gpiod_*/gpio_set_value接口,直接编译会报几十个错。

先做一轮静态检查:

grep -n "include.*mach/" $KSRC/drivers/media/video/ov2710.c grep -n "include.*mach/" $KSRC/drivers/media/video/xc6130.c grep -n "gpio_request\|gpio_free" $KSRC/drivers/media/video/ov2710.c

看到mach/board.h、mach/gpio.h这类头文件,直接把它们删掉,改用linux/gpio.h和linux/gpio/consumer.h。而gpio_request这一类老接口在 RK3288 4.6、4.4 内核里虽然还存在,但它不认设备树里通过pinctrl-0申请过的 pin,会造成申请冲突。我习惯的做法是把复位、电源控制全部改成设备树节点,在probe里用devm_gpiod_get取句柄。

另一个要检查的地方是 xc6130 的 I2C 地址。xc6130 作为 companion 芯片,驱动里多半会写死一个 device address,例如 0x11 或 0x12。不要只信驱动里的注释,要拿模组原理图或者模组厂家邮件确认 7-bit I2C 地址。I2C 地址错位的表现很迷惑,它不会立即报错,而是在ov2710_probe里回读 chip ID 时得到 0x00,然后被当成“硬件未连接”。

静态检查通过后再执行编译,范围尽量小:

cd $KSRC make ARCH=arm CROSS_COMPILE=arm-linux-android- names...

这里只编译内核映像个 image 即可。RK3288 的 kernel 编出boot.img后,还需要单独打包进resource.img,这部分放到后面提到固件打包时再说。第一次编译如果 OV2710 寄存器表里的数组定义很大,不必担心,那是正常的,几百行{0x12, 0x80}形式的配置表是 OmniVision 驱动的传统风格。

3. 设备树:OV2710 和 XC6130 的平台参数

3.1 在 RK3288 的设备树里声明 ov2710 节点

RK3288 上的 Camera 一般挂在一路 I2C 上,常见是 I2C2 或 I2C4。在板级 dts 里找到那一路 i2c 节点,追加 ov2710 子节点:

&i2c2 { status = "okay"; clock-frequency = <400000>; ov2710: ov2710@36 { compatible = "ovti,ov2710"; reg = <0x36>; pinctrl-names = "default"; pinctrl-0 = <&cam0_rst>, <&cam0_pwdn>; reset-gpios = <&gpio2 RK_PB2 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio2 RK_PB3 GPIO_ACTIVE_HIGH>; clocks = <&cru SCLK_CAMERA0>; clock-names = "xvclk"; clock-frequency = <24000000>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; avdd-supply = <&vcc2v8_cam>; dovdd-supply = <&vcc1v8_cam>; dvdd-supply = <&vcc1v2_cam>; }; };

reg = <0x36>这里先说明一下:这个是 7-bit I2C 地址的常见示例值,不同模组会把 SID 引脚拉到不同电平,实际要用i2cdetect探测为准,我会在后面的排查章节再展开。clocks里的SCLK_CAMERA0是 RK3288 的 camera 参考时钟,频率默认给 24MHz,OV2710 能接收的 xvclk 范围通常是 6MHz 到 27MHz,24MHz 是最稳妥值。

avdd/dovdd/dvdd三个 supply 是否需要写,取决于 xc6130 的控制方式。如果 xc6130 通过 I2C 寄存器控制内部 LDO,那这行可以不加,让 xc6130 驱动自己调;如果板子上用的是独立 LDO,需要在内核里找到对应 regulator 节点。二者不要重复配置,否则两个驱动会争抢同一路电源。

3.2 xc6130 节点与上下电时序

xc6130 在设备树里不是 sensor,是 sensor 的配套芯片,所以它挂在同一个 I2C 总线,但代表一个独立的从设备:

xc6130: xc6130@11 { compatible = "xc,6130"; reg = <0x11>; status = "okay"; };

在很多模组里,xc6130 实际是一个连续输出的时钟发生器和电源管理芯片。它的工作方式是:主控上电后先给 xc6130 写寄存器,让它提供 24MHz 参考时钟并建立好 2.8V/1.8V/1.2V 三路电压,再触发 OV2710 复位,最后才去读 sensor ID。因此ov2710.c的power_on函数里要按顺序调用 xc6130 的操作函数,而不是直接拉 GPIO:

static int ov2710_power_on(struct ov2710_priv *priv) { int ret; /* 先让 xc6130 输出 24MHz xvclk,并建立 DOVDD 1.8V */ ret = xc6130_on(); if (ret) return ret; usleep_range(10000, 20000); /* OV2710 复位信号从低到高,拉高后等待内部 PLL 稳定 */ gpiod_set_value_cansleep(priv->reset_gpio, 0); usleep_range(5000, 10000); gpiod_set_value_cansleep(priv->reset_gpio, 1); usleep_range(20000, 30000); return 0; }

这一段是结构示意,重点看顺序:xc6130_on()必须在复位释放之前完成。如果先给 sensor 复位再开时钟,OV2710 内部会检测不到 xvclk,芯片 ID 可能读到,但后续 MIPI 信号会一直不稳定。

xc6130 关闭的顺序反过来:先拉低复位,再关 xc6130。热切换摄像头时,这个顺序比想象中敏感。RK3288 的 Android 框架在切换前后两个预览 session 时,会调power_off,如果顺序错,第二路预览就可能黑屏。

3.3 MIPI CSI-2 的 endpoint 与时钟参数

OV2710 输出是并行或者 MIPI CSI-2,RK3288 方案里通常走 MIPI 2-lane。设备树里要定义 sensor 端和 PHY 端的连接关系:

port { ov2710_ep: endpoint { remote-endpoint = <&mipi_dphy0_ep>; >i2cdetect -y 2 i2cdetect -y 4

如果扫到地址是 0x6c,而驱动写的是 0x36,8-bit 和 7-bit 是一回事,0x36 << 1 = 0x6c。把驱动地址宏改成 0x36 后,再读到 chip ID 0x2710,就说明地址问题解决了。

4.2 probe 成功但 dmesg 里没有 MIPI 中断

现象:cat /sys/kernel/debug/gpio能看到复位引脚状态正常,sensor chip ID 读到了,但isr一次都不触发,多路摄像头视频流起不来。

原因:最常见的是 MIPI CSI 的>frame_rate = xvclk / (HTS * VTS)

注意寄存器值是 16-bit 大端格式,低字节和高字节不要反。如果算出来偏差超过 5%,优先改时钟源配置,而不是硬调 VTS,因为 VTS 调太大会压缩曝光范围,暗光下画面会噪点爆炸。

4.5 热启动时 probe 偶尔失败

现象:冷启动每次都能出图,但 Android 上执行reboot后,有 30% 概率黑屏,必须断电再开才正常。

原因:OV2710 模组上的大电容没有完全放电,power_off了但电压还维持在 1.5V 左右。下次 probe 时,复用 I2C 的 xc6130 读到的是一个半初始化状态的 sensor,chip ID 可能读到 0x2710,但寄存器表没被执行干净。

解决:在power_off里让 xc6130 先断 AVDD,然后主动拉低 xvclk 输出,并保持 50ms 以上放电。不要担心这 50ms 影响切换速度,它换来的是热启动稳定性。这个坑我在量产 RK3288 点歌机主板时遇到过不下十次,随后把power_off里的usleep_range(50000, 60000)写成了固定流程的一部分,再没黑屏过。

5. 验证与进阶:用 V4L2 工具和日志习惯收尾

5.1 用 v4l2-ctl 确认驱动真正工作

设备树和 Kconfig 都改完后,不要急着打开 Android 相机应用,先用 V4L2 工具确认内核侧状态。在 RK3288 的 Android shell 里跑:

v4l2-ctl -d /dev/video0 --list-formats v4l2-ctl -d /dev/video0 --get-fmt-video

--list-formats列出内核驱动支持的像素格式,如果里面只有YUYV而没有NV21,说明驱动没有做格式转换,HAL 层要按实际能力去接。--get-fmt-video能直接看到当前分辨率和四字符编码,这一步排除 HAL 和 framework 黑匣子问题,比看 logcat 更快。

还可以配合media-ctl查看 pipeline:

media-ctl -d /dev/media0 -p

RK3288 的 media controller 会把ov2710、mipi_dphy、isp、mipi_vdev四个 entity 串在一条链路上。如果缺了任何一个节点,Android 的 Camera HAL 就会卡在open阶段,预览不出来。刷完驱动我先跑这条命令,看到xc6130节点正常挂在 i2c 总线上,才继续往下调。

5.2 一个值得坚持的习惯:先量波形再改寄存器

这是我想强调的最后一件事。OV2710 这类 sensor 的驱动看起来是一张几百行的寄存器表,但真正出问题的地方通常在看不到的时序里。遇到黑屏或花屏,我习惯先用示波器量三个点:xc6130 输出的 xvclk 是否稳定 24MHz、reset 引脚的高低电平是否干净、MIPI lane 上有没有数据包。这些量完,只剩两种可能:寄存器配置表与模组不对,或者 PHY 硬件连接有断线。

不做波形检查就直接调 VTS/HTS,只会把问题压到别的场景。示波器量完后,把每个测量点的频率和幅值记录到一个本地文件里,下次换同一模组批次时,拿出来比对基线。这个小习惯帮我省过很多无意义的寄存器改动。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询