Skip to main content

K1_OH5.0 MIPI 摄像头适配指南

修订记录

修订版本修订日期修订说明
0012026-06-12初始版本

1. 适用范围

本文面向基于 SpacemiT K1 平台用户,说明如何在 OpenHarmony 5.0 中点亮并调试一款 MIPI 摄像头。

2. 代码路径

内容路径
Camera HAL源码device/soc/spacemit/k1/hardware/camera
Sensor 通用驱动device/soc/spacemit/k1/hardware/camera/sensors/sensor
Sensor 模组配置device/soc/spacemit/k1/hardware/camera/sensors/module
Sensor 列表device/soc/spacemit/k1/hardware/camera/sensors/cam_sensors_module_list.h
cam-test 入口device/soc/spacemit/k1/hardware/camera/demo/main.c
cam-test 配置示例device/soc/spacemit/k1/hardware/camera/demo/cfgs
OH GN 构建文件device/soc/spacemit/k1/hardware/camera/BUILD.gn
Kernel Camera Sensor 公共 DTSIkernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x-camera-sensor.dtsi
Kernel Camera SDK 公共 DTSIkernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x-camera-sdk.dtsi
Camera MCLK pinctrlkernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x_pinctrl.dtsi
板级 DTSkernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x_<board>.dts
OH Camera Framework 适配参考device/board/spacemit/musepaper2/camera
HDI HCS 配置vendor/spacemit/<product>/hdf_config/uhdf/camera/hdi_impl/camera_host_config.hcs

3. Camera 数据通路

K1 OH Camera 的典型数据流如下:

原始光信号 -> SENSOR(光电转换) -> CCIC(数据接收) -> IDI(数据路由) -> ISP-PIPE(图像处理) -> ISP-FORMAT(格式转换) -> ISP-DMA(内存写入) -> CPP(后处理) -> svivi(/dev/video23、24) -> Camera HDI -> OH Camera Framework -> Camera App

其中 cam-test 主要用于直接验证底层 Sensor/CCIC/ISP/CPP 链路;svivi 向 OH Camera HDI 暴露 V4L2 视频节点,OH 应用预览、拍照最终通过 HDI 访问对应的 svivi 节点。

4. 添加一款 Sensor 总体流程

假设新 Sensor 名称为 xxx,模组名称为 xxx_spm

  1. 选择一个接近的参考 Sensor。
  2. 在 DTS 中配置 Sensor 节点。
  3. 新增 sensors/sensor/xxx_sensor.c
  4. 新增 sensors/module/xxx_spm/xxx_spm.c
  5. cam_sensors_module_list.h 注册 Sensor 和 Module。
  6. BUILD.gn 中加入新源码。
  7. 修改或新增 cam-test JSON。
  8. 使用 cam-test 进行 ID、出流、帧率和图像验证。
  9. 更新 OH Camera Framework 的 JSON/HCS 配置。
  10. 完整编译并打包固件。

5. DTS 配置

5.1 Sensor 节点位置

通用 Camera Sensor 节点定义在:

kernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x-camera-sensor.dtsi

Camera SDK/控制器相关公共配置定义在:

kernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x-camera-sdk.dtsi

一般情况下不要直接修改这两个 Camera 公共 DTSI 文件:

  • k1-x-camera-sensor.dtsi
  • k1-x-camera-sdk.dtsi

这两个文件属于平台级公共配置,可能被多个板型共用。新增或适配某个板子的 Sensor 时,通常只在板级 DTS 中通过 &backsensor&backsensor_aux&frontsensor 等节点覆盖 statustwsi-indexdphy-indexclockspinctrlpwdn-gpiosreset-gpios、regulator 和 pinmulti-enable 等板级差异配置。

当前默认包含三个节点:

节点设备 ID典型设备MCLKpinctrl说明
backsensor: cam_sensor@0cell-index = <0>CSI0 通路cam_mclk0pinctrl_camera0/dev/cam_sensor0
backsensor_aux: cam_sensor@1cell-index = <1>CSI1 通路cam_mclk1pinctrl_camera1/dev/cam_sensor1
frontsensor: cam_sensor@2cell-index = <2>CSI2 通路cam_mclk2pinctrl_camera2/dev/cam_sensor2

5.2 DTS 关键字段

cam_sensor@0 为例:

backsensor: cam_sensor@0 {
cell-index = <0>;
twsi-index = <0>;
dphy-index = <0>;
compatible = "spacemit,cam-sensor";
clocks = <&ccu CLK_CAMM0>;
clock-names = "cam_mclk0";

pinctrl-names = "default";
pinctrl-0 = <&pinctrl_camera0>;
pwdn-gpios = <&gpio 113 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio 111 GPIO_ACTIVE_HIGH>;

status = "okay";
};

