RK平台车载摄像头开发:从底层到应用的全面解析 电子说
在智能汽车快速发展的今天,车载摄像头作为环境感知、辅助驾驶的核心部件,其稳定性、实时性和安全性直接影响着整车的智能化体验。瑞芯微(Rockchip)凭借其高性能的 SoC 平台和完善的车载摄像头开发框架,为开发者提供了一套高效、可靠的车载摄像头解决方案。本文将基于瑞芯微车载摄像头开发指南,从驱动开发、中间件使用到应用层实战,全面解析 RK 平台车载摄像头的开发流程与技术要点,配套完整可落地的代码示例,手把手带你完成车载摄像头全链路开发。
一、系统整体架构:车载摄像头开发的基础框架

1.1 系统模块与框架
瑞芯微车载摄像头系统是一套经过精心设计的中间件软件系统,旨在满足汽车行业严格的高效、安全和可靠性标准。整个系统采用分层设计,从底层硬件到上层应用全链路覆盖,核心模块如下:
|
层级
|
核心模块
|
核心职责
|
|
内核驱动层
|
SEDES 驱动层
|
负责Maxim SerDes串行解串器、远端Sensor的驱动适配,实现长距离图像数据的采集、传输与解析
|
|
核心中间件层
|
RVCAM 中间件
|
车载摄像头方案的核心,提供统一的摄像头数据管理、虚拟多摄多开、算法扩展、跨系统通信能力,屏蔽底层硬件差异
|
|
硬件抽象层
|
EVS HAL、Automotive HAL3
|
对接AAOS车载安卓标准框架,为倒车影像、环视、座舱相机应用提供标准化接口
|
|
应用层
|
EVS APP、Native/APK应用
|
实现倒车影像、360°环视、行车记录、DMS舱内监控等业务功能
|
1.2 安全与质量标准
车载系统对安全性要求极高,瑞芯微车载摄像头系统完全符合汽车行业的安全标准,包括ISO26262 功能安全标准,确保在各种工况下的稳定运行。
•编码规范:遵循MISRA-C/MISRA-C++ 编码规范,通过Google C++ Lint做代码审查,减少潜在缺陷;
•故障防护:内置FMEA故障模式与影响分析机制,支持摄像头热插拔检测、故障诊断与恢复;
•功能安全:以FuSa功能安全为核心设计,确保系统出现故障时能以安全方式响应。
1.3 功能开发路线图
瑞芯微为车载摄像头系统规划了完善的功能开发路线,以满足不同车载场景的需求:
•已完成功能:YUV 摄像头虚拟多摄映射、SerDes 多路 RAW + ISP 支持;
•开发中功能:单 SerDes 多 MIPI PHY 输出、车辆电源管理事件订阅、摄像头热插拔支持;
•规划中功能:低延时系统优化、ISP HAL 支持虚拟多摄、摄像头诊断功能、第三方摄像头接口扩展、SOME/IP车载服务、多传感器融合框架等。
二、SEDES 驱动开发:摄像头数据传输的底层基石
车载摄像头与消费级摄像头最大的差异,在于通过SerDes芯片实现长距离同轴/双绞线传输,这也是车载摄像头开发的第一个核心门槛。瑞芯微提供了完整的Maxim Camera SerDes V3驱动框架,完美适配主流车规级SerDes芯片与Sensor组合。
2.1 Maxim Camera Serdes 驱动概述
Maxim(美信)的GMSL SerDes是车载场景最常用的串行解串器方案,瑞芯微驱动已原生支持以下芯片组合:
|
近端解串器
|
远端串化器
|
远端Sensor
|
|
MAX96712、MAX96722、MAX96716、MAX96718
|
MAX9295、MAX96715、MAX96717
|
OV2311/OV2312、OX01F10、OX03J10、SC320AT、OS04A10
|
驱动基于Linux media框架设计,核心分为近端解串器(Local)驱动和远端设备(Remote)驱动两大部分,通过I2C-MUX技术实现远端多设备的独立控制,将多路摄像头数据通过不同MIPI VC通道隔离,最终输出给SoC的ISP/CIF模块。

