diff --git a/scripts/examples/01-Camera/07-Sensor-Control/sensor_save_and_restore_settings.py b/scripts/examples/01-Camera/07-Sensor-Control/sensor_save_and_restore_settings.py new file mode 100644 index 000000000..5c3fce1e7 --- /dev/null +++ b/scripts/examples/01-Camera/07-Sensor-Control/sensor_save_and_restore_settings.py @@ -0,0 +1,81 @@ +# Sensor Save and Restore Settings +# +# This example shows off how to save and then restore camera settings +# after a sensor reset and/or powerdown. + +import sensor +import time + +# Set pixel format to RGB565 (or GRAYSCALE) +target_pixformat = sensor.RGB565 + +# Set frame size to QVGA (320x240) +target_framesize = sensor.QVGA + +sensor.reset() +sensor.set_pixformat(target_pixformat) +sensor.set_framesize(target_framesize) + +# Delay 3 seconds to let the camera auto functions run and measure the fps. +t = time.ticks_ms() +clock = time.clock() +fps = 0 +while time.ticks_diff(time.ticks_ms(), t) < 3000: + clock.tick() + sensor.snapshot() + fps = clock.fps() + +# The sensor should have time now to warm up and adjust to the current environment. +# Grab the settings now before turning off the sensor. + +gain = sensor.get_gain_db() +exposure = sensor.get_exposure_us() +rgb_gain = sensor.get_rgb_gain_db() + +print("Gain == %f db" % gain) +print("Exposure == %d us" % exposure) +print("RGB Gain == %f db, %f db, %f db" % (rgb_gain[0], rgb_gain[1], rgb_gain[2])) + +print("Powering Off Sensor") +sensor.shutdown(True) +time.sleep(2) + +# Disable all settling time delays in the sensor driver code. Turning this off WILL result in +# corrupted images appearing when modifying settings. However, now you can change sensor settings +# in bulk quickly. +sensor.disable_delays(True) + +ts = time.ticks_ms() +print("Powering On Sensor") +sensor.shutdown(False) +sensor.reset() + +sensor.set_pixformat(target_pixformat) +sensor.set_framesize(target_framesize) + +sensor.set_auto_gain(False, gain_db=gain) +sensor.set_auto_exposure(False, exposure_us=exposure) +sensor.set_auto_whitebal(False, rgb_gain_db=rgb_gain) + +# You need to delay before calling snapshot as the image coming out of the camera right now is +# most likely to be very corrupt. The exact amount of time to delay below is application +# dependent. However, you should probably wait for a frame. +time.sleep(1.0 / fps) + +print("Restore Delay %d ms" % (time.ticks_ms() - ts)) + +gain = sensor.get_gain_db() +exposure = sensor.get_exposure_us() +rgb_gain = sensor.get_rgb_gain_db() + +print("Gain == %f db" % gain) +print("Exposure == %d us" % exposure) +print("RGB Gain == %f db, %f db, %f db" % (rgb_gain[0], rgb_gain[1], rgb_gain[2])) + +clock = time.clock() + +# Image should look like it did before save and restore. +while True: + clock.tick() + img = sensor.snapshot() + # print(clock.fps()) diff --git a/src/omv/common/sensor.h b/src/omv/common/sensor.h index aa1a9347d..42b5d7e1f 100644 --- a/src/omv/common/sensor.h +++ b/src/omv/common/sensor.h @@ -141,6 +141,8 @@ typedef enum { IOCTL_PAUSE_AUTO_FOCUS, IOCTL_RESET_AUTO_FOCUS, IOCTL_WAIT_ON_AUTO_FOCUS, + IOCTL_SET_NIGHT_MODE, + IOCTL_GET_NIGHT_MODE, IOCTL_LEPTON_GET_WIDTH, IOCTL_LEPTON_GET_HEIGHT, IOCTL_LEPTON_GET_RADIOMETRY, @@ -219,9 +221,11 @@ typedef struct _sensor { uint32_t yuv_swap : 1; // Byte-swap 2BPP YUV formats after capture. uint32_t bayer : 3; // Bayer/CFA pattern. uint32_t yuv_order : 1; // YUV/YVU order. + uint32_t blc_size : 4; // Number of black level calibration registers. } hw_flags; const uint16_t *color_palette; // Color palette used for color lookup. + bool disable_delays; // Set to true to disable all sensor settling time delays. bool disable_full_flush; // Turn off default frame buffer flush policy when full. vsync_cb_t vsync_callback; // VSYNC callback. @@ -265,6 +269,8 @@ typedef struct _sensor { int (*get_exposure_us) (sensor_t *sensor, int *exposure_us); int (*set_auto_whitebal) (sensor_t *sensor, int enable, float r_gain_db, float g_gain_db, float b_gain_db); int (*get_rgb_gain_db) (sensor_t *sensor, float *r_gain_db, float *g_gain_db, float *b_gain_db); + int (*set_auto_blc) (sensor_t *sensor, int enable, int *regs); + int (*get_blc_regs) (sensor_t *sensor, int *regs); int (*set_hmirror) (sensor_t *sensor, int enable); int (*set_vflip) (sensor_t *sensor, int enable); int (*set_special_effect) (sensor_t *sensor, sde_t sde); @@ -378,6 +384,12 @@ int sensor_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float // Get the rgb gain values. int sensor_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_gain_db); +// Enable auto blc (black level calibration) or set from previous calibration. +int sensor_set_auto_blc(int enable, int *regs); + +// Get black level valibration register values. +int sensor_get_blc_regs(int *regs); + // Enable/disable the hmirror mode. int sensor_set_hmirror(int enable); diff --git a/src/omv/common/sensor_utils.c b/src/omv/common/sensor_utils.c index 355c75b71..becbc6d3f 100644 --- a/src/omv/common/sensor_utils.c +++ b/src/omv/common/sensor_utils.c @@ -583,7 +583,9 @@ __weak int sensor_set_pixformat(pixformat_t pixformat) { return SENSOR_ERROR_CTL_FAILED; } - mp_hal_delay_ms(100); // wait for the camera to settle + if (!sensor.disable_delays) { + mp_hal_delay_ms(100); // wait for the camera to settle + } // Set pixel format sensor.pixformat = pixformat; @@ -619,7 +621,9 @@ __weak int sensor_set_framesize(framesize_t framesize) { return SENSOR_ERROR_CTL_FAILED; } - mp_hal_delay_ms(100); // wait for the camera to settle + if (!sensor.disable_delays) { + mp_hal_delay_ms(100); // wait for the camera to settle + } // Set framebuffer size sensor.framesize = framesize; @@ -906,6 +910,34 @@ __weak int sensor_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_g return 0; } +__weak int sensor_set_auto_blc(int enable, int *regs) { + // Check if the control is supported. + if (sensor.set_auto_blc == NULL) { + return SENSOR_ERROR_CTL_UNSUPPORTED; + } + + // Call the sensor specific function. + if (sensor.set_auto_blc(&sensor, enable, regs) != 0) { + return SENSOR_ERROR_CTL_FAILED; + } + + return 0; +} + +__weak int sensor_get_blc_regs(int *regs) { + // Check if the control is supported. + if (sensor.get_blc_regs == NULL) { + return SENSOR_ERROR_CTL_UNSUPPORTED; + } + + // Call the sensor specific function. + if (sensor.get_blc_regs(&sensor, regs) != 0) { + return SENSOR_ERROR_CTL_FAILED; + } + + return 0; +} + __weak int sensor_set_hmirror(int enable) { // Check if the value has changed. if (sensor.hmirror == ((bool) enable)) { @@ -929,7 +961,9 @@ __weak int sensor_set_hmirror(int enable) { sensor.hmirror = enable; // Wait for the camera to settle - mp_hal_delay_ms(100); + if (!sensor.disable_delays) { + mp_hal_delay_ms(100); + } return 0; } @@ -961,7 +995,9 @@ __weak int sensor_set_vflip(int enable) { sensor.vflip = enable; // Wait for the camera to settle - mp_hal_delay_ms(100); + if (!sensor.disable_delays) { + mp_hal_delay_ms(100); + } return 0; } diff --git a/src/omv/modules/py_sensor.c b/src/omv/modules/py_sensor.c index af40db33e..a13ae5a31 100644 --- a/src/omv/modules/py_sensor.c +++ b/src/omv/modules/py_sensor.c @@ -451,6 +451,48 @@ static mp_obj_t py_sensor_get_rgb_gain_db() { }); } +static mp_obj_t py_sensor_set_auto_blc(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_enable, ARG_regs }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_enable, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_regs, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + int enable = args[ARG_enable].u_int; + int regs[sensor.hw_flags.blc_size]; + bool regs_present = args[ARG_regs].u_obj != mp_const_none; + if (regs_present) { + mp_obj_t *arg_array; + mp_obj_get_array_fixed_n(args[ARG_regs].u_obj, sensor.hw_flags.blc_size, &arg_array); + for (uint32_t i = 0; i < sensor.hw_flags.blc_size; i++) { + regs[i] = mp_obj_get_int(arg_array[i]); + } + } + + int error = sensor_set_auto_blc(enable, regs_present ? regs : NULL); + if (error != 0) { + sensor_raise_error(error); + } + return mp_const_none; +} + +static mp_obj_t py_sensor_get_blc_regs() { + int regs[sensor.hw_flags.blc_size]; + int error = sensor_get_blc_regs(regs); + if (error != 0) { + sensor_raise_error(error); + } + + mp_obj_list_t *l = mp_obj_new_list(sensor.hw_flags.blc_size, NULL); + for (uint32_t i = 0; i < sensor.hw_flags.blc_size; i++) { + l->items[i] = mp_obj_new_int(regs[i]); + } + return l; +} + static mp_obj_t py_sensor_set_hmirror(mp_obj_t enable) { int error = sensor_set_hmirror(mp_obj_is_true(enable)); if (error != 0) { @@ -522,6 +564,15 @@ static mp_obj_t py_sensor_get_framebuffers() { return mp_obj_new_int(framebuffer->n_buffers); } +static mp_obj_t py_sensor_disable_delays(uint n_args, const mp_obj_t *args) { + if (!n_args) { + return mp_obj_new_bool(sensor.disable_delays); + } + + sensor.disable_delays = mp_obj_get_int(args[0]); + return mp_const_none; +} + static mp_obj_t py_sensor_disable_full_flush(uint n_args, const mp_obj_t *args) { if (!n_args) { return mp_obj_new_bool(sensor.disable_full_flush); @@ -656,6 +707,22 @@ static mp_obj_t py_sensor_ioctl(uint n_args, const mp_obj_t *args) { } #endif + case IOCTL_SET_NIGHT_MODE: { + if (n_args >= 2) { + error = sensor_ioctl(request, mp_obj_get_int(args[1])); + } + break; + } + + case IOCTL_GET_NIGHT_MODE: { + int enabled; + error = sensor_ioctl(request, &enabled); + if (error == 0) { + ret_obj = mp_obj_new_bool(enabled); + } + break; + } + case IOCTL_LEPTON_GET_WIDTH: { int width; error = sensor_ioctl(request, &width); @@ -915,6 +982,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_sensor_set_auto_exposure_obj, 1, py_sensor_ STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_sensor_get_exposure_us_obj, py_sensor_get_exposure_us); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_sensor_set_auto_whitebal_obj, 1, py_sensor_set_auto_whitebal); STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_sensor_get_rgb_gain_db_obj, py_sensor_get_rgb_gain_db); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_sensor_set_auto_blc_obj, 1, py_sensor_set_auto_blc); +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_sensor_get_blc_regs_obj, py_sensor_get_blc_regs); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_hmirror_obj, py_sensor_set_hmirror); STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_sensor_get_hmirror_obj, py_sensor_get_hmirror); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_vflip_obj, py_sensor_set_vflip); @@ -925,6 +994,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_auto_rotation_obj, py_sensor_se STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_sensor_get_auto_rotation_obj, py_sensor_get_auto_rotation); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_framebuffers_obj, py_sensor_set_framebuffers); STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_sensor_get_framebuffers_obj, py_sensor_get_framebuffers); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_sensor_disable_delays_obj, 0, 1, py_sensor_disable_delays); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_sensor_disable_full_flush_obj, 0, 1, py_sensor_disable_full_flush); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_special_effect_obj, py_sensor_set_special_effect); STATIC MP_DEFINE_CONST_FUN_OBJ_3(py_sensor_set_lens_correction_obj, py_sensor_set_lens_correction); @@ -1028,6 +1098,8 @@ STATIC const mp_map_elem_t globals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_IOCTL_RESET_AUTO_FOCUS), MP_OBJ_NEW_SMALL_INT(IOCTL_RESET_AUTO_FOCUS)}, { MP_OBJ_NEW_QSTR(MP_QSTR_IOCTL_WAIT_ON_AUTO_FOCUS), MP_OBJ_NEW_SMALL_INT(IOCTL_WAIT_ON_AUTO_FOCUS)}, #endif + { MP_OBJ_NEW_QSTR(MP_QSTR_IOCTL_SET_NIGHT_MODE), MP_OBJ_NEW_SMALL_INT(IOCTL_SET_NIGHT_MODE)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_IOCTL_GET_NIGHT_MODE), MP_OBJ_NEW_SMALL_INT(IOCTL_GET_NIGHT_MODE)}, { MP_OBJ_NEW_QSTR(MP_QSTR_IOCTL_LEPTON_GET_WIDTH), MP_OBJ_NEW_SMALL_INT(IOCTL_LEPTON_GET_WIDTH)}, { MP_OBJ_NEW_QSTR(MP_QSTR_IOCTL_LEPTON_GET_HEIGHT), MP_OBJ_NEW_SMALL_INT(IOCTL_LEPTON_GET_HEIGHT)}, { MP_OBJ_NEW_QSTR(MP_QSTR_IOCTL_LEPTON_GET_RADIOMETRY), MP_OBJ_NEW_SMALL_INT(IOCTL_LEPTON_GET_RADIOMETRY)}, @@ -1091,6 +1163,8 @@ STATIC const mp_map_elem_t globals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_get_exposure_us), (mp_obj_t) &py_sensor_get_exposure_us_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_auto_whitebal), (mp_obj_t) &py_sensor_set_auto_whitebal_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_get_rgb_gain_db), (mp_obj_t) &py_sensor_get_rgb_gain_db_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_set_auto_blc), (mp_obj_t) &py_sensor_set_auto_blc_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_get_blc_regs), (mp_obj_t) &py_sensor_get_blc_regs_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_hmirror), (mp_obj_t) &py_sensor_set_hmirror_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_get_hmirror), (mp_obj_t) &py_sensor_get_hmirror_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_vflip), (mp_obj_t) &py_sensor_set_vflip_obj }, @@ -1101,6 +1175,7 @@ STATIC const mp_map_elem_t globals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_get_auto_rotation), (mp_obj_t) &py_sensor_get_auto_rotation_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_framebuffers), (mp_obj_t) &py_sensor_set_framebuffers_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_get_framebuffers), (mp_obj_t) &py_sensor_get_framebuffers_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disable_delays), (mp_obj_t) &py_sensor_disable_delays_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_disable_full_flush), (mp_obj_t) &py_sensor_disable_full_flush_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_special_effect), (mp_obj_t) &py_sensor_set_special_effect_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_lens_correction), (mp_obj_t) &py_sensor_set_lens_correction_obj }, diff --git a/src/omv/ports/mimxrt/sensor.c b/src/omv/ports/mimxrt/sensor.c index b90572da1..22d85550b 100644 --- a/src/omv/ports/mimxrt/sensor.c +++ b/src/omv/ports/mimxrt/sensor.c @@ -55,6 +55,8 @@ void sensor_init0() { omv_i2c_init(&sensor.i2c_bus, sensor.i2c_bus.id, sensor.i2c_bus.speed); } + sensor.disable_delays = false; + // Disable VSYNC IRQ and callback sensor_set_vsync_callback(NULL); diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index 90ab6e01b..4ea420ba6 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -521,6 +521,7 @@ endif ifeq ($(OMV_ENABLE_UVC), 1) UVC = uvc # UVC object files +UVC_OBJ += $(BUILD)/$(MICROPY_DIR)/lib/libm/math.o UVC_OBJ += $(wildcard $(BUILD)/$(UVC_DIR)/src/*.o) UVC_OBJ += $(wildcard $(BUILD)/$(HAL_DIR)/src/*.o) UVC_OBJ += $(addprefix $(BUILD)/$(CMSIS_DIR)/src/,\ diff --git a/src/omv/ports/stm32/sensor.c b/src/omv/ports/stm32/sensor.c index 91299c2df..470e2a113 100644 --- a/src/omv/ports/stm32/sensor.c +++ b/src/omv/ports/stm32/sensor.c @@ -102,6 +102,8 @@ void sensor_init0() { omv_i2c_init(&sensor.i2c_bus, sensor.i2c_bus.id, sensor.i2c_bus.speed); } + sensor.disable_delays = false; + // Disable VSYNC IRQ and callback sensor_set_vsync_callback(NULL); diff --git a/src/omv/sensors/hm01b0.c b/src/omv/sensors/hm01b0.c index 30c944b54..f06d32a79 100644 --- a/src/omv/sensors/hm01b0.c +++ b/src/omv/sensors/hm01b0.c @@ -342,13 +342,13 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain int ret = 0; if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { gain_db = IM_MAX(IM_MIN(gain_db, 24.0f), 0.0f); - int gain = fast_ceilf(fast_log2(fast_expf((gain_db / 20.0f) * fast_log(10.0f)))); + int gain = fast_ceilf(logf(expf((gain_db / 20.0f) * M_LN10)) / M_LN2); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AE_CTRL, 0); // Must disable AE ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, ANALOG_GAIN, ((gain & 0x7) << 4)); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, GRP_PARAM_HOLD, 0x01); } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { gain_db_ceiling = IM_MAX(IM_MIN(gain_db_ceiling, 24.0f), 0.0f); - int gain = fast_ceilf(fast_log2(fast_expf((gain_db_ceiling / 20.0f) * fast_log(10.0f)))); + int gain = fast_ceilf(logf(expf((gain_db_ceiling / 20.0f) * M_LN10) / M_LN2)); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, MAX_AGAIN_FULL, (gain & 0x7)); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, MAX_AGAIN_BIN2, (gain & 0x7)); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AE_CTRL, 1); @@ -361,7 +361,7 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { if (omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, ANALOG_GAIN, &gain) != 0) { return -1; } - *gain_db = fast_floorf(fast_log(1 << (gain >> 4)) / fast_log(10.0f) * 20.0f); + *gain_db = fast_floorf(log10f(1 << (gain >> 4)) * 20.0f); return 0; } diff --git a/src/omv/sensors/hm0360.c b/src/omv/sensors/hm0360.c index 3c63cf0a4..84cc25548 100644 --- a/src/omv/sensors/hm0360.c +++ b/src/omv/sensors/hm0360.c @@ -537,12 +537,12 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain int ret = omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AE_CTRL, &ae_ctrl); if (!enable && (!isnanf(gain_db)) && (!isinff(gain_db))) { gain_db = IM_MAX(IM_MIN(gain_db, 24.0f), 0.0f); - uint8_t gain = fast_ceilf(fast_log2(fast_expf((gain_db / 20.0f) * fast_log(10.0f)))); + uint8_t gain = fast_ceilf(logf(expf((gain_db / 20.0f) * M_LN10)) / M_LN2); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AE_CTRL, (ae_ctrl & 0xFE)); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, ANALOG_GAIN, ((gain & 0x7) << 4)); } else if (enable && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { gain_db_ceiling = IM_MAX(IM_MIN(gain_db_ceiling, 24.0f), 0.0f); - uint8_t gain = fast_ceilf(fast_log2(fast_expf((gain_db_ceiling / 20.0f) * fast_log(10.0f)))); + uint8_t gain = fast_ceilf(logf(expf((gain_db_ceiling / 20.0f) * M_LN10)) / M_LN2); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, MAX_AGAIN, (gain & 0x07)); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AE_CTRL, (ae_ctrl | 0x01)); } @@ -555,7 +555,7 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { if (omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, ANALOG_GAIN, &gain) != 0) { return -1; } - *gain_db = fast_floorf(fast_log(1 << (gain >> 4)) / fast_log(10.0f) * 20.0f); + *gain_db = fast_floorf(log10f(1 << (gain >> 4)) * 20.0f); return 0; } diff --git a/src/omv/sensors/mt9m114.c b/src/omv/sensors/mt9m114.c index e6751578b..da39d84f1 100644 --- a/src/omv/sensors/mt9m114.c +++ b/src/omv/sensors/mt9m114.c @@ -785,7 +785,7 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain } if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { - int gain = IM_MAX(IM_MIN(fast_expf((gain_db / 20.f) * fast_log(10.f)) * 32.f, 0xffff), 0x0000); + int gain = IM_MAX(IM_MIN(expf((gain_db / 20.0f) * M_LN10) * 32.0f, 0xffff), 0x0000); if (omv_i2c_writew2(&sensor->i2c_bus, sensor->slv_addr, MT9M114_REG_UVC_GAIN_CONTROL, gain) != 0) { return -1; @@ -802,7 +802,7 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { return -1; } - *gain_db = 20.f * (fast_log(gain / 32.f) / fast_log(10.f)); + *gain_db = 20.0f * log10f(gain / 32.0f); return 0; } diff --git a/src/omv/sensors/mt9v0xx.c b/src/omv/sensors/mt9v0xx.c index f74f41428..cacb94c47 100644 --- a/src/omv/sensors/mt9v0xx.c +++ b/src/omv/sensors/mt9v0xx.c @@ -247,7 +247,9 @@ static int set_colorbar(sensor_t *sensor, int enable) { ret = omv_i2c_readw(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_ROW_NOISE_CORR_CONTROL, ®); ret |= omv_i2c_writew(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_ROW_NOISE_CORR_CONTROL, (reg & (~mask)) | ((enable == 0) ? mask : 0)); - ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + if (!sensor->disable_delays) { + ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + } return ret; } @@ -259,10 +261,12 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain int ret = omv_i2c_readw(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_AEC_AGC_ENABLE, ®); ret |= omv_i2c_writew(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_AEC_AGC_ENABLE, (reg & (~agc_mask)) | ((enable != 0) ? agc_mask : 0)); - ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + if (!sensor->disable_delays) { + ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + } if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { - int gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((gain_db / 20.0f) * fast_log(10.0f)) * 16.0f), 64), 16); + int gain = IM_MAX(IM_MIN(fast_roundf(expf((gain_db / 20.0f) * M_LN10) * 16.0f), 64), 16); ret |= omv_i2c_readw(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_ANALOG_GAIN, ®); ret |= omv_i2c_writew(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_ANALOG_GAIN, (reg & 0xFF80) | gain); @@ -272,7 +276,7 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain ret |= omv_i2c_writew(&sensor->i2c_bus, sensor->slv_addr, MT9V0X4_ANALOG_GAIN_B, (reg & 0xFF80) | gain); } } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { - int gain_ceiling = IM_MAX(IM_MIN(fast_roundf(fast_expf((gain_db_ceiling / 20.0f) * fast_log(10.0f)) * 16.0f), 64), 16); + int gain_ceiling = IM_MAX(IM_MIN(fast_roundf(expf((gain_db_ceiling / 20.0f) * M_LN10) * 16.0f), 64), 16); int max_gain = (is_mt9v0x4(sensor)) ? MT9V0X4_MAX_GAIN : MT9V0X2_MAX_GAIN; ret |= omv_i2c_readw(&sensor->i2c_bus, sensor->slv_addr, max_gain, ®); @@ -295,7 +299,7 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { ret |= omv_i2c_readw(&sensor->i2c_bus, sensor->slv_addr, analog_gain, &gain); } - *gain_db = 20.0 * (fast_log((gain & 0x7F) / 16.0f) / fast_log(10.0f)); + *gain_db = 20.0f * log10f((gain & 0x7F) / 16.0f); return ret; } @@ -309,8 +313,9 @@ static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { ret |= omv_i2c_readw(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_AEC_AGC_ENABLE, ®); ret |= omv_i2c_writew(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_AEC_AGC_ENABLE, (reg & (~aec_mask)) | ((enable != 0) ? aec_mask : 0)); - ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... - + if (!sensor->disable_delays) { + ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + } int read_mode = context ? MT9V0X4_READ_MODE_B : MT9V0XX_READ_MODE; int window_width = context ? MT9V0X4_WINDOW_WIDTH_B : MT9V0XX_WINDOW_WIDTH; int horizontal_blanking = context ? MT9V0X4_HORIZONTAL_BLANKING_B : MT9V0XX_HORIZONTAL_BLANKING; @@ -389,7 +394,9 @@ static int set_hmirror(sensor_t *sensor, int enable) { (read_mode & (~MT9V0XX_READ_MODE_COL_FLIP)) | ((enable == 0) ? MT9V0XX_READ_MODE_COL_FLIP : 0)); } - ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + if (!sensor->disable_delays) { + ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + } return ret; } @@ -405,7 +412,9 @@ static int set_vflip(sensor_t *sensor, int enable) { (read_mode & (~MT9V0XX_READ_MODE_ROW_FLIP)) | ((enable == 0) ? MT9V0XX_READ_MODE_ROW_FLIP : 0)); } - ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + if (!sensor->disable_delays) { + ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + } return ret; } @@ -462,7 +471,9 @@ static int ioctl(sensor_t *sensor, int request, va_list ap) { ret |= omv_i2c_writew(&sensor->i2c_bus, sensor->slv_addr, MT9V0XX_CHIP_CONTROL, (chip_control & (~MT9V0XX_CHIP_CONTROL_MODE_MASK)) | ((enable != 0) ? MT9V0XX_CHIP_CONTROL_SNAP_MODE : MT9V0XX_CHIP_CONTROL_MASTER_MODE)); - ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + if (!sensor->disable_delays) { + ret |= sensor->snapshot(sensor, NULL, 0); // Force shadow mode register to update... + } break; } case IOCTL_GET_TRIGGERED_MODE: { diff --git a/src/omv/sensors/ov2640.c b/src/omv/sensors/ov2640.c index 5a8f434a6..048002a9d 100644 --- a/src/omv/sensors/ov2640.c +++ b/src/omv/sensors/ov2640.c @@ -359,7 +359,9 @@ static int reset(sensor_t *sensor) { } // Delay 300 ms - mp_hal_delay_ms(300); + if (!sensor->disable_delays) { + mp_hal_delay_ms(300); + } return ret; } @@ -584,20 +586,20 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM8, (reg & (~COM8_AGC_EN)) | ((enable != 0) ? COM8_AGC_EN : 0)); if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { - float gain = IM_MAX(IM_MIN(fast_expf((gain_db / 20.0) * fast_log(10.0)), 32.0), 1.0); + float gain = IM_MAX(IM_MIN(expf((gain_db / 20.0f) * M_LN10), 32.0f), 1.0f); - int gain_temp = fast_roundf(fast_log2(IM_MAX(gain / 2.0, 1.0))); + int gain_temp = fast_ceilf(logf(IM_MAX(gain / 2.0f, 1.0f)) / M_LN2); int gain_hi = 0xF >> (4 - gain_temp); - int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0) * 16.0), 15); + int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0f) * 16.0f), 15); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, GAIN, (gain_hi << 4) | (gain_lo << 0)); } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { - float gain_ceiling = IM_MAX(IM_MIN(fast_expf((gain_db_ceiling / 20.0) * fast_log(10.0)), 128.0), 2.0); + float gain_ceiling = IM_MAX(IM_MIN(expf((gain_db_ceiling / 20.0f) * M_LN10), 128.0f), 2.0f); ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, COM9, ®); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM9, - (reg & 0x1F) | ((fast_ceilf(fast_log2(gain_ceiling)) - 1) << 5)); + (reg & 0x1F) | ((fast_ceilf(logf(gain_ceiling) / M_LN2) - 1) << 5)); } return ret; @@ -623,8 +625,8 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { // DISABLED int hi_gain = 1 << (((gain >> 7) & 1) + ((gain >> 6) & 1) + ((gain >> 5) & 1) + ((gain >> 4) & 1)); - float lo_gain = 1.0 + (((gain >> 0) & 0xF) / 16.0); - *gain_db = 20.0 * (fast_log(hi_gain * lo_gain) / fast_log(10.0)); + float lo_gain = 1.0f + (((gain >> 0) & 0xF) / 16.0f); + *gain_db = 20.0f * log10f(hi_gain * lo_gain); return ret; } diff --git a/src/omv/sensors/ov5640.c b/src/omv/sensors/ov5640.c index 18a7cb055..32f10c7a2 100644 --- a/src/omv/sensors/ov5640.c +++ b/src/omv/sensors/ov5640.c @@ -705,7 +705,9 @@ static int reset(sensor_t *sensor) { #endif // Delay 300 ms - mp_hal_delay_ms(300); + if (!sensor->disable_delays) { + mp_hal_delay_ms(300); + } return ret; } @@ -1077,13 +1079,13 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AEC_PK_MANUAL, (reg & 0xFD) | ((enable == 0) << 1)); if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { - int gain = IM_MAX(IM_MIN(fast_expf((gain_db / 20.0) * fast_log(10.0)) * 16.0, 1023), 0); + int gain = IM_MAX(IM_MIN(fast_roundf(expf((gain_db / 20.0f) * M_LN10) * 16.0f), 1023), 0); ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AEC_PK_REAL_GAIN_H, ®); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AEC_PK_REAL_GAIN_H, (reg & 0xFC) | (gain >> 8)); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AEC_PK_REAL_GAIN_L, gain); } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { - int gain_ceiling = IM_MAX(IM_MIN(fast_expf((gain_db_ceiling / 20.0) * fast_log(10.0)) * 16.0, 1023), 0); + int gain_ceiling = IM_MAX(IM_MIN(fast_roundf(expf((gain_db_ceiling / 20.0f) * M_LN10) * 16.0f), 1023), 0); ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AEC_GAIN_CEILING_H, ®); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AEC_GAIN_CEILING_H, (reg & 0xFC) | (gain_ceiling >> 8)); @@ -1099,7 +1101,7 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { int ret = omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AEC_PK_REAL_GAIN_H, &gainh); ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AEC_PK_REAL_GAIN_L, &gainl); - *gain_db = 20.0 * (fast_log((((gainh & 0x3) << 8) | gainl) / 16.0) / fast_log(10.0)); + *gain_db = 20.0f * log10f((((gainh & 0x3) << 8) | gainl) / 16.0f); return ret; } @@ -1173,7 +1175,7 @@ static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { } static int get_exposure_us(sensor_t *sensor, int *exposure_us) { - uint8_t spc0, spc1, spc2, spc3, sysrootdiv, aec_0, aec_1, aec_2, hts_h, hts_l; + uint8_t spc0, spc1, spc2, spc3, sysrootdiv, aec_0, aec_1, aec_2, hts_h, hts_l, vts_h, vts_l; int ret = 0; ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, SC_PLL_CONTRL0, &spc0); @@ -1189,8 +1191,14 @@ static int get_exposure_us(sensor_t *sensor, int *exposure_us) { ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, TIMING_HTS_H, &hts_h); ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, TIMING_HTS_L, &hts_l); + ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, TIMING_VTS_H, &vts_h); + ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, TIMING_VTS_L, &vts_l); + uint32_t aec = ((aec_0 << 16) | (aec_1 << 8) | aec_2) >> 4; uint16_t hts = (hts_h << 8) | hts_l; + uint16_t vts = (vts_h << 8) | vts_l; + + aec = IM_MIN(aec, vts); int pclk_freq = calc_pclk_freq(spc0, spc1, spc2, spc3, sysrootdiv); int clocks_per_us = pclk_freq / 1000000; @@ -1207,9 +1215,9 @@ static int set_auto_whitebal(sensor_t *sensor, int enable, float r_gain_db, floa if ((enable == 0) && (!isnanf(r_gain_db)) && (!isnanf(g_gain_db)) && (!isnanf(b_gain_db)) && (!isinff(r_gain_db)) && (!isinff(g_gain_db)) && (!isinff(b_gain_db))) { - int r_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((r_gain_db / 20.0) * fast_log(10.0))), 4095), 0); - int g_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((g_gain_db / 20.0) * fast_log(10.0))), 4095), 0); - int b_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((b_gain_db / 20.0) * fast_log(10.0))), 4095), 0); + int r_gain = IM_MAX(IM_MIN(fast_roundf(expf((r_gain_db / 20.0f) * M_LN10)), 4095), 0); + int g_gain = IM_MAX(IM_MIN(fast_roundf(expf((g_gain_db / 20.0f) * M_LN10)), 4095), 0); + int b_gain = IM_MAX(IM_MIN(fast_roundf(expf((b_gain_db / 20.0f) * M_LN10)), 4095), 0); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AWB_R_GAIN_H, r_gain >> 8); ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AWB_R_GAIN_L, r_gain); @@ -1232,9 +1240,35 @@ static int get_rgb_gain_db(sensor_t *sensor, float *r_gain_db, float *g_gain_db, ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AWB_B_GAIN_H, &blueh); ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AWB_B_GAIN_L, &bluel); - *r_gain_db = 20.0 * (fast_log(((redh & 0xF) << 8) | redl) / fast_log(10.0)); - *g_gain_db = 20.0 * (fast_log(((greenh & 0xF) << 8) | greenl) / fast_log(10.0)); - *b_gain_db = 20.0 * (fast_log(((blueh & 0xF) << 8) | bluel) / fast_log(10.0)); + *r_gain_db = 20.0f * log10f(((redh & 0xF) << 8) | redl); + *g_gain_db = 20.0f * log10f(((greenh & 0xF) << 8) | greenl); + *b_gain_db = 20.0f * log10f(((blueh & 0xF) << 8) | bluel); + + return ret; +} + +static int set_auto_blc(sensor_t *sensor, int enable, int *regs) { + uint8_t reg; + int ret = omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, BLC_CTRL_00, ®); + ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, BLC_CTRL_00, (reg & 0xFE) | (enable != 0)); + + if ((enable == 0) && (regs != NULL)) { + for (uint32_t i = 0; i < sensor->hw_flags.blc_size; i++) { + ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, BLACK_LEVEL_00_H + i, regs[i]); + } + } + + return ret; +} + +static int get_blc_regs(sensor_t *sensor, int *regs) { + int ret = 0; + + for (uint32_t i = 0; i < sensor->hw_flags.blc_size; i++) { + uint8_t reg; + ret |= omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, BLACK_LEVEL_00_H + i, ®); + regs[i] = reg; + } return ret; } @@ -1298,6 +1332,7 @@ static int set_lens_correction(sensor_t *sensor, int enable, int radi, int coef) static int ioctl(sensor_t *sensor, int request, va_list ap) { int ret = 0; + uint8_t reg; switch (request) { case IOCTL_SET_READOUT_WINDOW: { @@ -1343,7 +1378,6 @@ static int ioctl(sensor_t *sensor, int request, va_list ap) { case IOCTL_WAIT_ON_AUTO_FOCUS: { mp_uint_t start_tick = mp_hal_ticks_ms(), delay_ms = va_arg(ap, uint32_t); for (;;) { - uint8_t reg; ret = omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AF_CMD_ACK, ®); if ((ret < 0) || (!reg)) { break; @@ -1356,6 +1390,21 @@ static int ioctl(sensor_t *sensor, int request, va_list ap) { break; } #endif + case IOCTL_SET_NIGHT_MODE: { + int enable = va_arg(ap, int); + ret = omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AEC_CTRL_00, ®); + ret |= omv_i2c_writeb2(&sensor->i2c_bus, sensor->slv_addr, AEC_CTRL_00, + (reg & 0xFB) | ((enable != 0) << 2)); + break; + } + case IOCTL_GET_NIGHT_MODE: { + int *enable = va_arg(ap, int *); + ret = omv_i2c_readb2(&sensor->i2c_bus, sensor->slv_addr, AEC_CTRL_00, ®); + if (ret >= 0) { + *enable = reg & 0x4; + } + break; + } default: { ret = -1; break; @@ -1385,6 +1434,8 @@ int ov5640_init(sensor_t *sensor) { sensor->get_exposure_us = get_exposure_us; sensor->set_auto_whitebal = set_auto_whitebal; sensor->get_rgb_gain_db = get_rgb_gain_db; + sensor->set_auto_blc = set_auto_blc; + sensor->get_blc_regs = get_blc_regs; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; sensor->set_special_effect = set_special_effect; @@ -1401,6 +1452,7 @@ int ov5640_init(sensor_t *sensor) { sensor->hw_flags.gs_bpp = 1; sensor->hw_flags.rgb_swap = 0; sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; + sensor->hw_flags.blc_size = 8; return 0; } diff --git a/src/omv/sensors/ov5640_regs.h b/src/omv/sensors/ov5640_regs.h index b713c1a99..927db4c2a 100644 --- a/src/omv/sensors/ov5640_regs.h +++ b/src/omv/sensors/ov5640_regs.h @@ -87,9 +87,25 @@ #define TIMING_TC_REG_20 0x3820 #define TIMING_TC_REG_21 0x3821 +#define AEC_CTRL_00 0x3A00 + #define AEC_GAIN_CEILING_H 0x3A18 #define AEC_GAIN_CEILING_L 0x3A18 +#define BLC_CTRL_00 0x4000 + +#define BLACK_LEVEL_00_H 0x402C +#define BLACK_LEVEL_00_L 0x402D + +#define BLACK_LEVEL_01_H 0x402E +#define BLACK_LEVEL_01_L 0x402F + +#define BLACK_LEVEL_10_H 0x4030 +#define BLACK_LEVEL_10_L 0x4031 + +#define BLACK_LEVEL_11_H 0x4032 +#define BLACK_LEVEL_11_L 0x4033 + #define FORMAT_CONTROL 0x4300 #define VFIFO_HSIZE_H 0x4602 diff --git a/src/omv/sensors/ov7670.c b/src/omv/sensors/ov7670.c index af6066de1..099fb0d15 100644 --- a/src/omv/sensors/ov7670.c +++ b/src/omv/sensors/ov7670.c @@ -336,7 +336,9 @@ static int reset(sensor_t *sensor) { } // Delay 300 ms - mp_hal_delay_ms(300); + if (!sensor->disable_delays) { + mp_hal_delay_ms(300); + } return ret; } diff --git a/src/omv/sensors/ov7690.c b/src/omv/sensors/ov7690.c index a22b92129..f11bdd778 100644 --- a/src/omv/sensors/ov7690.c +++ b/src/omv/sensors/ov7690.c @@ -196,7 +196,9 @@ static int reset(sensor_t *sensor) { } // Delay 300 ms - mp_hal_delay_ms(300); + if (!sensor->disable_delays) { + mp_hal_delay_ms(300); + } return ret; } @@ -379,24 +381,24 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG13, (reg & 0xFB) | ((enable != 0) << 2)); if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { - float gain = IM_MAX(IM_MIN(fast_expf((gain_db / 20.0) * fast_log(10.0)), 128.0), 1.0); + float gain = IM_MAX(IM_MIN(expf((gain_db / 20.0f) * M_LN10), 128.0f), 1.0f); - int gain_temp = fast_roundf(fast_log2(IM_MAX(gain / 2.0, 1.0))); + int gain_temp = fast_ceilf(logf(IM_MAX(gain / 2.0f, 1.0f)) / M_LN2); int gain_hi = 0x3F >> (6 - gain_temp); - int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0) * 16.0), 15); + int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0f) * 16.0f), 15); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, GAIN, (gain_hi << 4) | (gain_lo << 0)); ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, REG15, ®); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG15, (reg & 0xFC) | (gain_hi >> 4)); } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { - float gain_ceiling = IM_MAX(IM_MIN(fast_expf((gain_db_ceiling / 20.0) * fast_log(10.0)), 128.0), 2.0); + float gain_ceiling = IM_MAX(IM_MIN(expf((gain_db_ceiling / 20.0f) * M_LN10), 128.0f), 2.0f); ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, REG14, ®); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG14, - (reg & 0x8F) | ((fast_ceilf(fast_log2(gain_ceiling)) - 1) << 4)); + (reg & 0x8F) | ((fast_ceilf(logf(gain_ceiling) / M_LN2) - 1) << 4)); } return ret; @@ -413,8 +415,8 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { (((reg15 >> 1) & 1) + ((reg15 >> 0) & 1) + ((gain >> 7) & 1) + ((gain >> 6) & 1) + ((gain >> 5) & 1) + ((gain >> 4) & 1)); - float lo_gain = 1.0 + (((gain >> 0) & 0xF) / 16.0); - *gain_db = 20.0 * (fast_log(hi_gain * lo_gain) / fast_log(10.0)); + float lo_gain = 1.0f + (((gain >> 0) & 0xF) / 16.0f); + *gain_db = 20.0f * log10f(hi_gain * lo_gain); return ret; } @@ -507,9 +509,9 @@ static int set_auto_whitebal(sensor_t *sensor, int enable, float r_gain_db, floa if ((enable == 0) && (!isnanf(r_gain_db)) && (!isnanf(g_gain_db)) && (!isnanf(b_gain_db)) && (!isinff(r_gain_db)) && (!isinff(g_gain_db)) && (!isinff(b_gain_db))) { - int r_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((r_gain_db / 20.0) * fast_log(10.0))), 255), 0); - int g_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((g_gain_db / 20.0) * fast_log(10.0))), 255), 0); - int b_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((b_gain_db / 20.0) * fast_log(10.0))), 255), 0); + int r_gain = IM_MAX(IM_MIN(fast_roundf(expf((r_gain_db / 20.0f) * M_LN10)), 255), 0); + int g_gain = IM_MAX(IM_MIN(fast_roundf(expf((g_gain_db / 20.0f) * M_LN10)), 255), 0); + int b_gain = IM_MAX(IM_MIN(fast_roundf(expf((b_gain_db / 20.0f) * M_LN10)), 255), 0); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, BGAIN, b_gain); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, RGAIN, r_gain); @@ -527,9 +529,9 @@ static int get_rgb_gain_db(sensor_t *sensor, float *r_gain_db, float *g_gain_db, ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, RGAIN, &red); ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, GGAIN, &green); - *r_gain_db = 20.0 * (fast_log(red) / fast_log(10.0)); - *g_gain_db = 20.0 * (fast_log(green) / fast_log(10.0)); - *b_gain_db = 20.0 * (fast_log(blue) / fast_log(10.0)); + *r_gain_db = 20.0f * log10f(red); + *g_gain_db = 20.0f * log10f(green); + *b_gain_db = 20.0f * log10f(blue); return ret; } diff --git a/src/omv/sensors/ov7725.c b/src/omv/sensors/ov7725.c index 27ff3820a..ef808aa5e 100644 --- a/src/omv/sensors/ov7725.c +++ b/src/omv/sensors/ov7725.c @@ -163,7 +163,9 @@ static int reset(sensor_t *sensor) { } // Delay 300 ms - mp_hal_delay_ms(300); + if (!sensor->disable_delays) { + mp_hal_delay_ms(300); + } return ret; } @@ -346,20 +348,20 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM8, COM8_SET_AGC(reg, (enable != 0))); if ((enable == 0) && (!isnanf(gain_db)) && (!isinff(gain_db))) { - float gain = IM_MAX(IM_MIN(fast_expf((gain_db / 20.0) * fast_log(10.0)), 32.0), 1.0); + float gain = IM_MAX(IM_MIN(expf((gain_db / 20.0f) * M_LN10), 32.0f), 1.0f); - int gain_temp = fast_roundf(fast_log2(IM_MAX(gain / 2.0, 1.0))); + int gain_temp = fast_ceilf(logf(IM_MAX(gain / 2.0f, 1.0f)) / M_LN2); int gain_hi = 0xF >> (4 - gain_temp); - int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0) * 16.0), 15); + int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0f) * 16.0f), 15); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, GAIN, (gain_hi << 4) | (gain_lo << 0)); } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinff(gain_db_ceiling))) { - float gain_ceiling = IM_MAX(IM_MIN(fast_expf((gain_db_ceiling / 20.0) * fast_log(10.0)), 32.0), 2.0); + float gain_ceiling = IM_MAX(IM_MIN(expf((gain_db_ceiling / 20.0f) * M_LN10), 32.0f), 2.0f); ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, COM9, ®); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM9, - (reg & 0x8F) | ((fast_ceilf(fast_log2(gain_ceiling)) - 1) << 4)); + (reg & 0x8F) | ((fast_ceilf(logf(gain_ceiling) / M_LN2) - 1) << 4)); } return ret; @@ -384,8 +386,8 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { // DISABLED int hi_gain = 1 << (((gain >> 7) & 1) + ((gain >> 6) & 1) + ((gain >> 5) & 1) + ((gain >> 4) & 1)); - float lo_gain = 1.0 + (((gain >> 0) & 0xF) / 16.0); - *gain_db = 20.0 * (fast_log(hi_gain * lo_gain) / fast_log(10.0)); + float lo_gain = 1.0f + (((gain >> 0) & 0xF) / 16.0f); + *gain_db = 20.0f * log10f(hi_gain * lo_gain); return ret; } @@ -499,11 +501,11 @@ static int set_auto_whitebal(sensor_t *sensor, int enable, float r_gain_db, floa if ((enable == 0) && (!isnanf(r_gain_db)) && (!isnanf(g_gain_db)) && (!isnanf(b_gain_db)) && (!isinff(r_gain_db)) && (!isinff(g_gain_db)) && (!isinff(b_gain_db))) { ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, AWB_CTRL1, ®); - float gain_div = (reg & 0x2) ? 64.0 : 128.0; + float gain_div = (reg & 0x2) ? 64.0f : 128.0f; - int r_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((r_gain_db / 20.0) * fast_log(10.0)) * gain_div), 255), 0); - int g_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((g_gain_db / 20.0) * fast_log(10.0)) * gain_div), 255), 0); - int b_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((b_gain_db / 20.0) * fast_log(10.0)) * gain_div), 255), 0); + int r_gain = IM_MAX(IM_MIN(fast_roundf(expf((r_gain_db / 20.0f) * M_LN10) * gain_div), 255), 0); + int g_gain = IM_MAX(IM_MIN(fast_roundf(expf((g_gain_db / 20.0f) * M_LN10) * gain_div), 255), 0); + int b_gain = IM_MAX(IM_MIN(fast_roundf(expf((b_gain_db / 20.0f) * M_LN10) * gain_div), 255), 0); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, BLUE, b_gain); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, RED, r_gain); @@ -534,11 +536,37 @@ static int get_rgb_gain_db(sensor_t *sensor, float *r_gain_db, float *g_gain_db, // DISABLED ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, AWB_CTRL1, ®); - float gain_div = (reg & 0x2) ? 64.0 : 128.0; + float gain_div = (reg & 0x2) ? 64.0f : 128.0f; - *r_gain_db = 20.0 * (fast_log(red / gain_div) / fast_log(10.0)); - *g_gain_db = 20.0 * (fast_log(green / gain_div) / fast_log(10.0)); - *b_gain_db = 20.0 * (fast_log(blue / gain_div) / fast_log(10.0)); + *r_gain_db = 20.0f * log10f(red / gain_div); + *g_gain_db = 20.0f * log10f(green / gain_div); + *b_gain_db = 20.0f * log10f(blue / gain_div); + + return ret; +} + +static int set_auto_blc(sensor_t *sensor, int enable, int *regs) { + uint8_t reg; + int ret = omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, COM13, ®); + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM13, COM13_SET_BLC(reg, (enable != 0))); + + if ((enable == 0) && (regs != NULL)) { + for (uint32_t i = 0; i < sensor->hw_flags.blc_size; i++) { + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, ADOFF_B + i, regs[i]); + } + } + + return ret; +} + +static int get_blc_regs(sensor_t *sensor, int *regs) { + int ret = 0; + + for (uint32_t i = 0; i < sensor->hw_flags.blc_size; i++) { + uint8_t reg; + ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, ADOFF_B + i, ®); + regs[i] = reg; + } return ret; } @@ -590,6 +618,38 @@ static int set_lens_correction(sensor_t *sensor, int enable, int radi, int coef) return ret; } +static int ioctl(sensor_t *sensor, int request, va_list ap) { + int ret = 0; + uint8_t reg; + + switch (request) { + case IOCTL_SET_NIGHT_MODE: { + int enable = va_arg(ap, int); + ret = omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, COM5, ®); + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, COM5, COM5_SET_AFR(reg, (enable != 0))); + if (enable == 0) { + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, ADVFL, 0); + ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, ADVFH, 0); + } + break; + } + case IOCTL_GET_NIGHT_MODE: { + int *enable = va_arg(ap, int *); + ret = omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, COM5, ®); + if (ret >= 0) { + *enable = reg & COM5_AFR; + } + break; + } + default: { + ret = -1; + break; + } + } + + return ret; +} + int ov7725_init(sensor_t *sensor) { // Initialize sensor structure. sensor->reset = reset; @@ -609,10 +669,13 @@ int ov7725_init(sensor_t *sensor) { sensor->get_exposure_us = get_exposure_us; sensor->set_auto_whitebal = set_auto_whitebal; sensor->get_rgb_gain_db = get_rgb_gain_db; + sensor->set_auto_blc = set_auto_blc; + sensor->get_blc_regs = get_blc_regs; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; sensor->set_special_effect = set_special_effect; sensor->set_lens_correction = set_lens_correction; + sensor->ioctl = ioctl; // Set sensor flags sensor->hw_flags.vsync = 1; @@ -623,6 +686,7 @@ int ov7725_init(sensor_t *sensor) { sensor->hw_flags.gs_bpp = 2; sensor->hw_flags.rgb_swap = 1; sensor->hw_flags.yuv_order = SENSOR_HW_FLAGS_YVU422; + sensor->hw_flags.blc_size = 8; return 0; } diff --git a/src/omv/sensors/ov7725_regs.h b/src/omv/sensors/ov7725_regs.h index 550127d54..dea8ee1c9 100644 --- a/src/omv/sensors/ov7725_regs.h +++ b/src/omv/sensors/ov7725_regs.h @@ -68,6 +68,7 @@ #define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */ #define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */ #define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */ +#define COM5_SET_AFR(r, x) ((r & 0x7F) | ((x & 0x1) << 7)) #define COM6 0x0F /* Common Control 6 */ #define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */ @@ -184,6 +185,7 @@ #define COM13_ADC_EN 0x40 /* ADC channel BLC ON/OFF control */ #define COM13_ANALOG_BLC 0x20 /* Analog processing channel BLC ON/OFF control */ #define COM13_ABLC_GAIN_EN 0x04 /* ABLC gain trigger enable */ +#define COM13_SET_BLC(r, x) ((r & 0x7F) | ((x & 0x1) << 7)) #define COM14 0x3F /* Common Control 14 */ #define COM15 0x40 /* Common Control 15 */ diff --git a/src/omv/sensors/ov9650.c b/src/omv/sensors/ov9650.c index a1021f660..a08f03d88 100644 --- a/src/omv/sensors/ov9650.c +++ b/src/omv/sensors/ov9650.c @@ -331,22 +331,22 @@ static int set_auto_gain(sensor_t *sensor, int enable, float gain_db, float gain (reg & (~REG_COM8_AGC)) | ((enable != 0) ? REG_COM8_AGC : 0)); if ((enable == 0) && (!isnanf(gain_db)) && (!isinf(gain_db))) { - float gain = IM_MAX(IM_MIN(fast_expf((gain_db / 20.0) * fast_log(10.0)), 128.0), 1.0); + float gain = IM_MAX(IM_MIN(expf((gain_db / 20.0f) * M_LN10), 128.0f), 1.0f); - int gain_temp = fast_roundf(fast_log2(IM_MAX(gain / 2.0, 1.0))); + int gain_temp = fast_ceilf(logf(IM_MAX(gain / 2.0f, 1.0f)) / M_LN2); int gain_hi = 0x3F >> (6 - gain_temp); - int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0) * 16.0), 15); + int gain_lo = IM_MIN(fast_roundf(((gain / (1 << gain_temp)) - 1.0f) * 16.0f), 15); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG_GAIN, ((gain_hi & 0x0F) << 4) | (gain_lo << 0)); ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, REG_VREF, ®); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG_VREF, ((gain_hi & 0x30) << 2) | (reg & 0x3F)); } else if ((enable != 0) && (!isnanf(gain_db_ceiling)) && (!isinf(gain_db_ceiling))) { - float gain_ceiling = IM_MAX(IM_MIN(fast_expf((gain_db_ceiling / 20.0) * fast_log(10.0)), 128.0), 2.0); + float gain_ceiling = IM_MAX(IM_MIN(expf((gain_db_ceiling / 20.0f) * M_LN10), 128.0f), 2.0f); ret |= omv_i2c_readb(&sensor->i2c_bus, sensor->slv_addr, REG_COM9, ®); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG_COM9, - (reg & 0x8F) | ((fast_ceilf(fast_log2(gain_ceiling)) - 1) << 4)); + (reg & 0x8F) | ((fast_ceilf(logf(gain_ceiling) / M_LN2) - 1) << 4)); } return ret; @@ -376,8 +376,8 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) { (((gain >> 9) & 1) + ((gain >> 8) & 1) + ((gain >> 7) & 1) + ((gain >> 6) & 1) + ((gain >> 5) & 1) + ((gain >> 4) & 1)); - float lo_gain = 1.0 + (((gain >> 0) & 0xF) / 16.0); - *gain_db = 20.0 * (fast_log(hi_gain * lo_gain) / fast_log(10.0)); + float lo_gain = 1.0f + (((gain >> 0) & 0xF) / 16.0f); + *gain_db = 20.0f * log10f(hi_gain * lo_gain); return ret; } @@ -486,8 +486,8 @@ static int set_auto_whitebal(sensor_t *sensor, int enable, float r_gain_db, floa if ((enable == 0) && (!isnanf(r_gain_db)) && (!isnanf(g_gain_db)) && (!isnanf(b_gain_db)) && (!isinff(r_gain_db)) && (!isinff(g_gain_db)) && (!isinff(b_gain_db))) { - int r_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((r_gain_db / 20.0) * fast_log(10.0)) * 128.0), 255), 0); - int b_gain = IM_MAX(IM_MIN(fast_roundf(fast_expf((b_gain_db / 20.0) * fast_log(10.0)) * 128.0), 255), 0); + int r_gain = IM_MAX(IM_MIN(fast_roundf(expf((r_gain_db / 20.0f) * M_LN10) * 128.0f), 255), 0); + int b_gain = IM_MAX(IM_MIN(fast_roundf(expf((b_gain_db / 20.0f) * M_LN10) * 128.0f), 255), 0); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG_BLUE, b_gain); ret |= omv_i2c_writeb(&sensor->i2c_bus, sensor->slv_addr, REG_RED, r_gain); @@ -515,8 +515,8 @@ static int get_rgb_gain_db(sensor_t *sensor, float *r_gain_db, float *g_gain_db, // } // DISABLED - *r_gain_db = 20.0 * (fast_log(red / 128.0) / fast_log(10.0)); - *b_gain_db = 20.0 * (fast_log(blue / 128.0) / fast_log(10.0)); + *r_gain_db = 20.0f * log10f(red / 128.0f); + *b_gain_db = 20.0f * log10f(blue / 128.0f); return ret; } diff --git a/src/omv/sensors/paj6100.c b/src/omv/sensors/paj6100.c index 5eb384ea1..3e75c0ae5 100644 --- a/src/omv/sensors/paj6100.c +++ b/src/omv/sensors/paj6100.c @@ -314,7 +314,7 @@ static void auto_exposure(sensor_t *sensor) { gain_target = 8; } //gain_target = IM_MIN(IM_MAX(fast_ceilf(GEP_target/expo_target), R_AE_MinGain), R_AE_MaxGain); - gain_db = 20 * (fast_log((float) gain_target) / fast_log(10.0)); + gain_db = 20 * log10f((float) gain_target); expo_target = IM_MIN(IM_MAX(fast_roundf(GEP_target / gain_target), R_AE_MinExpoTime), R_AE_MaxExpoTime); #ifdef DEBUG_AE printf("Gain Target: %ld (%d DB (x1000))\n", gain_target, (int) (gain_db * 1000));