字段说明:

字段说明新 Sensor 修改建议
cell-indexSensor 设备 ID必须唯一;对应 /dev/cam_sensorXcam-testsensor_id
twsi-indexI2C/TWSI 控制器编号按硬件原理图连接填写
dphy-indexMIPI DPHY 通路编号按 Sensor 接到的 CSI/DPHY 通路填写
compatibleKernel 驱动匹配字符串保持 spacemit,cam-sensor
clocks / clock-namesSensor MCLKCLK_CAMM0/1/2cam_mclk0/1/2 对应
pinctrl-0MCLK pinmux对应 pinctrl_camera0/1/2
pwdn-gpiosPWDN GPIO按硬件填写;不用时可注释
reset-gpiosRESET GPIO按硬件填写;不用时可注释
pinmulti-enableMCLK/PWDN/RESET 复用模式开关多个 Sensor 复用同一组 MCLK/PWDN/RESET 时配置
status节点状态使用时配置为 okay

5.3 pinmulti 和 mclk_multi 配置

pinmulti-enablemclk_multi 只在多个 Sensor 存在管脚复用时配置,按实际复用的资源决定。

配置规则:

  • 如果多个 Sensor 共用一组 PWDN 和 RESET 管脚,需要配置 pinmulti-enable;
  • 如果多个 Sensor 共用 MCLK,需要同时配置 pinmulti-enable;pinctrl-names = "mclk_multi";
  • 如果 PWDN、RESET、MCLK 都是独立管脚,则不需要配置 pinmulti-enablemclk_multi

其中 mclk_multi 是驱动固定查找的 pinctrl state 名称,只有 MCLK 复用时才需要配置。

配置示例:

  1. 共用 PWDN/RESET:
&backsensor {
pinmulti-enable;

clocks = <&ccu CLK_CAMM0>;
clock-names = "cam_mclk0";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_camera0>;

/* 两个 Sensor 共用同一组 PWDN/RESET */
pwdn-gpios = <&gpio 124 0>;
reset-gpios = <&gpio 97 0>;
status = "okay";
};

&backsensor_aux {
pinmulti-enable;

clocks = <&ccu CLK_CAMM1>;
clock-names = "cam_mclk1";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_camera1>;

/* 两个 Sensor 共用同一组 PWDN/RESET */
pwdn-gpios = <&gpio 124 0>;
reset-gpios = <&gpio 97 0>;
status = "okay";
};
  1. 共用 MCLK:
&backsensor {
pinmulti-enable;

/* 两个 Sensor 共用同一个 MCLK */
clocks = <&ccu CLK_CAMM0>;
clock-names = "cam_mclk0";
pinctrl-names = "mclk_multi";
pinctrl-0 = <&pinctrl_camera0>;

pwdn-gpios = <&gpio 124 0>;
reset-gpios = <&gpio 97 0>;
status = "okay";
};

&backsensor_aux {
pinmulti-enable;

/* 两个 Sensor 共用同一个 MCLK */
clocks = <&ccu CLK_CAMM0>;
clock-names = "cam_mclk0";
pinctrl-names = "mclk_multi";
pinctrl-0 = <&pinctrl_camera0>;

pwdn-gpios = <&gpio 124 0>;
reset-gpios = <&gpio 97 0>;
status = "okay";
};

5.4 MCLK pinctrl

MCLK pinmux 在:

kernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x_pinctrl.dtsi

当前定义:

pinctrl_camera0: camera0_grp {
pinctrl-single,pins =<
K1X_PADCONF(GPIO_53, MUX_MODE1, (EDGE_NONE | PULL_DIS | PAD_1V8_DS2)) /* cam_mclk0 */
>;
};

pinctrl_camera1: camera1_grp {
pinctrl-single,pins =<
K1X_PADCONF(GPIO_58, MUX_MODE1, (EDGE_NONE | PULL_DIS | PAD_1V8_DS2)) /* cam_mclk1 */
>;
};

pinctrl_camera2: camera2_grp {
pinctrl-single,pins =<
K1X_PADCONF(GPIO_120, MUX_MODE1, (EDGE_NONE | PULL_DIS | PAD_1V8_DS2)) /* cam_mclk2 */
>;
};

如硬件 MCLK 引脚变化,需要同步修改 pinctrl。

6. 用户态 Sensor 驱动源码

6.1 新增 xxx_sensor.c

路径:

device/soc/spacemit/k1/hardware/camera/sensors/sensor/xxx_sensor.c

