# Camera 调试指南

## 范围

- 本章节概述了 X5 Camera Bring-Up 的流程，帮助读者快速掌握 X5 Camera 框架。
- 讲解如何新增 Camera 配置并成功点亮 Camera。
- 本指南以 EVB 开发板和 SC1330T Camera 模组为例进行配置说明。

## 准备工作

- **软件资源**：系统 BSP、camera 驱动源码、sensor datasheet、sensor 的 initialize settings 等。
- **硬件资源**：EVB 开发板、camera 模组。

![image-20240425003534337](./media/Camera_sensor_bringup/image-20240425003534337.png)

上图是X5 EVB 开发板对应的接口，每个接口对应的相关硬件 IO 资源如下：

| 接口 | MIPI CSI Host | 最大支持 lane 数  | I2C Bus  | Reset GPIO           | LPWM/PWD          |MCLK                 |
| ---- | ------------- | ---------------- | ------- | ------------------- |-------------------|-------------------|
| 20   | MIPI_CSI0&1   | 4 lane           | I2C4    | AON_GPIO_PIN0 - 498 |  LSIO_SPI5_SCLK - lpwm0 | LSIO_SPI3_SCLK - mclk0 |
| 21   | MIPI_CSI2     | 2 lane           | I2C2    | AON_GPIO_PIN4 - 502 |  LSIO_SPI5_SSN - lpwm1 | LSIO_SPI3_MISO - mclk2 |
| 22   | MIPI_CSI3     | 2 lane           | I2C7    | LSIO_GPIO1_06 - 353 |  LSIO_SPI5_MISO - lpwm2 | LSIO_SPI3_MOSI - mclk3 |

## 添加新 sensor 点亮步骤

在 X5 平台上适配新硬件和新 Camera 时，需完成以下步骤：
- **修改平台设备树 (dts)**：调整硬件相关的节点配置。
- **修改 camera 驱动库**：添加或调整相应的驱动支持。
- **修改 camera 配置文件**：确保 camera 的功能和参数设置正确。

### dts 修改

`vcon` 是 X5 平台用于管理 Sensor 硬件配置的 DTS 节点模块。需根据硬件连接情况调整以下配置：
- Reset GPIO
- I2C Bus
- LPWM Channel

以下是 X5 EVB 开发板的配置情况:

#### sensor Reset gpio 配置

```dts
// dts： 在对应 vcon node 中设置 gpio，注意 vcon 端口号与 mipi rx 端口号 一 一对应
// vcon0 -- mipi_host0
// ....
// vcon3 -- mipi_host3
&vin_vcon0 {
	status = "okay";
	/* camera sensor
	 * reset gpio:	AON_GPIO0_00:	498
	 * pwd gpio:	LSIO_GPIO1_00:	347
	 */
	pinctrl-names = "default";
	pinctrl-0 = <&aon_gpio_0>;
	gpio_oth = <498>;   //reset gpio
	bus = <4>; //i2c4
	lpwm_chn = <0>;  //open lpwm0 - channel0
};
```

#### sensor i2c 配置

X5 I2C bus number 需要在 dts vcon 中与 MIPI RX 端口进行绑定，请根据硬件连接的实际情况配置，该相关信息可以从原理图中获取。

```c
// 在对应 vcon 中设置 i2c bus，如 RX0 设置 I2C4
&vin_vcon0 {
	status = "okay";
	/* camera sensor
	 * reset gpio:	AON_GPIO0_00:	498
	 * pwd gpio:	LSIO_GPIO1_00:	347
	 */
	pinctrl-names = "default";
	pinctrl-0 = <&aon_gpio_0>;
	gpio_oth = <498>;   //reset gpio
	bus = <4>; //i2c4
	lpwm_chn = <0>;  //open lpwm0 - channel0
```

#### sensor MCLK 配置

MCLK（Master Clock）是 Camera Sensor 的时钟源，通常由 X5 SOC 内部时钟输出提供。以下是配置 MCLK 的详细步骤：

#### 功能说明

- EVB 开发板上的 SC1330T Camera Sensor 需要使用 X5 SOC 的 MCLK 作为时钟源（EXTCLK）。
- MIPI HOST0 使用 MCLK2 作为时钟输出。

#### 硬件连接

- 根据原理图，确认 MIPI HOST0 使用的 MCLK 引脚及其对应的 GPIO。
- 确保硬件连接正确，MCLK 信号能够正常传输到 Sensor。

#### DTS 配置

在 DTS 中设置对应的 Pin Mux，将 Pin 配置为 GPIO 或 MCLK 复用功能：

```dts
// 通过原理图找到 MIPI HOST0 使用的 MCLK 和对应的 GPIO，并填充 pinctrl
// Sensor 启动前，会选择 MCLK 功能
&mipi_host0 {
	status = "okay";
	pinctrl-names = "enable", "disable";
	pinctrl-0 = <&pinctrl_sensor0_mclk>; // MCLK0
	pinctrl-1 = <&lsio_gpio0_24>;       // GPIO 复用
	snrclk-idx = <0>;  // MCLK 索引
};
```
#### 注意事项

- 确保 MCLK 引脚配置正确，避免与其他功能冲突。
- 在 Sensor 启动前，程序会提前配置 MCLK 的频率。

#### lpwm 配置

#### 功能说明

- X5 LPWM 通常用于 Camera Sensor 作为 Slave Mode 时，由 X5 SOC 输出触发或同步信号。
- 如果 Camera Sensor 不需要 LPWM 功能，可跳过本节配置。

#### 硬件连接

- 根据原理图，EVB 开发板的 MIPI HOST0 接口引出了 `LSIO_SPI5_MISO` 管脚。
- 该 Pin 可复用于 `LSIO_LPWM0_2`，用于 LPWM 功能。

#### DTS 配置

在 DTS 中启用 LPWM 功能并配置对应的 Channel：

```c
&lpwm0 {
	status = "okay";
	/* conflict with camera pwd gpio */
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_lpwm0_0 &pinctrl_lpwm0_1
			&pinctrl_lpwm0_2 &pinctrl_lpwm0_3>;
};

 &vin_vcon0 {
         status = "okay";
         ...
         lpwm_chn = <0>;  //open lpwm0 - channel0
 };
```