2.1.1 驱动目录结构
Maxim Camera Serdes 驱动的目录结构清晰,所有代码均在内核源码的drivers/media/i2c/maxim/路径下,便于开发者适配和扩展:
kernel/drivers/media/i2c/maxim/├── Kconfig # 驱动总配置入口├── Makefile # 驱动总编译规则├── local/ # 近端解串器驱动│ ├── maxim2c/ # 双路解串器驱动(MAX96716/MAX96718)│ │ ├── Kconfig、Makefile│ │ ├── maxim2c_drv.c # 驱动入口、I2C设备管理│ │ ├── maxim2c_link.c # GMSL链路管理│ │ ├── maxim2c_video_pipe.c # 视频管道映射│ │ └── maxim2c_mipi_txphy.c # MIPI输出PHY配置│ └── maxim4c/ # 四路解串器驱动(MAX96712/MAX96722)│ └── 结构同maxim2c└── remote/ # 远端设备驱动├── Kconfig、Makefile├── max9295.c/max96715.c/max96717.c # 串化器驱动├── ov231x.c/ox03j10.c/sc320at.c # YUV Sensor驱动├── os04a10.c # RAW Sensor驱动参考└── dummy.c # 虚拟Sensor调试驱动
2.1.2 config 宏配置
驱动功能的裁剪与使能,通过内核config宏配置完成,需要在芯片对应的defconfig文件中添加以下配置,可根据实际硬件选型裁剪:
# ========== Maxim SerDes 驱动总开关 ==========CONFIG_VIDEO_MAXIM_SERDES=y# ========== 近端解串器驱动使能 ==========# 4路解串器(MAX96712/MAX96722)CONFIG_VIDEO_MAXIM_DES_MAXIM4C=y# 2路解串器(MAX96716/MAX96718)CONFIG_VIDEO_MAXIM_DES_MAXIM2C=y# ========== 远端串化器驱动使能 ==========CONFIG_VIDEO_MAXIM_SER_MAX9295=yCONFIG_VIDEO_MAXIM_SER_MAX96715=yCONFIG_VIDEO_MAXIM_SER_MAX96717=y# ========== 远端Sensor驱动使能 ==========CONFIG_VIDEO_MAXIM_CAM_DUMMY=y # 调试用虚拟SensorCONFIG_VIDEO_MAXIM_CAM_SC320AT=y # 思特威SC320ATCONFIG_VIDEO_MAXIM_CAM_OV231X=y # 豪威OV2311/OV2312CONFIG_VIDEO_MAXIM_CAM_OX01F10=y # 安森美OX01F10CONFIG_VIDEO_MAXIM_CAM_OX03J10=y # 安森美OX03J10CONFIG_VIDEO_MAXIM_CAM_OS04A10=y # 豪威OS04A10(RAW Sensor)
2.2 DTS 配置详解:硬件参数的关键配置
DTS(Device Tree Source)设备树配置,是SerDes驱动适配的核心,90%的驱动适配工作都集中在DTS配置上。瑞芯微官方提供了EVB板的参考配置,我们以RK3588M + MAX96712 4路解串器为例,拆解完整的DTS配置细节。
2.2.1 I2C Init Sequence 的 DTS 配置
SerDes芯片的寄存器配置繁多,驱动提供了标准化的I2C初始化序列配置方式,无需修改驱动代码,直接在DTS中配置寄存器序列即可,解串器、串化器、Sensor均可使用该配置方式。
完整配置示例:
link-init-sequence {// 单个配置项的字节长度:reg地址长度 + 寄存器值长度 + mask长度 + 延时长度 = 2+1+1+1=5seq-item-size = <5>;reg-addr-len = <2>; // 寄存器地址长度:2表示16位地址reg-val-len = <1>; // 寄存器值长度:1表示8位数据// 配置序列格式:寄存器地址 寄存器值 掩码 延时(ms) 保留位init-sequence = [14 D1 03 00 00 // 配置VGA高增益,寄存器0x14D1写入0x0314 45 00 00 00 // 关闭SSC扩频,寄存器0x1445写入0x0004 0A 01 00 00 // 使能链路A,寄存器0x040A写入0x01];};
2.2.2 解串器核心节点与Dummy Sensor配置
首先需要在SoC对应的I2C总线节点下,添加解串器的基础设备节点,将解串器抽象为虚拟Sensor设备,完成电源、时钟、GPIO、MIPI输出端口的基础配置。
完整配置示例:
/* 以RK3588M的I2C6总线为例,挂载MAX96712解串器 */&i2c6 {status = "okay";#address-cells = <1>;#size-cells = <0>;pinctrl-names = "default";pinctrl-0 = <&i2c6m3_xfer>;/* MAX96712 4路解串器核心节点 */max96712_dphy3: max96712@29 {compatible = "maxim4c,max96712";status = "okay";reg = <0x29>; // 解串器I2C 7位地址clock-names = "xvclk";clocks = <&max96712_dphy3_osc0 0>; // 外部时钟输入/* 电源域与电源配置 */power-domains = <&power RK3588_PD_VI>;rockchip,grf = <&sys_grf>;vcc1v2-supply = <&max96712_dphy3_vcc1v2>; // 1.2V电源vcc1v8-supply = <&max96712_dphy3_vcc1v8>; // 1.8V电源pwdn-supply = <&max96712_dphy3_pwdn_regulator>; // 掉电控制电源poc-supply = <&max96712_dphy3_poc_regulator>; // 远端摄像头PoC同轴供电/* GPIO与状态引脚配置 */pinctrl-names = "default";pinctrl-0 = <&max96712_dphy3_errb>, <&max96712_dphy3_lock>;lock-gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_HIGH>; // 链路锁定状态引脚errb-gpios = <&gpio3 RK_PB5 GPIO_ACTIVE_LOW>; // 错误中断引脚/* 摄像头模组标识(RK摄像头框架必填) */rockchip,camera-module-index = <0>;rockchip,camera-module-facing = "back";rockchip,camera-module-name = "default";rockchip,camera-module-lens-name = "default";/* ========== 1. 输出模式配置 ========== */support-mode-config {status = "okay";bus-format =; // 输出数据格式 sensor-width = <1920>; // 图像宽度sensor-height = <1440>; // 图像高度max-fps-numerator = <10000>;max-fps-denominator = <300000>; // 30fpsbpp = <16>; // 每像素比特数vc-array = <0x10 0x20 0x40 0x80>; // 4路摄像头映射到VC0-VC3};/* ========== 2. RAW Sensor ISP路由配置(多路RAW Sensor必填) ========== */// 使能后,远端Sensor的控制权交给ISP驱动,关闭则由解串器统一管理remote-routing-to-isp = <0>;/* ========== 3. MIPI输出端口绑定 ========== */port {max96712_dphy3_out: endpoint {remote-endpoint = <&mipi_dphy3_in_max96712>; // 绑定到SoC的MIPI DPHY3data-lanes = <1 2 3 4>; // MIPI数据线数量};};/* ========== 4. GMSL链路配置(4路Link对应4个远端摄像头) ========== */gmsl-links {status = "okay";// Link A 链路配置(对应第1路摄像头)gmsl-link-config-0 {status = "okay";link-id = <0>; // 链路ID:0=Link A,1=Link B,以此类推link-type = <1>; // 链路类型:0=GMSL1,1=GMSL2link-rx-rate = <1>; // 正向速率:0=3Gbps,1=6Gbpslink-tx-rate = <0>; // 反向速率:0=187.5Mbpslink-remote-cam = <&max96712_dphy3_cam0>; // 绑定远端摄像头节点// 链路专属初始化序列link-init-sequence {seq-item-size = <5>;reg-addr-len = <2>;reg-val-len = <1>;init-sequence = [14 D1 03 00 0014 45 00 00 00];};};// Link B 链路配置(对应第2路摄像头)gmsl-link-config-1 {status = "okay";link-id = <1>;link-type = <1>;link-rx-rate = <1>;link-tx-rate = <0>;link-remote-cam = <&max96712_dphy3_cam1>;};// Link C、Link D 配置以此类推...};/* ========== 5. 视频管道配置(实现链路数据到VC通道的映射) ========== */video-pipes {status = "okay";// 管道0:对应Link A的摄像头数据video-pipe-config-0 {status = "okay";pipe-id = <0>;pipe-idx = <2>;link-idx = <0>; // 绑定到Link A(link-id=0)// 管道初始化序列:配置CSI2映射、VC通道、数据类型pipe-init-sequence {seq-item-size = <5>;reg-addr-len = <2>;reg-val-len = <1>;init-sequence = [09 0B 07 00 00 // 使能Src/Dst映射09 2D 15 00 00 // 映射到CSI2控制器109 0D 1e 00 00 // 配置SRC0 VC=0,DT=YUV422 8bit09 0E 1e 00 00 // 配置DST0 VC=0,DT=YUV422 8bit];};};// 管道1:对应Link B的摄像头数据,以此类推...};/* ========== 6. MIPI TXPHY输出配置 ========== */mipi-txphys {status = "okay";phy-mode = <0>; // PHY模式:0=2X4Lanes,1=4X2Lanesphy-force-clock-out = <1>; // 强制输出MIPI时钟,避免无摄像头时PHY不工作// PHY0配置mipi-txphy-config-0 {status = "okay";phy-id = <0>;phy-type = <0>; // 0=DPHY,1=CPHYdata-lane-num = <4>;data-lane-map = <0x4>;};};/* ========== 7. 额外初始化序列(帧同步、GPIO等扩展配置) ========== */extra-init-sequence {seq-item-size = <5>;reg-addr-len = <2>;reg-val-len = <1>;init-sequence = [08 17 01 00 00];};/* ========== 8. 远端设备I2C-MUX配置(核心!实现远端串化器/Sensor的I2C通信) ========== */i2c-mux {#address-cells = <1>;#size-cells = <0>;// Link A对应的远端I2C通道i2c@0 {#address-cells = <1>;#size-cells = <0>;reg = <0>; // 对应link-id=0// 注意:串化器节点必须写在Sensor节点前面,保证驱动probe顺序max96712_dphy3_ser0: max96717@41 {compatible = "maxim,ser,max96717";reg = <0x41>; // 映射后的I2C地址ser-i2c-addr-def = <0x40>; // 串化器默认上电地址// 串化器初始化序列ser-init-sequence {seq-item-size = <5>;reg-addr-len = <2>;reg-val-len = <1>;init-sequence = [03 02 10 00 0014 17 00 00 00];};};// Link A对应的远端Sensor节点max96712_dphy3_cam0: sc320at@31 {compatible = "maxim,smartsens,sc320at";reg = <0x31>; // 映射后的I2C地址cam-i2c-addr-def = <0x30>; // Sensor默认上电地址cam-remote-ser = <&max96712_dphy3_ser0>; // 绑定对应的串化器poc-supply = <&max96712_dphy3_poc_regulator>; // PoC供电// RK摄像头框架必填参数rockchip,camera-module-index = <0>;rockchip,camera-module-facing = "back";rockchip,camera-module-name = "default";rockchip,camera-module-lens-name = "default";// 端口配置(RAW Sensor需绑定到ISP节点)port {max96712_dphy3_cam0_out: endpoint {data-lanes = <1 2 3 4>;};};};};// Link B对应的远端I2C通道i2c@1,以此类推...};};};
2.2.3 RAW Sensor专属DTS配置
如果使用RAW输出的Sensor,需要额外配置ISP路由,核心修改3处:
1.解串器节点添加remote-routing-to-isp = <1>,使能多路RAW Sensor路由;
2.远端Sensor的port节点添加remote-endpoint,链接到ISP的输入节点;
3.在SoC的rkcif_mipi_lvds节点添加camera-over-bridge属性,开启远端RAW Sensor模式。
2.3 近端解串器驱动开发
瑞芯微已经完成了maxim4c(4路)和maxim2c(2路)解串器的完整驱动实现,常规场景无需修改驱动代码,仅需通过DTS配置即可完成适配。驱动核心能力包括:
•解串器的虚拟Sensor抽象与v4l2 subdev接口实现;
•GMSL链路管理、锁定状态检测、热插拔恢复;
•远端设备的上下电、开关流统一管理;
•MIPI TXPHY配置与CSI-2数据输出控制;
•内置测试彩条Pattern输出,可用于硬件通路调试。
如果需要自定义扩展,可直接修改对应驱动文件,核心API接口均在maxim4c_api.h/maxim2c_api.h中声明。
2.4 远端摄像头驱动适配
对于YUV输出的摄像头模组,模组内部已完成ISP处理,上电后自动加载配置,直接使用官方适配好的Sensor驱动,或dummy驱动即可;对于RAW输出的Sensor,需要基于官方框架完成驱动适配,以下是适配的核心代码细节与8大关键改动点。
2.4.1 Kconfig与Makefile配置
首先在remote/目录下添加新Sensor的编译配置,避免和原生Sensor驱动冲突。
Kconfig配置示例(drivers/media/i2c/maxim/remote/Kconfig):
config VIDEO_MAXIM_CAM_OS04A10tristate "Maxim Remote Sensor OV OS04A10"depends on VIDEO_MAXIM_SERDESdepends on I2ChelpThis driver supports the remote OS04A10 sensor over Maxim SerDes.To compile this driver as a module, the module will be called maxim-os04a10.
Makefile配置示例(drivers/media/i2c/maxim/remote/Makefile):
给驱动模块加maxim-前缀,避免和原生OS04A10驱动重名maxim-os04a10-objs := os04a10.o(CONFIG_VIDEO_MAXIM_CAM_OS04A10) += maxim-os04a10.o
2.4.2 驱动核心适配代码
和消费级Sensor驱动相比,SerDes远端Sensor驱动有8个核心改动点,以下是关键代码示例:
1. 驱动名与compatible适配
添加maxim,前缀,避免和原生驱动冲突,示例如下:
static const struct of_device_id os04a10_of_match[] = {{ .compatible = "maxim,ovti,os04a10" }, // 必须加maxim前缀{ /* sentinel */ },};MODULE_DEVICE_TABLE(of, os04a10_of_match);static struct i2c_driver os04a10_i2c_driver = {.driver = {.name = "maxim-os04a10", // 驱动名加前缀.pm = &os04a10_pm_ops,.of_match_table = of_match_ptr(os04a10_of_match),},.probe = &os04a10_probe,.remove = &os04a10_remove,};module_i2c_driver(os04a10_i2c_driver);
2. 核心数据结构添加SerDes相关成员
struct os04a10 {struct v4l2_subdev sd;struct media_pad pad;struct regulator *poc_regulator; // PoC同轴供电,替代原生多电源struct maxim_remote_ser *serializer; // 绑定的串化器设备u8 cam_i2c_addr_def; // Sensor默认I2C地址u8 cam_i2c_addr_map; // 映射后的I2C地址// 其他原有Sensor参数...};
3. probe函数核心适配
probe阶段不再做MCLK、GPIO、电源的硬件配置,核心完成串化器绑定、PoC电源获取,不再做Chip ID检测(链路未初始化时I2C无法通信)。
static int os04a10_probe(struct i2c_client *client, const struct i2c_device_id *id){struct device *dev = &client->dev;struct os04a10 *sensor;int ret;// 1. 分配驱动结构体sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);if (!sensor)return -ENOMEM;// 2. 获取PoC供电 regulator(替代原生的多电源配置)sensor->poc_regulator = devm_regulator_get(dev, "poc");if (IS_ERR(sensor->poc_regulator)) {dev_err(dev, "Failed to get PoC regulatorn");return PTR_ERR(sensor->poc_regulator);}// 3. 绑定远端串化器(核心!必须实现)sensor->serializer = maxim_remote_cam_bind_ser(dev);if (!sensor->serializer) {dev_err(dev, "Bind remote serializer failedn");return -ENODEV;}// 传递Sensor的I2C地址信息给串化器sensor->cam_i2c_addr_def = 0x30; // 替换为你的Sensor默认地址sensor->serializer->cam_i2c_addr_def = sensor->cam_i2c_addr_def;sensor->serializer->cam_i2c_addr_map = client->addr;// 4. 初始化v4l2_subdev、media pad等框架代码// ... 原有Sensor的v4l2框架初始化代码,此处省略...dev_info(dev, "Maxim remote OS04A10 sensor probe successn");return 0;}
4. 电源上下电适配
移除原生的多电源、GPIO复位代码,改为PoC电源控制,示例如下:
static int __os04a10_power_on(struct os04a10 *sensor){int ret;// 开启PoC同轴供电ret = regulator_enable(sensor->poc_regulator);if (ret) {dev_err(&sensor->sd.client->dev, "PoC power enable failedn");return ret;}// 等待电源稳定usleep_range(10000, 12000);return 0;}static int __os04a10_power_off(struct os04a10 *sensor){return regulator_disable(sensor->poc_regulator);}
5. 开关流接口适配(核心)
开流阶段才完成串化器初始化、Chip ID检测、Sensor寄存器配置,这是和消费级Sensor最大的区别。
开流函数完整示例:
static int __os04a10_start_stream(struct os04a10 *sensor){struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);struct device *dev = &client->dev;struct maxim_remote_ser *serializer = sensor->serializer;int ret;// 1. 先初始化串化器ret = serializer->ser_ops->ser_module_init(serializer);if (ret) {dev_err(dev, "Serializer init failed, ret=%dn", ret);return ret;}// 2. 检测Sensor Chip ID(必须在串化器初始化后执行)ret = os04a10_check_chip_id(sensor);if (ret) {dev_err(dev, "Sensor chip id check failedn");goto err_ser_deinit;}// 3. 写入Sensor初始化寄存器序列ret = os04a10_write_regs(client, sensor->cur_mode->reg_list);if (ret) {dev_err(dev, "Sensor init regs write failedn");goto err_ser_deinit;}// 4. 配置v4l2控制参数(曝光、增益等)ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler);if (ret) {dev_err(dev, "v4l2 ctrl setup failedn");goto err_ser_deinit;}// 5. 启动Sensor流输出ret = os04a10_set_streaming(sensor, true);if (ret) {dev_err(dev, "Sensor start streaming failedn");goto err_ser_deinit;}// 6. 检测串化器PCLK,确认Sensor数据正常输出ret = serializer->ser_ops->ser_pclk_detect(serializer);if (ret) {dev_err(dev, "Serializer PCLK detect failed, no data inputn");goto err_stop_stream;}dev_info(dev, "OS04A10 start stream successn");return 0;err_stop_stream:os04a10_set_streaming(sensor, false);err_ser_deinit:serializer->ser_ops->ser_module_deinit(serializer);return ret;}
关流函数完整示例:
static int __os04a10_stop_stream(struct os04a10 *sensor){struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);struct device *dev = &client->dev;struct maxim_remote_ser *serializer = sensor->serializer;int ret;// 1. 停止Sensor流输出ret = os04a10_set_streaming(sensor, false);if (ret)dev_warn(dev, "Sensor stop streaming failedn");// 2. 反初始化串化器ret = serializer->ser_ops->ser_module_deinit(serializer);if (ret)dev_warn(dev, "Serializer deinit failedn");dev_info(dev, "OS04A10 stop stream successn");return 0;}
6. 移除MCLK、GPIO相关代码
SerDes架构下,Sensor的MCLK、复位引脚均由远端串化器提供,无需SoC控制,直接删除驱动中相关的时钟申请、GPIO配置代码即可。
三、RVCAM 中间件:车载摄像头应用开发的利器
RVCAM(Rockchip Vehicle Camera)是瑞芯微车载摄像头方案的灵魂,它屏蔽了底层硬件差异,提供了统一的API接口,完美解决了车载场景一摄多开、多应用共享、低延时传输、跨系统通信等核心痛点,是应用开发的核心抓手。
3.1 RVCAM 介绍
RVCAM是专为车载场景设计的摄像头中间件,核心能力包括:
•虚拟多摄多开:一个物理摄像头可映射为多个虚拟摄像头,支持倒车影像、行车记录、环视应用同时访问,无资源抢占问题;
•全接口适配:同时支持Native API直接调用、AAOS EVS HAL、Android Camera HAL3,满足不同开发场景;
•低开销高性能:RK3576M平台四路1080P@30fps摄像头同时开启,CPU占用率仅10%左右;
•跨系统支持:原生支持Android+Linux双系统架构,基于eRPC实现跨系统摄像头控制与数据共享;
•可扩展架构:支持插件式集成图像处理算法,如畸变校正、HDR、BEV融合等。
3.2 RVCAM API 使用指南
RVCAM提供了极简的C语言API接口,Native应用可直接调用,无需关心底层V4L2驱动细节,以下是API分类与完整的采集流程代码示例。
3.2.1 核心API分类
|
API分类
|
核心功能
|
关键接口
|
|
初始化与终止
|
管理RVCAM上下文生命周期
|
rvcam_initialize、rvcam_uninitialize
|
|
设备信息查询
|
获取摄像头列表与参数
|
rvcam_query_inputs
|
|
流管理
|
摄像头流的开启/关闭/暂停/恢复
|
rvcam_open、rvcam_close、rvcam_start、rvcam_stop
|
|
参数管理
|
摄像头参数的查询与配置
|
rvcam_g_param、rvcam_s_param
|
|
帧数据管理
|
图像帧的获取与释放
|
rvcam_get_frame、rvcam_release_frame、rvcam_s_buffers
|
3.2.2 完整的单摄采集代码示例
以下是基于RVCAM API实现的摄像头采集全流程代码,可直接用于倒车影像、单路预览等场景:
int main(int argc, char *argv[]){int ret = 0;rvcam_init_t init_params = {0};rvcam_input_t *camera_list = NULL;unsigned int camera_num = 0;rvcam_hndl_t cam_handle = NULL;rvcam_frame_info_t frame_info = {0};int capture_count = 100; // 采集100帧// ========== 1. 初始化RVCAM上下文 ==========ret = rvcam_initialize(&init_params);if (ret != 0) {printf("RVCAM initialize failed, ret=%dn", ret);return -1;}printf("RVCAM initialize successn");// ========== 2. 查询系统中的摄像头列表 ==========// 第一次调用:获取摄像头数量ret = rvcam_query_inputs(NULL, 0, &camera_num);if (ret != 0 || camera_num == 0) {printf("Query camera num failed, ret=%d, num=%dn", ret, camera_num);goto err_uninit;}printf("Found %d cameras in systemn", camera_num);// 分配内存,第二次调用:获取摄像头详细信息camera_list = (rvcam_input_t *)malloc(sizeof(rvcam_input_t) * camera_num);if (!camera_list) {printf("Malloc camera list failedn");goto err_uninit;}ret = rvcam_query_inputs(camera_list, camera_num, &camera_num);if (ret != 0) {printf("Query camera info failed, ret=%dn", ret);goto err_free_list;}// 打印摄像头信息for (int i = 0; i < camera_num; i++) {printf("Camera %d: name=%s, resolution=%dx%dn",camera_list[i].id, camera_list[i].name,camera_list[i].width, camera_list[i].height);}// ========== 3. 打开指定虚拟摄像头(以camId=0为例) ==========int target_cam_id = 0;cam_handle = rvcam_open(target_cam_id);if (!cam_handle) {printf("Open camera %d failedn", target_cam_id);goto err_free_list;}printf("Open camera %d successn", target_cam_id);// ========== 4. 配置摄像头参数(可选) ==========// 示例:设置分辨率、帧率等参数// rvcam_param_value_t param_val = {0};// param_val.width = 1920;// param_val.height = 1080;// ret = rvcam_s_param(cam_handle, RVCAM_PARAM_RESOLUTION, ¶m_val);// ========== 5. 启动摄像头数据流 ==========ret = rvcam_start(cam_handle);if (ret != 0) {printf("Start camera stream failed, ret=%dn", ret);goto err_close_cam;}printf("Start camera stream successn");// ========== 6. 循环采集帧数据 ==========for (int i = 0; i < capture_count; i++) {// 获取一帧数据,超时时间500msret = rvcam_get_frame(cam_handle, &frame_info, 500, 0);if (ret != 0) {printf("Get frame %d failed, ret=%dn", i, ret);continue;}// 帧数据处理printf("Capture frame %d success: timestamp=%llu, width=%d, height=%d, format=%d, buf_fd=%dn",i, frame_info.timestamp, frame_info.width, frame_info.height,frame_info.format, frame_info.buf_fd);// ========== 此处可添加图像处理、渲染、编码等业务逻辑 ==========// 例如:通过RGA做格式转换、缩放;通过OpenGL渲染显示;保存为图片/视频等// 释放帧缓冲区,必须调用,否则会出现缓冲区耗尽ret = rvcam_release_frame(cam_handle, frame_info.idx);if (ret != 0) {printf("Release frame %d failed, ret=%dn", i, ret);}// 控制采集帧率usleep(30000); // 30fps}// ========== 7. 停止数据流 ==========ret = rvcam_stop(cam_handle);if (ret != 0) {printf("Stop camera stream failed, ret=%dn", ret);}printf("Stop camera stream successn");// ========== 8. 资源释放 ==========err_close_cam:rvcam_close(cam_handle);err_free_list:free(camera_list);err_uninit:rvcam_uninitialize();printf("RVCAM resource release donen");return ret;}
3.3 RVCAM 配置文件详解
RVCAM的所有物理摄像头配置、虚拟摄像头映射关系,都在rvcam_config.xml中定义,编译后部署到板端/vendor/etc/rvcam_config.xml,是实现多摄多开的核心。
3.3.1 完整的配置文件示例
以下是支持4路物理摄像头、一摄三开的完整配置示例,可直接参考修改:
<CameraConfig version="0x100"><board name="Rockchip RK3588 VEHICLE EVB V22 Board"><cameras><camera id="0"><entity>m00_b_max96712 6-0029entity><subDev>/dev/v4l-subdev5subDev><mbusCode>UYVY8_2X8/1920x1440mbusCode><channels><channel id="0" dev="/dev/video11"><cameraModel>Rear_CamcameraModel><stream id="0" format="YUYV" width="1920" height="1440" bufCnt="4"/>channel><channel id="1" dev="/dev/video12"><cameraModel>Right_CamcameraModel><stream id="0" format="YUYV" width="1920" height="1440" bufCnt="4"/>channel><channel id="2" dev="/dev/video13"><cameraModel>Left_CamcameraModel><stream id="0" format="YUYV" width="1920" height="1440" bufCnt="4"/>channel><channel id="3" dev="/dev/video14"><cameraModel>Front_CamcameraModel><stream id="0" format="YUYV" width="1920" height="1440" bufCnt="4"/>channel>channels>camera>cameras><inputMapping><inputMap camId="0"><inputSrc cameraId="0" channelId="0" streamId="0"/>inputMap><inputMap camId="1"><inputSrc cameraId="0" channelId="1" streamId="0"/>inputMap><inputMap camId="2"><inputSrc cameraId="0" channelId="2" streamId="0"/>inputMap><inputMap camId="3"><inputSrc cameraId="0" channelId="3" streamId="0"/>inputMap><inputMap camId="4"><inputSrc cameraId="0" channelId="0" streamId="0"/>inputMap><inputMap camId="5"><inputSrc cameraId="0" channelId="0" streamId="0"/>inputMap><inputMap camId="6"><inputSrc cameraId="0" channelId="1" streamId="0"/>inputMap>inputMapping>board>CameraConfig>
3.3.2 关键配置说明
•board name:必须和主板的device-tree model一致,否则配置不生效;
•camera节点:对应物理解串器设备,channels下的每个channel对应一个物理摄像头通道;
•inputMapping节点:核心的虚拟映射配置,同一个物理channel可以映射给多个inputMap,实现一摄多开;
•camId分配:AAOS系统默认0-3号给EVS使用,Camera HAL3从4号开始,避免ID冲突导致上层应用找不到设备。
3.4 RVCAM 调试功能
RVCAM内置了完善的调试功能,可通过系统属性开启,用于排查帧率、丢帧、多开冲突等问题:
# 1. 开启RVCAM调试日志,自动写入文件adb shell setprop persist.vendor.rockchip.rvcam.dbgfile.on true# 2. 设置日志写入间隔(单位:ms,默认200ms)adb shell setprop persist.vendor.rockchip.rvcam.dbgfile.interval 200
调试日志路径:/data/vendor/camera/rvcam_debug.log,日志中包含摄像头开关状态、实时帧率、丢帧统计、buffer状态等关键信息。
四、EVS 模块:车载视觉应用的核心框架
EVS(Exterior View System)是Android Automotive OS专为车载视觉设计的标准框架,核心优势是启动速度快、低延时,能在Android启动早期就完成倒车影像的渲染,满足车规级倒车影像<2s的启动时效要求,是车载后视、环视应用的首选方案。
4.1 EVS 模块划分与目录介绍
RK平台的EVS方案完全遵循AAOS标准架构,底层基于RVCAM中间件实现,无需修改核心代码,仅需完成配置即可快速落地。EVS框架分为以下几个核心部分:
|
模块
|
源码路径
|
核心职责
|
|
EVS APP
|
packages/services/Car/cpp/evs/apps/default/
|
负责图像渲染、车辆状态响应、UI交互、环视拼接
|
|
EVS Manager
|
Android12: packages/services/Car/cpp/evs/manager/
|
中间管理层,封装EVS HAL接口,管理摄像头与显示设备
|
|
EVS HAL
|
瑞芯微定制实现,基于RVCAM
|
硬件抽象层,对接RVCAM中间件,向上提供标准EVS HIDL/AIDL接口
|
|
CarEvsService
|
packages/services/Car/service/
|
Java层服务,为APK应用提供EVS接口
|
4.2 EVS 核心数据流程
EVS的核心数据流程分为摄像头数据采集流程和显示渲染流程两部分:
1.摄像头数据流程:EVS APP启动后,通过EVS Manager打开EVS HAL的摄像头设备,EVS HAL通过RVCAM获取摄像头帧数据,通过deliverFrame接口将帧数据上报给EVS APP,APP将帧数据存入双Buffer,通过OpenGL ES完成畸变校正、拼接、渲染后,释放buffer给底层。
2.显示渲染流程:EVS APP从EVS HAL获取显示Buffer,完成图像绘制后,将Buffer返回给EVS HAL完成上屏显示。
4.3 EVS 产品配置详解
EVS的适配核心在于配置文件,分为EVS APP配置、EVS HAL配置、服务与SELinux配置三大部分。
4.3.1 EVS APP 配置文件
EVS APP的核心配置文件为config_override.json,编译后部署到板端/vendor/etc/automotive/evs/config_override.json,用于配置车辆参数、显示参数、摄像头内外参、畸变校正参数等,完整配置示例如下:
{"car": {"width": 180,"wheelBase": 280,"frontExtent": 90,"rearExtent": 70},"displays": [{"displayPort": 0,"frontRange": 200,"rearRange": 200}],"graphic": {"frontPixel": 23,"rearPixel": 223},// 核心:摄像头参数配置,每个摄像头对应一个节点"cameras": [{"cameraId": "0","function": "reverse,park","x": 0.0,"y": -70.0,"z": 120,"yaw": 180,"pitch": -20,"roll": 0,"hfov": 130,"vfov": 100,"hflip": true,"vflip": true},{"cameraId": "1","function": "right","x": 90.0,"y": 100.0,"z": 120,"yaw": 90,"pitch": -15,"roll": 0,"hfov": 130,"vfov": 100,"hflip": false,"vflip": false},{"cameraId": "2","function": "left","x": -90.0,"y": 100.0,"z": 120,"yaw": -90,"pitch": -15,"roll": 0,"hfov": 130,"vfov": 100,"hflip": false,"vflip": false},{"cameraId": "3","function": "front","x": 0.0,"y": 90.0,"z": 120,"yaw": 0,"pitch": -15,"roll": 0,"hfov": 130,"vfov": 100,"hflip": false,"vflip": false}]}
关键说明:
•cameraId:必须和rvcam_config.xml中的虚拟摄像头camId一一对应;
•function:定义摄像头的用途,EVS APP会根据车辆档位、转向灯状态自动切换对应摄像头;
•位姿参数x/y/z/yaw/pitch/roll、视场角hfov/vfov:需根据摄像头实际安装位置和参数填写,直接影响环视拼接的效果。
4.3.2 EVS HAL 配置文件
EVS HAL的配置文件为evs_configuration_override.xml,部署到板端/vendor/etc/automotive/evs/evs_configuration_override.xml,用于定义EVS HAL向上暴露的摄像头设备信息,完整配置示例如下:
<evs_config><camera><device id='0' position='rear'><caps><supported_controls>supported_controls><stream id='0' width='1920' height='1440' format='YCRCB_420_SP' framerate='30'/><stream id='1' width='1280' height='720' format='YCRCB_420_SP' framerate='30'/>caps><characteristics/>device><device id='1' position='right'><caps><stream id='0' width='1920' height='1440' format='YCRCB_420_SP' framerate='30'/>caps><characteristics/>device><device id='2' position='left'><caps><stream id='0' width='1920' height='1440' format='YCRCB_420_SP' framerate='30'/>caps><characteristics/>device><device id='3' position='front'><caps><stream id='0' width='1920' height='1440' format='YCRCB_420_SP' framerate='30'/>caps><characteristics/>device>camera><display><device id='0' type='display'><caps><stream id='0' width='1920' height='720' format='RGBA_8888'/>caps>device>display>evs_config>
4.3.3 编译与服务配置
需要在产品的mk文件中添加EVS相关模块的编译配置,示例如下:
# Android14 AIDL版本编译配置PRODUCT_PACKAGES +=evs_appevsmanagerdandroid.hardware.automotive.evs-rvcamcardisplayproxyd# Android12 HIDL版本编译配置# PRODUCT_PACKAGES +=# evs_app# evs_manager# android.hardware.automotive.evs@1.1-rvcam# android.frameworks.automotive.display@1.0-service
同时,瑞芯微已经提供了配套的rc启动脚本、SELinux权限配置,无需额外修改,直接编译即可。
五、AutomotiveCamera HAL3 层:安卓框架与硬件的桥梁
Automotive Camera HAL3(简称 AutHAL3)是瑞芯微为车载场景定制的Camera HAL,对接Android标准Camera2 API,底层基于RVCAM中间件实现,为行车记录、舱内监控、DMS/OMS、拍照录像等应用提供标准化的安卓相机接口。
5.1 基本功能说明
和Android原生的ExternalCamera HAL相比,AutHAL3完美适配车载场景,核心优势如下:
•基于RVCAM中间件,原生支持一摄多开、多应用同时访问,解决了原生HAL摄像头独占的问题;
•支持Buffer Management功能,降低内存峰值,提升捕获请求响应速度;
•支持Buffer透传功能,减少内存拷贝,降低CPU占用和传输延时;
•兼容Android HIDL/AIDL接口,适配Android12/14系统,无需上层应用修改代码。
AutHAL3的核心链路:Android Camera Framework → AutomotiveCamera HAL3 → RVCAM中间件 → 底层V4L2驱动 → 物理摄像头。
5.2 核心功能详解
5.2.1 BUFFER MANAGEMENT 功能
Buffer Management是Android 10+推出的可选内存管理机制,核心优势是HAL层按需申请buffer,而非框架层提前分配,减少了内存峰值占用,同时让捕获请求可以更快下发。
AutHAL3已完整实现该功能,Android14+系统可通过配置文件开启,Android12及以下系统默认开启。
5.2.2 BUFFER 透传功能
这是RK AutHAL3的核心优化功能。非透传模式下,图像数据需要经过「V4L2驱动 → RVCAM中间件buffer → HAL层buffer → 框架层buffer」两次拷贝;开启透传功能后,框架层的输出buffer直接透传给RVCAM,仅需一次拷贝,CPU占用减半,延时大幅降低。
该功能默认开启,可通过配置文件关闭。
5.3 HAL3 产品配置详解
5.3.1 核心配置文件
AutHAL3的核心配置文件为automotive_camera_config.xml,编译后部署到板端,完整配置示例如下:
<CameraHalConfig><Provider><ignore><id>0id><id>1id><id>2id><id>3id>ignore><CameraIdOffset>-4CameraIdOffset>Provider><BufferManager enabled="false"/><HalBufferTransfer enabled="true"/>CameraHalConfig>
关键说明:
•ignore节点:必须配置EVS占用的camId,避免HAL3和EVS争抢摄像头资源;
•CameraIdOffset:ID偏移配置,例如RVCAM的camId=4,经过-4偏移后,上报给Android框架的camera id为0,符合安卓应用的使用习惯。
5.3.2 编译与服务配置
在产品mk文件中添加HAL3模块的编译配置,示例如下:
# Android14 AIDL版本PRODUCT_PACKAGES +=android.hardware.camera.provider-V1-automotive-implandroid.hardware.camera.provider-V1-automotive-service# Android12 HIDL版本# PRODUCT_PACKAGES +=# android.hardware.camera.provider@2.4-impl-automotive# android.hardware.camera.provider@2.4-automotive-service
瑞芯微已配套提供了rc启动脚本、SELinux权限配置、兼容性矩阵配置,无需额外修改。
5.4 调试与性能说明
5.4.1 核心性能指标
|
指标项
|
规格说明
|
|
虚拟摄像头数量
|
由rvcam_config.xml配置决定,无硬件限制
|
|
每路摄像头支持的tall stream数量
|
1路(拍照流)
|
|
每路摄像头支持的其他stream数量
|
2路(预览/录像流)
|
|
支持的图像格式
|
BLOB、YCbCr_422_I、YCbCr_420_888、厂商自定义格式
|
|
支持的分辨率
|
以物理摄像头参数为准,最高支持4K
|
|
支持的帧率
|
以物理摄像头参数为准,最高支持60fps
|
5.4.2 常用调试命令
1.查看摄像头信息
# 查看HAL3上报给安卓框架的所有摄像头信息、支持的格式/分辨率/帧率adb shell dumpsys media.camera
2.Camera Trace性能调试
使用Perfetto工具抓取Camera链路的trace数据,分析帧率、延时、性能瓶颈,完整抓取脚本如下:
adb shell perfetto-c - --txt-o /data/misc/perfetto-traces/camera_trace<buffers: {size_kb: 63488fill_policy: DISCARD}buffers: {size_kb: 2048fill_policy: DISCARD}data_sources: {config {name: "android.packages_list"target_buffer: 1}}data_sources: {config {name: "linux.ftrace"ftrace_config {ftrace_events: "ftrace/print"atrace_categories: "camera" "gfx" "input" "view" "sched"atrace_apps: "*"}}}duration_ms: 10000 # 抓取时长,单位msEOF
抓取完成后,将trace文件导出,通过https://ui.perfetto.dev/网页即可可视化分析。
六、调试与问题排查:打造稳定高效的车载摄像头系统
车载摄像头开发过程中,会遇到驱动加载失败、无图像、画面异常、多开冲突等问题,以下是常用的调试技巧和常见问题的排查方案。
6.1 驱动层调试命令
1.检查驱动是否加载成功
# 查看内核启动日志,搜索SerDes、Sensor相关的probe日志dmesg | grep -i maximdmesg | grep -i sensor# 查看系统中加载的v4l2子设备ls /dev/v4l-subdev*# 查看系统中的视频设备节点ls /dev/video*
2.查看media拓扑链路
# 查看media设备的完整拓扑,确认解串器、Sensor、ISP、CIF的链路是否正确media-ctl -d /dev/media0 -p
3.V4L2通道测试
# 查看video设备支持的格式、分辨率v4l2-ctl -d /dev/video11 --list-formats-ext# 测试视频采集,保存为原始文件v4l2-ctl -d /dev/video11 --set-fmt-video=width=1920,height=1440,pixelformat=YUYV --stream-mmap=4 --stream-count=100 --stream-to=/data/capture.yuv
4.I2C通信调试
# 检测I2C总线上的设备,确认解串器、Sensor的I2C地址是否可访问i2cdetect -y 6 # 6为解串器挂载的I2C总线号
6.2 RVCAM与上层调试
1.查看RVCAM服务状态
# Android系统查看RVCAM服务是否正常运行ps -A | grep rvcam# 查看RVCAM服务日志logcat | grep -i rvcam
2.EVS服务调试
# 查看EVS相关进程ps -A | grep -i evs# 查看EVS日志logcat | grep -i evs# 手动重启EVS相关服务stop evs_app && start evs_app
6.3 常见问题与解决方案
|
常见问题
|
核心排查方向
|
解决方案
|
|
解串器驱动probe失败
|
1. I2C地址与硬件是否匹配;2. 电源、GPIO配置是否正确;3. 解串器硬件供电是否正常
|
1. 核对硬件原理图,修正DTS中的I2C地址、电源、GPIO配置;2. 用万用表测量解串器供电引脚电压是否正常
|
|
远端Sensor probe成功,但无法通信
|
1. 串化器与Sensor的DTS节点顺序是否正确;2. I2C地址映射是否正确;3. 串化器绑定是否成功
|
1. 确保DTS中串化器节点写在Sensor节点前面;2. 核对cam-i2c-addr-def与Sensor硬件默认地址是否一致;3. 查看内核日志,确认串化器绑定成功
|
|
驱动加载正常,但无图像输出
|
1. 解串器链路是否锁定;2. MIPI配置与Sensor输出是否匹配;3. video-pipe与VC通道映射是否正确;4. Sensor是否正常输出数据
|
1. 查看内核日志,确认GMSL链路lock成功;2. 核对Sensor输出格式、分辨率、帧率与解串器配置是否一致;3. 检查video-pipe的寄存器配置,确保数据映射到正确的VC通道;4. 通过串化器的PCLK检测功能,确认Sensor有数据输出
|
|
上层应用找不到摄像头
|
1. rvcam_config.xml配置是否正确;2. 虚拟摄像头camId是否配置;3. EVS与HAL3的ID是否冲突;4. RVCAM服务是否正常启动
|
1. 核对board name与主板model是否一致;2. 确认inputMapping中配置了对应的camId;3. 检查HAL3的ignore配置,避免ID冲突;4. 查看RVCAM服务日志,确认配置文件加载成功
|
|
多个应用同时访问摄像头失败
|
1. 是否配置了一摄多开的虚拟映射;2. 每个应用是否使用不同的虚拟camId
|
1. 在rvcam_config.xml的inputMapping中,给同一个物理通道配置多个虚拟camId;2. 不同应用使用不同的虚拟camId打开摄像头
|
|
图像画面花屏、卡顿
|
1. MIPI信号质量是否正常;2. SerDes链路速率是否匹配;3. 帧率与带宽是否匹配;4. 缓冲区数量是否足够
|
1. 检查硬件线缆、阻抗匹配;2. 核对GMSL链路速率配置,确保满足摄像头数据带宽需求;3. 增加stream的bufCnt缓冲区数量
|
七、总结与展望
瑞芯微的车载摄像头开发框架,为开发者提供了从底层SerDes驱动适配、中间件配置,到上层AAOS EVS/HAL3应用落地的全链路解决方案,极大降低了车载摄像头的开发门槛。
对于开发者而言,车载摄像头开发的核心要点可以总结为3步:
1.底层驱动适配:完成SerDes芯片与Sensor的DTS配置,RAW Sensor完成驱动适配,确保内核层能正常采集到图像数据;
2.中间件配置:通过rvcam_config.xml完成物理摄像头与虚拟摄像头的映射,实现一摄多开能力;
3.上层应用适配:根据业务场景,完成EVS倒车/环视应用配置,或基于Camera2 API开发座舱相机应用。
希望本文能帮助各位开发者快速掌握RK平台车载摄像头的开发全流程,少走弯路,打造出稳定、高效的车载视觉应用。
全部0条评论
快来发表一下你的评论吧 !