建议从相近 Sensor 拷贝,例如:

  • imx219_sensor.c
  • ov16a10_sensor.c
  • gc5035_sensor.c

需要重点修改以下内容。

6.1.1 I2C 地址宽度和数据宽度

参考 imx219_sensor.c

static const uint8_t imx219_reg_addr_byte = I2C_16BIT;
static const uint8_t imx219_reg_data_byte = I2C_8BIT;

按 Sensor 手册修改为:

  • I2C_8BIT / I2C_16BIT:寄存器地址宽度。
  • I2C_8BIT / I2C_16BIT:寄存器数据宽度。

如果该配置错误,通常会导致 ID 读取失败或寄存器写入无效。

6.1.2 Stream on/off 和初始化寄存器

需要配置:

  • stream_on_regs
  • stream_off_regs
  • stream_soft_reset_regs
  • color_bar_regs,如 Sensor 支持 colorbar,建议保留用于调试。

寄存器表应来自 Sensor 厂商提供的 setting,必须与目标分辨率、FPS、lane 数、bit depth 匹配。

6.1.3 曝光、增益和 VTS 寄存器

参考 imx219_sensor.c

#define IMX219_VTS_ADDR_H ...
#define IMX219_VTS_ADDR_L ...
#define IMX219_EXPO_H ...
#define IMX219_EXPO_L ...
#define IMX219_AGAIN_GLOBAL ...
#define IMX219_GROUP_ACCESS ...

新 Sensor 需要按手册分别修改 VTS、曝光、增益和 group hold。下面四项是最容易出错的位置,建议逐项对照 datasheet 和厂商 setting 修改。

6.1.3.1 VTS 配置

VTS 是一帧图像包含的总行数,主要影响帧率和长曝光上限。典型修改位置:

  • xxx_sensor_fps_set():根据目标 FPS 重新计算 VTS,并写入 VTS 寄存器缓存。
  • xxx_sensor_expotime_update():当曝光行数超过当前 VTS 可容纳范围时,需要扩展 VTS。
  • xxx_sensor_get_expotime_by_fps():根据 FPS 反推该帧率下的最大曝光时间。

移植新 Sensor 时,需要按手册修改:

  • VTS 高低字节寄存器地址,例如 IMX219_VTS_ADDR_HIMX219_VTS_ADDR_L
  • 最大 VTS、最小 VTS、VTS 和最大曝光行之间的安全 margin,例如 IMX219_VTS_ADJUST
6.1.3.2 曝光行数配置

曝光接口接收的通常是 us 单位的曝光时间,驱动需要把它换算成 Sensor 曝光行数后写入曝光寄存器。典型函数:

  • xxx_sensor_expotime_update():将 u32ExpoTime 转成 expLine,并更新曝光寄存器和必要的 VTS。
  • xxx_sensor_get_ae_default():配置 AE 默认曝光、最大/最小曝光范围。
  • xxx_sensor_dump_info():调试时读取并打印当前曝光寄存器值。

移植新 Sensor 时,需要按手册修改:

  • 曝光寄存器地址,例如 IMX219_EXPO_HIMX219_EXPO_L
  • 曝光寄存器位宽和排列方式。有些 Sensor 是高/低字节,有些是 20bit 或多个寄存器拼接。
  • 最小曝光行和最大曝光行限制,例如 IMX219_EXPO_LINES_MIN
6.1.3.3 增益配置

ISP/AE 传入的模拟增益通常是 Q 格式数值,Sensor 驱动需要把它转换为 Sensor 手册定义的 gain register。典型函数:

  • xxx_sensor_gain_update():将 pAgainVal / pDgainVal 转换为模拟增益和数字增益寄存器值。
  • xxx_sensor_get_ae_default():配置 AE 可用的最大/最小模拟增益、数字增益和总增益范围。
  • xxx_sensor_get_reg_info():注册需要由 ISP 同步更新的增益寄存器地址。

移植新 Sensor 时,需要按手册修改:

  • 模拟增益寄存器地址,例如 IMX219_AGAIN_GLOBAL
  • gain register 的公式、范围和分段。有些 Sensor 是线性 gain,有些是查表或多段模拟/数字组合。
  • pAgainVal 不是 Sensor 寄存器值,而是 ISP/AE 传入的目标模拟增益值,以 256 表示 1x 增益。驱动需要先把它换算成实际增益倍数,再按 Sensor datasheet 的公式或表格转换为寄存器值。
  • AE 返回值。xxx_sensor_gain_update() 最后应把实际可实现的增益回写到 *pAgainVal / *pDgainVal,让 AE 知道 Sensor 实际设置了多少增益。
  • 数字增益是否使用。如果暂不使用数字增益,通常保持 *pDgainVal = 4096,表示 1x 数字增益。

