5.17.3. OmniVision Sensor Bring-Up
本文以 os08c10 为例,说明在 X5 平台上适配 OmniVision 系列 Camera Sensor 的完整流程
流程包括驱动实现、App 配置与程序验证。通用概念与 dts 配置请先阅读 Camera 调试指南。
源码位置
驱动接口实现:
hbre/camsys/libcam/src/sensor/os08c10/os08c10_setting.c增益表与 Setting 配置:
hbre/camsys/libcam/src/sensor/os08c10/inc/os08c10_setting.h(gain_lut、init/stream_on/stream_off 寄存器数组)
5.17.3.1. dts配置
关于dts配置和上电和sensor的id识别验证参考修改平台设备树
5.17.3.2. 驱动
sensor_module_t 与文件命名
文件名:
os08c10_utility.c;结构体/模块名:os08c10;库名:libos08c10.so.1.0.0,部署到板端/usr/hobot/lib/sensor/。需实现的接口:init、deinit、start、stop、power_on、power_off、aexp_gain_control、aexp_line_control、userspace_control。
#ifdef CAMERA_FRAMEWORK_HBN
SENSOR_MODULE_F(os08c10, CAM_MODULE_FLAG_A16D8);
sensor_module_t os08c10 = {
.module = SENSOR_MNAME(os08c10),
#else
sensor_module_t os08c10 = {
.module = "os08c10",
#endif
.init = sensor_init,
.start = sensor_start,
.stop = sensor_stop,
.deinit = sensor_deinit,
.power_on = sensor_poweron,
.power_off = sensor_poweroff,
.aexp_line_control = sensor_aexp_line_control,
.aexp_gain_control = sensor_aexp_gain_control,
.userspace_control = sensor_userspace_control,
};
power_on
power_on 通过 X5 GPIO 控制 sensor 上电,需结合 spec 上电时序。
sensor_poweron 一般在 sensor_init 中调用,只需要控制 XSHUTDOWN 即可,以下为 os08c10 power_on的代码实现:

int sensor_poweron(sensor_info_t *sensor_info)
{
int gpio, ret = RET_OK;
vin_dbg("%s gpio_num = %d \n", sensor_info->sensor_name, sensor_info->gpio_num);
if(sensor_info->gpio_num > 0) {
for(gpio = 0; gpio < sensor_info->gpio_num; gpio++) {
vin_dbg("%s gpio_pin[%d] = %d \n", sensor_info->sensor_name, gpio, sensor_info->gpio_pin[gpio]);
if(sensor_info->gpio_pin[gpio] != -1) {
ret = vin_power_ctrl(sensor_info->gpio_pin[gpio],
sensor_info->gpio_level[gpio]);
usleep(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
power_off 通过 X5 GPIO 控制 sensor 下电,需结合 spec 上电时序。
sensor_poweroff 一般在 sensor_deinit 中调用,只需要控制 XSHUTDOWN 即可,以下为 os08c10 power_off 的代码实现:

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
将 sensor 初始化寄存器列表通过i2c写函数写入 sensor;不同分辨率使用不同 setting 数组
os08c10_linear_data_init 用于填充turning_data信息。
sensor_init 调用时机为 hbn_camera_attach_to_vin
int sensor_init(sensor_info_t *sensor_info)
{
int ret = RET_OK;
int setting_size = 0;
pr_debug("os08c10 sensor_init \n");
ret = sensor_poweron(sensor_info);
if (ret < 0) {
pr_err("%d : sensor reset %s fail\n", __LINE__, sensor_info->sensor_name);
return ret;
}
if (sensor_info->resolution == 2160) {
pr_debug("os08c10 resolution is 2160 \n");
setting_size = sizeof(os08c10_3840x2160_30fps_27MHz_linear_12bit_1701Mbps_2lane) / sizeof(uint32_t) / 2;
ret = vin_write_array(sensor_info->bus_num, sensor_info->sensor_addr, 2,
setting_size, os08c10_3840x2160_30fps_27MHz_linear_12bit_1701Mbps_2lane);
if (ret < 0) {
pr_err("%d : init %s fail\n", __LINE__, sensor_info->sensor_name);
return ret;
}
} else {
pr_err("config mode is err\n");
return -RET_ERROR;
}
ret = os08c10_linear_data_init(sensor_info);
if (ret < 0) {
pr_err("%d : turning data init %s fail\n", __LINE__, sensor_info->sensor_name);
return ret;
}
return ret;
}
deinit
deinit 一般调用于 sensor的反初始化,直接调用 power_off 下电即可。
sensor_deinit 调用时机为 hbn_camera_destroy 或者 hbn_camera_detach_from_vin。
int sensor_deinit(sensor_info_t *sensor_info)
{
int ret = RET_OK;
ret = sensor_poweroff(sensor_info);
if (ret < 0) {
pr_err("%d : deinit %s fail\n", __LINE__, sensor_info->sensor_name);
return ret;
}
return ret;
}
start
start 实现下发开流寄存器0x0100 为0x01,使 sensor 出流。
sensor_start 调用时机为 上层调用 hbn_vflow_start。

int sensor_start(sensor_info_t *sensor_info)
{
int ret = RET_OK;
int setting_size = 0;
pr_debug("os08c10 sensor start\n");
setting_size = sizeof(os08c10_2lane_stream_on_setting) / sizeof(uint32_t) / 2;
ret = vin_write_array(sensor_info->bus_num, sensor_info->sensor_addr, 2,
setting_size, os08c10_2lane_stream_on_setting);
if (ret < 0) {
pr_err("start %s fail\n", sensor_info->sensor_name);
return ret;
}
return ret;
}
stop
stop 实现下发关流寄存器 0x0100 为0x00,使 sensor 关流。
sensor_stop 调用时机为 上层调用 hbn_vflow_stop
int sensor_stop(sensor_info_t *sensor_info)
{
int ret = RET_OK;
int setting_size = 0;
printf("os08c10 sensor stop \n");
setting_size = sizeof(os08c10_2lane_stream_off_setting) / sizeof(uint32_t) / 2;
ret = vin_write_array(sensor_info->bus_num, sensor_info->sensor_addr, 2,
setting_size, os08c10_2lane_stream_off_setting);
if (ret < 0) {
pr_err("stop %s fail\n", sensor_info->sensor_name);
return ret;
}
return ret;
}
ISP 配置与 linear_data_init

在 os08c10_linear_data_init 中填充 tuning 参数:
从 sensor 读 VTS(寄存器 0x380e/0x380f),计算 lines_per_second;
exposure_time_max/min、analog_gain_max、digital_gain_max;
填充 bayer(sensor_data_bayer_fill、sensor_data_bits_fill);
设置 stream_ctrl;将 again_lut 从
os08c10_setting.h中的os08c10_gain_lut拷贝到turning_data.normal.again_lut,再通过 ioctl 下发。
关键参数配置:
lines_per_second = vts * sensor_info->fpsexposure_time_max = vts手册中未找到按照最大VTS配置analog_gain_max = 191与 gain_lut 最大索引一致,63.5x 对应 191,参照 gain 索引值和 增益倍率 对照表digital_gain_max = 0dgain 未用配置0即可,如果使用参照analog_gain_maxexposure_time_min = 1sensor_data_bayer_fill(..., 12, BAYER_START_B, BAYER_PATTERN_RGGB);sensor_data_bits_fill(..., 12),0,0位置BAYER_START是 BAYER_START_B,BAYER_PATTERN是RGGB,raw12格式,sensor_data_bits_fill pwl mode 才会用到,这里配置12即可
aexp_line_control
aexp_line_control这个函数是主要实现了曝光的控制,主要负责:
接收ISP 3A算法计算的下发的曝光的行数
将曝光的行数转换为sensor寄存器值
配置通过i2c下发给sensor 曝光寄存器
这里曝光寄存器为 0x3500、0x3501、0x3502。

static int sensor_aexp_line_control(hal_control_info_t *info, uint32_t mode, uint32_t *line, uint32_t line_num)
{
const uint16_t EXP_LINE0 = 0x3500;
const uint16_t EXP_LINE1 = 0x3501;
const uint16_t EXP_LINE2 = 0x3502;
char temp0 = 0, temp1 = 0, temp2 = 0;
if (mode == NORMAL_M) {
uint32_t sline = line[0];
if (sline > 2314) sline = 2314;
if (sline < 8) sline = 8;
temp0 = (sline >> 16) & 0xFF;
vin_i2c_write8(info->bus_num, 16, info->sensor_addr, EXP_LINE0, temp0);
temp1 = (sline >> 8) & 0x0F;
vin_i2c_write8(info->bus_num, 16, info->sensor_addr, EXP_LINE1, temp1);
temp2 = (sline) & 0xFF;
vin_i2c_write8(info->bus_num, 16, info->sensor_addr, EXP_LINE2, temp2);
} else {
vin_err(" unsupport mode %d\n", mode);
}
return 0;
}
aexp_gain_control:
aexp_gain_control这个函数是主要实现了增益的控制,主要负责:
接收ISP 3A算法计算的下发的增益的索引值
查 gain_lut 表根据增益的索引值找到sensor寄存器值
配置通过i2c下发给sensor 增益寄存器
gain_lut 一般需要参照 spec 进行配置。
对于 OmniVision 的sensor gain控制一般以整数+小数线性组合,以 os08c10 为例,需要根据增益倍数推导寄存器值计算公式,具体方式如下:
参考文档和gain_table.xlsx:



= "0x" & DEC2HEX(INT(B4)*256 + ROUND(MOD(B4,1)*128,0)*2, 4)
公式解释:
INT(B4):获取整数部分
MOD(B4,1):获取小数部分(等价于 B4-INT(B4))
*128:转换为 1/128 单位
ROUND(...,0):四舍五入到整数
*2:左移1位(因为要放在高7位)
*256:整数部分移到高字节
DEC2HEX(...,4):转换为4位十六进制
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_H = 0x3548;
const uint16_t AGAIN_L = 0x3549;
char again_reg_value_h = 0, again_reg_value_l = 0;
int gain_index = 0;
if (mode == NORMAL_M) {
if (again[0] >= sizeof(os08c10_gain_lut)/sizeof(uint32_t))
gain_index = sizeof(os08c10_gain_lut)/sizeof(uint32_t) - 1;
else
gain_index = again[0];
again_reg_value_h = (os08c10_gain_lut[gain_index] >> 8) & 0x7F;
again_reg_value_l = (os08c10_gain_lut[gain_index]) & 0xFE;
vin_i2c_write8(info->bus_num, 16, info->sensor_addr, AGAIN_H, again_reg_value_h);
vin_i2c_write8(info->bus_num, 16, info->sensor_addr, AGAIN_L, again_reg_value_l);
} else {
vin_err(" unsupport mode %d\n", mode);
}
return 0;
}
userspace_control
这里 sensor_userspace_control 用于 Gain Control 与 Exposure Control开关。
用于 hbplayer 调试时排查 flicker 或画面异常,可开关 HAL_GAIN_CONTROL / HAL_LINE_CONTROL确认问题。
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;
}
驱动部署
./bd.sh hbre camsys/libcam编译。将
out/deploy/hbre/lib/sensor/libos08c10.so.1.0.0拷贝到板端/usr/hobot/lib/sensor/。
5.17.3.3. App 配置
在路径
app/samples/platform_samples/vp_sensors/os08c10/中为 os08c10 添加配置,需配置 mipi(lane、datatype、mipiclk、linelenth、framelenth、settle)、camera(name、addr、sensor_mode、gpio、fps、width、height、format)、vin_node_attr(mipi_rx、cim_isp_flyby 等)、vin_ichn_attr、vin_ochn_attr(wstride 等)、isp_attr、isp_ichn_attr、isp_ochn_attr。
HTS/VTS 可从寄存器 0x380c/0x380d、0x380e/0x380f 读取或咨询 Sensor FAE 提供或者使用示波器测量,测量方法参考 mipi-参数确定
mipiclk 一般询问 Sensor FAE即可,计算方法参考 MIPI CLK
其他的都为通用配置,参考文档 数据结构。 进行配置即可
vp_sensor 配置文件:linear_3840x2160_raw10_30fps_2lane.c
配置文件路径:
os08c10/linear_3840x2160_raw10_30fps_2lane.c。配置结构体名:
os08c10_linear_3480x2160_raw12_30fps_2lane(需在 vp_sensor_config_list 中注册)。当前示例为 RAW12 输出;datatype/format 配置为 0x2C主要参数如下:
| 配置项 | 参数名 / 位置 | 取值说明 |
|---|---|---|
| 顶层 | config_file |
"linear_3840x2160_raw12_30fps_2lane.c" |
sensor_name |
"os08c10-30fps-2lane" |
|
chip_id_reg / chip_id |
0x300A / 0x53 | |
sensor_i2c_addr_list |
{0x21} | |
support_sensor_mode |
{NORMAL_M} | |
| MIPI | lane |
2 |
datatype |
RAW12 (0x2C) | |
fps |
30 | |
mipiclk |
1701(Mbps) | |
width / height |
3840 / 2160 | |
linelenth / framelenth |
4860 / 2314(参考 sensor HTS/VTS) | |
settle |
0(报 MIPI 错时可调 0–120) | |
channel_num |
1 | |
| Camera | name / addr |
"os08c10" / 0x21 |
sensor_mode |
1 (NORMAL_M) | |
format |
RAW12 | |
gpio_enable_bit / gpio_level_bit |
0x01 / 0x00 | |
calib_lname |
"disable" | |
| VIN | mipi_rx |
0 |
cim_isp_flyby |
0(offline) | |
hdr_mode |
NOT_HDR | |
mclk_freq (vin_attr_ex) |
27000000(27MHz) | |
| VIN 输出 | wstride |
SENSOR_WIDTH * 2(RAW12 为 ×2) |
| ISP 输入 | input_mode |
DDR_MODE |
sensor_mode |
ISP_NORMAL_M | |
crop |
x=0, y=0, w=3840, h=2160 | |
input_fmt |
FRM_FMT_RAW | |
input_bit_width |
12 | |
| ISP 输出 | output_fmt |
FRM_FMT_NV12 |
output_bit_width |
8 | |
ddr_en |
1 |
5.17.3.4. 程序验证
sample_vin:运行 get_vin_data,确认 raw 图无严重偏色、帧率正常;f: 计算打印实际帧率,并判断配置的帧率是否正确。
sample_isp:拷贝同分辨率 tuning.json 为
os08c10_tuning.json并修改其中sensor_name为os08c10,运行 get_isp_data 确认 YUV 正常。hbplayer:连接板端预览 YUV,见 hbplayer 和 tuning_tool 工具使用指南。
tuning_tool:使用tuning_tool连接hbplayer后,输入命令 e,配置 Manual AE,设置 aGain、integrationTime,看驱动打印核对 0x3508/0x3509、0x3500~0x3502 与 spec 一致,ISP 增益限制可在 os08c10_tuning.json 中配置以便自测。
常见问题见 Camera Sensor FAQ。