5.17.4. SmartSens Sensor Bring-Up

  • This document uses sc132gs as an example to describe the complete process of adapting SmartSens series Camera Sensors on the X5 platform

  • The process includes driver implementation, App configuration, and program verification. For general concepts and DTS configuration, please first read the Camera Debugging Guide.

Source Code Location

  • Driver interface implementation: hbre/camsys/libcam/src/sensor/sc132gs/sc132gs_setting.c

  • Gain table and setting configuration: hbre/camsys/libcam/src/sensor/sc132gs/inc/sc132gs_setting.h (gain_lut, init/stream_on/stream_off register arrays)


5.17.4.1. DTS Configuration


5.17.4.2. Driver

sensor_module_t and File Naming

  • File name: sc132gs_utility.c; structure/module name: sc132gs; library name: libsc132gs.so.1.0.0, deployed to /usr/hobot/lib/sensor/ on the board.

  • Required interfaces: init, deinit, start, stop, power_on, power_off, aexp_gain_control, aexp_line_control, userspace_control.

#ifdef CAMERA_FRAMEWORK_HBN
SENSOR_MODULE_F(sc132gs, CAM_MODULE_FLAG_A16D8);
sensor_module_t sc132gs = {
        .module = SENSOR_MNAME(sc132gs),
#else
sensor_module_t sc132gs = {
        .module = "sc132gs",
#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,
};

power_on

  1. power_on controls sensor power-on via X5 GPIO, following the spec power-up timing.

  2. sensor_poweron is typically called in sensor_init and only needs to control XSHUTDN. Below is the power_on implementation for sc132gs:

../../_images/screenshot-20260210-145501.png

int sensor_poweron(sensor_info_t *sensor_info)
{
        int gpio, ret = RET_OK;
        if(sensor_info->gpio_num > 0) {
                for(gpio = 0; gpio < sensor_info->gpio_num; gpio++) {
                        if(sensor_info->gpio_pin[gpio] != -1) {
                                ret = vin_power_ctrl(sensor_info->gpio_pin[gpio],
                                                sensor_info->gpio_level[gpio]);
                                usleep(1 * 100 * 1000);  // 100ms
                                ret |= vin_power_ctrl(sensor_info->gpio_pin[gpio],
                                                1 - sensor_info->gpio_level[gpio]);
                                if(ret < 0) {
                                        vin_err("vin_power_ctrl fail\n");
                                        return -HB_CAM_SENSOR_POWERON_FAIL;
                                }
                                usleep(100 * 1000);  // 100ms
                        }
                }
        }
        return ret;
}

power_off

  1. power_off controls sensor power-off via X5 GPIO, following the spec power-down timing.

  2. sensor_poweroff is typically called in sensor_deinit and only needs to control XSHUTDN. Below is the power_off implementation for sc132gs:

int sensor_poweroff(sensor_info_t *sensor_info)
{
        int gpio, ret = RET_OK;
        if(sensor_info->gpio_num > 0) {
                for(gpio = 0; gpio < sensor_info->gpio_num; gpio++) {
                        if(sensor_info->gpio_pin[gpio] != -1) {
                                ret = vin_power_ctrl(sensor_info->gpio_pin[gpio],
                                                                sensor_info->gpio_level[gpio]);
                                if(ret < 0) {
                                        vin_err("vin_power_ctrl fail\n");
                                        return -1;
                                }
                        }
                }
        }
        return ret;
}

init

  1. Writes the sensor initialization register list to the sensor via the I2C write function; different resolutions use different setting arrays

  2. sc132gs_linear_data_init is used to populate turning_data information.

  3. sensor_init is called when hbn_camera_attach_to_vin is invoked

int sensor_init(sensor_info_t *sensor_info)
{
        int ret = RET_OK;
        int setting_size = 0;

        ret = sensor_poweron(sensor_info);
        if (ret < 0) {
                vin_err("%d : sensor reset %s fail\n", __LINE__, sensor_info->sensor_name);
                return ret;
        }
        switch(sensor_info->sensor_mode) {
        case NORMAL_M:
                vin_info("sc132gs in normal mode\n");
                setting_size = sizeof(sc132gs_linear_init_1088x1280_60fps_setting_master) / sizeof(uint32_t) / 2;
                ret = sensor_configure(sensor_info, sc132gs_linear_init_1088x1280_60fps_setting_master, setting_size);
                if (ret < 0) {
                        vin_err("%d : init %s fail\n", __LINE__, sensor_info->sensor_name);
                        return ret;
                }
                ret = sc132gs_linear_data_init_1088x1280(sensor_info);
                if (ret < 0) {
                        vin_err("%d : linear data init %s fail\n", __LINE__, sensor_info->sensor_name);
                        return ret;
                }
                break;
        default:
                vin_err("not support mode %d\n", sensor_info->sensor_mode);
                ret = -RET_ERROR;
                break;
        }
        vin_info("sc132gs config success under %d mode\n\n", sensor_info->sensor_mode);
        return ret;
}

Here sensor_configure internally calls vin_write_array to write registers.

deinit

  1. deinit is typically called for sensor de-initialization; simply call power_off to power down.

  2. sensor_deinit is called when hbn_camera_destroy or hbn_camera_detach_from_vin is invoked.

int sensor_deinit(sensor_info_t *sensor_info)
{
        int ret = RET_OK;
        ret = sensor_poweroff(sensor_info);
        if (ret < 0) {
                vin_err("%d : deinit %s fail\n", __LINE__, sensor_info->sensor_name);
                return ret;
        }
        return ret;
}

start

  1. start implements sending the stream-on register 0x0100 as 0x01 to make the sensor start streaming.

  2. sensor_start is called when the upper layer calls hbn_vflow_start.

../../_images/screenshot-20260210-204357.png

int sensor_start(sensor_info_t *sensor_info)
{
        int ret = RET_OK;
        int setting_size = 0;

        switch(sensor_info->sensor_mode) {
        case NORMAL_M:
        case SLAVE_M:
                setting_size = sizeof(sc132gs_stream_on_setting)/sizeof(uint32_t)/2;
                vin_info(" start linear mode, sensor_name %s, setting_size = %d\n", sensor_info->sensor_name, setting_size);
                ret = vin_write_array(sensor_info->bus_num, sensor_info->sensor_addr, 2,
                                setting_size, sc132gs_stream_on_setting);
                if(ret < 0) {
                        vin_err("start %s fail\n", sensor_info->sensor_name);
                        return ret;
                }
                break;
        }
        return ret;
}

stop

  1. stop implements sending the stream-off register 0x0100 as 0x00 to make the sensor stop streaming.

  2. sensor_stop is called when the upper layer calls hbn_vflow_stop

int sensor_stop(sensor_info_t *sensor_info)
{
        int ret = RET_OK;
        int setting_size = 0;

        setting_size = sizeof(sc132gs_stream_off_setting) / sizeof(uint32_t) / 2;
        vin_info("sensor stop sensor_name %s, setting_size = %d\n", sensor_info->sensor_name, setting_size);
        ret = vin_write_array(sensor_info->bus_num, sensor_info->sensor_addr, 2,
                        setting_size, sc132gs_stream_off_setting);
        if (ret < 0) {
                vin_err("start %s fail\n", sensor_info->sensor_name);
                return ret;
        }
        return ret;
}

ISP Configuration and linear_data_init

../../_images/screenshot-20260210-151023.png

../../_images/screenshot-20260210-145837.png

In sc132gs_linear_data_init, populate the tuning parameters:

  • Read VTS from sensor (registers 0x320e/0x320f), calculate lines_per_second;

  • exposure_time_max/min, analog_gain_max, digital_gain_max;

  • Populate bayer (sensor_data_bayer_fill, sensor_data_bits_fill);

  • Set stream_ctrl; copy again_lut from sc132gs_setting.h (sc132gs_gain_lut) to turning_data.normal.again_lut, then submit via ioctl.

Key parameter configuration:

  • lines_per_second = vts * sensor_info->fps

  • exposure_time_max = vts - 8 Documentation specifies FRM_LENGTH_LINES - 8, i.e., VTS - 8

  • analog_gain_max = 156 consistent with gain_lut maximum index, 29.34x corresponds to 156, refer to Gain index and gain ratio lookup table

  • digital_gain_max = 159 consistent with gain_lut maximum index, 31.3x corresponds to 159, refer to Gain index and gain ratio lookup table

  • exposure_time_min = 1 Default minimum configuration is 1; no restriction in spec

  • sensor_data_bayer_fill(..., 10, BAYER_START_B, BAYER_PATTERN_RGGB); sensor_data_bits_fill(..., 12), BAYER_START at position 0,0 is BAYER_START_B, BAYER_PATTERN is RGGB, raw10 format. sensor_data_bits_fill is only used for PWL mode; configure 12 here

aexp_gain_control

  • For SmartSens sensors, gain control generally uses discrete steps + fine adjustment. Lookup tables are typically the fastest approach; fill in again_lut and dgain_lut directly from the table (no need to use gain_table.xlsx), which is convenient and efficient.

  • SmartSens uses again + dgain dual LUT, with registers AGAIN 0x3e08/0x3e09 and DGAIN 0x3e06/0x3e07. Look up the table based on the again[0]/dgain[0] index from the ISP.

Reference documentation:

../../_images/screenshot-20260116-103909.png

static int sensor_aexp_gain_control(hal_control_info_t *info, uint32_t mode, uint32_t *again, uint32_t *dgain, uint32_t gain_num)
{
        const uint16_t AGAIN_LOW = 0x3e08;
        const uint16_t AGAIN_HIGH = 0x3e09;
        const uint16_t DGAIN_LOW = 0x3e06;
        const uint16_t DGAIN_HIGH = 0x3e07;
        char ana_gain = 0, ana_fine_gain = 0;
        char dig_gain = 0, dig_fine_gain = 0;
        int again_index = 0, dgain_index = 0;

        if (mode == NORMAL_M || mode == DOL2_M) {
                if (again[0] >= sizeof(sc132gs_gain_lut)/sizeof(uint32_t))
                        again_index = sizeof(sc132gs_gain_lut)/sizeof(uint32_t) - 1;
                else
                        again_index = again[0];
                if (dgain[0] >= sizeof(sc132gs_dgain_lut)/sizeof(uint32_t))
                        dgain_index = sizeof(sc132gs_dgain_lut)/sizeof(uint32_t) - 1;
                else
                        dgain_index = dgain[0];

                ana_gain = (sc132gs_gain_lut[again_index] >> 8) & 0xFF;
                ana_fine_gain = sc132gs_gain_lut[again_index] & 0xFF;
                dig_gain = (sc132gs_dgain_lut[dgain_index] >> 8) & 0xFF;
                dig_fine_gain = sc132gs_dgain_lut[dgain_index] & 0xFF;

                vin_i2c_write8(info->bus_num, 16, info->sensor_addr, AGAIN_LOW, ana_gain);
                vin_i2c_write8(info->bus_num, 16, info->sensor_addr, AGAIN_HIGH, ana_fine_gain);
                vin_i2c_write8(info->bus_num, 16, info->sensor_addr, DGAIN_LOW, dig_gain);
                vin_i2c_write8(info->bus_num, 16, info->sensor_addr, DGAIN_HIGH, dig_fine_gain);
                if (mode == DOL2_M) {
                        vin_i2c_write8(info->bus_num, 16, info->sensor_addr, 0x3e12, ana_gain);
                        vin_i2c_write8(info->bus_num, 16, info->sensor_addr, 0x3e13, ana_fine_gain);
                        vin_i2c_write8(info->bus_num, 16, info->sensor_addr, 0x3e10, dig_gain);
                        vin_i2c_write8(info->bus_num, 16, info->sensor_addr, 0x3e11, dig_fine_gain);
                }
        } else {
                vin_err(" unsupport mode %d\n", mode);
        }
        return 0;
}

aexp_line_control

The aexp_line_control function primarily implements exposure control, responsible for:

  1. Receiving the exposure line count calculated by the ISP 3A algorithm

  2. Converting the exposure line count to sensor register values

  3. Configuring and sending exposure registers to the sensor via I2C

  4. The exposure registers here are 0x3e00, 0x3e01, 0x3e02.

static int sc132gs_ae_set(uint32_t bus, uint32_t addr, uint32_t line)
{
        const uint16_t EXP_LINE0 = 0x3e00;
        const uint16_t EXP_LINE1 = 0x3e01;
        const uint16_t EXP_LINE2 = 0x3e02;
        char temp0 = 0, temp1 = 0, temp2 = 0;
        uint32_t sline = line;
        if (sline >= 2560) sline = 2560;

        temp0 = (sline & 0xF000) >> 12;
        temp1 = (sline & 0x0FF0) >> 4;
        temp2 = (sline & 0x000F) << 4;
        vin_i2c_write8(bus, 16, addr, EXP_LINE0, temp0);
        vin_i2c_write8(bus, 16, addr, EXP_LINE1, temp1);
        vin_i2c_write8(bus, 16, addr, EXP_LINE2, temp2);
        return 0;
}

static int sensor_aexp_line_control(hal_control_info_t *info, uint32_t mode, uint32_t *line, uint32_t line_num)
{
        uint32_t val;
        if (mode == NORMAL_M) {
                val = line[0];
                sc132gs_ae_set(info->bus_num, info->sensor_addr, val);

        } else {
                vin_err(" unsupport mode %d\n", mode);
        }
        return 0;
}

userspace_control

  • sensor_userspace_control is used for Gain Control and Exposure Control switches.

  • When debugging with hbplayer to troubleshoot flicker or image abnormalities, you can enable/disable HAL_GAIN_CONTROL / HAL_LINE_CONTROL to confirm the issue.

static int sensor_userspace_control(uint32_t port, uint32_t *enable)
{
        vin_info("enable userspace gain control and line control\n");
        *enable = HAL_GAIN_CONTROL | HAL_LINE_CONTROL;
        return 0;
}

Driver Deployment

  1. Compile with ./bd.sh hbre camsys/libcam.

  2. Copy out/deploy/hbre/lib/sensor/libsc132gs.so.1.0.0 to /usr/hobot/lib/sensor/ on the board.


5.17.4.3. App Configuration

  • In the path app/samples/platform_samples/vp_sensors/sc132gs/, add configuration for sc132gs.

  • Configure mipi (lane, datatype, mipiclk, linelength, framelength, settle), camera (name, addr, sensor_mode, gpio, fps, width, height, format), vin_node_attr (mipi_rx, cim_isp_flyby, etc.), vin_ichn_attr, vin_ochn_attr (wstride, etc.), isp_attr, isp_ichn_attr, isp_ochn_attr.

  • HTS/VTS can be read from registers 0x342/0x343 and 0x340/0x341, or obtained from the sensor FAE, or measured with an oscilloscope. For measurement methods, refer to MIPI parameter determination

  • mipiclk can generally be obtained from the sensor FAE. For calculation methods, refer to MIPI CLK

  • Other configurations are generic; refer to the Data Structures documentation for configuration.

vp_sensor configuration file: linear_1088x1280_raw10_60fps_1lane.c

  • Configuration file path: sc132gs/linear_1088x1280_raw10_60fps_1lane.c.

  • Configuration structure name: sc132gs_linear_1088x1280_raw10_60fps_1lane (must be registered in vp_sensor_config_list). Main parameters are as follows:

Configuration Item Parameter Name / Location Value Description
Top-level config_file "linear_1088x1280_raw10_60fps_1lane.c"
sensor_name "sc132gs-1280p"
chip_id_reg / chip_id 0x3107 / 0x0132
sensor_i2c_addr_list {0x30, 0x33}
support_sensor_mode {NORMAL_M}
MIPI lane 1
datatype RAW10 (0x2B)
fps 60
mipiclk 1200 (Mbps)
width / height 1088 / 1280
linelength / framelength 1400 / 1500 (matching sensor HTS/VTS)
settle 20
channel_num 1
Camera name / addr "sc132gs" / 0x33
sensor_mode 1 (NORMAL_M)
format RAW10
gpio_enable_bit / gpio_level_bit 0x01 / 0x00
calib_lname "disable"
VIN mipi_rx 1
cim_isp_flyby 0 (offline)
hdr_mode NOT_HDR
mclk_freq (vin_attr_ex) 24000000 (24MHz)
lpwm_enable 1
lpwm_period / offset / duty_time 33333us / 10us / 100us
VIN Output wstride SENSOR_WIDTH * 2 (RAW10 is x2)
ISP Input input_mode DDR_MODE
sensor_mode ISP_NORMAL_M
crop x=0, y=0, w=1088, h=1280
input_fmt FRM_FMT_RAW
input_bit_width 10
ISP Output output_fmt FRM_FMT_NV12
output_bit_width 8
ddr_en 1

5.17.4.4. Program Verification

  1. get_vin_data: Confirm raw image has no severe color cast and frame rate matches configuration.

  2. get_isp_data: Prepare sc132gs_tuning.json (can copy a JSON with the same resolution and change sensor_name), confirm YUV is normal.

  3. hbplayer: Connect to the board to preview, see hbplayer and tuning_tool Usage Guide.

  4. tuning_tool: After connecting to hbplayer using tuning_tool, enter command e, configure Manual AE, set aGain and integrationTime. Check driver printout to verify registers 0x3e08/0x3e09, 0x3e06/0x3e07, and 0x3e00~0x3e02 match the spec. ISP gain limits can be configured in sc132gs_tuning.json for self-testing.

For common issues, see Camera Sensor FAQ.