典型转换过程如下:

  1. 读取 AE 传入的 *pAgainVal
  2. 计算目标模拟增益倍数:again = *pAgainVal / 256.0
  3. 根据 Sensor datasheet 中的 analog gain 公式或 gain table,计算该倍数对应的寄存器值。
  4. 将寄存器值写入 Sensor 的模拟增益寄存器。
  5. 如果寄存器步进无法精确表示目标增益,应选择最接近的可支持值,并把实际生效的增益回写到 *pAgainVal

例如 *pAgainVal = 384 时,表示目标模拟增益为:

384 / 256 = 1.5x

此时要按该 Sensor datasheet 的计算公式,求出 1.5x 模拟增益对应的 register code,再写入对应寄存器。不同 Sensor 的公式不同,例如有的 Sensor 使用线性 register code,有的 Sensor 使用 gain = base / (base - reg) 这类反推公式,有的则需要查表选择最接近的档位,因此新 Sensor 必须按手册重新实现该转换。

不同 Sensor 的 gain register 不是通用公式,不能直接复用 imx219_sensor_gain_update() 中的计算方式。

6.1.3.4 group hold 配置

group hold 是 Sensor 的成组寄存器更新机制。它的作用是在更新曝光、增益、VTS 等多个相关寄存器时,先把寄存器写入暂存组,最后一次性生效,避免同一帧中部分寄存器已更新、部分寄存器未更新导致画面亮度跳变、撕裂或 AE 震荡。

典型函数:

  • xxx_sensor_group_reg_start():进入 group hold,开始暂存一组寄存器更新。
  • xxx_sensor_group_reg_done():退出 group hold,使这组寄存器更新同时生效。
  • xxx_sensor_get_reg_info():把曝光、增益、VTS 等寄存器放入 ISP 同步更新列表,通常配合 group hold 使用。
  • xxx_sensor_expotime_update()xxx_sensor_gain_update()xxx_sensor_fps_set():这些函数更新的寄存器通常属于同一组成像参数,需要避免跨帧不一致。

移植新 Sensor 时,需要按手册修改:

  • group hold 控制寄存器地址,例如 IMX219_GROUP_ACCESS
  • 开始和结束的写入值。不同 Sensor 可能是 0x01/0x000x00/0x10,也可能需要多步 sequence。
  • 如果 Sensor 不支持 group hold,可将 xxx_sensor_group_reg_start() / xxx_sensor_group_reg_done() 做成空实现并返回 0,但要确认曝光、增益、VTS 更新不会出现明显闪烁或撕裂。

6.1.4 Power on/off

如果平台通用上下电流程满足要求,可直接使用:

  • sensor_hw_init(sensor_context->devId)
  • sensor_hw_unreset(sensor_context->devId)
  • sensor_hw_reset(sensor_context->devId)
  • sensor_hw_exit(sensor_context->devId)

这些接口位于:

device/soc/spacemit/k1/hardware/camera/sensors/sensor/cam_sensor.c

典型调用流程可参考 ov16a10_sensor.c

  • xxx_init() 中先调用 sensor_hw_init() 打开 /dev/cam_sensorX,再调用 sensor_hw_unreset() 触发内核上电和释放复位,之后通过 sensor_get_hw_info() 获取 twsi_no
  • xxx_deinit() 中如果正在出流,先写 stream_off_regs 关流,再调用 sensor_hw_reset() 拉低复位并关闭电源,最后调用 sensor_hw_exit() 关闭 /dev/cam_sensorX

用户态 sensor_hw_unreset()sensor_hw_reset() 本身不直接操作 GPIO 或 regulator,而是通过 ioctl 通知内核:

用户态接口ioctl内核处理函数作用
sensor_hw_unreset()CAM_SENSOR_UNRESETcamsnr_unreset_sensor()调用 cam_sensor_power_set(msnr_dev, 1) 执行上电和释放复位
sensor_hw_reset()CAM_SENSOR_RESETcamsnr_reset_sensor()调用 cam_sensor_power_set(msnr_dev, 0) 执行复位和下电

内核 cam_sensor_power_set(msnr_dev, 1) 的上电流程大致为:

  1. 打开 MCLK:clk_prepare_enable(),并设置为 24MHz
  2. 依次使能 regulator:avdd 设置为 2.8Vdovdd 设置为 1.8Vdvdd 设置为 1.2V
  3. 如果配置了 dvdden-gpios,拉高 DVDDEN。
  4. 如果配置了 afvdd,设置为 2.8V 并使能。
  5. 如果配置了 pwdn-gpios,拉高 PWDN。
  6. 如果配置了 reset-gpios,先拉低 RESET,延时约 5ms,再拉高 RESET,延时约 10ms