<div class="note">
<strong> 注意：</strong> Pin 若作为 LPWM 输出功能，则无法作为普通 GPIO 使用。在 DTS 中需注意相关配置，避免冲突。
</div>

#### dts 修改验证

一般 dts 配置正确，硬件正确连接后，手动输入命令满足 sensor 供电和 mclk ，便可以使用 i2cdetect 检测到模组的 i2c 地址。

- 通过 echo 命令进行控制 sensor 上电或者 reset
```shell
# 如：EVB 底座开启对应电源
echo 440 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio440/direction
echo 1 > /sys/class/gpio/gpio440/value
echo 440 > /sys/class/gpio/unexport

# 拉高 reset
echo 434 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio434/direction
echo 1 > /sys/class/gpio/gpio434/value
echo 434 > /sys/class/gpio/unexport
```
- 手动设置 mclk 频率和使能：
```shell
# 以 host0/rx0 为例，实际需要换成对应的 mipi rx 端口
# 一般是 24M 时钟输入，以实际为例
echo 24000000 > /sys/class/vps/mipi_host0/param/snrclk_freq
echo 1 > /sys/class/vps/mipi_host0/param/snrclk_en
```
- 使用 i2cdetect 检测 sensor i2c 地址。如果检测到正确的地址，如下图所示，则表示 dts 配置正确，否则需要检查 dts 配置，或者按照 [FAQ-i2c不通](#i2c) 步骤进行排查。

  ![](./media/Camera_sensor_bringup/5-7-3-1.png)

### sensor 驱动文件添加

不同厂家的 sensor，都会搭配风格各异的 driver 和 setting。因此需要将原厂 sensor 驱动，转换成 X5 camera 驱动代码，并编译生成 so 库，然后将 so 库拷贝到设备的 /usr/hobot/lib/sensor/ 目录下。**需要说明的是，在 X5 mipi start 之前，必须保证 sensor 没有开流。**

系统 BSP 源码包的 hbre/camsys/libcam/src/sensor 目录下提供了 sensor 驱动模板文件 sc1330t_utility.c 以及适配过的其他 sensor 驱动，当添加新 camera sensor 支持时，可以仿照该部分文件进行修改。

```c
#ifdef CAMERA_FRAMEWORK_HBN
SENSOR_MODULE_F(sc1330t, CAM_MODULE_FLAG_A16D8);
sensor_module_t sc1330t = {
        .module = SENSOR_MNAME(sc1330t),
#else
sensor_module_t sc1330t = {
        .module = "sc1330t",
#endif
        .init = sensor_init,
        .start = sensor_start,
        .stop = sensor_stop,
        .deinit = sensor_deinit,
        .power_on = sensor_poweron,
        .power_off = sensor_poweroff,
        .aexp_gain_control = sensor_aexp_gain_control,
        .aexp_line_control = sensor_aexp_line_control,
        .userspace_control = sensor_userspace_control,
};
```

如上代码所示，X5 camera 框架下的 sensor 驱动接口包含在 sensor_module_t 的结构体中，文件名、结构体名和 module 字段要统一，例如文件名为 sc1330t_utility.c，那么结构体名和 module 字段要统一为 sc1330t。对于新 sensor 点亮，下列函数需要用户自行实现：

- init：sensor 初始化、setting 下发
- deinit：sensor 去初始化
- start：sensor 开流
- stop：sensor 关流
- power on: sensor 上电
- power off: sensor 下电
- aexp_gain_control: sensor gain 增益控制
- aexp_line_control: sensor line 曝光控制
- userspace_control: 用户回调功能开启控制

对于 3A 控制，X5 系统支持驱动注册和应用层回调两种方式，默认使用应用层回调函数的方式，接口定义如下：

  |函数|功能|传入参数|
  | :- | :- | :- |
  |aexp_gain_control|sensor 增益控制|info：sensor 总线信息 mode：sensor 运行模式；linear/hdr/pwl again：sensor again 参数，最大4个 dgain：sensor dgain 参数，最大4个 gain_num：sensor gain 参数个数|
  |aexp_line_control|sensor 曝光控制|info：sensor 总线信息 mode：sensor 运行模式；linear/hdr/pwl line：sensor line 参数，最大4个 line_num：sensor line 参数个数|
  |awb_control|sensor 端 awb 控制|info：sensor 总线信息； mode：sensor 运行模式；linear/hdr/pwl rgain：sensor rgain bgain：sensor bgain grgain：sensor rrgain gbgain：sensor gbgain|
  |userspace_control|hal 层各类控制开关|port：sensor 端口号 enable：使能用户回调控制开关，默认全部关闭。 位定义： #define HAL_LINE_CONTROL 0x00000001 #define HAL_GAIN_CONTROL 0x00000002 #define HAL_AWB_CONTROL 0x00000004 |

如下代码是对 sensor 驱动主要结构体的初始化，需要根据每个 sensor 的实际情况来对应填写
```c
// sensor 实际输出的宽度
turning_data.sensor_data.active_width = 1280;
// sensor 实际输出的高度
turning_data.sensor_data.active_height = 960;

// 每秒多少曝光行，计算公式为1/每行时间或者(fps*vts)，其中vts在不同sensor的里面的名字可能有所不同，
//可能是frame_length、vts等，但含义都是每帧的所含的总共行数，
//包括有效行和blanking。lines_per_second 亦可以理解成HMAX,需要注意的是有些sensor 无HMAX 概念
turning_data.sensor_data.lines_per_second = 31500;

// 短曝光最大曝光时间(短曝光每帧最大曝光行数),短曝光每帧最大曝光行数可以通过公式
//单帧曝光时间/单行曝光时间，
//即(1/fps)/(1/lines_per_second)
turning_data.sensor_data.exposure_time_max = 968;

// a_gain最大倍数，举一个例子，turning_data.sensor_data.analog_gain_max =126，
//最大倍率计算公式就是2^(X/32)，其中X就是126，X这个值每一个sensor厂家不一样，
//需要从sensor手册或者sensor厂家查找得到；最大倍率也可以这样得到：先获取sensor最大增益，
//然后查找 ISP的增益表，得到对应的索引值，该索引值就是最大倍率。
turning_data.sensor_data.analog_gain_max = 205;
turning_data.sensor_data.digital_gain_max = 255;

//短曝光最小曝光时间(短曝光每帧最小曝光行数)，
//每行的曝光时间可以用公式1秒/(帧率*(有效行+blank))即1/lines_per_second推算得出
turning_data.sensor_data.exposure_time_min = 8;

//长曝光最大曝光时间(长曝光每帧最大曝光行数)
turning_data.sensor_data.exposure_time_long_max = 4000;

// 填充 sensor 位宽data_width、bayer_start(RGGB pattern start (R/Gr/Gb/B))、
// bayer_pattern(RGGB/RCCC/RIrGB/RGIrB) 信息
sensor_data_bayer_fill(&turning_data.sensor_data, 10, (uint32_t)BAYER_START_B, (uint32_t)BAYER_PATTERN_RGGB);

// 填充 exposure_max_bit_width(pwl mode bits ) 信息
sensor_data_bits_fill(&turning_data.sensor_data, 12);

// setting stream ctrl
// 开流、关流
turning_data.stream_ctrl.data_length = 1;

// again lut表，fireware根据index索引lut表，查找sensor对应的寄存器值，lut表区分a_gain/d_gain，
// lut表格大小：again_lut[again_control_num][256], dgain_lut[dgain_control_num][256]
turning_data.normal.again_lut = malloc(256 * sizeof(uint32_t));
if (turning_data.normal.again_lut != NULL)
{
    memset(turning_data.normal.again_lut, 0xff, 256 * sizeof(uint32_t));
    memcpy(turning_data.normal.again_lut, sc1330t_gain_lut,
           sizeof(sc1330t_gain_lut));
}

turning_data.normal.dgain_lut = malloc(256*sizeof(uint32_t));
if (turning_data.normal.dgain_lut != NULL) {
        memset(turning_data.normal.dgain_lut, 0xff, 256*sizeof(uint32_t));
        memcpy(turning_data.normal.dgain_lut, sc1330t_dgain_lut,
                sizeof(sc1330t_dgain_lut));
}
```
- turning_data.sensor_data.active_width：sensor 实际输出的宽度。
- turning_data.sensor_data.active_height：sensor 实际输出的高度。
- turning_data.sensor_data.analog_gain_max：a_gain 最大倍数，举一个例子，turning_data.sensor_data.analog_gain_max =126，最大倍率计算公式就是2^(X/32)，其中 X 就是 126，X这个值每一个 sensor 厂家不一样，需要从 sensor 手册或者 sensor 厂家查找得到。
- turning_data.sensor_data.digital_gain_max：d_gain 最大倍数。
- turning_data.sensor_data.exposure_time_min：短曝光最小曝光时间(短曝光每帧最小曝光行数)，每行的曝光时间可以用公式1秒/(帧率*(有效行+blank))即1/lines_per_second推算得出。
- turning_data.sensor_data.exposure_time_max：短曝光最大曝光时间(短曝光每帧最大曝光行数)，短曝光每帧最大曝光行数可以通过公式单帧曝光时间/单行曝光时间，即(1/fps)/(1/lines_per_second)。
- turning_data.sensor_data.exposure_time_long_max：长曝光最大曝光时间(长曝光每帧最大曝光行数)，HDR sensor 会用到。
- turning_data.sensor_data.lines_per_second：每秒多少曝光行，计算公式为1/每行时间或者(fps*vts)，其中vts在不同 sensor 的里面的名字可能有所不同，可能是frame_length、vts等，但含义都是每帧的所含的总共行数，包括有效行和 blanking。lines_per_second 亦可以理解成HMAX,需要注意的是有些 sensor 无 HMAX 概念。
- turning_data.sensor_data.analog_gain_init: sensor analog gain 初始值。可选配置。该值供isp ae算法使用，影响前几帧图像的亮度。如果关心前几帧图像亮度则需要配置，配置值为sensor setting的寄存器初始值。
- turning_data.sensor_data.digital_gain_init：sensor digital gain 初始值。可选配置。该值供isp ae算法使用，影响前几帧图像的亮度。如果关心前几帧图像亮度则需要配置，配置值为sensor setting的寄存器初始值。
- turning_data.sensor_data.exposure_time_init: sensor 曝光初始值。可选配置。该值供isp ae算法使用，影响前几帧图像的亮度。如果关心前几帧图像亮度需要配置，配置值为sensor setting的寄存器初始值。
- turning_data.normal.again_lut：again lut 表，fireware 根据 index 索引 lut 表，查找 sensor 对应的寄存器值，lut 表区分 a_gain/d_gain，lut 表格大小：again_lut[again_control_num][256], dgain_lut[dgain_control_num][256]。

注意事项1：当某个 gain 值不存在时，该位填充 0xffffffff，分配 gain 时，程序会向下查找，直到可以查到可以分配的 gain，下发到 kernel 中的 lut 表需要是完成高低位转换，避免在 kernel 中进行，比如gain = 0x1234，写入寄存器 0x3012，0x3013，有些 sensor 在 0x3012 中写 0x12，有些 sensor 在 0x3013 中写 0x12，在 hal 中转换屏蔽该差异性；

注意事项2：lut 表示 [0,255] 共 256 gain 控制，转换公式为 2^(x/32)，即实际的 gain 倍数为[2^(0/32),2^(255/32)]， gain 的控制曲线为 log 类型，即任何一颗 sensor 的 gain 的控制被离散为 256 个控制点，原因是现在 3a 的控制算法给出 256 个控制点，给出更多的控制点并不会提高 gain 的控制精度

camera 在 mipi start 之前，需要保证 sensor 没有开流，在 camera sensor init settings 中进行更改。
```c
static uint32_t sc1330t_linear_init_setting[] = {
    ....
    // 0x0100,0x01,  // 在 setting 最后不包含 开流 的配置
}
```
当 sensor 驱动和 setting 编写完成后，拷贝 \*_utility.c 和 \*_setting.h 到 BSP 对应目录中，并重新编译 BSP 生成 sensor 驱动库，生成文件位于 out/deploy/hbre/lib/sensor 中。
一般代码结构没有问题，即使 tuning_data 参数配置有不当的地方，框架也能正常加载 sensor驱动。如果 logcat 有 sensor 库 check 失败或者加载失败，则需要检查代码结构，是否按照 HBN 框架来编写。

### 用户程序

参考 BSP 已有的用户程序，包含 SIF、ISP、VSE、GDC 的参数配置，这些配置需要根据具体的 sensor 的分辨率，帧率，数据格式进行配置。下面列出文件中需要单独配置的部分，其余部分可保持默认值，无需关注。

#### mipi 配置

  |字段|描述|
  | :- | :- |
  |rx_enable|<p>MIPI 接收(RX) 设备使能，使能对应的 MIPI RX 端口，默认填 1。</p><p>注意 该字段不是配置 MIPI RX 端口号，只是使能 MIPI RX。</p>|
  |phy|0: 代表 mipi dphy，目前只支持该协议。|
  |lane|mipi lane 数，目前每一个 MIPI RX 默认支持 2 lane，如果设置为 4 lane，则会开启 lane 拼接功能。|
  |datatype|<p>mipi 输入的数据格式，与 sensor 配置保持一致。</p><p>常见的如下：</p><p>RAW8：0x2A</p><p>RAW10: 0x2B</p><p>RAW12: 0x2C</p><p>YUV422 8-bit: 0x1E</p>|
  |fps|帧率，供计算 MIPI 一些配置使用，按照 sensor 输出帧率填写即可，可从 Sensor 原厂 FAE 获取。|
  |mipiclk|mipi 总传输率 (所有 LANE)，可从 Sensor 原厂 FAE 获取，一般 Sensor 原厂 FAE 提供 sensor init setting 时有描述。|
  |width|输入图像 宽度 pixel。|
  |height|输入图像 高度 pixel。|
  |linelenth|mipi linelenth， 根据 sensor 实际情况配置，可从sensor spec 寄存器读取，或者实际硬件测量。|
  |framelenth|mipi framelenth， 根据 sensor 实际情况配置，可从sensor spec 寄存器读取，或者实际硬件测量。|
  |settle|mipi settle， phy 的 settle 时间配置，可实际硬件测量。报 mipi phy 错时可调整， 0 - 120 范围。|
  |channel_num|mipi 虚拟通道 number，linear mode 填 1，HDR DOL2 mode 填 2。|
  |channel_sel[MIPIHOST_CHANNEL_NUM]|mipi 虚拟通道对应的 ipi channel。|

 ###### camera sensor 配置

  |字段|描述|
  | :- | :- |
  |name[CAMERA_MODULE_NAME_LEN]|camera 模组名称，需要和 sensor lib名称对应，如：sensor 驱动名称为：libsc1330t.so，那么 name 为 sc1330t|
  |addr|sensor 设备地址，一般是 i2c 7位地址。|
  |sensor_mode|<p>sensor 工作模式：</p><p>1：NORMAL_M，linear 模式</p><p>2：DOL2_M，hdr 2帧合成1帧</p><p>3：DOL3_M，hdr 3帧合成1帧</p><p>4:  DOL4_M，hdr 4帧合成1帧</p><p>5:  PWL_M，hdr 模式 sensor 内部合成</p> 6:  SLAVE，slave 模式</p>|
  |gpio_enable_bit|<p>是否使用 X5 gpio 控制 camera sensor 的引脚，以满足 sensor 上下电的时序要求。如：使用 gpio 来控制 sensor XSHUTDN 引脚。**注意**：需要在 dts 中配置对应的 gpio number。</p><p>0: 不使用 gpio 来控制。 </p><p>非 0: 使用 gpio 来控制 sensor，按照 bit 来使能 gpio 数量。</p><p>比如: 0x07 则代表使能 [gpio_a, gpio_b, gpio_c] 3 个 gpio。</p>|
  |gpio_level_bit|<p>如果选择 gpio_enable_bit，则可以配置 gpio_level_bit 来控制 sensor 引脚高低电平。某个 gpio bit 与 sensor 管脚高低电平关系如下：</p><p>0: 先输出低电平，sleep 1s (休眠时间可以在 sensor 驱动文件 power_on 函数中，通过 usleep 自行定义)，再输出高电平。</p><p>1: 先输出高电平，sleep 1s，再输出低电平</p><p>比如：0x05 = 101，从 bit0 到 bit2 分别代表 gpio_a 先输出高电平，再输出低电平，gpio_b 先输出低电平，再输出高电平，gpio_c 先输出高电平，再输出低电平。</p><p>**注意**：需要根据 sensor spec 上电时序来自定义。</p>|
  |fps|sensor 帧率配置|
  |width|sensor 出图宽度 pixel|
  |height|sensor 出图高度 pixel|
  |format|<p>sensor mipi 数据类型，常见的如下：</p><p>RAW8：0x2A</p><p>RAW10: 0x2B</p><p>RAW12: 0x2C</p><p>YUV422 8-bit: 0x1E</p>|
  |extra_mode|模组索引配置，部分 sensor 驱动中会用到|
  |config_index|功能配置，部分 sensor 驱动中会用到|
  |calib_lname|sensor 效果库路径，默认路径为 /usr/hobot/lib/sensor，支持自定义路径，总长度不超过 100 字节，</p><p>比如： calib_lname="/userdata/test_tuning.json" </p><p> 目前软件版本，如果设置为 disable，则代码中默认使用 sensor_name_tuning.json 的字符串传给 isp |

#### vio 配置

|字段1|字段2|字段3|描述|
| :- | :- | :- | :- |
|<p>SIF</p><p></p>|<p>cim/sif</p><p></p>|mipi_rx|mipi rx 端口号|
|||vc_index|mipi virtual index，mipi 虚拟通道，默认填写 0 即可|
|||ipi_channel|ipi channel number，linear mode 为 1， hdr mode dol2 为 2|
|||cim_isp_flyby|<p>cim/sif online 到 isp。</p><p>0: sif offline 到 isp，数据经过 ddr。</p><p>1：sif online 到 isp，数据不经过 ddr。</p>|
|||enable_frame_id|调试使用，使能 frame id，使能后，在 dump buffer 中可获取 frame id 值|
|||set_init_frame_id|frame id init 值|
|||hdr_mode |hdr sensor 需要设置， dol2 为 1， dol3 设置为 2|
|||time_stamp_en|调试使用，使能 time stamp，使能后，在 dump buffer 中可获取 time stamp 值，目前为 kernel 获取的时间|
|||struct mclk_attr_ex_s|<p>用于设置 sensor mclk，24M 则设置为 24000000</p><p>sensor 初始化的时候，会自动打开，sensor close 的时候，会自动关闭 clk</p>|
|||vin_attr_ex_type_e|<p>用于设置 VIN_STATIC_MCLK_ATTR，通知底层 Extended Attribute Type</p><p>底层收到 type 后，实现对应的功能。</p>|
||input channel|format|vin format，sensor 输出 format|
|||width|sensor 输出分辨率 宽度 pixel|
|||height|sensor 输出分辨率 高度 pixel|
||<p>output channel / ddr</p><p></p>|ddr_en|数据是否 dump 到 DDR|
|||vin_ochn_attr_type_e |<p>vin ochn 属性</p><p>- VIN_BASIC_ATTR,   // 默认，最基础的 vin 属性</p><p>- VIN_EMB_ATTR，//  mipi 输入是否为 emb，sensor 输出为 emb 才需要打开</p>|
|||wstride|<p>cim/sif 下 ddr 的 wide stride，16字节对齐。</p><p>sensor raw8 为 width，raw10 - raw16 为 2 * width</p><p>比如 sc1330t 为 raw10 输出，wide stride = 2 * 1280 = 2560</p>|
|||format|下 ddr 时，设置的 sensor format，与 vin format 保持一致即可。|
|||buffers_num|cim/sif 下 ddr 的buffer number，设置为 1 - 6|
|||<p>flags</p><p></p>|一般在程序设置 <br> hbn_buf_alloc_attr_t.flags = HB_MEM_USAGE_CPU_READ_OFTEN |
|LPWM|base|enable|是否使能 LPWM|
|||trigger_source|<p>trigger_mode 如果选择为 0，则填写为 0。</p><p>trigger_mode 如果选择为 1，则根据实际情况选择 trigger source，请与 X5 FAE 确定方案。</p>|
|||trigger_mode|<p>0：软件触发LPWM，软件定时器作为触发源，由 LPWM 驱动自己生成。一般客户的选择。</p><p>1：硬件触发LPWM，可以包括 GPS，MCU 等外界触发源。如果使用硬件触发源，请与 X5 FAE 确定方案。</p>|
|||period|<p>LPWM 波形的周期，单位为 us，范围为 1 ~ 1000000 us，</p><p>即频率范围为：1hz ~ 1Mhz</p>|
|||offset|LPWM 模块从收到触发源到出波形之间的 offset 时间，单位为 us，范围为 0~ 1000000 us，默认为 0 即可。|
|||duty_time|LPWM 方波高电平，单位为 us，范围为 1 ~ 4000 us|
|||threshold|缓慢同步功能阈值，默认设置为 0 即可。硬件触发 LPWM 并且 LPWM 波形可能出现推迟或者提前时才需要打开。如果需要打开，请找 X5 FAE 确认。|
|||adjust_step|缓慢同步每次调整周期的数值，默认为 0 即可。|
|<p>ISP</p><p></p>|base|input_mode|<p>前级模块输入方式 </p><p>0 - PASSTHROUGH_MODE </p><p>1 - MCM_MODE 预留，暂时不推荐使用 </p><p>2 - DDR_MODE</p>|
|||sensor_mode|<p>对应的sensor模式 </p><p>0 - ISP_NORMAL_M </p><p>1 - ISP_DOL2_M </p><p>2 - ISP_PWL_M</p>|
|||crop 结构体|<p>对输入尺寸的crop</p><p>crop = {</p><p>        .x = 0,</p><p>        .y = 0,</p><p>        .h = SENSOR_HEIGHT,</p><p>        .w = SENSOR_WIDTH,</p><p>},</p>|
||<p>input channel</p><p></p>|width|输入图像高度|
|||height|输入图像宽度|
|||fmt|<p>FRM_FMT_NULL - 无效值</p><p>FRM_FMT_RAW - RAW</p><p>FRM_FMT_NV12 - NV12</p><p>FRM_FMT_UYVY - UYVY</p><p>**目前只支持RAW格式**</p>|
|||bit_width|输入图像数据的bit宽度|
||<p>output</p><p>channel</p>|ddr_en|图像输出到ddr|
|||fmt|<p>FRM_FMT_NULL - 无效值 </p><p>FRM_FMT_RAW - RAW</p><p>FRM_FMT_NV12 - NV12 </p><p>FRM_FMT_UYVY - UYVY </p><p>目前只支持 NV12 格式</p>|
|||bit_width|输出图像数据的 bit宽度，目前只 支持nv12格式，因此bit宽度为8|
|VSE|<p>input channel</p><p></p>|width|输入图像原始的宽度|
|||height|输入图像原始的高度|
|||fmt|<p>FRM_FMT_NULL - 无效值</p><p>FRM_FMT_RAW - RAW</p><p>FRM_FMT_NV12 - NV12</p><p>FRM_FMT_UYVY - UYVY</p><p>目前只支持nv12格式</p>|
|||bit_width|输入图像bit宽度|
||<p>output</p><p>channel</p><p></p>|chn_en|<p>使能通道，VSE 一共 6个输出通道</p><p>VSE_DOWN_SCALE_4K：downscale输出通道， 最大支持4k</p><p>VSE_DOWN_SCALE_1080P0：downscale输出通道， 最大支持1080p-0</p><p>VSE_DOWN_SCALE_1080P1：downscale输出通道， 最大支持1080p-1</p><p>VSE_DOWN_SCALE_720P0：downscale输出通道， 最大支持720p-0</p><p>VSE_DOWN_SCALE_720P1：downscale输出通道， 最大支持720p-1</p><p>VSE_UP_SCALE_4K：upscale输出通道， 最大支持4k</p>|
|||target_w|输出图像的宽度，值为0表示禁止scale|
|||target_h|输出图像的高度，值为0表示禁止scale|
|||fmt|<p>FRM_FMT_NULL - 无效值 </p><p>FRM_FMT_RAW - RAW</p><p>FRM_FMT_NV12 - NV12 </p><p>FRM_FMT_UYVY - UYVY</p><p>目前只支持nv12格式</p>|
|||bit_width|输入图像bit宽度|
|GDC|base|input_width|输入图像宽度|
|||input_height|输入图像高度|
|||output_width|输出图像宽度|
|||output_height|输出图像高度|
|||buf_num|输出 buf 数目|

除了基本的 vio 配置，在 user 程序中还需要注意 gpio 与 mclk 的设置。

在用户程序中设置 gpio，代替手动 echo 设置 gpio 及对应电平高低。

```shell
// 用户程序：sensor 配置结构体中，分别使能 gpio, 设置 gpio 有效电平
g_camera_config[0].gpio_enable_bit = 0x07;  //0x07: 0111, 使能 3 个 gpio
g_camera_config[0].gpio_level_bit = 0x00;   //enable 的 3 个 gpio，都先输出 低电平，再输出高电平
```
在用户程序中设置 mclk 频率，代替手动设置mclk 频率和使能
```shell
vin_attr_ex.ex_attr_type = VIN_STATIC_MCLK_ATTR;
vin_attr_ex.mclk_ex_attr.mclk_freq = 24000000; // 24MHz
```
### 板端运行程序
```shell
# dump raw 图
./get_vin_data
Unsupport sensor index:-1
Usage: get_vin_data [OPTIONS]
Options:
  -s <sensor_index>      Specify sensor index
  -t <settle_value>      Specify settle time for debug
  -h                     Show this help message
index:0  sensor_name:sc1330t  config_file:linear_1280x960_raw10_30fps_1lane.c
index:1  sensor_name:irs2875  config_file:linear_208x1413_raw12_15fps_2lane.c
index:2  sensor_name:sc230ai-10fps  config_file:linear_1920x1080_raw10_10fps_1lane.c
index:3  sensor_name:sc230ai-30fps  config_file:linear_1920x1080_raw10_30fps_1lane.c
index:4  sensor_name:sc132gs  config_file:linear_1088x1280_raw10_10fps_2lane.c
index:5  sensor_name:sc132gs  config_file:linear_896x896_raw10_10fps_1lane.c
index:6  sensor_name:sc035hgs  config_file:linear_640x480_raw10_30fps_1lane.c

./get_vin_data -s 0
Using index:0  sensor_name:sc1330t  config_file:linear_1280x960_raw10_30fps_1lane.c
vin dump raw size:2457600x0(stride:2560), frame id: 1
vin dump raw size:2457600x0(stride:2560), frame id: 2
vin dump raw size:2457600x0(stride:2560), frame id: 3
vin dump raw size:2457600x0(stride:2560), frame id: 4
vin dump raw size:2457600x0(stride:2560), frame id: 5
vin dump raw size:2457600x0(stride:2560), frame id: 6
vin dump raw size:2457600x0(stride:2560), frame id: 7
vin dump raw size:2457600x0(stride:2560), frame id: 8
vin dump raw size:2457600x0(stride:2560), frame id: 9
vin dump raw size:2457600x0(stride:2560), frame id: 10

```
## isp 图像预览

参考 [ hbplayer 和 tuning_tool 工具使用指南 ](./Hobot_Player_User_Guide.html#tuning-tool)

## 错误码

  下面是 sensor  常见的错误码及简单的排查方向：

  |错误码|定义|排查方向|
  | :- | :- | :- |
  |203|HB_CAM_INIT_FAIL|sensor 初始化失败，一般可能 i2c 不通，配置的 sensor mode 不支持|
  |205|HB_CAM_START_FAIL|sensor 启动失败，一般可能 i2c 不通，配置的 sensor mode 不支持|
  |207|HB_CAM_I2C_WRITE_FAIL|sensor i2c 不通，可以参考 FAQ-i2c 不通|
  |217|HB_CAM_SENSOR_POWERON_FAIL|sensor 上电失败，可能是 sensor gpio 配置错误。|
  |218|HB_CAM_SENSOR_POWEROFF_FAIL|sensor 下电失败，可能是 sensor gpio 配置错误。|

## FAQ

### i2c不通

使用 i2cdetect 命令探测总线 id，如果挂在 i2c bus 上的 sensor 可以被探测到，说明 sensor 接入正常，如下图：

  ![](./media/Camera_sensor_bringup/5-7-3-1.png)

通过 logcat 可以看到 sensor init error, i2c 不通相关的 log

  ![](./media/Camera_sensor_bringup/5-7-3-3.png)

一般 i2c 不通有几种情况：
- 硬件：camera 模组没有接好；reset gpio、pwd(sensor xshutdown) gpio、power_en gpio、mclk 没有配置正确或者硬件上没有生效。可以使用万用表和示波器进行测量并一一排除。
- 保证模组供电正常，查看模组pin list 或者原理图，测量供电情况，可能包括 3.0v/2.8v/1.8v/1.2v 等。
- 保证 i2c sda 和 sclk 上拉，一般是上拉到 1.8v。现在默认 sclk 为 100K，确保满足 sensor i2c 速率要求，否则需要修改 dts i2c 默认速率。
- 保证 mclk (sensor extclk) 管脚有波形，一般是 24M 或者 27M。
- 保证 sensor 上电时序符合 sensor spec 要求。
- 软件：i2c number 与实际硬件配置不相符，i2c addr 与实际硬件配置不相符。
- 目前 X5 mipi host 与 i2c number 在 dts 中进行绑定，如 mipi host0 使用 i2c number 4，必须保证 dts 绑定的 bus 与实际相符。

### 无法进入 hs，mipi start error
mipi 无法进入hs，可能有几下几种可能：

  ![](./media/Camera_sensor_bringup/5-7-3-4.jpg)

- sensor 没有正常出流
- sensor 为 slave mode，需要外界 trigger 信号，才能正常出流。
- sensor 为 master mode，给 sensor 写入寄存器，会主动出流，可以检查代码，确认流程没有问题，或者使用示波器测量 mipi data lane 确定有数据。
- sensor 提前出流
- sensor setting 中可能会有出流的配置，那么 sensor init 后，便会自己出流。这种情况则不满足 X5 mipi 时序要求，需要将 sensor setting 中的出流配置注释掉，在 sensor_start 中出流。
- 如果 sensor 特性要求必须在 sensor setting 中有出流配置，则需要手动跳过 hs check。

```shell
# 注意需要把 mipi host 换成对应的端口号
echo 1 > /sys/class/vps/mipi_host0/param/nocheck
```

### mipi 报错

- mipi phy fatal 报错

    ![](./media/Camera_sensor_bringup/5-7-3-5.png)

    mipi phy fatal 报错，为 mipi 物理层报错，表明 sensor 到 X5 mipi phy 之间传输有问题，可能的原因如下：
- mipi clk 配置不正确，参见 [mipi 的一些参数该如何确定，包括 framelength、linelength、settle 值等](#mipi-framelengthlinelengthsettle)。
- mipi settle 配置不正确，参见 [mipi 的一些参数该如何确定，包括 framelength、linelength、settle 值等](#mipi-framelengthlinelengthsettle)。

### sensor 库 check 或者 load error
sensor 库 check error，说明 sensor 驱动有问题

  ![](./media/Camera_sensor_bringup/5-7-3-6.jpg)

- 编译问题，sensor 编译错误，没有编译正常，就 push 到板端进行加载。
- 编译正常，但是没有按照 HBN 框架来编写。需要按照 [sensor 驱动文件添加](#id11) 规则来编写 sensor 驱动。

### sensor 作为 slave mode，该如何配置

如果 sensor 作为 slave mode，则需要外部触发信号，sensor 才能正常曝光，输出图像数据。需要注意以下几点：
- 触发信号：依据客户需求，触发信号可以来自于其他作为master mode 的 sensor 输出的 FSYNC 信号，也可以来自于 X5 SOC  LPWM 管脚输出的信号。而触发信号的要求，如高低电平，最小脉冲宽度请咨询 Sensor 原厂 FAE 。
- sensor setting: sensor 作为 slave mode，需要修改对应的 init setting，可能需要修改 power on/power off setting 等，请咨询 Sensor 原厂 FAE 。
- 配置完成后，如果没有触发信号，sensor 是无法正常出图，mipi 可能会报无法进入 hs 的错误。可以配置 mipi nocheck 属性，参考如下：
  ![](./media/Camera_sensor_bringup/5-7-3-7.png)

或者使用 echo 命令设置 nockeck 属性：
```shell
# 以 host0/rx0 为例，实际需要换成对应的 mipi rx 端口
echo 1 > /sys/class/vps/mipi_host0/param/nocheck
```
### mipi 的一些参数该如何确定，包括 framelength、linelength、settle 值等

- framelength: 又称为 VTS，帧长，表示 camera 一帧曝光多少行。帧长包括有效高度和消隐区(VBlank)的行数。一般 sensor spec 中会提供读取帧长的寄存器，在 sensor 完成初始化后，可以读取该寄存器，并打印出来。如果 sensor spec 没有提供，需要实际测量 mipi data 管脚，一般示波器测量的图如下：

  ![](./media/Camera_sensor_bringup/5-7-3-8.png)

    蓝色圈内为有效信号: 32ms，红色圈内为blanking信号，长度: 68ms，总长度为：100ms
    图像 width 为 1080，则传输 1080 个 pixel 用了 32ms，则加上 blanking 传输的 pixel，可计算出：
    framelength = 1080 * 100 / 32 = 3375。
- linelength: 又称为 HTS，行长，表示 camera 曝光一行有多少个 pixel。行长包括有效宽度和消隐区(HBlank)的宽度。一般 sensor spec 中会提供读取行长的寄存器，在 sensor 完成初始化后，可以读取该寄存器，并打印出来。如果 sensor spec 没有提供，需要实际测量 mipi data 管脚，一般示波器测量的图如下：

  ![](./media/Camera_sensor_bringup/5-7-3-9.png)

  蓝色圈内是一行有效数据信号，长度为 24.2us，红色圈内为 blanking 信号，长度：5.4us，总长度为
  24\.2 + 5.4 = 29.6us。 图像 high 为 1920，则传输 1920 个pixel 用了 24.2us，则带上 blanking 传输的 pixel，可以计算出：
  linelength = 1920 \* 29.6 / 24.2 = 2348。
- mipi clk: sensor 为 mipi tx 端，X5 SOC 为 mipi rx 端，mipi clk 由 sensor 端决定，配置一般可由 Sensor 原厂 FAE 获取。
X5 mipi clk 为双边沿采样，是实际物理上(或者示波器上测量的信号频率)的 2 倍，且为多个 lane 的 clk 总和。
比如: sensor 2 lane 的配置，物理上每条 lane 的 物理 clk 为 500M，则 X5 mipi clk = 2 \* 2 \* 500M = 2000M，配置填写 2000。
硬件条件允许的情况下，也可以使用差分示波器进行测量。
- settle: settle 是 MIPI 协议中的概念，先简单介绍一下 settle 信号：

  ![](./media/Camera_sensor_bringup/5-7-3-10.png)

  MIPI 从 LP 进入到 HS 状态时，硬件信号上可能会有一个脉冲信号。MIPI 接收器则需要通过延时 settle time，避开这个脉冲信号，再打开接收器，接收 MIPI 数据。SETTLE 值由 prepare 和部分 zero 信号组成，如上图所示。
  如果只是为了 bringup 及测试，可以先填写为 0，MIPI 驱动中会自动计算一个能够运行的 settle 值，但如果是压测及量产，最好通过试错法或者实际测量法，获取到相对准确的值。
- 试错法：settle 值是一个范围 [0-120]，如果设置不正确则会报 mipi phy_fatal 错误，类似下图所示：

  ![](./media/Camera_sensor_bringup/5-7-3-5.png)

    所以我们可以通过试错法来找到该范围。
    一般调试阶段，可以从 0 - 120 之间数值尝试，低于某个值 A 会报该错误，高于某个值 B 也会报该错误，则 settle 取 [A B] 的中间值。
- 实际测量法：可以使用示波器抓取 mipi data lane(D+ 和 D- 两条 lane 需要同时测量) 从 LP 到 HS 的过程，一般示波器测量图如下：

  ![](./media/Camera_sensor_bringup/5-7-3-11.png)

    蓝色圈内，为 hs-preapre = 14ns
    红色圈内，为 hs-zero = 154ns
    单位时间 UI = 1s / (MIPI 物理单个 lane clk / 2) = 1s / (405M / 2) = 4.94 ns
    min = prepare / UI = 14 / 4.94 = 3 UI
    max = (prepare + zero) / UI = (154 + 14) / 4.94 = 34 UI
    settle = (min+max) / 2 = 19 UI

### 写好 sensor 驱动了，如何连接 tuning tool？

1 X5 板端与 PC 电脑端正确连接好网络，互相可以 ping 通，PC cmd 可以通过 ssh 登录板端。
2 进入板端，执行命令，启动 tuning service 和 tuning 程序：
```shell
echo 1 > /sys/kernel/debug/isp/tune
./run_tuning.sh --run 4
```
其中，run_tuning.sh 脚本请参考 [isp 图像预览](#isp) 这一节进行添加
3 PC 电脑端打开 tuning tool 《VTunerClient》

  ![](./media/Camera_sensor_bringup/5-7-3-12.png)

4 新建立一个工程

  ![](./media/Camera_sensor_bringup/5-7-3-13.png)

  ![](./media/Camera_sensor_bringup/5-7-3-14.png)

  ![](./media/Camera_sensor_bringup/5-7-3-15.png)

5 通过 TCP 连接到板端。

  ![](./media/Camera_sensor_bringup/5-7-3-16.png)

注意：IP 地址及端口号是固定的，需要按照截图内容填写。点击 Test 按钮后， TCP 后面出现绿色的对钩，说明连接成功，再点击 OK 按钮。
6 连接工程

  ![](./media/Camera_sensor_bringup/5-7-3-17.png)

这里只是简单连接 tuning tool，可以任意选择一个 sensor name，实际 tuning，应该参考工程文件夹下面的 isp_sensor.json 新建立自己项目的 sensor 选项。
执行完上述命令后，便连接完成，ISP tuning 同事可以调试 sensor 效果。

### 如何确定 sensor 驱动中的 2A 控制是正常的？

sensor 驱动完成后，在 isp tuning 之前，需要确认 sensor 的 2A(AE，自动调节亮度，AWB，自动调节白平衡) 有调节的效果，AE 调节的中给 sensor 写入的寄存器符合 spec 要求，如果 sensor 有写入 AWB 寄存器的功能，也需要确认写入的寄存器是否符合 spec 要求。
sensor 曝光出图，SOC ISP 收到 sensor 一帧数据后，会先对该帧数据的亮度信息进行统计，而 ISP tuning 中会设置一个 AE target，当 ISP 对 sensor 数据亮度统计的结果，不接近 AE target 时，ISP AE 算法会计算出一个合适的 sensor 曝光值和 gain 值，并传给 sensor 驱动，改变 sensor 曝光时间和 gain 值。
只有当 sensor 数据亮度统计的结果接近 AE target 值时，整个调节才会停止。而当环境亮度变化后，又会重新开启整个亮度调节过程，直至 sensor 亮度数据接近 AE target。
实际验证中，需要按照 《写好 sensor 驱动了，如何连接 tuning tool？》连接好 tuning tool，将 AE 切换为 manual 模式，

  ![](./media/Camera_sensor_bringup/5-7-3-18.png)

如果 Manual Mode View UI 显示为灰色，则需要先进入 auto mode view，点击 disable 按钮，再切换为 Manual Mode View:

  ![](./media/Camera_sensor_bringup/5-7-3-19.png) ![](./media/Camera_sensor_bringup/5-7-3-20.png)

自行设置 aGain，dGain 及 integrationTime 值，来进行验证。
比如，将曝光时间调整为 5ms (记得点一下 Write Page)

  ![](./media/Camera_sensor_bringup/5-7-3-21.png)

然后通过 sensor 驱动中的打印(需要自己添加)，或者使用 i2ctransfer 工具来查看 sensor 寄存器值，确认写给 sensor 的寄存器值是否符合 spec 要求。

### 如何配置 yuv sensor ?
**yuv 排列方式**
- planar：把 Y 数据，U 数据和 V 数据单独放置3块位置，形成3个平面。
- semi-planar：把 Y 数据独立，UV 数据放在一起，形成2个平面。
- packed：YUV 数据打包在一起，无论单独的 Y 数据 U 数据还是 V 数据，地址都不是连续的。

**yuv 命名方式**
- YUVxxxP，YUVxxxSP：比如：YUV422P，YUV420SP。根据 planar，semi-planer 和 packed 的类型来定义的。planar 就是P，注意顺序都是 Y 前 U 中 V 后。semi-planar 就是 SP，注意顺序都是 Y 前 UV 后。
- Ixxx：比如：I422，I420。planar 的别称，I422 就是 YUV422P，I420 就是 YUV420P。
- NVxx，YVxx：比如：NV12，NV21，NV16，YV12。NV 代表属于 semi-planar，12代表一个像素所占的实际 bits。NV12 是 YUV420SP，每个像素占据1个 Y(8Bit)+1/4 个UV(8 * 2 / 4 = 4bit) =12bits。


添加 yuv sensor, 首先需要确定 yuv sensor 配置输出的具体格式，目前 X5 支持的有以下几种格式：
- 8bit YUV422SP(semi-planar): 又称为 NV16，Y 单独占一块地方，U、V紧挨着排在一起，占一块地方，且 U 前 V 后，即 YYYYYYY.......UVUVUVUV......，sensor 及 mipi 配置中，format 填写 0x1E，pack_mode 填写 2，stride 为 1倍的 width。
- 8bit YUV422P(packed): 又称为 YUYV422，YUV 4:2:2 采样，YUV 共占一块地方，按像素点交错存储，如 YUVYUVYUVYUV，sensor 及 mipi 配置中，format 填写 0x1E，pack_mode 填写 1，stride 为 2倍的 width。
- 8bit YUV420SP(semi-planar): 又称为 NV12，Y 单独占一块地方，U、V紧挨着排在一起，占一块地方，且 U 前 V 后，即 YYYYYYY.......UVUVUVUV......，sensor 及 mipi 配置中，format 填写 0x18, pack_mode 填写 2，stride 为 1倍的 width。

如需其他 yuv 格式，请联系 Sensor 原厂 FAE 支持。

注意，yuv sensor 接入到 mipi 后，直接从 sif dump 数据到 ddr，如果需要后级模块，则要跳过 isp 模块(因为 isp 只支持输入 raw 格式)，直接将 sif 绑定到 vse 或者后级模块，且 sensor 效果由 sensor 模组本身保证，X5 本身无法通过 isp 做效果 tuning。
