RK3588 + OV13850/OV5645 MIPI摄像头 完整开发指南
·
/** * @file rk3588_ov13850/ov5645.c * @brief RK3588平台 OV13850/OV5645 MIPI摄像头完整开发指南 * @author zhilin_tang Technology * * 摄像头型号: * - OV13850: 13MP 高性能摄像头,支持MIPI CSI-2 * - OV5645: 5MP 主流摄像头,支持MIPI CSI-2 * * 本分析涵盖: * 1. 硬件引脚连接与设备树配置 * 2. 驱动源码目录树形分析 * 3. 关键函数树形分析 * 4. Pipeline数据流树形分析 * 5. 调试流程与工具树形分析 */
一、摄像头工作原理与硬件连接
1.1 MIPI CSI-2 协议与摄像头通信
MIPI CSI-2是专门为摄像头设计的高速串行接口协议,其核心工作原理如下:
【MIPI CSI-2 通信架构】 ┌─────────────────────────────────────────────────────────────────────────────┐ │ OV13850/OV5645 摄像头 │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 图像传感器 (Image Sensor) │ │ │ │ 光信号 → 数字信号 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 8B/10B 编码器 │ │ │ │ 原始8bit数据 → 查表编码 → 10bit数据(保证时钟跳变) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ MIPI D-PHY 发送器 │ │ │ │ 差分信号:CLK+/- , DATA0+/- , DATA1+/- , ... │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ 差分信号线 ┌─────────────────────────────────────────────────────────────────────────────┐ │ RK3588 主控 │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ MIPI D-PHY 接收器 │ │ │ │ 差分信号 → 时钟恢复 → 10bit数据流 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ MIPI CSI-2 协议解析器 │ │ │ │ 识别帧头/帧尾 → 提取有效像素数据 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ VICAP (视频捕获单元) │ │ │ │ 数据格式转换 → 内存存储 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘
1.2 为什么需要I2C控制通道
MIPI CSI-2仅负责高速图像数据传输,不承载控制指令。因此需要独立的I2C总线进行摄像头配置:
【双通道架构】 ┌─────────────────────────────────────────────────────────────────────────────┐ │ RK3588 │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ │ │ MIPI D-PHY │ │ I2C 控制器 │ │ │ │ │ │ (高速数据通道) │ │ (低速控制通道) │ │ │ │ │ │ 1对时钟 + 2对数据 │ │ SCL / SDA │ │ │ │ │ └──────────┬──────────┘ └──────────┬──────────┘ │ │ │ └─────────────┼───────────────────────────────┼────────────────────────┘ │ │ │ │ │ │ ↓ ↓ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ ↓ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ OV13850/OV5645 摄像头 │ │ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ │ │ MIPI CSI-2 发送器 │ │ I2C 从设备 │ │ │ │ (图像数据输出) │ │ (寄存器配置接口) │ │ │ └─────────────────────────────┘ └─────────────────────────────┘ │ │ │ │ 功能:传输像素数据 功能:接收配置命令 │ │ - 分辨率设置 - 曝光时间 │ │ - 帧率控制 - 增益设置 │ │ - 格式选择 - 白平衡 │ └─────────────────────────────────────────────────────────────────────────────┘
1.3 RK3588 MIPI CSI硬件资源
RK3588内部有3个独立的MIPI D-PHY控制器:
【RK3588 MIPI CSI硬件资源】 ┌─────────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ 控制器名称 │ 寄存器基地址 │ 最大Lane数 │ 说明 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ csi2_dcphy0 │ 0xFED90000 │ 4 lanes │ 支持DPHY/CPHY双协议 │ │ csi2_dcphy1 │ 0xFED91000 │ 4 lanes │ 支持DPHY/CPHY双协议 │ │ mipi_dcphy0 │ 0xFED92000 │ 4 lanes │ 专门用于高速摄像头 │ └─────────────────┴─────────────────┴─────────────────┴─────────────────────────────┘ 【硬件连接模式】 - Full Mode: 使用全部4条数据lane,连接一个摄像头 - Split Mode: 拆分成2个2-lane接口,可同时连接两个摄像头
1.4 引脚连接表
根据RK3588开发板设计,OV13850和OV5645的典型引脚连接如下:
【RK3588 ←→ OV13850/OV5645 连接表】 ┌─────────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ 信号名称 │ RK3588 引脚 │ OV13850/5645 │ 功能描述 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ MIPI 数据通道 │ │ │ │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ MIPI_RX_D0P │ MIPI_CSI0_RXD0_P│ DATA0+ │ 数据通道0正 │ │ MIPI_RX_D0N │ MIPI_CSI0_RXD0_N│ DATA0- │ 数据通道0负 │ │ MIPI_RX_D1P │ MIPI_CSI0_RXD1_P│ DATA1+ │ 数据通道1正 │ │ MIPI_RX_D1N │ MIPI_CSI0_RXD1_N│ DATA1- │ 数据通道1负 │ │ MIPI_RX_CLKP │ MIPI_CSI0_RXCLK_P│ CLK+ │ 时钟正 │ │ MIPI_RX_CLKN │ MIPI_CSI0_RXCLK_N│ CLK- │ 时钟负 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ I2C 控制接口 │ │ │ │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ I2C_SCL │ I2C2_SCL_M0 │ SCL │ I2C 时钟线 │ │ I2C_SDA │ I2C2_SDA_M0 │ SDA │ I2C 数据线 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ 电源和控制 │ │ │ │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ MCLK │ GPIO0_C4 │ XVCLK │ 24MHz主时钟 │ │ RESET │ GPIO4_C6 │ RESET │ 复位信号 │ │ PWDN │ GPIO4_C7 │ PWDN │ 掉电控制 │ │ AVDD │ VCC2V8_CAM │ AVDD │ 模拟电源 2.8V │ │ DOVDD │ VCC1V8_CAM │ DOVDD │ I/O电源 1.8V │ │ DVDD │ VCC1V2_CAM │ DVDD │ 数字核心电源 1.2V │ └─────────────────┴─────────────────┴─────────────────┴─────────────────────────────┘
二、设备树完整配置
2.1 摄像头模块设备树 rk3588/ov13850.dtsi
/**
* @file rk3588-ov13850.dtsi
* @brief RK3588 OV13850/OV5645 MIPI摄像头设备树配置
* @author zhilin_tang Technology
*
* @note 设计模式分析:采用"复合模式(Composite Pattern)"
* 将摄像头传感器、MIPI PHY、CSI控制器、VICAP、ISP
* 组成完整的摄像头数据通路。
*
* @remark 参考文档:迅为RK3588开发板摄像头使用手册
*/
/dts-v1/;
/plugin/;
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/clock/rk3588-cru.h>
/ {
compatible = "rockchip,rk3588";
/* ========== 1. 摄像头电源定义 ========== */
vcc_cam_avdd: vcc-cam-avdd {
compatible = "regulator-fixed";
regulator-name = "vcc_cam_avdd";
regulator-min-microvolt = <2800000>;
regulator-max-microvolt = <2800000>;
regulator-always-on;
status = "okay";
};
vcc_cam_dovdd: vcc-cam-dovdd {
compatible = "regulator-fixed";
regulator-name = "vcc_cam_dovdd";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
status = "okay";
};
vcc_cam_dvdd: vcc-cam-dvdd {
compatible = "regulator-fixed";
regulator-name = "vcc_cam_dvdd";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-always-on;
status = "okay";
};
/* ========== 2. 摄像头时钟定义 ========== */
ext_cam_clk: ext-cam-clk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>; /* 24MHz 主时钟 */
};
};
/* ========== 3. I2C 控制器配置 (连接摄像头控制接口) ========== */
&i2c2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c2m0_xfer>;
clock-frequency = <400000>;
/* OV13850 摄像头传感器节点 */
ov13850: ov13850@10 {
compatible = "ovti,ov13850";
reg = <0x10>; /* I2C 设备地址 0x10 */
/* 电源供应 */
AVDD-supply = <&vcc_cam_avdd>;
DOVDD-supply = <&vcc_cam_dovdd>;
DVDD-supply = <&vcc_cam_dvdd>;
/* GPIO 控制 */
reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
pwdn-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_LOW>;
/* 时钟配置 */
clocks = <&ext_cam_clk>;
clock-names = "xvclk";
/* 旋转方向 */
rotation = <0>;
/* 支持的MIPI Lane数 */
lane = <2>; /* 2-lane MIPI */
/* 电源时序延迟 */
power-on-delay = <50>;
power-off-delay = <50>;
reset-delay = <10>;
port {
ov13850_out: endpoint {
remote-endpoint = <&mipi_in_ov13850>;
data-lanes = <1 2>; /* 使用 lane1 和 lane2 */
};
};
};
/* OV5645 摄像头传感器节点 (备选) */
ov5645: ov5645@3c {
compatible = "ovti,ov5645";
reg = <0x3c>; /* I2C 设备地址 0x3C */
status = "disabled";
AVDD-supply = <&vcc_cam_avdd>;
DOVDD-supply = <&vcc_cam_dovdd>;
DVDD-supply = <&vcc_cam_dvdd>;
reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
pwdn-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_LOW>;
clocks = <&ext_cam_clk>;
clock-names = "xvclk";
rotation = <0>;
lane = <2>;
power-on-delay = <50>;
reset-delay = <10>;
port {
ov5645_out: endpoint {
remote-endpoint = <&mipi_in_ov5645>;
data-lanes = <1 2>;
};
};
};
};
/* ========== 4. MIPI CSI D-PHY 配置 ========== */
&csi2_dcphy0 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
/* 输入端口:连接摄像头传感器 */
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_in_ov13850: endpoint@0 {
reg = <0>;
remote-endpoint = <&ov13850_out>;
data-lanes = <1 2>;
};
mipi_in_ov5645: endpoint@1 {
reg = <1>;
remote-endpoint = <&ov5645_out>;
data-lanes = <1 2>;
status = "disabled";
};
};
/* 输出端口:连接CSI主机 */
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
mipi_out_csi2: endpoint {
remote-endpoint = <&csi2_in_mipi>;
};
};
};
};
/* ========== 5. MIPI CSI2 主机配置 ========== */
&mipi0_csi2 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
/* 输入端口:连接D-PHY */
port@0 {
reg = <0>;
csi2_in_mipi: endpoint {
remote-endpoint = <&mipi_out_csi2>;
};
};
/* 输出端口:连接VICAP */
port@1 {
reg = <1>;
csi2_out_vicap: endpoint {
remote-endpoint = <&vicap_in_csi2>;
};
};
};
};
/* ========== 6. VICAP 视频捕获单元配置 ========== */
&rkcif {
status = "okay";
};
&rkcif_mipi_lvds {
status = "okay";
port {
vicap_in_csi2: endpoint {
remote-endpoint = <&csi2_out_vicap>;
};
};
};
&rkcif_mipi_lvds_sditf {
status = "okay";
port {
vicap_sditf_out: endpoint {
remote-endpoint = <&isp_in_vicap>;
};
};
};
/* ========== 7. ISP 图像信号处理器配置 ========== */
&rkisp {
status = "okay";
};
&rkisp_vir0 {
status = "okay";
port {
isp_in_vicap: endpoint {
remote-endpoint = <&vicap_sditf_out>;
};
};
};
/* ========== 8. 引脚复用配置 ========== */
&pinctrl {
camera {
cam_pwdn_pin: cam-pwdn-pin {
rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
};
cam_rst_pin: cam-rst-pin {
rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
};
cam_mclk_pin: cam-mclk-pin {
rockchip,pins = <0 RK_PC4 RK_FUNC_1 &pcfg_pull_none>;
};
};
mipi {
mipi_csi0_pins: mipi-csi0-pins {
rockchip,pins =
<2 RK_PB0 RK_FUNC_1 &pcfg_pull_none>, /* MIPI_RX_D0P */
<2 RK_PB1 RK_FUNC_1 &pcfg_pull_none>, /* MIPI_RX_D0N */
<2 RK_PB2 RK_FUNC_1 &pcfg_pull_none>, /* MIPI_RX_D1P */
<2 RK_PB3 RK_FUNC_1 &pcfg_pull_none>, /* MIPI_RX_D1N */
<2 RK_PB4 RK_FUNC_1 &pcfg_pull_none>, /* MIPI_RX_CLKP */
<2 RK_PB5 RK_FUNC_1 &pcfg_pull_none>; /* MIPI_RX_CLKN */
};
};
};
2.2 OV13850 和 OV5645 的差异配置
/** * OV13850 和 OV5645 的主要差异 * * ┌─────────────┬─────────────────┬─────────────────┐ * │ 参数 │ OV13850 │ OV5645 │ * ├─────────────┼─────────────────┼─────────────────┤ * │ 分辨率 │ 4224×3136 (13MP)│ 2592×1944 (5MP)│ * │ I2C地址 │ 0x10 │ 0x3C │ * │ 帧率 │ 30fps @ 1080p │ 30fps @ 1080p │ * │ 功耗 │ 约 200mW │ 约 150mW │ * └─────────────┴─────────────────┴─────────────────┘ */
三、驱动源码目录树形分析
3.1 Rockchip VIN 驱动架构
根据Rockchip官方文档,VIN (Video Input) 驱动由三个层次组成:
【Rockchip VIN 驱动架构树】 drivers/media/platform/rockchip/ │ ├── rockchip-vin/ # VIN 驱动主目录 ★★★ │ ├── rockchip-vin-core.c # VIN 核心驱动 │ ├── rockchip-vin-mipi.c # MIPI CSI 接口处理 │ ├── rockchip-vin-dvp.c # DVP 并口处理 │ ├── rockchip-vin-csi2.c # CSI2 协议解析 │ └── rockchip-vin-reg.c # 寄存器操作 │ ├── rockchip-isp/ # ISP 驱动目录 ★★★ │ ├── rockchip-isp.c # ISP 核心驱动 │ ├── rockchip-isp-subdev.c # ISP 子设备 │ ├── rockchip-isp-params.c # ISP 参数配置 │ └── rockchip-isp-stats.c # ISP 统计信息 │ └── rockchip-cif/ # CIF 摄像头接口驱动 ├── rockchip-cif.c └── rockchip-cif-reg.c 【Sensor 驱动目录】 drivers/media/i2c/ │ ├── ov13850.c # OV13850 传感器驱动 ★★★ │ ├── ov13850_probe() # I2C 设备探测 │ ├── ov13850_s_stream() # 视频流控制 │ ├── ov13850_set_fmt() # 格式设置 │ └── ov13850_set_ctrl() # 控制设置 │ ├── ov5645.c # OV5645 传感器驱动 ★★★ │ ├── ov5645_probe() │ ├── ov5645_s_stream() │ ├── ov5645_set_fmt() │ └── ov5645_set_ctrl() │ └── ov13850_regs.h # OV13850 寄存器定义
3.2 驱动内部结构树
【ov13850.c 驱动内部结构树】
ov13850.c
│
├── 1. 寄存器定义区
│ ├── OV13850_REG_CHIP_ID # 芯片ID寄存器 (0x300A)
│ ├── OV13850_REG_MODE_SELECT # 模式选择寄存器
│ ├── OV13850_REG_FORMAT1 # 格式寄存器1
│ ├── OV13850_REG_EXPOSURE # 曝光寄存器
│ └── OV13850_REG_GAIN # 增益寄存器
│
├── 2. 模式配置表 (分辨率/帧率)
│ ├── ov13850_mode_4224_3136_30fps # 13MP@30fps
│ ├── ov13850_mode_1920_1080_30fps # 1080p@30fps
│ ├── ov13850_mode_1280_720_60fps # 720p@60fps
│ └── ov13850_mode_640_480_120fps # VGA@120fps
│
├── 3. 私有数据结构体
│ ├── struct ov13850_device
│ │ ├── struct v4l2_subdev *sd # V4L2 子设备
│ │ ├── struct i2c_client *client # I2C 客户端
│ │ ├── struct clk *xvclk # 主时钟
│ │ ├── struct gpio_desc *reset_gpio # 复位GPIO
│ │ ├── struct gpio_desc *pwdn_gpio # 掉电GPIO
│ │ └── struct v4l2_ctrl_handler ctrl_handler # 控制句柄
│
├── 4. 寄存器读写函数
│ ├── ov13850_read_reg() # 读寄存器
│ ├── ov13850_write_reg() # 写寄存器
│ └── ov13850_update_bits() # 更新位
│
├── 5. V4L2 子设备操作函数
│ ├── ov13850_s_power() # 电源控制
│ ├── ov13850_s_stream() # 流控制 ★★★
│ ├── ov13850_g_frame_interval() # 获取帧间隔
│ ├── ov13850_s_frame_interval() # 设置帧间隔
│ └── ov13850_enum_mbus_code() # 枚举格式
│
├── 6. 格式设置函数
│ ├── ov13850_set_fmt() # 设置格式
│ ├── ov13850_get_fmt() # 获取格式
│ └── ov13850_try_fmt() # 尝试格式
│
├── 7. 控制函数 (曝光/增益/白平衡)
│ ├── ov13850_set_exposure() # 设置曝光时间
│ ├── ov13850_set_gain() # 设置增益
│ ├── ov13850_set_white_balance() # 设置白平衡
│ └── ov13850_set_banding_filter() # 防闪烁
│
├── 8. 初始化序列
│ └── ov13850_init_seq[] # 初始化寄存器表
│
├── 9. I2C 驱动结构体
│ ├── ov13850_i2c_probe() # I2C 探测
│ ├── ov13850_i2c_remove() # 移除
│ └── struct i2c_driver ov13850_i2c_driver
│
└── 10. 模块初始化
└── module_i2c_driver()
四、关键函数树形分析
4.1 传感器 Probe 函数
/**
* @brief OV13850 I2C 设备探测函数
* @param client I2C 客户端
* @param id 设备ID
* @return 0 成功,负数错误码
*
* @note 设计模式分析:采用"建造者模式(Builder Pattern)"
* 逐步构建传感器驱动私有数据,完成资源分配、硬件初始化、
* V4L2子设备注册等步骤。
*
* @remark 关键点:需要正确读取芯片ID验证硬件连接
*/
static int ov13850_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct ov13850_device *ov13850;
struct v4l2_subdev *sd;
int ret;
/* ========== 步骤1:分配私有数据 ========== */
ov13850 = devm_kzalloc(dev, sizeof(*ov13850), GFP_KERNEL);
if (!ov13850)
return -ENOMEM;
ov13850->client = client;
/* ========== 步骤2:获取时钟资源 ========== */
ov13850->xvclk = devm_clk_get(dev, "xvclk");
if (IS_ERR(ov13850->xvclk)) {
dev_err(dev, "Failed to get xvclk\n");
return PTR_ERR(ov13850->xvclk);
}
/* ========== 步骤3:获取 GPIO 资源 ========== */
ov13850->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ov13850->reset_gpio)) {
dev_err(dev, "Failed to get reset gpio\n");
return PTR_ERR(ov13850->reset_gpio);
}
ov13850->pwdn_gpio = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_LOW);
if (IS_ERR(ov13850->pwdn_gpio)) {
dev_err(dev, "Failed to get pwdn gpio\n");
return PTR_ERR(ov13850->pwdn_gpio);
}
/* ========== 步骤4:电源上电和复位 ========== */
ret = ov13850_power_on(ov13850);
if (ret) {
dev_err(dev, "Failed to power on sensor\n");
return ret;
}
/* ========== 步骤5:读取芯片ID验证 ========== */
ret = ov13850_check_chip_id(ov13850);
if (ret) {
dev_err(dev, "Failed to read chip ID\n");
goto err_power_off;
}
/* ========== 步骤6:初始化 V4L2 子设备 ========== */
sd = &ov13850->sd;
v4l2_i2c_subdev_init(sd, client, &ov13850_subdev_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
/* ========== 步骤7:注册控制句柄 ========== */
ov13850_init_controls(ov13850);
/* ========== 步骤8:注册子设备 ========== */
ret = v4l2_async_register_subdev(sd);
if (ret) {
dev_err(dev, "Failed to register subdev\n");
goto err_free_ctrl;
}
dev_info(dev, "OV13850 sensor probe success\n");
return 0;
err_free_ctrl:
v4l2_ctrl_handler_free(&ov13850->ctrl_handler);
err_power_off:
ov13850_power_off(ov13850);
return ret;
}
4.2 流控制函数 (关键)
/**
* @brief 视频流控制函数
* @param sd V4L2子设备
* @param enable 1: 开始流, 0: 停止流
* @return 0 成功,负数错误码
*
* @note 设计模式分析:采用"状态模式(State Pattern)"
* 根据enable参数切换传感器工作状态。
*
* @remark 重要:必须在MIPI控制器准备好后才能使能传感器输出
*/
static int ov13850_s_stream(struct v4l2_subdev *sd, int enable)
{
struct ov13850_device *ov13850 = to_ov13850(sd);
int ret = 0;
mutex_lock(&ov13850->mutex);
if (enable) {
/* ========== 开始视频流 ========== */
/* 1. 根据当前选择的模式配置传感器寄存器 */
ret = ov13850_set_mode(ov13850);
if (ret)
goto out;
/* 2. 配置MIPI输出 */
ret = ov13850_set_mipi(ov13850);
if (ret)
goto out;
/* 3. 使能传感器输出 (关键:此时MIPI信号开始输出) */
ret = ov13850_write_reg(ov13850, OV13850_REG_MODE_SELECT, 0x01);
if (ret)
goto out;
ov13850->streaming = true;
dev_dbg(sd->dev, "Stream started\n");
} else {
/* ========== 停止视频流 ========== */
/* 关闭传感器输出 */
ret = ov13850_write_reg(ov13850, OV13850_REG_MODE_SELECT, 0x00);
if (ret)
goto out;
ov13850->streaming = false;
dev_dbg(sd->dev, "Stream stopped\n");
}
out:
mutex_unlock(&ov13850->mutex);
return ret;
}
4.3 MIPI参数计算
/**
* @brief 计算MIPI链路参数
* @param mode 显示模式
* @return 计算出的link_freq值
*
* @note 设计模式分析:采用"策略模式(Strategy Pattern)"
* 根据不同的分辨率/帧率计算对应的MIPI时钟频率。
*
* @remark link_freq计算公式:
* link_freq = width * height * fps * bits_per_pixel / lanes / 2
*/
static u32 ov13850_calc_link_freq(struct ov13850_mode *mode)
{
u32 link_freq;
/* 计算公式:
* 像素时钟 = 宽 × 高 × 帧率
* 单lane速率 = 像素时钟 × 位深 / lane数
* link_freq = 单lane速率 / 2 (DDR传输,每时钟2个数据)
*/
link_freq = mode->width * mode->height * mode->fps *
mode->bpp / mode->lanes / 2;
/* 向上取整到最近的合理频率 */
link_freq = (link_freq + 50000000) / 100000000 * 100000000;
return link_freq;
}
五、Pipeline数据流树形分析
5.1 完整数据流架构
根据RK3588摄像头子系统设计,数据流经过以下层次:
【RK3588 摄像头数据流 Pipeline】
┌─────────────────────────────────────────────────────────────────────────────┐
│ 硬件层 (Hardware) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ OV13850/OV5645 传感器 │ │
│ │ - 光信号 → 数字信号 │ │
│ │ - 8B/10B编码 │ │
│ │ - MIPI D-PHY 输出 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ MIPI 差分信号 │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ RK3588 MIPI D-PHY (csi2_dcphy0) │ │
│ │ - 时钟恢复 (CDR) │ │
│ │ - 串并转换 │ │
│ │ - 10bit → 8bit 解码 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MIPI CSI-2 协议解析 (mipi0_csi2) │ │
│ │ - 识别帧起始 (FS) / 帧结束 (FE) │ │
│ │ - 提取像素数据 │ │
│ │ - 错误检测 (CRC/ECC) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ VICAP 视频捕获单元 (rkcif_mipi_lvds) │ │
│ │ - 数据格式转换 │ │
│ │ - DMA 传输到内存 │ │
│ │ - 生成 /dev/videoX 节点 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
↓ (RAW数据)
┌─────────────────────────────────────────────────────────────────────────────┐
│ 内核空间 (Kernel) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ VICAP → ISP 桥接 (rkcif_mipi_lvds_sditf) │ │
│ │ - 将VICAP数据导入ISP处理 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ISP 图像信号处理器 (rkisp_vir0) │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ 图像处理流水线 │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │
│ │ │ │ 黑电平 │→│ 坏点校正 │→│ 镜头阴影 │→│ 去马赛克 │ │ │ │
│ │ │ │ 校正(BLC)│ │ (DPCC) │ │ (LSC) │ │ │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │
│ │ │ ↓ ↓ ↓ ↓ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │
│ │ │ │ 降噪 │→│ 色彩校正 │→│ 伽马校正 │→│ 缩放 │ │ │ │
│ │ │ │ (2D/3D) │ │ (CCM) │ │ (Gamma) │ │ │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 输出格式转换 │ │
│ │ - RAW → YUV/RGB │ │
│ │ - 生成最终视频节点 /dev/videoX │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────────┐
│ 用户空间 (User Space) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 应用程序 / 测试工具 │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ V4L2应用 │ │ GStreamer │ │ OpenCV │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
5.2 数据流阶段详解
| 阶段 | 组件 | 输入 | 输出 | 说明 |
|---|---|---|---|---|
| 1. 感光 | OV13850/5645 | 光信号 | RAW10/RAW12 | 传感器将光转换为数字信号 |
| 2. 编码 | 传感器内部 | RAW数据 | 8B/10B编码 | 保证时钟跳变,便于接收端同步 |
| 3. 传输 | MIPI D-PHY | 并行数据 | 差分串行信号 | 4 lane高速传输 |
| 4. 接收 | RK3588 D-PHY | 差分信号 | 并行数据 | 时钟恢复和串并转换 |
| 5. 解析 | CSI-2主机 | MIPI包 | 像素数据 | 解析帧头/帧尾,提取有效数据 |
| 6. 捕获 | VICAP | 像素数据 | DMA到内存 | 生成 /dev/video 节点 |
| 7. 处理 | ISP | RAW数据 | YUV/RGB | 图像增强、降噪、色彩校正 |
六、调试流程与工具树形分析
6.1 调试工具树
【摄像头调试工具树】 media-ctl (媒体控制器工具) │ ├── media-ctl -p -d /dev/media0 # 打印媒体拓扑结构 ★★★ ├── media-ctl -l -d /dev/media0 # 查看当前链路 ├── media-ctl --set-link '"ov13850 1-0010":0->"csi2_dcphy0":0[1]' # 设置链路 └── media-ctl -d /dev/media0 -e "rkisp_mainpath" # 查看ISP主通路 v4l2-ctl (V4L2控制工具) │ ├── v4l2-ctl --list-devices # 列出所有视频设备 ★★★ ├── v4l2-ctl -d /dev/video0 --all # 查看设备详细信息 ├── v4l2-ctl --list-formats-ext -d /dev/video0 # 列出支持格式 ★★★ ├── v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=NV12 # 设置格式 ├── v4l2-ctl --stream-mmap --stream-count=1 --stream-to=frame.raw # 采集一帧 ★★★ └── v4l2-ctl -d /dev/video0 -l # 列出控制项 I2C调试工具 │ ├── i2cdetect -y 2 # 检测I2C设备 (OV13850地址0x10) ├── i2cget -y 2 0x10 0x300A w # 读取芯片ID └── i2cset -y 2 0x10 0x0100 0x01 # 写入寄存器 系统调试接口 │ ├── cat /proc/rkcif-dvp # RKCIF状态 ├── cat /proc/rkrkisp0 # ISP0状态 ├── dmesg | grep -i ov13850 # 查看传感器驱动日志 ├── dmesg | grep -i mipi # 查看MIPI相关日志 └── cat /sys/kernel/debug/clk/clk_summary | grep -E "cif|isp|mipi" # 时钟状态
6.2 完整调试脚本
#!/bin/bash
# camera_debug.sh - OV13850/OV5645 摄像头完整调试脚本
echo "========================================="
echo "RK3588 + OV13850/OV5645 摄像头调试"
echo "========================================="
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
# 1. 检查I2C设备 (传感器ID检测)
echo -e "\n[1] 检查I2C设备 (OV13850地址0x10, OV5645地址0x3C)..."
i2cdetect -y 2
echo ""
# 2. 检查传感器芯片ID
echo -e "\n[2] 读取传感器芯片ID..."
if i2cget -y 2 0x10 0x300A w 2>/dev/null; then
ID=$(i2cget -y 2 0x10 0x300A w 2>/dev/null)
echo -e "${GREEN} ✅ OV13850检测成功 (ID: 0x$ID)${NC}"
elif i2cget -y 2 0x3C 0x300A w 2>/dev/null; then
echo -e "${GREEN} ✅ OV5645检测成功${NC}"
else
echo -e "${RED} ❌ 未检测到摄像头传感器${NC}"
echo " 请检查: 电源、I2C连接、复位时序"
fi
# 3. 检查MCLK时钟输出
echo -e "\n[3] 检查MCLK时钟..."
if [ -f "/sys/kernel/debug/clk/clk_summary" ]; then
grep -E "clk_cam|mclk|24M" /sys/kernel/debug/clk/clk_summary | head -5
fi
# 4. 查看媒体设备拓扑
echo -e "\n[4] 查看媒体设备拓扑..."
for media in /dev/media*; do
echo -e "${YELLOW} $media:${NC}"
media-ctl -p -d $media 2>/dev/null | grep -E "entity|pad|link" | head -10
done
# 5. 查看V4L2设备
echo -e "\n[5] V4L2设备列表..."
v4l2-ctl --list-devices
# 6. 查看摄像头支持的格式
echo -e "\n[6] 摄像头支持格式..."
CAM_DEV=$(v4l2-ctl --list-devices | grep -A1 "rkisp" | grep video | head -1 | tr -d '[:space:]')
if [ -n "$CAM_DEV" ]; then
echo " 设备: $CAM_DEV"
v4l2-ctl -d $CAM_DEV --list-formats-ext 2>/dev/null | head -20
else
echo " ⚠️ 未找到摄像头设备"
fi
# 7. 配置MIPI链路
echo -e "\n[7] 配置MIPI链路..."
for media in /dev/media*; do
media-ctl -l -d $media 2>/dev/null
done
# 8. 设置分辨率和格式
echo -e "\n[8] 设置采集参数..."
if [ -n "$CAM_DEV" ]; then
v4l2-ctl -d $CAM_DEV --set-fmt-video=width=1920,height=1080,pixelformat=NV12
v4l2-ctl -d $CAM_DEV --set-ctrl=exposure=256
v4l2-ctl -d $CAM_DEV --set-ctrl=gain=0
fi
# 9. 采集单帧测试
echo -e "\n[9] 采集单帧..."
if [ -n "$CAM_DEV" ]; then
v4l2-ctl --verbose -d $CAM_DEV \
--set-fmt-video=width=1920,height=1080,pixelformat=NV12 \
--stream-mmap --stream-count=1 --stream-to=./capture.raw
if [ -f capture.raw ]; then
echo -e "${GREEN} ✅ 采集成功: capture.raw ($(du -h capture.raw | cut -f1))${NC}"
else
echo -e "${RED} ❌ 采集失败${NC}"
fi
fi
# 10. 查看内核日志
echo -e "\n[10] 摄像头相关内核日志..."
dmesg | grep -E "ov13850|ov5645|mipi|csi|vicap|isp" | tail -20
echo -e "\n========================================="
echo -e "${GREEN}调试完成${NC}"
echo "========================================="
6.3 常见问题排查表
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
| I2C无设备 | 电源/连线问题 | i2cdetect -y 2 |
检查AVDD(2.8V)/DOVDD(1.8V)/DVDD(1.2V)电源 |
| 芯片ID读不到 | 复位时序问题 | dmesg \| grep ov13850 |
检查reset-gpios和pwdn-gpios配置 |
| MCLK无输出 | 时钟未配置 | grep mclk /sys/kernel/debug/clk/clk_summary |
检查设备树clocks属性 |
| media-ctl无拓扑 | 驱动未加载 | lsmod \| grep ov13850 |
modprobe ov13850 |
| 无/dev/video节点 | VICAP未绑定 | media-ctl -p |
检查设备树链路配置 |
| MIPI错误 | 链路训练失败 | dmesg \| grep MIPI |
检查lane数和link_freq配置 |
| 图像偏色 | ISP未工作 | ps aux \| grep rkaiq |
启动3A服务: rkaiq_3A_server |
| 帧率低 | MIPI带宽不足 | 计算link_freq公式 | 减少分辨率或增加lane数 |
七、总结
| 项目 | OV13850 | OV5645 |
|---|---|---|
| 分辨率 | 4224×3136 (13MP) | 2592×1944 (5MP) |
| I2C地址 | 0x10 | 0x3C |
| MIPI Lane | 2或4 | 2 |
| 驱动路径 | drivers/media/i2c/ov13850.c |
drivers/media/i2c/ov5645.c |
| 设备树节点 | ov13850@10 |
ov5645@3c |
| 调试工具 | media-ctl, v4l2-ctl, i2cget | 同上 |
关键配置要点:
-
电源时序:必须按照 AVDD → DOVDD → DVDD 顺序上电,复位时序满足规格书要求
-
MIPI参数:link_freq需通过公式
width × height × fps × bpp / lanes / 2计算 -
I2C地址:OV13850是0x10,OV5645是0x3C,注意区分7位和8位地址
-
数据通路:sensor → dphy → csi2 → vicap → isp,每一环节都需正确配置
-
调试流程:先确认I2C通信,再检查MIPI信号,最后验证ISP处理
更多推荐


所有评论(0)