上述 5ms10ms 是内核通用流程里的固定延时。其中 5ms 用于保证 Sensor 进入稳定 reset 状态,10ms 用于 reset 释放后等待 Sensor 内部时钟、电源域和 I2C 接口稳定。不同 Sensor 对 MCLK 起振后等待时间、各路电源稳定时间、PWDN 到 RESET 的间隔、RESET 释放到首次 I2C 访问的间隔要求不同,应以 Sensor datasheet 的 power sequence 为准。

内核 cam_sensor_power_set(msnr_dev, 0) 的下电流程大致为:

  1. 如果配置了 reset-gpios,拉低 RESET。
  2. 如果配置了 pwdn-gpios,拉低 PWDN。
  3. 关闭 dvddavdddovdd
  4. 如果配置了 dvdden-gpios,拉低 DVDDEN。
  5. 关闭 afvdd
  6. 关闭 MCLK:clk_disable_unprepare()

因此,默认通用流程适合大多数 Sensor:init 阶段 sensor_hw_init() + sensor_hw_unreset(),deinit 阶段 sensor_hw_reset() + sensor_hw_exit()。如果 Sensor 手册要求不同的 MCLK 频率、LDO 电压、上电顺序、PWDN/RESET 有效电平或额外延时,则不要直接复用通用流程,应通过下列用户态接口实现私有上下电函数。

私有上下电常用 API:

API作用常用参数说明
sensor_set_mclk_rate(sns_id, clk_rate)设置 Sensor MCLK 频率常用 24000000,具体按 Sensor datasheet
sensor_set_mclk_enable(sns_id, clk_enable)使能或关闭 Sensor MCLK1 使能,0 关闭
sensor_set_power_voltage(sns_id, regulator_id, voltage)设置 LDO/regulator 电压电压单位为 uV,例如 280000018000001200000
sensor_set_power_on(sns_id, regulator_id, on)使能或关闭 LDO/regulator1 使能,0 关闭
sensor_set_gpio_enable(sns_id, gpio_id, enable)设置 Sensor GPIO 电平常用于 SENSOR_GPIO_PWDNSENSOR_GPIO_RSTSENSOR_GPIO_DVDDEN

常用 regulator ID:

regulator ID典型电源常用电压
SENSOR_REGULATOR_AVDD模拟电源2800000
SENSOR_REGULATOR_DOVDDIO 电源1800000
SENSOR_REGULATOR_DVDD数字核心电源1200000
SENSOR_REGULATOR_AFVDDAF/VCM 电源2800000

常用 GPIO ID:

GPIO ID作用
SENSOR_GPIO_PWDNSensor PWDN 控制
SENSOR_GPIO_RSTSensor RESET 控制
SENSOR_GPIO_DVDDEN外部 DVDD enable 控制,如果板级未使用可不配置

私有上下电函数中的延时建议按以下原则配置:

  • MCLK 打开后到释放 PWDN/RESET 前,通常需要等待几毫秒,确保外部时钟稳定。
  • regulator 或外部电源使能后,需要等待电压爬升和稳定;不同电源轨之间的间隔按硬件和 Sensor 手册要求配置。
  • PWDN 和 RESET 的先后顺序、有效电平和间隔不通用,必须按具体 Sensor 修改。
  • RESET 释放后到读 Sensor ID 或写初始化寄存器前,应预留启动时间;如果 ID 偶现读取失败,可优先适当增加该延时验证。
  • 延时不宜盲目过大。调试阶段可先放宽延时提高稳定性,确认链路正常后再按手册收敛到合理值。

如果 Sensor 需要特殊上下电时序,可新增私有 xxx_power_on() / xxx_power_off() 函数。下面示例把 MCLK、LDO 电压、LDO 使能、PWDN/RESET 都写完整,实际顺序、电压、电平和延时必须按硬件设计与 Sensor 手册调整:

