K1_OH5.0 MIPI 摄像头适配指南
修订记录
| 修订版本 | 修订日期 | 修订说明 |
|---|---|---|
| 001 | 2026-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 公共 DTSI | kernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x-camera-sensor.dtsi |
| Kernel Camera SDK 公共 DTSI | kernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x-camera-sdk.dtsi |
| Camera MCLK pinctrl | kernel/linux/spacemit_kernel-6.6/arch/riscv/boot/dts/spacemit/k1-x_pinctrl.dtsi |
| 板级 DTS | kernel/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。
- 选择一个接近的参考 Sensor。
- 在 DTS 中配置 Sensor 节点。
- 新增
sensors/sensor/xxx_sensor.c。 - 新增
sensors/module/xxx_spm/xxx_spm.c。 - 在
cam_sensors_module_list.h注册 Sensor 和 Module。 - 在
BUILD.gn中加入新源码。 - 修改或新增
cam-testJSON。 - 使用
cam-test进行 ID、出流、帧率和图像验证。 - 更新 OH Camera Framework 的 JSON/HCS 配置。
- 完整编译并打包固件。
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.dtsik1-x-camera-sdk.dtsi
这两个文件属于平台级公共配置,可能被多个板型共用。新增或适配某个板子的 Sensor 时,通常只在板级 DTS 中通过 &backsensor、&backsensor_aux、&frontsensor 等节点覆盖 status、twsi-index、dphy-index、clocks、pinctrl、pwdn-gpios、reset-gpios、regulator 和 pinmulti-enable 等板级差异配置。
当前默认包含三个节点:
| 节点 | 设备 ID | 典型设备 | MCLK | pinctrl | 说明 |
|---|---|---|---|---|---|
backsensor: cam_sensor@0 | cell-index = <0> | CSI0 通路 | cam_mclk0 | pinctrl_camera0 | /dev/cam_sensor0 |
backsensor_aux: cam_sensor@1 | cell-index = <1> | CSI1 通路 | cam_mclk1 | pinctrl_camera1 | /dev/cam_sensor1 |
frontsensor: cam_sensor@2 | cell-index = <2> | CSI2 通路 | cam_mclk2 | pinctrl_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-index | Sensor 设备 ID | 必须唯一;对应 /dev/cam_sensorX 和 cam-test 的 sensor_id |
twsi-index | I2C/TWSI 控制器编号 | 按硬件原理图连接填写 |
dphy-index | MIPI DPHY 通路编号 | 按 Sensor 接到的 CSI/DPHY 通路填写 |
compatible | Kernel 驱动匹配字符串 | 保持 spacemit,cam-sensor |
clocks / clock-names | Sensor MCLK | 与 CLK_CAMM0/1/2 和 cam_mclk0/1/2 对应 |
pinctrl-0 | MCLK pinmux | 对应 pinctrl_camera0/1/2 |
pwdn-gpios | PWDN GPIO | 按硬件填写;不用时可注释 |
reset-gpios | RESET GPIO | 按硬件填写;不用时可注释 |
pinmulti-enable | MCLK/PWDN/RESET 复用模式开关 | 多个 Sensor 复用同一组 MCLK/PWDN/RESET 时配置 |
status | 节点状态 | 使用时配置为 okay |
5.3 pinmulti 和 mclk_multi 配置
pinmulti-enable 和 mclk_multi 只在多个 Sensor 存在管脚复用时配置,按实际复用的资源决定。
配置规则:
- 如果多个 Sensor 共用一组 PWDN 和 RESET 管脚,需要配置
pinmulti-enable;。 - 如果多个 Sensor 共用 MCLK,需要同时配置
pinmulti-enable;和pinctrl-names = "mclk_multi";。 - 如果 PWDN、RESET、MCLK 都是独立管脚,则不需要配置
pinmulti-enable和mclk_multi。
其中 mclk_multi 是驱动固定查找的 pinctrl state 名称,只有 MCLK 复用时才需要配置。
配置示例:
- 共用 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";
};
- 共用 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.cov16a10_sensor.cgc5035_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_regsstream_off_regsstream_soft_reset_regscolor_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_H、IMX219_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_H、IMX219_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 数字增益。
典型转换过程如下:
- 读取 AE 传入的
*pAgainVal。 - 计算目标模拟增益倍数:
again = *pAgainVal / 256.0。 - 根据 Sensor datasheet 中的 analog gain 公式或 gain table,计算该倍数对应的寄存器值。
- 将寄存器值写入 Sensor 的模拟增益寄存器。
- 如果寄存器步进无法精确表示目标增益,应选择最接近的可支持值,并把实际生效的增益回写到
*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/0x00、0x00/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_UNRESET | camsnr_unreset_sensor() | 调用 cam_sensor_power_set(msnr_dev, 1) 执行上电和释放复位 |
sensor_hw_reset() | CAM_SENSOR_RESET | camsnr_reset_sensor() | 调用 cam_sensor_power_set(msnr_dev, 0) 执行复位和下电 |
内核 cam_sensor_power_set(msnr_dev, 1) 的上电流程大致为:
- 打开 MCLK:
clk_prepare_enable(),并设置为24MHz。 - 依次使能 regulator:
avdd设置为2.8V,dovdd设置为1.8V,dvdd设置为1.2V。 - 如果配置了
dvdden-gpios,拉高 DVDDEN。 - 如果配置了
afvdd,设置为2.8V并使能。 - 如果配置了
pwdn-gpios,拉高 PWDN。 - 如果配置了
reset-gpios,先拉低 RESET,延时约5ms,再拉高 RESET,延时约10ms。
上述 5ms 和 10ms 是内核通用流程里的固定延时。其中 5ms 用于保证 Sensor 进入稳定 reset 状态,10ms 用于 reset 释放后等待 Sensor 内部时钟、电源域和 I2C 接口稳定。不同 Sensor 对 MCLK 起振后等待时间、各路电源稳定时间、PWDN 到 RESET 的间隔、RESET 释放到首次 I2C 访问的间隔要求不同,应以 Sensor datasheet 的 power sequence 为准。
内核 cam_sensor_power_set(msnr_dev, 0) 的下电流程大致为:
- 如果配置了
reset-gpios,拉低 RESET。 - 如果配置了
pwdn-gpios,拉低 PWDN。 - 关闭
dvdd、avdd、dovdd。 - 如果配置了
dvdden-gpios,拉低 DVDDEN。 - 关闭
afvdd。 - 关闭 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 MCLK | 1 使能,0 关闭 |
sensor_set_power_voltage(sns_id, regulator_id, voltage) | 设置 LDO/regulator 电压 | 电压单位为 uV,例如 2800000、1800000、1200000 |
sensor_set_power_on(sns_id, regulator_id, on) | 使能或关闭 LDO/regulator | 1 使能,0 关闭 |
sensor_set_gpio_enable(sns_id, gpio_id, enable) | 设置 Sensor GPIO 电平 | 常用于 SENSOR_GPIO_PWDN、SENSOR_GPIO_RST、SENSOR_GPIO_DVDDEN |
常用 regulator ID:
| regulator ID | 典型电源 | 常用电压 |
|---|---|---|
SENSOR_REGULATOR_AVDD | 模拟电源 | 2800000 |
SENSOR_REGULATOR_DOVDD | IO 电源 | 1800000 |
SENSOR_REGULATOR_DVDD | 数字核心电源 | 1200000 |
SENSOR_REGULATOR_AFVDD | AF/VCM 电源 | 2800000 |
常用 GPIO ID:
| GPIO ID | 作用 |
|---|---|
SENSOR_GPIO_PWDN | Sensor PWDN 控制 |
SENSOR_GPIO_RST | Sensor 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;
}
注意:
- 如果某个电源或 GPIO 在 DTS 中没有配置,对应 API 不要调用;例如没有 AF/VCM 时可去掉
SENSOR_REGULATOR_AFVDD,没有外部 DVDD enable 时可去掉SENSOR_GPIO_DVDDEN。 - 私有函数写好后,应在 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 / height | Sensor 输出有效分辨率 |
bitDepth | RAW bit,如 8/10/12 |
maxFps / minFps | 支持帧率范围 |
image_mode | 图像模式,一般使用 Linar 线性模式 |
lane_num | MIPI lane 数,必须和寄存器 setting 一致 |
pattern | Bayer pattern,如 RGGB/GRBG/GBRG/BGGR |
supportPDAF | 是否支持 PDAF |
work_mode | Sensor mode 编号,对应 JSON 的 sensor_work_mode |
6.2.4 Work info
SENSOR_WORK_INFO_S 中常见字段:
| 字段 | 说明 | 修改依据 |
|---|---|---|
linetime | 行时间,通常单位 ns | 根据 HTS、PCLK 计算 |
vts | 默认 VTS | Sensor setting / datasheet |
f32maxFps | 当前 mode 最大 FPS | 与 VTS、HTS、PCLK 匹配 |
exp_time[] | 初始曝光时间 | 调试初期可设为合理中间值 |
again[] / dgain[] | 初始模拟/数字增益 | 通常从 1x 开始 |
setting_table | 初始化寄存器表 | Sensor 厂商 setting |
setting_table_size | 寄存器表大小 | 使用 ARRAY_SIZE() |
mipi_clock | MIPI clock | 按 Sensor 输出速率配置 |
行时间、VTS 和 FPS 必须自洽。常用关系:
$$ PCLK = FPS \times HTS \times VTS $$
$$ LineTime = \frac{HTS}{PCLK} $$
如果曝光异常、FPS 不稳定或 ISP AE 不收敛,优先检查 linetime、vts、f32maxFps 和曝光寄存器换算。
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_test、dual_pipeline_capture_test 和 only_dual_ccic_test 属于双路底层验证场景,OH 前后摄调试不需要使用。
| case | 函数 | 用途 |
|---|---|---|
0 | single_pipeline_online_test | 单路 Sensor 在线出图,OH 前后摄调试使用该模式 |
1 | dual_pipeline_online_test | 双路在线预览,OH 前后摄调试不用 |
2 | dual_pipeline_capture_test | 双路拍照,OH 前后摄调试不用 |
3 | only_rawdump_test | 只 dump RAW |
4 | only_viisp_online_test | 只验证 VI/ISP |
5 | only_viisp_offline_preview_test | 离线预览 |
6 | only_cpp_test | 只验证 CPP |
7 | only_ccic_test | 只验证 CCIC |
8 | slice_capture_test | slice capture |
9 | only_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_enable | ISP 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_node | CPP 配置数组 | 每个元素对应一个 CPP group,例如 cpp0、cpp1 |
isp_node | ISP 配置数组 | 每个元素对应一路 ISP pipeline,例如 isp0、isp1 |
cpp_node 字段说明:
| 字段 | 说明 | 配置要求 |
|---|---|---|
name | CPP 节点名称 | 双路时通常为 cpp0、cpp1 |
enable | 是否使能该 CPP 节点 | 1 使能,0 关闭 |
format | CPP 输出格式 | 常用 NV12;svivi/OH 验证通常使用 NV12 |
src_from_file | CPP 输入是否来自文件 | 0 表示来自 ISP 输出;1 表示从 src_path 读取文件,常用于 only CPP 测试 |
src_path | CPP 输入文件目录 | src_from_file = 1 时有效;online Sensor 出图时通常不关注 |
size_width / size_height | CPP 输入/处理尺寸 | 应与前级 ISP 输出或输入文件尺寸匹配 |
isp_node 字段说明:
| 字段 | 说明 | 新 Sensor 配置要求 |
|---|---|---|
name | ISP pipeline 名称 | 单路通常用 isp0;双路可用 isp0、isp1 |
enable | 是否使能该 ISP 节点 | OH 前后摄调试只开一路;双路节点不用开启 |
work_mode | ISP 工作模式 | Sensor 在线出图使用 online;离线/文件输入测试按对应 case 配置 |
format | ISP 输出格式 | 常用 NV12;需和后级 CPP/svivi 支持格式一致 |
out_width / out_height | ISP 输出尺寸 | 可等于或小于 Sensor 输入尺寸,但ISP支持输出分辨率最高支持1080p |
sensor_name | Sensor Module 名称 | 必须和 MODULE_OBJ_S xxx_spm_Obj 匹配,例如 xxx_spm |
sensor_id | Sensor 设备 ID | 必须和 DTS cell-index 对应,即 /dev/cam_sensorX 的 X |
sensor_work_mode | Sensor 工作模式编号 | 必须和 xxx_spm.c 中 capability/work info 的 mode 编号对应 |
fps | 目标 FPS | online 模式有效,不能超过该 mode 的 maxFps,需和 vts、linetime、setting 匹配 |
src_file | 离线 RAW 输入文件 | single online 出图时不生效;该字段用于离线 ISP/文件输入模式 |
bit_depth | 输入 RAW bit depth | single online 出图时不生效;离线文件测试时必须和 RAW 文件一致 |
in_width / in_height | ISP 输入尺寸 | single online 出图时不生效;离线文件测试时必须和 RAW 文件一致 |
8. OH Camera通路适配
8.1 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-indexi2c总线是否配对。- 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 或曝光异常
检查:
linetime、vts、f32maxFps是否自洽。xxx_sensor_fps_set()是否正确写 VTS。xxx_sensor_expotime_update()是否正确计算曝光行数。xxx_sensor_gain_update()增益公式是否按 Sensor 手册实现。