目录
[开篇先搞懂:RK 平台的摄像头系统,完整链路是什么样的?](#开篇先搞懂:RK 平台的摄像头系统,完整链路是什么样的?)
[RK 平台摄像头核心架构](#RK 平台摄像头核心架构)
[一、核心框架:V4L2 子系统,大白话全解析](#一、核心框架:V4L2 子系统,大白话全解析)
[什么是 V4L2?](#什么是 V4L2?)
[V4L2 的核心作用](#V4L2 的核心作用)
[我们适配摄像头,需要和 V4L2 打什么交道?](#我们适配摄像头,需要和 V4L2 打什么交道?)
[常用的 V4L2 调试工具](#常用的 V4L2 调试工具)
[1. 核心硬件:图像传感器(Sensor)](#1. 核心硬件:图像传感器(Sensor))
[2. 核心接口:MIPI CSI](#2. 核心接口:MIPI CSI)
[3. 控制总线:I2C/SCCB](#3. 控制总线:I2C/SCCB)
[4. 核心时钟:MCLK 主时钟](#4. 核心时钟:MCLK 主时钟)
[三、保姆级实战:OV5640 摄像头驱动完整适配,基于 RK3568](#三、保姆级实战:OV5640 摄像头驱动完整适配,基于 RK3568)
[步骤 1:确认硬件连接](#步骤 1:确认硬件连接)
[步骤 2:修改设备树,添加摄像头配置](#步骤 2:修改设备树,添加摄像头配置)
[步骤 3:内核配置,开启 OV5640 驱动和 ISP 相关配置](#步骤 3:内核配置,开启 OV5640 驱动和 ISP 相关配置)
[步骤 4:编译烧录,验证驱动是否正常工作](#步骤 4:编译烧录,验证驱动是否正常工作)
[步骤 5:安卓系统相机适配](#步骤 5:安卓系统相机适配)
[问题 1:摄像头完全识别不到,驱动没加载](#问题 1:摄像头完全识别不到,驱动没加载)
[问题 2:驱动加载成功,但是不出图,v4l2-ctl 抓取不到图像](#问题 2:驱动加载成功,但是不出图,v4l2-ctl 抓取不到图像)
[问题 3:能出图,但是花屏、画面错乱](#问题 3:能出图,但是花屏、画面错乱)
[问题 4:能出图,但是画面偏色、发黑、发白](#问题 4:能出图,但是画面偏色、发黑、发白)
大家好,我是黒漂技术佬。上一篇我们搞定了音频驱动,实现了播放和录音功能,后台很多兄弟又问:
"佬,我接了个 OV5640 摄像头,改了设备树,结果安卓相机识别不到,要么就是不出图、花屏,根本不知道从哪里下手,摄像头驱动也太难了"
太懂了!摄像头驱动涉及到的模块多,从 V4L2 子系统、ISP 图像处理、MIPI CSI 接收,到安卓 Camera HAL3 框架,任何一个环节出问题,都会导致摄像头无法正常工作,新手很容易就懵了。
今天这篇,我就用大白话给你讲透 V4L2 子系统和 RK 平台的摄像头架构,手把手带你完成OV5640/OV5693 摄像头的完整驱动适配,从设备树配置、驱动调试,到不出图、花屏的全场景问题排查,小白也能让安卓相机原生识别摄像头。
开篇先搞懂:RK 平台的摄像头系统,完整链路是什么样的?
很多新手适配摄像头,上来就瞎改设备树,连摄像头的数据流链路都没搞懂,不踩坑才怪。先给大家用大白话讲清楚,从摄像头采集画面,到安卓相机 App 显示画面,完整的链路是什么样的。
RK 平台摄像头核心架构
RK3568 的摄像头系统,核心分为三大块,从硬件到软件依次是:
- 摄像头模组(Sensor):核心是图像传感器芯片,比如 OV5640、OV5693、IMX219,负责把光信号转换成电信号,输出 MIPI CSI 格式的图像数据,是整个系统的信号源;
- MIPI CSI Host 控制器:负责接收摄像头模组输出的 MIPI 信号,把串行的图像数据转换成并行数据,传给后面的 ISP 模块;
- ISP(Image Signal Processor,图像信号处理器):RK3568 内置了独立的 ISP 模块,负责对原始的图像数据做图像处理,比如自动曝光 AE、自动白平衡 AWB、自动对焦 AF、降噪、锐化、色彩校正等等,把原始的 RAW 数据,转换成 YUV/RGB 格式的图像数据,这是 RK 平台摄像头的核心优势,不用占用 CPU 做图像处理;
- V4L2 子系统:Linux 内核里标准的视频设备驱动框架,给上层提供统一的视频设备接口,把 ISP 处理好的图像数据,传给用户空间的应用;
- 安卓 Camera HAL 层:安卓系统的摄像头硬件抽象层,对接内核的 V4L2 设备,给上层安卓相机 App 提供标准的 Camera API。
完整的图像数据流链路
- 摄像头模组上电,通过 I2C 完成初始化,输出 MIPI 格式的 RAW 图像数据;
- MIPI CSI Host 控制器接收到图像数据,传给 ISP 模块;
- ISP 模块对 RAW 数据做 3A 处理(AE/AWB/AF)和图像增强,转换成 YUV 格式的图像;
- V4L2 子系统把处理好的图像数据,封装成标准的视频帧,通过
/dev/videoX设备文件,传给用户空间; - 安卓 Camera HAL 层读取 V4L2 的视频帧,传给安卓系统的 Camera Service;
- 安卓相机 App 调用系统 Camera API,拿到视频帧,显示在屏幕上。
我们做摄像头驱动适配,核心就是做两件事:
- 配置好摄像头模组的设备树节点,让内核的 Sensor 驱动能正常初始化摄像头,输出 MIPI 图像数据;
- 配置好 RK 平台的 ISP 和 MIPI CSI 控制器,让 ISP 能正常接收和处理图像数据,生成 V4L2 视频设备。
小白必记:RK 平台适配摄像头,99% 的工作就是修改设备树,Sensor 驱动、ISP 驱动、MIPI CSI 驱动、V4L2 驱动,瑞芯微官方都已经写好了,我们不用写一行驱动代码!
一、核心框架:V4L2 子系统,大白话全解析
什么是 V4L2?
V4L2 的全称是 Video for Linux 2,是 Linux 内核里标准的视频设备驱动框架,是 V4L 的升级版,现在所有的 Linux 视频设备(摄像头、采集卡、编码器),都是基于 V4L2 框架开发的。
V4L2 的核心作用
简单说,V4L2 就是视频设备和上层应用之间的统一桥梁,它给内核驱动开发者提供了一套标准的视频驱动开发 API,给上层应用提供了一套统一的操作接口,不管是什么品牌的摄像头、什么接口的采集卡,上层应用都可以用同一套 API 来操作,不用关心底层的硬件细节。
我们适配摄像头,需要和 V4L2 打什么交道?
99% 的场景下,我们不需要修改 V4L2 的驱动代码 ,RK 官方已经把 V4L2 框架、ISP 驱动、CSI 驱动都集成好了,我们只需要在设备树里配置好摄像头模组的参数,内核会自动生成对应的/dev/videoX设备文件,上层应用可以直接通过 V4L2 的标准 API 来操作摄像头。
我们只需要知道,摄像头驱动正常加载后,会在/dev/目录下生成video0、video1这类设备文件,上层应用就是通过这些文件,来控制摄像头、获取图像数据的。
常用的 V4L2 调试工具
内核里自带了一套 V4L2 的调试工具,是我们适配摄像头的神器,最常用的有这两个:
- v4l2-ctl:V4L2 设备的控制工具,可以查看摄像头的能力、支持的分辨率、帧率、格式,设置摄像头的参数,抓取图像,是最常用的调试工具;
- media-ctl:媒体控制器工具,用来查看和配置 RK 平台的 ISP、CSI、Sensor 之间的数据流链路,RK 平台的摄像头调试必备。
二、摄像头硬件核心知识点,小白必懂
1. 核心硬件:图像传感器(Sensor)
图像传感器,也就是我们常说的摄像头模组,是整个系统的核心,它的作用就是把光信号转换成数字图像信号,通过 MIPI CSI 接口输出给 SOC。
常用的传感器分为两大类:
- RAW Sensor:输出的是原始的 RAW 格式数据,需要 ISP 做图像处理,比如 OV5640、OV5693、IMX219、IMX477,绝大多数摄像头都是这类,需要搭配 ISP 使用,RK3568 的内置 ISP 完美支持;
- YUV Sensor:内置了 ISP,直接输出 YUV 格式的图像数据,不用 SOC 的 ISP 处理,比如一些 USB 摄像头、低成本的串口摄像头,适配起来更简单,但是图像处理效果不如外置 ISP。
我们今天的实战,就以最常用的OV5640为例,500 万像素,MIPI CSI 接口,RAW Sensor,内核已经集成了完整的驱动,RK 平台完美支持。
2. 核心接口:MIPI CSI
MIPI CSI 的全称是 Mobile Industry Processor Interface Camera Serial Interface,也就是移动行业处理器接口的摄像头串行接口,是现在摄像头模组最主流的接口,和 MIPI DSI 显示接口对应,一个是输入,一个是输出。
MIPI CSI 的核心参数,和 MIPI DSI 类似,适配的时候必须搞懂:
- 通道数(Lane 数):MIPI CSI 用差分对传输数据,一个差分对就是一个 Lane,常用的有 1 Lane、2 Lane、4 Lane,分辨率越高,需要的通道数越多,比如 500 万像素的 OV5640,一般用 2 Lane;
- D-PHY 速率:每个 Lane 的传输速率,单位是 Mbps,分辨率越高、帧率越高,需要的速率就越高,OV5640 的最大速率一般是 800Mbps;
- MIPI 时钟:MIPI 传输的同步时钟,由摄像头模组输出给 SOC。
3. 控制总线:I2C/SCCB
我们要配置摄像头的寄存器、设置分辨率、帧率、开启输出,必须通过控制总线给 Sensor 芯片发送命令,绝大多数图像传感器都是用 I2C 总线控制,也叫 SCCB 总线,和 I2C 完全兼容,OV5640 就是用 I2C 控制的。
4. 核心时钟:MCLK 主时钟
摄像头模组要正常工作,必须有一个主时钟 MCLK,一般由 SOC 的时钟控制器提供,常用的频率是 24MHz,这个时钟是摄像头模组的工作时钟,必须正确配置,不然摄像头不会工作。
三、保姆级实战:OV5640 摄像头驱动完整适配,基于 RK3568
我们以 RK3568+OV5640 500 万像素摄像头为例,手把手带你完成完整的摄像头驱动适配,所有步骤一步不落,小白跟着走就能让安卓相机原生识别摄像头。
前置准备
- RK3568 开发板,OV5640 摄像头模组,MIPI CSI 接口,硬件连接正常;
- 配套的安卓 SDK,已经搭建好编译环境;
- 摄像头模组的规格书,包含 I2C 地址、MIPI 通道数、MCLK 频率、初始化参数。
步骤 1:确认硬件连接
首先,我们要确认开发板和摄像头模组的硬件连接,整理出对应的引脚和总线信息,这是设备树配置的基础,举个例子:
表格
| 功能 | 硬件连接 |
|---|---|
| MIPI CSI 接口 | OV5640 的 MIPI 接口,接 RK3568 的 MIPI CSI2 控制器,2 Lane |
| 控制总线 | OV5640 挂在 I2C3 总线上,I2C 地址 0x3C |
| MCLK 主时钟 | 由 RK3568 的 CLK_CAM_OUT0 提供,频率 24MHz |
| 复位引脚 | RK3568 的 GPIO4_PA0,低电平复位 |
| 掉电引脚 | RK3568 的 GPIO4_PA1,高电平掉电 |
步骤 2:修改设备树,添加摄像头配置
RK3568 的摄像头配置,都在板级.dts 文件里,我们需要添加摄像头模组节点、MIPI CSI 控制器节点、ISP 节点的配置,每一行都加了注释,你只需要把参数换成你自己的摄像头就行。
打开你的板级.dts 文件,添加下面的配置:
dts
// 1. 摄像头供电引脚配置,根据你的开发板修改
&vcc2v8_dvp {
regulator-always-on;
regulator-boot-on;
};
&vcc1v8_dvp {
regulator-always-on;
regulator-boot-on;
};
// 2. 使能I2C3控制器,OV5640挂在I2C3上
&i2c3 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&i2c3_xfer>;
// OV5640摄像头模组节点
ov5640: ov5640@3c {
compatible = "ovti,ov5640"; // 和内核驱动的compatible匹配
reg = <0x3c>; // OV5640的I2C地址,一般是0x3C
status = "okay";
// 时钟配置,24MHz主时钟
clocks = <&cru CLK_CAM_OUT0>;
clock-names = "xvclk";
assigned-clocks = <&cru CLK_CAM_OUT0>;
assigned-clock-rates = <24000000>; // 24MHz MCLK
// 复位和掉电引脚
pwdn-gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio4 RK_PA0 GPIO_ACTIVE_LOW>;
// 摄像头参数
rotation = <0>; // 摄像头旋转角度
orientation = <0>; // 安卓摄像头方向
// MIPI通道数,2 Lane
data-lanes = <1 2>;
// 支持的最大帧率和分辨率
max-fps = <30>;
// 像素大小,单位微米
pixel-size = <1400 1400>;
// 端口节点,绑定MIPI CSI控制器
port {
ov5640_out: endpoint {
remote-endpoint = <&mipi_csi2_in>; // 对应下面的CSI2输入端口
data-lanes = <1 2>;
clock-noncontinuous; // 非连续时钟模式,根据摄像头修改
};
};
};
};
// 3. 使能MIPI CSI2控制器,接收摄像头的MIPI信号
&csi2_dphy0 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_csi2_in: endpoint@0 {
reg = <0>;
remote-endpoint = <&ov5640_out>; // 对应上面的摄像头输出端口
data-lanes = <1 2>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
csiphy_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&isp_in>; // 对应ISP的输入端口
};
};
};
};
// 4. 使能ISP模块,处理图像数据
&rkcif_mipi_lvds {
status = "okay";
port {
isp_in: endpoint {
remote-endpoint = <&csiphy_out>; // 对应CSI2的输出端口
};
};
};
&rkcif_mipi_lvds_sditf {
status = "okay";
};
// 5. 使能RK ISP虚拟设备,给V4L2提供接口
&rkisp_vir0 {
status = "okay";
};
核心配置讲解
- compatible = "ovti,ov5640":这个是核心,必须和内核里 OV5640 驱动的 compatible 属性完全一致,内核才能匹配到对应的驱动,完成摄像头的初始化;
- 时钟配置 :
assigned-clock-rates = <24000000>,设置 MCLK 主时钟为 24MHz,这个必须和摄像头规格书里的要求一致,不然摄像头不会工作; - data-lanes = <1 2>:配置 MIPI 通道数为 2 Lane,必须和摄像头模组的硬件一致,1 Lane 就写 <1>,4 Lane 就写 < 1 2 3 4>;
- port/endpoint 节点:这是设备树里的端口绑定机制,用来把摄像头的输出端口,和 MIPI CSI 的输入端口绑定,再把 CSI 的输出端口和 ISP 的输入端口绑定,打通整个数据流链路,必须一一对应,不能写错;
- pwdn-gpios/reset-gpios:摄像头的掉电和复位引脚,必须和硬件接线一致,驱动会通过这两个引脚,完成摄像头的上电复位流程。
步骤 3:内核配置,开启 OV5640 驱动和 ISP 相关配置
默认情况下,RK SDK 的内核已经开启了 OV5640 驱动和 ISP 相关配置,如果没开启,我们只需要修改内核配置,开启对应的选项:
-
进入内核目录,执行菜单配置: bash
运行
cd kernel make ARCH=arm64 menuconfig -
进入以下路径,开启对应的驱动和配置: plaintext
Device Drivers ---> Multimedia support ---> [*] Cameras/video grabbers support [*] Media controller support V4L2 sub-device userspace API [*] Rockchip Image Signal Processor (ISP) support I2C Encoders, decoders, sensors and other helper chips ---> <*> OmniVision OV5640 sensor support -
保存配置,重新编译内核和设备树: bash
运行
cd ../ ./build.sh -CKu
步骤 4:编译烧录,验证驱动是否正常工作
-
把编译好的 boot.img 和 update.img 烧录到开发板,重启;
-
重启完成后,进入 ADB shell,验证摄像头驱动是否正常加载: bash
运行
adb shell su dmesg | grep ov5640能看到 OV5640 的驱动初始化日志,没有报错,说明摄像头已经被正常识别,驱动加载成功了;
-
验证 V4L2 视频设备是否生成: bash
运行
ls /dev/video*能看到
/dev/video0、/dev/video1等设备文件,说明 V4L2 设备已经正常生成; -
用 v4l2-ctl 工具查看摄像头的能力: bash
运行
v4l2-ctl --list-devices能看到我们的 RK ISP 设备,对应的 video 节点,说明摄像头已经被 V4L2 子系统正常识别;
-
查看摄像头支持的分辨率和格式: bash
运行
v4l2-ctl -d /dev/video0 --list-formats-ext能看到 OV5640 支持的分辨率、帧率、格式,比如 2592x1944@15fps、1920x1080@30fps,说明驱动完全正常;
-
用 v4l2-ctl 抓取一帧图像,测试摄像头是否能正常出图: bash
运行
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=NV12 --stream-mmap=3 --stream-count=1 --stream-to=/data/test.yuv执行完成后,会在 /data 目录下生成 test.yuv 图像文件,说明摄像头能正常采集图像,驱动完全正常!
步骤 5:安卓系统相机适配
如果 v4l2-ctl 能正常抓取图像,但是安卓系统的相机 App 识别不到摄像头,说明安卓的 Camera HAL 层没有适配好,我们只需要修改两个配置文件就行:
- camera3_profiles.xml :这个文件是安卓 Camera HAL3 的配置文件,在 SDK 的
device/rockchip/common/camera/目录下,添加我们的 OV5640 摄像头的配置,包括支持的分辨率、格式、3A 参数; - camera_config.xml:修改摄像头的数量、ID、类型,对应我们的摄像头;
- 重新编译安卓系统,烧录 update.img,打开安卓系统的相机 App,就能看到摄像头的画面了,原生支持拍照、录像、切换分辨率!
四、小白摄像头驱动必踩的坑,全场景问题排查指南
摄像头驱动最常见的问题,就是识别不到、不出图、花屏、画面偏色,我把所有问题的排查步骤,按优先级给你整理好了,按这个顺序查,99% 的问题都能解决。
问题 1:摄像头完全识别不到,驱动没加载
按这个顺序排查:
- 先查硬件 :
- 测量摄像头的供电引脚,有没有 3.3V/1.8V 供电,没供电先查电源电路,确保设备树里的供电 regulator 已经开启;
- 测量 MCLK 主时钟,有没有 24MHz 的时钟输出,没时钟先查设备树里的时钟配置,确认时钟已经正确使能;
- 测量 I2C 总线,用
i2cdetect能不能扫到摄像头的 I2C 地址 0x3C,扫不到就是 I2C 接线错了,或者设备树里的 I2C 控制器没使能,或者摄像头没上电、没复位; - 测量复位引脚,上电的时候有没有正常的复位电平变化,没复位先查复位引脚的设备树配置,确认极性正确。
- 再查驱动和设备树 :
- 看内核日志,搜索
ov5640、i2c相关的日志,看有没有报错,比如 I2C 读写失败、摄像头 ID 读取失败、驱动匹配失败; - 检查设备树里的 compatible 属性,是不是和内核驱动里的完全一致,
ovti,ov5640不能写错; - 检查 I2C 地址是不是正确,和摄像头规格书里的一致,OV5640 的地址一般是 0x3C,也有 0x30 的;
- 检查摄像头的 port/endpoint 节点,是不是和 MIPI CSI 的节点正确绑定,remote-endpoint 有没有写错。
- 看内核日志,搜索
- 查内核配置:确认内核里已经开启了 OV5640 的驱动、ISP 驱动、V4L2 相关配置,没开启的话,驱动根本不会编译进内核,当然识别不到。
问题 2:驱动加载成功,但是不出图,v4l2-ctl 抓取不到图像
- MIPI 链路不通 :
- 检查 MIPI CSI 控制器的 status 是不是 okay,有没有正常使能;
- 检查 MIPI 通道数是不是和摄像头一致,2 Lane 的摄像头不能配成 1 Lane;
- 检查 MIPI 的时钟模式,是连续还是非连续,必须和摄像头规格书里的一致;
- 看内核日志,搜索
csi、mipi、isp相关的日志,看有没有 MIPI 信号丢失、同步失败的报错。
- 摄像头初始化失败 :
- 虽然驱动加载了,但是摄像头的初始化命令不对,没有正常输出 MIPI 信号,看内核日志里有没有摄像头 ID 读取成功的日志,ID 不对的话,初始化会失败;
- 摄像头的上电复位时序不对,导致初始化失败,调整设备树里的复位延时参数。
- ISP 链路没打通 :
- 检查 ISP 相关的节点是不是都使能了,
rkcif_mipi_lvds、rkisp_vir0的 status 是不是 okay; - 用
media-ctl -p命令,查看整个媒体链路,确认摄像头、CSI、ISP 之间的链路已经正确连接,没有断开。
- 检查 ISP 相关的节点是不是都使能了,
问题 3:能出图,但是花屏、画面错乱
99% 的原因是MIPI 配置不对:
- MIPI D-PHY 速率不够,超过了摄像头的最大值,或者太低,导致数据传输错误,降低或者提高速率试试;
- MIPI 通道数不对,比如 4 Lane 的摄像头,只配了 2 Lane,导致数据传输不完整,花屏;
- 分辨率配置不对,摄像头输出的分辨率,和 ISP 接收的分辨率不一致,导致画面错乱;
- MIPI 接线有问题,差分对接反了,或者干扰太大,导致数据错误,换短一点的 MIPI 排线,做好屏蔽。
问题 4:能出图,但是画面偏色、发黑、发白
核心原因是ISP 的 3A 算法没正常工作:
- 摄像头的 sensor_config 配置不对,ISP 的 3A 参数没有匹配对应的摄像头,修改 camera3_profiles.xml 里的 3A 参数;
- 摄像头的镜头、滤光片不对,导致色彩偏差,更换匹配的镜头;
- 自动曝光 AE 没正常工作,画面发黑 / 发白,调整 AE 的参数,看内核日志里有没有 AE 相关的报错;
- 自动白平衡 AWB 没正常工作,画面偏色,调整 AWB 的参数。
结尾说两句
这篇文章,我们彻底搞懂了 V4L2 子系统和 RK 平台的摄像头架构,完成了 OV5640 摄像头的完整驱动适配,实现了安卓相机的原生调用,还给了你一套万能的摄像头问题排查指南,以后再遇到摄像头不出图、花屏的问题,再也不用瞎改了。
下一篇,我们进入第五卷的收官内容,安卓驱动调试神器,全流程问题排查指南,把我们前面所有篇章用到的调试工具、排查思路,做一个完整的汇总,给你一套万能的驱动问题排查流程,以后遇到任何驱动问题,都知道从哪里下手。
我是黒漂技术佬,关注我,带你零基础入门 RK 安卓驱动开发,不踩坑。有任何摄像头驱动的问题,评论区留言,我都会一一回复