static int xxx_power_on(SENSOR_CONTEXT_S* sensor_context)
{
int devId = sensor_context->devId;

sensor_set_mclk_rate(devId, 24000000);
sensor_set_mclk_enable(devId, 1);

sensor_set_power_voltage(devId, SENSOR_REGULATOR_DOVDD, 1800000);
sensor_set_power_on(devId, SENSOR_REGULATOR_DOVDD, 1);

sensor_set_power_voltage(devId, SENSOR_REGULATOR_DVDD, 1200000);
sensor_set_power_on(devId, SENSOR_REGULATOR_DVDD, 1);

sensor_set_power_voltage(devId, SENSOR_REGULATOR_AVDD, 2800000);
sensor_set_power_on(devId, SENSOR_REGULATOR_AVDD, 1);

sensor_set_power_voltage(devId, SENSOR_REGULATOR_AFVDD, 2800000);
sensor_set_power_on(devId, SENSOR_REGULATOR_AFVDD, 1);

sensor_set_gpio_enable(devId, SENSOR_GPIO_DVDDEN, 1);
usleep(5000);

sensor_set_gpio_enable(devId, SENSOR_GPIO_PWDN, 0);
sensor_set_gpio_enable(devId, SENSOR_GPIO_RST, 0);
usleep(8000);

sensor_set_gpio_enable(devId, SENSOR_GPIO_PWDN, 1);
sensor_set_gpio_enable(devId, SENSOR_GPIO_RST, 1);
usleep(10000);

return 0;
}

static int xxx_power_off(SENSOR_CONTEXT_S* sensor_context)
{
int devId = sensor_context->devId;

sensor_set_gpio_enable(devId, SENSOR_GPIO_RST, 0);
sensor_set_gpio_enable(devId, SENSOR_GPIO_PWDN, 0);
sensor_set_gpio_enable(devId, SENSOR_GPIO_DVDDEN, 0);

sensor_set_power_on(devId, SENSOR_REGULATOR_AFVDD, 0);
sensor_set_power_on(devId, SENSOR_REGULATOR_AVDD, 0);
sensor_set_power_on(devId, SENSOR_REGULATOR_DVDD, 0);
sensor_set_power_on(devId, SENSOR_REGULATOR_DOVDD, 0);

sensor_set_mclk_enable(devId, 0);

return 0;
}

注意:

  1. 如果某个电源或 GPIO 在 DTS 中没有配置,对应 API 不要调用;例如没有 AF/VCM 时可去掉 SENSOR_REGULATOR_AFVDD,没有外部 DVDD enable 时可去掉 SENSOR_GPIO_DVDDEN
  2. 私有函数写好后,应在 Sensor init/deinit 流程中替代默认 sensor_hw_unreset() / sensor_hw_reset() 对应的通用上下电动作,避免同一路电源被重复控制。

6.1.5 全局配置函数

xxx_global_config() 会保存 Module 传入的 SENSOR_WORK_INFO_S,并写入 Sensor 初始化寄存器:

sensor_context->initVTS = sensor_context->work_info.vts;
sensor_context->initFps = sensor_context->work_info.f32maxFps;
sensor_context->lineTime = sensor_context->work_info.linetime;

xxx_write_burst_register(handle,
sensor_context->work_info.setting_table,
sensor_context->work_info.setting_table_size);

6.2 新增 Module 配置 xxx_spm.c

路径:

device/soc/spacemit/k1/hardware/camera/sensors/module/xxx_spm/xxx_spm.c

建议参考:

device/soc/spacemit/k1/hardware/camera/sensors/module/imx219_spm/imx219_spm.c

重点配置以下内容。

6.2.1 I2C 地址

static const uint8_t module_i2c_addr = 0x10;

填写 Sensor 的 7-bit I2C 地址。不要填写 8-bit 读写地址。

6.2.2 Vendor ID

static struct regval_tab xxx_spm_vendor_id[] = {
{0x0000, 0x02},
{0x0001, 0x19},
};

按 Sensor 手册填写 ID 寄存器和值。cam-test 自动检测会通过该表判断 Sensor 是否存在。

6.2.3 Capability

常见字段说明:

字段说明
width / heightSensor 输出有效分辨率
bitDepthRAW bit,如 8/10/12
maxFps / minFps支持帧率范围
image_mode图像模式,一般使用 Linar 线性模式
lane_numMIPI lane 数,必须和寄存器 setting 一致
patternBayer pattern,如 RGGB/GRBG/GBRG/BGGR
supportPDAF是否支持 PDAF
work_modeSensor mode 编号,对应 JSON 的 sensor_work_mode

6.2.4 Work info

SENSOR_WORK_INFO_S 中常见字段:

字段说明修改依据
linetime行时间,通常单位 ns根据 HTS、PCLK 计算
vts默认 VTSSensor setting / datasheet
f32maxFps当前 mode 最大 FPS与 VTS、HTS、PCLK 匹配
exp_time[]初始曝光时间调试初期可设为合理中间值
again[] / dgain[]初始模拟/数字增益通常从 1x 开始
setting_table初始化寄存器表Sensor 厂商 setting
setting_table_size寄存器表大小使用 ARRAY_SIZE()
mipi_clockMIPI clock按 Sensor 输出速率配置

行时间、VTS 和 FPS 必须自洽。常用关系:

$$ PCLK = FPS \times HTS \times VTS $$

$$ LineTime = \frac{HTS}{PCLK} $$

如果曝光异常、FPS 不稳定或 ISP AE 不收敛,优先检查 linetimevtsf32maxFps 和曝光寄存器换算。

6.3 注册 Sensor

修改:

device/soc/spacemit/k1/hardware/camera/sensors/cam_sensors_module_list.h

增加 extern:

extern SENSOR_OBJ_S xxxObj;
extern MODULE_OBJ_S xxx_spm_Obj;

sensors_module_list[] 中增加:

{&xxx_spm_Obj, &xxxObj, NULL, NULL},

6.4 修改 OH 构建文件

修改:

device/soc/spacemit/k1/hardware/camera/BUILD.gn

ohos_shared_library("libcam_sensors")sources 中加入:

"./sensors/sensor/xxx_sensor.c",
"./sensors/module/xxx_spm/xxx_spm.c",

7. cam-test 使用与参数配置

7.1 cam-test 入口和 case

入口文件:

device/soc/spacemit/k1/hardware/camera/demo/main.c

常用 case:

OH 相关调试只使用 single_pipeline_online_test 单路在线模式。即使产品同时配置前摄和后摄,也是分别按单路 Camera 打开和验证;dual_pipeline_online_testdual_pipeline_capture_testonly_dual_ccic_test 属于双路底层验证场景,OH 前后摄调试不需要使用。

case函数用途
0single_pipeline_online_test单路 Sensor 在线出图,OH 前后摄调试使用该模式
1dual_pipeline_online_test双路在线预览,OH 前后摄调试不用
2dual_pipeline_capture_test双路拍照,OH 前后摄调试不用
3only_rawdump_test只 dump RAW
4only_viisp_online_test只验证 VI/ISP
5only_viisp_offline_preview_test离线预览
6only_cpp_test只验证 CPP
7only_ccic_test只验证 CCIC
8slice_capture_testslice capture
9only_dual_ccic_test双路 CCIC,OH 前后摄调试不用

7.2 自动检测 Sensor

cam-test /system/profile/csi1_camera_detect.json

正常日志示例:

: ../../device/soc/spacemit/k1/hardware/camera/sensors/cam_sensors_module.c(241): "detect gc13a0_spm sensors in csi3: success, set 4208x3120 to 1920x1080"
I: auto_detect_camera(1401): "auto detect sensor ===================== finish "
I: update_json_file(728): "save json to /data/csi3_camera_auto.json success"

检测成功后,会生成:

/data/csi1_camera_auto.json

后续可直接使用该 JSON 文件

7.3 单路在线出图

detect生成的csix_camera_auto.json默认跑的是单路在线模式,可以直接运行:

cam-test /data/csi1_camera_auto.json

正常运行时应看到 FPS 日志,例如:

CPP0: frameid 1, fps 30.0
CPP0: frameid 2, fps 30.0
CPP0: frameid 3, fps 30.0

程序会自动dump第250帧图像到/tmp目录下,文件名为cpp0_output_1920x1080_s1920.nv12,可以拉到本地查看画面是否正常。 若保存失败需要修改目录权限:mount -o remount,rw /tmp,然后重新运行。

7.4 JSON 字段说明

参考:

device/soc/spacemit/k1/hardware/camera/demo/cfgs/1/camtest_main_aux.json

{
"tuning_server_enable": 1,
"show_fps": 1,
"auto_run": 1,
"cpp_node": [
{
"name": "cpp0",
"enable": 1,
"format": "NV12",
"src_from_file": 1,
"src_path": "/tmp/cpp_case_in_data/1920x1080/",
"size_width": 1920,
"size_height": 1080
},
{
"name": "cpp1",
"enable": 0,
"format": "NV12",
"src_from_file": 0,
"src_path": "/vendor/etc/camera/",
"size_width": 1920,
"size_height": 1080
}
],
"isp_node": [
{
"name": "isp0",
"enable": 1,
"work_mode": "online",
"format": "NV12",
"out_width": 1920,
"out_height": 1080,
"sensor_name": "imx135_spm",
"sensor_id": 0,
"sensor_work_mode": 0,
"fps": 30,
"src_file": "/tmp/1920x1080_raw12_long_packed.vrf",
"bit_depth": 12,
"in_width": 1920,
"in_height": 1080
},
{
"name": "isp1",
"enable": 0,
"work_mode": "online",
"format": "NV12",
"out_width": 1600,
"out_height": 1200,
"src_file": "/tmp/1920x1080_raw12_long_packed.vrf",
"bit_depth": 12,
"in_width": 1920,
"in_height": 1080,
"sensor_name": "gc2375h_spm",
"sensor_id": 1,
"sensor_work_mode": 0,
"fps": 30
}
]
}

顶层字段说明:

字段说明调试建议
tuning_server_enableISP tuning 服务使能,在 only isp online、单 pipeline online、双 pipeline online 测试中有效需要在线调试 ISP tuning 时设为 1,普通出图验证可设为 0
show_fps是否打印 FPS,并统计 0~120 帧平均帧率bring-up 阶段建议设为 1
auto_run是否自动运行测试设为 1 时无交互流程;设为 0 时进入命令交互
cpp_nodeCPP 配置数组每个元素对应一个 CPP group,例如 cpp0cpp1
isp_nodeISP 配置数组每个元素对应一路 ISP pipeline,例如 isp0isp1

cpp_node 字段说明:

字段说明配置要求
nameCPP 节点名称双路时通常为 cpp0cpp1
enable是否使能该 CPP 节点1 使能,0 关闭
formatCPP 输出格式常用 NV12;svivi/OH 验证通常使用 NV12
src_from_fileCPP 输入是否来自文件0 表示来自 ISP 输出;1 表示从 src_path 读取文件,常用于 only CPP 测试
src_pathCPP 输入文件目录src_from_file = 1 时有效;online Sensor 出图时通常不关注
size_width / size_heightCPP 输入/处理尺寸应与前级 ISP 输出或输入文件尺寸匹配

isp_node 字段说明:

字段说明新 Sensor 配置要求
nameISP pipeline 名称单路通常用 isp0;双路可用 isp0isp1
enable是否使能该 ISP 节点OH 前后摄调试只开一路;双路节点不用开启
work_modeISP 工作模式Sensor 在线出图使用 online;离线/文件输入测试按对应 case 配置
formatISP 输出格式常用 NV12;需和后级 CPP/svivi 支持格式一致
out_width / out_heightISP 输出尺寸可等于或小于 Sensor 输入尺寸,但ISP支持输出分辨率最高支持1080p
sensor_nameSensor Module 名称必须和 MODULE_OBJ_S xxx_spm_Obj 匹配,例如 xxx_spm
sensor_idSensor 设备 ID必须和 DTS cell-index 对应,即 /dev/cam_sensorXX
sensor_work_modeSensor 工作模式编号必须和 xxx_spm.c 中 capability/work info 的 mode 编号对应
fps目标 FPSonline 模式有效,不能超过该 mode 的 maxFps,需和 vtslinetime、setting 匹配
src_file离线 RAW 输入文件single online 出图时不生效;该字段用于离线 ISP/文件输入模式
bit_depth输入 RAW bit depthsingle online 出图时不生效;离线文件测试时必须和 RAW 文件一致
in_width / in_heightISP 输入尺寸single online 出图时不生效;离线文件测试时必须和 RAW 文件一致

8. OH Camera通路适配

8.1 MIPI通路定制参考

3.5.2 Camera 通路定制(MIPI)

9. FAQ

9.1 sensor detect失败

检查:

  • module_i2c_addr 是否为 7-bit 地址且地址和手册一致。
  • xxx_spm_vendor_id[] 寄存器和值是否正确。
  • xxx_reg_addr_byte / xxx_reg_data_byte 地址位宽和数据位宽是否正确。
  • twsi-index i2c总线是否配对。
  • PWDN/MCLK/AVDD/DVDD/IOVDD 是否输出,时序是否符合datasheet要求。

9.2 能detect到sensor但出流无帧打印

检查:

  • stream_on_regs 寄存器数组是否正确。
  • 初始化 setting 是否匹配当前分辨率、FPS、lane、raw bit。
  • dphy-index 是否正确。
  • lane_num 是否与硬件和 Sensor setting 一致。
  • mipi_clock 是否合理。

9.3 图像颜色异常

检查:

  • pattern 的 Bayer 顺序是否正确。
  • RAW bit depth 是否配置一致。
  • 是否存在镜像/翻转导致 Bayer pattern 改变。
  • ISP tuning 是否匹配该 Sensor。

9.4 FPS 或曝光异常

检查:

  • linetimevtsf32maxFps 是否自洽。
  • xxx_sensor_fps_set() 是否正确写 VTS。
  • xxx_sensor_expotime_update() 是否正确计算曝光行数。
  • xxx_sensor_gain_update() 增益公式是否按 Sensor 手册实现。