diff --git a/src/omv/Makefile b/src/omv/Makefile index 0a17fb307..2d43e9450 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -23,6 +23,7 @@ SRCS += $(addprefix common/, \ trace.c \ mutex.c \ usbdbg.c \ + sensor_utils.c \ ) SRCS += $(addprefix sensors/, \ diff --git a/src/omv/common/sensor.h b/src/omv/common/sensor.h index 29d9d546a..8578c059a 100644 --- a/src/omv/common/sensor.h +++ b/src/omv/common/sensor.h @@ -223,6 +223,8 @@ typedef struct _sensor { int (*snapshot) (sensor_t *sensor, image_t *image, uint32_t flags); } sensor_t; +extern sensor_t sensor; + // Resolution table extern const int resolution[][2]; @@ -232,6 +234,9 @@ void sensor_init0(); // Initialize the sensor hardware and probe the image sensor. int sensor_init(); +// Detect and initialize the image sensor. +int sensor_probe_init(); + // Configure DCMI hardware interface. int sensor_dcmi_config(uint32_t pixformat); @@ -274,6 +279,15 @@ int sensor_set_framesize(framesize_t framesize); // Set the sensor frame rate. int sensor_set_framerate(int framerate); +// Return the number of bytes per pixel to read from the image sensor. +uint32_t sensor_get_src_bpp(); + +// Return the number of bytes per pixel to write to memory. +uint32_t sensor_get_dst_bpp(); + +// Returns true if a crop is being applied to the frame buffer. +bool sensor_get_cropped(); + // Set window size. int sensor_set_windowing(int x, int y, int w, int h); @@ -362,6 +376,12 @@ int sensor_set_color_palette(const uint16_t *color_palette); // Get color palette const uint16_t *sensor_get_color_palette(); +// Return true if the current frame size/format fits in RAM. +int sensor_check_framebuffer_size(); + +// Auto-crop frame buffer until it fits in RAM (may switch pixel format to BAYER). +int sensor_auto_crop_framebuffer(); + // Default snapshot function. int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags); diff --git a/src/omv/common/sensor_utils.c b/src/omv/common/sensor_utils.c new file mode 100644 index 000000000..fc6cf1101 --- /dev/null +++ b/src/omv/common/sensor_utils.c @@ -0,0 +1,1070 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2021 Ibrahim Abdelkader + * Copyright (c) 2013-2021 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * This file contains image sensor driver utility functions and some default (weak) + * implementations of common functions that can be replaced by port-specific drivers. + */ +#if MICROPY_PY_SENSOR +#include +#include +#include +#include "py/mphal.h" +#include "cambus.h" +#include "sensor.h" +#include "ov2640.h" +#include "ov5640.h" +#include "ov7725.h" +#include "ov7670.h" +#include "ov7690.h" +#include "ov9650.h" +#include "mt9v034.h" +#include "mt9m114.h" +#include "lepton.h" +#include "hm01b0.h" +#include "paj6100.h" +#include "gc2145.h" +#include "framebuffer.h" +#include "omv_boardconfig.h" + +#if (OMV_ENABLE_PAJ6100 == 1) +#define OMV_ENABLE_NONI2CIS +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +// Sensor frame size/resolution table. +const int resolution[][2] = { + {0, 0 }, + // C/SIF Resolutions + {88, 72 }, /* QQCIF */ + {176, 144 }, /* QCIF */ + {352, 288 }, /* CIF */ + {88, 60 }, /* QQSIF */ + {176, 120 }, /* QSIF */ + {352, 240 }, /* SIF */ + // VGA Resolutions + {40, 30 }, /* QQQQVGA */ + {80, 60 }, /* QQQVGA */ + {160, 120 }, /* QQVGA */ + {320, 240 }, /* QVGA */ + {640, 480 }, /* VGA */ + {30, 20 }, /* HQQQQVGA */ + {60, 40 }, /* HQQQVGA */ + {120, 80 }, /* HQQVGA */ + {240, 160 }, /* HQVGA */ + {480, 320 }, /* HVGA */ + // FFT Resolutions + {64, 32 }, /* 64x32 */ + {64, 64 }, /* 64x64 */ + {128, 64 }, /* 128x64 */ + {128, 128 }, /* 128x128 */ + // Himax Resolutions + {160, 160 }, /* 160x160 */ + {320, 320 }, /* 320x320 */ + // Other + {128, 160 }, /* LCD */ + {128, 160 }, /* QQVGA2 */ + {720, 480 }, /* WVGA */ + {752, 480 }, /* WVGA2 */ + {800, 600 }, /* SVGA */ + {1024, 768 }, /* XGA */ + {1280, 768 }, /* WXGA */ + {1280, 1024}, /* SXGA */ + {1280, 960 }, /* SXGAM */ + {1600, 1200}, /* UXGA */ + {1280, 720 }, /* HD */ + {1920, 1080}, /* FHD */ + {2560, 1440}, /* QHD */ + {2048, 1536}, /* QXGA */ + {2560, 1600}, /* WQXGA */ + {2592, 1944}, /* WQXGA2 */ +}; + +__weak void sensor_init0() +{ + // Reset the sesnor state + memset(&sensor, 0, sizeof(sensor_t)); +} + +__weak int sensor_init() +{ + // Reset the sesnor state + memset(&sensor, 0, sizeof(sensor_t)); + return -1; +} + +__weak int sensor_abort() +{ + return -1; +} + +__weak int sensor_reset() +{ + sensor_abort(); + + // Reset the sensor state + sensor.sde = 0; + sensor.pixformat = 0; + sensor.framesize = 0; + sensor.framerate = 0; + sensor.last_frame_ms = 0; + sensor.last_frame_ms_valid = false; + sensor.gainceiling = 0; + sensor.hmirror = false; + sensor.vflip = false; + sensor.transpose = false; + #if MICROPY_PY_IMU + sensor.auto_rotation = (sensor.chip_id == OV7690_ID); + #else + sensor.auto_rotation = false; + #endif // MICROPY_PY_IMU + sensor.vsync_callback = NULL; + sensor.frame_callback = NULL; + + // Reset default color palette. + sensor.color_palette = rainbow_table; + + sensor.disable_full_flush = false; + + // Restore shutdown state on reset. + sensor_shutdown(false); + + // Hard-reset the sensor + if (sensor.reset_pol == ACTIVE_HIGH) { + DCMI_RESET_HIGH(); + mp_hal_delay_ms(10); + DCMI_RESET_LOW(); + } else { + DCMI_RESET_LOW(); + mp_hal_delay_ms(10); + DCMI_RESET_HIGH(); + } + mp_hal_delay_ms(20); + + // Call sensor-specific reset function + if (sensor.reset(&sensor) != 0) { + return -1; + } + + // Reset framebuffers + framebuffer_reset_buffers(); + return 0; +} + +int sensor_probe_init() +{ + int init_ret = 0; + + // Do a power cycle + DCMI_PWDN_HIGH(); + mp_hal_delay_ms(10); + + DCMI_PWDN_LOW(); + mp_hal_delay_ms(10); + + /* Some sensors have different reset polarities, and we can't know which sensor + is connected before initializing cambus and probing the sensor, which in turn + requires pulling the sensor out of the reset state. So we try to probe the + sensor with both polarities to determine line state. */ + sensor.pwdn_pol = ACTIVE_HIGH; + sensor.reset_pol = ACTIVE_HIGH; + + // Reset the sensor + DCMI_RESET_HIGH(); + mp_hal_delay_ms(10); + + DCMI_RESET_LOW(); + mp_hal_delay_ms(10); + + // Initialize the camera bus. + cambus_init(&sensor.bus, ISC_I2C_ID, ISC_I2C_SPEED); + mp_hal_delay_ms(10); + + // Probe the sensor + sensor.slv_addr = cambus_scan(&sensor.bus); + if (sensor.slv_addr == 0) { + /* Sensor has been held in reset, + so the reset line is active low */ + sensor.reset_pol = ACTIVE_LOW; + + // Pull the sensor out of the reset state. + DCMI_RESET_HIGH(); + mp_hal_delay_ms(10); + + // Probe again to set the slave addr. + sensor.slv_addr = cambus_scan(&sensor.bus); + if (sensor.slv_addr == 0) { + sensor.pwdn_pol = ACTIVE_LOW; + + DCMI_PWDN_HIGH(); + mp_hal_delay_ms(10); + + sensor.slv_addr = cambus_scan(&sensor.bus); + if (sensor.slv_addr == 0) { + sensor.reset_pol = ACTIVE_HIGH; + + DCMI_RESET_LOW(); + mp_hal_delay_ms(10); + + sensor.slv_addr = cambus_scan(&sensor.bus); + #ifndef OMV_ENABLE_NONI2CIS + if (sensor.slv_addr == 0) { + return -2; + } + #endif + } + } + } + + switch (sensor.slv_addr) { + #if (OMV_ENABLE_OV2640 == 1) + case OV2640_SLV_ADDR: // Or OV9650. + cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); + break; + #endif // (OMV_ENABLE_OV2640 == 1) + + #if (OMV_ENABLE_OV5640 == 1) + case OV5640_SLV_ADDR: + cambus_readb2(&sensor.bus, sensor.slv_addr, OV5640_CHIP_ID, &sensor.chip_id); + break; + #endif // (OMV_ENABLE_OV5640 == 1) + + #if (OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) + case OV7725_SLV_ADDR: // Or OV7690 or OV7670. + cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); + break; + #endif //(OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) + + #if (OMV_ENABLE_MT9V034 == 1) + case MT9V034_SLV_ADDR: + cambus_readb(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id); + break; + #endif //(OMV_ENABLE_MT9V034 == 1) + + #if (OMV_ENABLE_MT9M114 == 1) + case MT9M114_SLV_ADDR: + cambus_readw2(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id_w); + break; + #endif // (OMV_ENABLE_MT9M114 == 1) + + #if (OMV_ENABLE_LEPTON == 1) + case LEPTON_SLV_ADDR: + sensor.chip_id = LEPTON_ID; + break; + #endif // (OMV_ENABLE_LEPTON == 1) + + #if (OMV_ENABLE_HM01B0 == 1) + case HM01B0_SLV_ADDR: + cambus_readb2(&sensor.bus, sensor.slv_addr, HIMAX_CHIP_ID, &sensor.chip_id); + break; + #endif //(OMV_ENABLE_HM01B0 == 1) + + #if (OMV_ENABLE_GC2145 == 1) + case GC2145_SLV_ADDR: + cambus_readb(&sensor.bus, sensor.slv_addr, GC_CHIP_ID, &sensor.chip_id); + break; + #endif //(OMV_ENABLE_GC2145 == 1) + + #if (OMV_ENABLE_PAJ6100 == 1) + case 0: + if (paj6100_detect(&sensor)) { + // Found PixArt PAJ6100 + sensor.chip_id_w = PAJ6100_ID; + sensor.pwdn_pol = ACTIVE_LOW; + sensor.reset_pol = ACTIVE_LOW; + break; + } + // No sensors detected. + return -2; + #endif + + default: + return -3; + break; + } + + switch (sensor.chip_id_w) { + #if (OMV_ENABLE_OV2640 == 1) + case OV2640_ID: + if (sensor_set_xclk_frequency(OV2640_XCLK_FREQ) != 0) { + return -3; + } + init_ret = ov2640_init(&sensor); + break; + #endif // (OMV_ENABLE_OV2640 == 1) + + #if (OMV_ENABLE_OV5640 == 1) + case OV5640_ID: + if (sensor_set_xclk_frequency(OV5640_XCLK_FREQ) != 0) { + return -3; + } + init_ret = ov5640_init(&sensor); + break; + #endif // (OMV_ENABLE_OV5640 == 1) + + #if (OMV_ENABLE_OV7670 == 1) + case OV7670_ID: + if (sensor_set_xclk_frequency(OV7670_XCLK_FREQ) != 0) { + return -3; + } + init_ret = ov7670_init(&sensor); + break; + #endif // (OMV_ENABLE_OV7670 == 1) + + #if (OMV_ENABLE_OV7690 == 1) + case OV7690_ID: + if (sensor_set_xclk_frequency(OV7690_XCLK_FREQ) != 0) { + return -3; + } + init_ret = ov7690_init(&sensor); + break; + #endif // (OMV_ENABLE_OV7690 == 1) + + #if (OMV_ENABLE_OV7725 == 1) + case OV7725_ID: + init_ret = ov7725_init(&sensor); + break; + #endif // (OMV_ENABLE_OV7725 == 1) + + #if (OMV_ENABLE_OV9650 == 1) + case OV9650_ID: + init_ret = ov9650_init(&sensor); + break; + #endif // (OMV_ENABLE_OV9650 == 1) + + #if (OMV_ENABLE_MT9V034 == 1) + case MT9V034_ID: + if (sensor_set_xclk_frequency(MT9V034_XCLK_FREQ) != 0) { + return -3; + } + init_ret = mt9v034_init(&sensor); + break; + #endif //(OMV_ENABLE_MT9V034 == 1) + + #if (OMV_ENABLE_MT9M114 == 1) + case MT9M114_ID: + if (sensor_set_xclk_frequency(MT9M114_XCLK_FREQ) != 0) { + return -3; + } + init_ret = mt9m114_init(&sensor); + break; + #endif //(OMV_ENABLE_MT9M114 == 1) + + #if (OMV_ENABLE_LEPTON == 1) + case LEPTON_ID: + if (sensor_set_xclk_frequency(LEPTON_XCLK_FREQ) != 0) { + return -3; + } + init_ret = lepton_init(&sensor); + break; + #endif // (OMV_ENABLE_LEPTON == 1) + + #if (OMV_ENABLE_HM01B0 == 1) + case HM01B0_ID: + init_ret = hm01b0_init(&sensor); + break; + #endif //(OMV_ENABLE_HM01B0 == 1) + + #if (OMV_ENABLE_GC2145 == 1) + case GC2145_ID: + if (sensor_set_xclk_frequency(GC2145_XCLK_FREQ) != 0) { + return -3; + } + init_ret = gc2145_init(&sensor); + break; + #endif //(OMV_ENABLE_GC2145 == 1) + + #if (OMV_ENABLE_PAJ6100 == 1) + case PAJ6100_ID: + if (sensor_set_xclk_frequency(PAJ6100_XCLK_FREQ) != 0) { + return -3; + } + init_ret = paj6100_init(&sensor); + break; + #endif // (OMV_ENABLE_PAJ6100 == 1) + + default: + return -3; + break; + } + + if (init_ret != 0 ) { + // Sensor init failed. + return -4; + } + + return 0; +} + +__weak int sensor_get_id() +{ + return sensor.chip_id_w; +} + +__weak uint32_t sensor_get_xclk_frequency() +{ + return 0; +} + +__weak int sensor_set_xclk_frequency(uint32_t frequency) +{ + return -1; +} + +__weak bool sensor_is_detected() +{ + return sensor.detected; +} + +__weak int sensor_sleep(int enable) +{ + sensor_abort(); + + if (sensor.sleep == NULL + || sensor.sleep(&sensor, enable) != 0) { + // Operation not supported + return -1; + } + return 0; +} + +__weak int sensor_shutdown(int enable) +{ + int ret = 0; + sensor_abort(); + + if (enable) { + if (sensor.pwdn_pol == ACTIVE_HIGH) { + DCMI_PWDN_HIGH(); + } else { + DCMI_PWDN_LOW(); + } + } else { + if (sensor.pwdn_pol == ACTIVE_HIGH) { + DCMI_PWDN_LOW(); + } else { + DCMI_PWDN_HIGH(); + } + } + + mp_hal_delay_ms(10); + return ret; +} + +__weak int sensor_read_reg(uint16_t reg_addr) +{ + if (sensor.read_reg == NULL) { + // Operation not supported + return -1; + } + return sensor.read_reg(&sensor, reg_addr); +} + +__weak int sensor_write_reg(uint16_t reg_addr, uint16_t reg_data) +{ + if (sensor.write_reg == NULL) { + // Operation not supported + return -1; + } + return sensor.write_reg(&sensor, reg_addr, reg_data); +} + +__weak int sensor_set_pixformat(pixformat_t pixformat) +{ + if (sensor.pixformat == pixformat) { + // No change + return 0; + } + + // Some sensor drivers automatically switch to BAYER to reduce the frame size if it does not fit in RAM. + // If the current format is BAYER (1BPP), and the target format is RGB-565 (2BPP) and the frame does not + // fit in RAM it will just be switched back again to BAYER, so we keep the current format unchanged. + uint32_t size = framebuffer_get_buffer_size(); + if ((sensor.pixformat == PIXFORMAT_BAYER) + && (pixformat == PIXFORMAT_RGB565) + && (MAIN_FB()->u * MAIN_FB()->v * 2 > size) + && (MAIN_FB()->u * MAIN_FB()->v * 1 <= size)) { + // No change + return 0; + } + + // Cropping and transposing (and thus auto rotation) don't work in JPEG mode. + if ((pixformat == PIXFORMAT_JPEG) + && (sensor_get_cropped() || sensor.transpose || sensor.auto_rotation)) { + return -1; + } + + sensor_abort(); + + // Flush previous frame. + framebuffer_update_jpeg_buffer(); + + if (sensor.set_pixformat == NULL + || sensor.set_pixformat(&sensor, pixformat) != 0) { + // Operation not supported + return -1; + } + + mp_hal_delay_ms(100); // wait for the camera to settle + + // Set pixel format + sensor.pixformat = pixformat; + + // Skip the first frame. + MAIN_FB()->bpp = -1; + + // Pickout a good buffer count for the user. + framebuffer_auto_adjust_buffers(); + + // Reconfigure the DCMI if needed. + return sensor_dcmi_config(pixformat); +} + +__weak int sensor_set_framesize(framesize_t framesize) +{ + if (sensor.framesize == framesize) { + // No change + return 0; + } + + sensor_abort(); + + // Flush previous frame. + framebuffer_update_jpeg_buffer(); + + // Call the sensor specific function + if (sensor.set_framesize == NULL + || sensor.set_framesize(&sensor, framesize) != 0) { + // Operation not supported + return -1; + } + + mp_hal_delay_ms(100); // wait for the camera to settle + + // Set framebuffer size + sensor.framesize = framesize; + + // Skip the first frame. + MAIN_FB()->bpp = -1; + + // Set MAIN FB x offset, y offset, width, height, backup width, and backup height. + MAIN_FB()->x = 0; + MAIN_FB()->y = 0; + MAIN_FB()->w = MAIN_FB()->u = resolution[framesize][0]; + MAIN_FB()->h = MAIN_FB()->v = resolution[framesize][1]; + + // Pickout a good buffer count for the user. + framebuffer_auto_adjust_buffers(); + + return 0; +} + +__weak int sensor_set_framerate(int framerate) +{ + if (sensor.framerate == framerate) { + // No change + return 0; + } + + if (framerate < 0) { + return -1; + } + + // Call the sensor specific function (does not fail if function is not set) + if (sensor.set_framerate != NULL) { + if (sensor.set_framerate(&sensor, framerate) != 0) { + // Operation not supported + return -1; + } + } + + // Set framerate + sensor.framerate = framerate; + return 0; +} + +__weak bool sensor_get_cropped() +{ + if (sensor.framesize != FRAMESIZE_INVALID) { + return (MAIN_FB()->x != 0) // should be zero if not cropped. + || (MAIN_FB()->y != 0) // should be zero if not cropped. + || (MAIN_FB()->u != resolution[sensor.framesize][0]) // should be equal to the resolution if not cropped. + || (MAIN_FB()->v != resolution[sensor.framesize][1]); // should be equal to the resolution if not cropped. + } + return false; +} + + +__weak uint32_t sensor_get_src_bpp() +{ + switch (sensor.pixformat) { + case PIXFORMAT_GRAYSCALE: + return sensor.gs_bpp; + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV422: + return 2; + case PIXFORMAT_BAYER: + case PIXFORMAT_JPEG: + return 1; + default: + return 0; + } +} + +__weak uint32_t sensor_get_dst_bpp() +{ + switch (sensor.pixformat) { + case PIXFORMAT_GRAYSCALE: + case PIXFORMAT_BAYER: + return 1; + case PIXFORMAT_RGB565: + case PIXFORMAT_YUV422: + return 2; + default: + return 0; + } +} + +__weak int sensor_set_windowing(int x, int y, int w, int h) +{ + if ((MAIN_FB()->x == x) && (MAIN_FB()->y == y) && + (MAIN_FB()->u == w) && (MAIN_FB()->v == h)) { + // No change + return 0; + } + + if (sensor.pixformat == PIXFORMAT_JPEG) { + return -1; + } + + sensor_abort(); + + // Flush previous frame. + framebuffer_update_jpeg_buffer(); + + // Skip the first frame. + MAIN_FB()->bpp = -1; + + MAIN_FB()->x = x; + MAIN_FB()->y = y; + MAIN_FB()->w = MAIN_FB()->u = w; + MAIN_FB()->h = MAIN_FB()->v = h; + + // Pickout a good buffer count for the user. + framebuffer_auto_adjust_buffers(); + + return 0; +} + +__weak int sensor_set_contrast(int level) +{ + if (sensor.set_contrast != NULL) { + return sensor.set_contrast(&sensor, level); + } + return -1; +} + +__weak int sensor_set_brightness(int level) +{ + if (sensor.set_brightness != NULL) { + return sensor.set_brightness(&sensor, level); + } + return -1; +} + +__weak int sensor_set_saturation(int level) +{ + if (sensor.set_saturation != NULL) { + return sensor.set_saturation(&sensor, level); + } + return -1; +} + +__weak int sensor_set_gainceiling(gainceiling_t gainceiling) +{ + if (sensor.gainceiling == gainceiling) { + /* no change */ + return 0; + } + + /* call the sensor specific function */ + if (sensor.set_gainceiling == NULL + || sensor.set_gainceiling(&sensor, gainceiling) != 0) { + /* operation not supported */ + return -1; + } + + sensor.gainceiling = gainceiling; + return 0; +} + +__weak int sensor_set_quality(int qs) +{ + /* call the sensor specific function */ + if (sensor.set_quality == NULL + || sensor.set_quality(&sensor, qs) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_set_colorbar(int enable) +{ + /* call the sensor specific function */ + if (sensor.set_colorbar == NULL + || sensor.set_colorbar(&sensor, enable) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_set_auto_gain(int enable, float gain_db, float gain_db_ceiling) +{ + /* call the sensor specific function */ + if (sensor.set_auto_gain == NULL + || sensor.set_auto_gain(&sensor, enable, gain_db, gain_db_ceiling) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_get_gain_db(float *gain_db) +{ + /* call the sensor specific function */ + if (sensor.get_gain_db == NULL + || sensor.get_gain_db(&sensor, gain_db) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_set_auto_exposure(int enable, int exposure_us) +{ + /* call the sensor specific function */ + if (sensor.set_auto_exposure == NULL + || sensor.set_auto_exposure(&sensor, enable, exposure_us) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_get_exposure_us(int *exposure_us) +{ + /* call the sensor specific function */ + if (sensor.get_exposure_us == NULL + || sensor.get_exposure_us(&sensor, exposure_us) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db) +{ + /* call the sensor specific function */ + if (sensor.set_auto_whitebal == NULL + || sensor.set_auto_whitebal(&sensor, enable, r_gain_db, g_gain_db, b_gain_db) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_gain_db) +{ + /* call the sensor specific function */ + if (sensor.get_rgb_gain_db == NULL + || sensor.get_rgb_gain_db(&sensor, r_gain_db, g_gain_db, b_gain_db) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +__weak int sensor_set_hmirror(int enable) +{ + if (sensor.hmirror == ((bool) enable)) { + /* no change */ + return 0; + } + + sensor_abort(); + + /* call the sensor specific function */ + if (sensor.set_hmirror == NULL + || sensor.set_hmirror(&sensor, enable) != 0) { + /* operation not supported */ + return -1; + } + sensor.hmirror = enable; + mp_hal_delay_ms(100); // wait for the camera to settle + return 0; +} + +__weak bool sensor_get_hmirror() +{ + return sensor.hmirror; +} + +__weak int sensor_set_vflip(int enable) +{ + if (sensor.vflip == ((bool) enable)) { + /* no change */ + return 0; + } + + sensor_abort(); + + /* call the sensor specific function */ + if (sensor.set_vflip == NULL + || sensor.set_vflip(&sensor, enable) != 0) { + /* operation not supported */ + return -1; + } + sensor.vflip = enable; + mp_hal_delay_ms(100); // wait for the camera to settle + return 0; +} + +__weak bool sensor_get_vflip() +{ + return sensor.vflip; +} + +__weak int sensor_set_transpose(bool enable) +{ + if (sensor.transpose == enable) { + /* no change */ + return 0; + } + + if (sensor.pixformat == PIXFORMAT_JPEG) { + return -1; + } + + sensor_abort(); + + sensor.transpose = enable; + return 0; +} + +__weak bool sensor_get_transpose() +{ + return sensor.transpose; +} + +__weak int sensor_set_auto_rotation(bool enable) +{ + if (sensor.auto_rotation == enable) { + /* no change */ + return 0; + } + + if (sensor.pixformat == PIXFORMAT_JPEG) { + return -1; + } + + sensor_abort(); + + sensor.auto_rotation = enable; + return 0; +} + +__weak bool sensor_get_auto_rotation() +{ + return sensor.auto_rotation; +} + +__weak int sensor_set_framebuffers(int count) +{ + sensor_abort(); + + // Flush previous frame. + framebuffer_update_jpeg_buffer(); + + return framebuffer_set_buffers(count); +} + +__weak int sensor_set_special_effect(sde_t sde) +{ + if (sensor.sde == sde) { + /* no change */ + return 0; + } + + /* call the sensor specific function */ + if (sensor.set_special_effect == NULL + || sensor.set_special_effect(&sensor, sde) != 0) { + /* operation not supported */ + return -1; + } + + sensor.sde = sde; + return 0; +} + +__weak int sensor_set_lens_correction(int enable, int radi, int coef) +{ + /* call the sensor specific function */ + if (sensor.set_lens_correction == NULL + || sensor.set_lens_correction(&sensor, enable, radi, coef) != 0) { + /* operation not supported */ + return -1; + } + + return 0; +} + +__weak int sensor_ioctl(int request, ... /* arg */) +{ + int ret = -1; + + sensor_abort(); + + if (sensor.ioctl != NULL) { + va_list ap; + va_start(ap, request); + /* call the sensor specific function */ + ret = sensor.ioctl(&sensor, request, ap); + va_end(ap); + } + + return ret; +} + +__weak int sensor_set_vsync_callback(vsync_cb_t vsync_cb) +{ + sensor.vsync_callback = vsync_cb; + return 0; +} + +__weak int sensor_set_frame_callback(frame_cb_t vsync_cb) +{ + sensor.frame_callback = vsync_cb; + return 0; +} + +__weak int sensor_set_color_palette(const uint16_t *color_palette) +{ + sensor.color_palette = color_palette; + return 0; +} + +__weak const uint16_t *sensor_get_color_palette() +{ + return sensor.color_palette; +} + +__weak int sensor_check_framebuffer_size() +{ + uint32_t bpp = sensor_get_dst_bpp(); + uint32_t size = framebuffer_get_buffer_size(); + return (((MAIN_FB()->u * MAIN_FB()->v * bpp) <= size) ? 0 : -1); +} + +__weak int sensor_auto_crop_framebuffer() +{ + uint32_t bpp = sensor_get_dst_bpp(); + uint32_t size = framebuffer_get_buffer_size(); + + // If the pixformat is NULL/JPEG there we can't do anything to check if it fits before hand. + if (!bpp) { + return 0; + } + + // MAIN_FB() fits, we are done. + if ((MAIN_FB()->u * MAIN_FB()->v * bpp) <= size) { + return 0; + } + + if (sensor.pixformat == PIXFORMAT_RGB565) { + // Switch to bayer for the quick 2x savings. + sensor_set_pixformat(PIXFORMAT_BAYER); + bpp = 1; + + // MAIN_FB() fits, we are done (bpp is 1). + if ((MAIN_FB()->u * MAIN_FB()->v) <= size) { + return 0; + } + } + + int window_w = MAIN_FB()->u; + int window_h = MAIN_FB()->v; + + // We need to shrink the frame buffer. We can do this by cropping. So, we will subtract columns + // and rows from the frame buffer until it fits within the frame buffer. + int max = IM_MAX(window_w, window_h); + int min = IM_MIN(window_w, window_h); + float aspect_ratio = max / ((float) min); + float r = aspect_ratio, best_r = r; + int c = 1, best_c = c; + float best_err = FLT_MAX; + + // Find the width/height ratio that's within 1% of the aspect ratio with a loop limit. + for (int i = 100; i; i--) { + float err = fast_fabsf(r - fast_roundf(r)); + + if (err <= best_err) { + best_err = err; + best_r = r; + best_c = c; + } + + if (best_err <= 0.01f) { + break; + } + + r += aspect_ratio; + c += 1; + } + + // Select the larger geometry to map the aspect ratio to. + int u_sub, v_sub; + + if (window_w > window_h) { + u_sub = fast_roundf(best_r); + v_sub = best_c; + } else { + u_sub = best_c; + v_sub = fast_roundf(best_r); + } + + // Crop the frame buffer while keeping the aspect ratio and keeping the width/height even. + while (((MAIN_FB()->u * MAIN_FB()->v * bpp) > size) || (MAIN_FB()->u % 2) || (MAIN_FB()->v % 2)) { + MAIN_FB()->u -= u_sub; + MAIN_FB()->v -= v_sub; + } + + // Center the new window using the previous offset and keep the offset even. + MAIN_FB()->x += (window_w - MAIN_FB()->u) / 2; + MAIN_FB()->y += (window_h - MAIN_FB()->v) / 2; + + if (MAIN_FB()->x % 2) { + MAIN_FB()->x -= 1; + } + if (MAIN_FB()->y % 2) { + MAIN_FB()->y -= 1; + } + + // Pickout a good buffer count for the user. + framebuffer_auto_adjust_buffers(); + return 0; +} + +__weak int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) +{ + return -1; +} +#endif //MICROPY_PY_SENSOR diff --git a/src/omv/ports/nrf/omv_portconfig.mk b/src/omv/ports/nrf/omv_portconfig.mk index 56fc9ad5d..545593c16 100644 --- a/src/omv/ports/nrf/omv_portconfig.mk +++ b/src/omv/ports/nrf/omv_portconfig.mk @@ -106,6 +106,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/common/, \ trace.o \ mutex.o \ usbdbg.o \ + sensor_utils.o \ ) FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/sensors/, \ diff --git a/src/omv/ports/nrf/sensor.c b/src/omv/ports/nrf/sensor.c index ce69fa7f2..786a041b3 100644 --- a/src/omv/ports/nrf/sensor.c +++ b/src/omv/ports/nrf/sensor.c @@ -14,24 +14,14 @@ #include "py/mphal.h" #include "cambus.h" #include "sensor.h" -#include "ov2640.h" -#include "ov5640.h" -#include "ov7725.h" -#include "ov7690.h" -#include "ov7670.h" -#include "ov9650.h" -#include "mt9v034.h" -#include "lepton.h" -#include "hm01b0.h" #include "framebuffer.h" #include "omv_boardconfig.h" #include "unaligned_memcpy.h" #include "nrf_i2s.h" #include "hal/nrf_gpio.h" -extern void __fatal_error(const char *msg); - -sensor_t sensor = {0}; +// Sensor struct. +sensor_t sensor = {}; static uint32_t _vsyncMask; static uint32_t _hrefMask; @@ -53,52 +43,7 @@ static const volatile uint32_t *_pclkPort; #define portInputRegister(P) ((P == 0) ? &NRF_P0->IN : &NRF_P1->IN) #endif -const int resolution[][2] = { - {0, 0 }, - // C/SIF Resolutions - {88, 72 }, /* QQCIF */ - {176, 144 }, /* QCIF */ - {352, 288 }, /* CIF */ - {88, 60 }, /* QQSIF */ - {176, 120 }, /* QSIF */ - {352, 240 }, /* SIF */ - // VGA Resolutions - {40, 30 }, /* QQQQVGA */ - {80, 60 }, /* QQQVGA */ - {160, 120 }, /* QQVGA */ - {320, 240 }, /* QVGA */ - {640, 480 }, /* VGA */ - {30, 20 }, /* HQQQQVGA */ - {60, 40 }, /* HQQQVGA */ - {120, 80 }, /* HQQVGA */ - {240, 160 }, /* HQVGA */ - {480, 320 }, /* HVGA */ - // FFT Resolutions - {64, 32 }, /* 64x32 */ - {64, 64 }, /* 64x64 */ - {128, 64 }, /* 128x64 */ - {128, 128 }, /* 128x128 */ - // Himax Resolutions - {160, 160 }, /* 160x160 */ - {320, 320 }, /* 320x320 */ - // Other - {128, 160 }, /* LCD */ - {128, 160 }, /* QQVGA2 */ - {720, 480 }, /* WVGA */ - {752, 480 }, /* WVGA2 */ - {800, 600 }, /* SVGA */ - {1024, 768 }, /* XGA */ - {1280, 768 }, /* WXGA */ - {1280, 1024}, /* SXGA */ - {1280, 960 }, /* SXGAM */ - {1600, 1200}, /* UXGA */ - {1280, 720 }, /* HD */ - {1920, 1080}, /* FHD */ - {2560, 1440}, /* QHD */ - {2048, 1536}, /* QXGA */ - {2560, 1600}, /* WQXGA */ - {2592, 1944}, /* WQXGA2 */ -}; +extern void __fatal_error(const char *msg); int sensor_init() { @@ -114,226 +59,25 @@ int sensor_init() DCMI_RESET_HIGH(); #endif - /* Do a power cycle */ - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(10); - - DCMI_PWDN_LOW(); - mp_hal_delay_ms(10); - - // Configure the sensor external clock (XCLK) to XCLK_FREQ. - #if (OMV_XCLK_SOURCE == OMV_XCLK_TIM) - // Configure external clock timer. - if (sensor_set_xclk_frequency(OMV_XCLK_FREQUENCY) != 0) { - // Timer problem - return -1; - } - #elif (OMV_XCLK_SOURCE == OMV_XCLK_OSC) - // An external oscillator is used for the sensor clock. - // Nothing to do. - #else - #error "OMV_XCLK_SOURCE is not set!" - #endif - - /* Reset the sesnor state */ + // Reset the sesnor state memset(&sensor, 0, sizeof(sensor_t)); - /* Some sensors have different reset polarities, and we can't know which sensor - is connected before initializing cambus and probing the sensor, which in turn - requires pulling the sensor out of the reset state. So we try to probe the - sensor with both polarities to determine line state. */ - sensor.pwdn_pol = ACTIVE_HIGH; - sensor.reset_pol = ACTIVE_HIGH; - - /* Reset the sensor */ - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - - // Initialize the camera bus. - cambus_init(&sensor.bus, ISC_I2C_ID, ISC_I2C_SPEED); - mp_hal_delay_ms(10); - - /* Probe the sensor */ - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - /* Sensor has been held in reset, - so the reset line is active low */ - sensor.reset_pol = ACTIVE_LOW; - - /* Pull the sensor out of the reset state */ - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - - /* Probe again to set the slave addr */ - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - sensor.pwdn_pol = ACTIVE_LOW; - - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(10); - - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - sensor.reset_pol = ACTIVE_HIGH; - - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - return -2; - } - } - } - } - - // Clear sensor chip ID. - sensor.chip_id = 0; - // Set default snapshot function. + // Some sensors need to call snapshot from init. sensor.snapshot = sensor_snapshot; - switch (sensor.slv_addr) { - #if (OMV_ENABLE_OV2640 == 1) - case OV2640_SLV_ADDR: // Or OV9650. - cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); - break; - #endif // (OMV_ENABLE_OV2640 == 1) - - #if (OMV_ENABLE_OV5640 == 1) - case OV5640_SLV_ADDR: - cambus_readb2(&sensor.bus, sensor.slv_addr, OV5640_CHIP_ID, &sensor.chip_id); - break; - #endif // (OMV_ENABLE_OV5640 == 1) - - #if (OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) - case OV7725_SLV_ADDR: // Or OV7690 or OV7670. - cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) - - #if (OMV_ENABLE_MT9V034 == 1) - case MT9V034_SLV_ADDR: - cambus_readb(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_MT9V034 == 1) - - #if (OMV_ENABLE_MT9M114 == 1) - case MT9M114_SLV_ADDR: - cambus_readw2(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id_w); - break; - #endif // (OMV_ENABLE_MT9M114 == 1) - - #if (OMV_ENABLE_LEPTON == 1) - case LEPTON_SLV_ADDR: - sensor.chip_id = LEPTON_ID; - break; - #endif // (OMV_ENABLE_LEPTON == 1) - - #if (OMV_ENABLE_HM01B0 == 1) - case HM01B0_SLV_ADDR: - cambus_readb2(&sensor.bus, sensor.slv_addr, HIMAX_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_HM01B0 == 1) - default: - return -3; - break; + // Configure the sensor external clock (XCLK). + if (sensor_set_xclk_frequency(OMV_XCLK_FREQUENCY) != 0) { + // Failed to initialize the sensor clock. + return -1; } - switch (sensor.chip_id) { - #if (OMV_ENABLE_OV2640 == 1) - case OV2640_ID: - if (sensor_set_xclk_frequency(OV2640_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov2640_init(&sensor); - break; - #endif // (OMV_ENABLE_OV2640 == 1) - - #if (OMV_ENABLE_OV5640 == 1) - case OV5640_ID: - if (sensor_set_xclk_frequency(OV5640_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov5640_init(&sensor); - break; - #endif // (OMV_ENABLE_OV5640 == 1) - - #if (OMV_ENABLE_OV7670 == 1) - case OV7670_ID: - if (sensor_set_xclk_frequency(OV7670_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov7670_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7670 == 1) - - #if (OMV_ENABLE_OV7690 == 1) - case OV7690_ID: - if (sensor_set_xclk_frequency(OV7690_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov7690_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7690 == 1) - - #if (OMV_ENABLE_OV7725 == 1) - case OV7725_ID: - init_ret = ov7725_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7725 == 1) - - #if (OMV_ENABLE_OV9650 == 1) - case OV9650_ID: - init_ret = ov9650_init(&sensor); - break; - #endif // (OMV_ENABLE_OV9650 == 1) - - #if (OMV_ENABLE_MT9V034 == 1) - case MT9V034_ID: - if (sensor_set_xclk_frequency(MT9V034_XCLK_FREQ) != 0) { - return -3; - } - init_ret = mt9v034_init(&sensor); - break; - #endif //(OMV_ENABLE_MT9V034 == 1) - - #if (OMV_ENABLE_MT9M114 == 1) - case MT9M114_ID: - if (sensor_set_xclk_frequency(MT9M114_XCLK_FREQ) != 0) { - return -3; - } - init_ret = mt9m114_init(&sensor); - break; - #endif //(OMV_ENABLE_MT9M114 == 1) - - #if (OMV_ENABLE_LEPTON == 1) - case LEPTON_ID: - if (sensor_set_xclk_frequency(LEPTON_XCLK_FREQ) != 0) { - return -3; - } - init_ret = lepton_init(&sensor); - break; - #endif // (OMV_ENABLE_LEPTON == 1) - - #if (OMV_ENABLE_HM01B0 == 1) - case HM01B0_ID: - init_ret = hm01b0_init(&sensor); - break; - #endif //(OMV_ENABLE_HM01B0 == 1) - - default: - return -3; - break; + // Detect and initialize the image sensor. + if ((init_ret = sensor_probe_init()) != 0) { + // Sensor probe/init failed. + return init_ret; } - if (init_ret != 0 ) { - // Sensor init failed. - return -4; - } // Configure the DCMI interface. if (sensor_dcmi_config(PIXFORMAT_INVALID) != 0){ @@ -343,7 +87,6 @@ int sensor_init() // Clear fb_enabled flag // This is executed only once to initialize the FB enabled flag. - // TODO //JPEG_FB()->enabled = 0; // Set default color palette. @@ -390,62 +133,6 @@ int sensor_dcmi_config(uint32_t pixformat) return 0; } -int sensor_reset() -{ - framebuffer_reset_buffers(); - - // Reset the sensor state - sensor.sde = 0; - sensor.pixformat = 0; - sensor.framesize = 0; - sensor.framerate = 0; - sensor.last_frame_ms = 0; - sensor.last_frame_ms_valid = false; - sensor.gainceiling = 0; - sensor.hmirror = false; - sensor.vflip = false; - sensor.transpose = false; - #if MICROPY_PY_IMU - sensor.auto_rotation = sensor.chip_id == OV7690_ID; - #else - sensor.auto_rotation = false; - #endif // MICROPY_PY_IMU - sensor.vsync_callback = NULL; - sensor.frame_callback = NULL; - - // Reset default color palette. - sensor.color_palette = rainbow_table; - - sensor.disable_full_flush = false; - - // Restore shutdown state on reset. - sensor_shutdown(false); - - // Hard-reset the sensor - if (sensor.reset_pol == ACTIVE_HIGH) { - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - DCMI_RESET_LOW(); - } else { - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - DCMI_RESET_HIGH(); - } - mp_hal_delay_ms(20); - - // Call sensor-specific reset function - if (sensor.reset(&sensor) != 0) { - return -1; - } - - return 0; -} - -int sensor_get_id() -{ - return sensor.chip_id; -} - uint32_t sensor_get_xclk_frequency() { return OMV_XCLK_FREQUENCY; @@ -472,598 +159,12 @@ int sensor_set_xclk_frequency(uint32_t frequency) return 0; } -bool sensor_is_detected() -{ - return sensor.detected; -} - -int sensor_sleep(int enable) -{ - if (sensor.sleep == NULL - || sensor.sleep(&sensor, enable) != 0) { - // Operation not supported - return -1; - } - return 0; -} - -int sensor_shutdown(int enable) -{ - int ret = 0; - if (enable) { - if (sensor.pwdn_pol == ACTIVE_HIGH) { - DCMI_PWDN_HIGH(); - } else { - DCMI_PWDN_LOW(); - } - } else { - if (sensor.pwdn_pol == ACTIVE_HIGH) { - DCMI_PWDN_LOW(); - } else { - DCMI_PWDN_HIGH(); - } - } - - mp_hal_delay_ms(10); - return ret; -} - -int sensor_read_reg(uint16_t reg_addr) -{ - if (sensor.read_reg == NULL) { - // Operation not supported - return -1; - } - return sensor.read_reg(&sensor, reg_addr); -} - -int sensor_write_reg(uint16_t reg_addr, uint16_t reg_data) -{ - if (sensor.write_reg == NULL) { - // Operation not supported - return -1; - } - return sensor.write_reg(&sensor, reg_addr, reg_data); -} - -int sensor_set_pixformat(pixformat_t pixformat) -{ - if (sensor.pixformat == pixformat) { - // No change - return 0; - } - - // sensor_check_buffsize() will switch from PIXFORMAT_BAYER to PIXFORMAT_RGB565 to try to fit - // the MAIN_FB() in RAM as a first step optimization. If the user tries to switch back to RGB565 - // and that would be bigger than the RAM buffer we would just switch back. - // - // So, just short-circuit doing any work. - // - // This code is explicitly here to allow users to set the resolution to RGB565 and have it - // switch to BAYER only once even though they are setting the resolution to RGB565 repeatedly - // in a loop. Only RGB565->BAYER has this problem and needs this fix because of sensor_check_buffsize(). - uint32_t size = framebuffer_get_buffer_size(); - if ((sensor.pixformat == PIXFORMAT_BAYER) - && (pixformat == PIXFORMAT_RGB565) - && (MAIN_FB()->u * MAIN_FB()->v * 2 > size) - && (MAIN_FB()->u * MAIN_FB()->v * 1 <= size)) { - // No change - return 0; - } - - // Cropping and transposing (and thus auto rotation) don't work in JPEG mode. - //if ((pixformat == PIXFORMAT_JPEG) && (cropped() || sensor.transpose || sensor.auto_rotation)) { - // return -1; - //} - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - if (sensor.set_pixformat == NULL - || sensor.set_pixformat(&sensor, pixformat) != 0) { - // Operation not supported - return -1; - } - - mp_hal_delay_ms(100); // wait for the camera to settle - - // Set pixel format - sensor.pixformat = pixformat; - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - return 0; -} - -int sensor_set_framesize(framesize_t framesize) -{ - if (sensor.framesize == framesize) { - // No change - return 0; - } - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - // Call the sensor specific function - if (sensor.set_framesize == NULL - || sensor.set_framesize(&sensor, framesize) != 0) { - // Operation not supported - return -1; - } - - mp_hal_delay_ms(100); // wait for the camera to settle - - // Set framebuffer size - sensor.framesize = framesize; - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - // Set MAIN FB x offset, y offset, width, height, backup width, and backup height. - MAIN_FB()->x = 0; - MAIN_FB()->y = 0; - MAIN_FB()->w = MAIN_FB()->u = resolution[framesize][0]; - MAIN_FB()->h = MAIN_FB()->v = resolution[framesize][1]; - - return 0; -} - -int sensor_set_framerate(int framerate) -{ - if (sensor.framerate == framerate) { - // No change - return 0; - } - - if (framerate < 0) { - return -1; - } - - // Call the sensor specific function (does not fail if function is not set) - if (sensor.set_framerate != NULL) { - if (sensor.set_framerate(&sensor, framerate) != 0) { - // Operation not supported - return -1; - } - } - - // Set framerate - sensor.framerate = framerate; - return 0; -} int sensor_set_windowing(int x, int y, int w, int h) { - if ((MAIN_FB()->x == x) && (MAIN_FB()->y == y) && (MAIN_FB()->u == w) && (MAIN_FB()->v == h)) { - // No change - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - MAIN_FB()->x = x; - MAIN_FB()->y = y; - MAIN_FB()->w = MAIN_FB()->u = w; - MAIN_FB()->h = MAIN_FB()->v = h; - - return 0; -} - -int sensor_set_contrast(int level) -{ - if (sensor.set_contrast != NULL) { - return sensor.set_contrast(&sensor, level); - } return -1; } -int sensor_set_brightness(int level) -{ - if (sensor.set_brightness != NULL) { - return sensor.set_brightness(&sensor, level); - } - return -1; -} - -int sensor_set_saturation(int level) -{ - if (sensor.set_saturation != NULL) { - return sensor.set_saturation(&sensor, level); - } - return -1; -} - -int sensor_set_gainceiling(gainceiling_t gainceiling) -{ - if (sensor.gainceiling == gainceiling) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_gainceiling == NULL - || sensor.set_gainceiling(&sensor, gainceiling) != 0) { - /* operation not supported */ - return -1; - } - - sensor.gainceiling = gainceiling; - return 0; -} - -int sensor_set_quality(int qs) -{ - /* call the sensor specific function */ - if (sensor.set_quality == NULL - || sensor.set_quality(&sensor, qs) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_colorbar(int enable) -{ - /* call the sensor specific function */ - if (sensor.set_colorbar == NULL - || sensor.set_colorbar(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_gain(int enable, float gain_db, float gain_db_ceiling) -{ - /* call the sensor specific function */ - if (sensor.set_auto_gain == NULL - || sensor.set_auto_gain(&sensor, enable, gain_db, gain_db_ceiling) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_gain_db(float *gain_db) -{ - /* call the sensor specific function */ - if (sensor.get_gain_db == NULL - || sensor.get_gain_db(&sensor, gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_exposure(int enable, int exposure_us) -{ - /* call the sensor specific function */ - if (sensor.set_auto_exposure == NULL - || sensor.set_auto_exposure(&sensor, enable, exposure_us) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_exposure_us(int *exposure_us) -{ - /* call the sensor specific function */ - if (sensor.get_exposure_us == NULL - || sensor.get_exposure_us(&sensor, exposure_us) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db) -{ - /* call the sensor specific function */ - if (sensor.set_auto_whitebal == NULL - || sensor.set_auto_whitebal(&sensor, enable, r_gain_db, g_gain_db, b_gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_gain_db) -{ - /* call the sensor specific function */ - if (sensor.get_rgb_gain_db == NULL - || sensor.get_rgb_gain_db(&sensor, r_gain_db, g_gain_db, b_gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_hmirror(int enable) -{ - if (sensor.hmirror == ((bool) enable)) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_hmirror == NULL - || sensor.set_hmirror(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - sensor.hmirror = enable; - mp_hal_delay_ms(100); // wait for the camera to settle - return 0; -} - -bool sensor_get_hmirror() -{ - return sensor.hmirror; -} - -int sensor_set_vflip(int enable) -{ - if (sensor.vflip == ((bool) enable)) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_vflip == NULL - || sensor.set_vflip(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - sensor.vflip = enable; - mp_hal_delay_ms(100); // wait for the camera to settle - return 0; -} - -bool sensor_get_vflip() -{ - return sensor.vflip; -} - -int sensor_set_transpose(bool enable) -{ - if (sensor.transpose == enable) { - /* no change */ - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - sensor.transpose = enable; - return 0; -} - -bool sensor_get_transpose() -{ - return sensor.transpose; -} - -int sensor_set_auto_rotation(bool enable) -{ - if (sensor.auto_rotation == enable) { - /* no change */ - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - sensor.auto_rotation = enable; - return 0; -} - -bool sensor_get_auto_rotation() -{ - return sensor.auto_rotation; -} - -int sensor_set_framebuffers(int count) -{ - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - return framebuffer_set_buffers(count); -} - -int sensor_set_special_effect(sde_t sde) -{ - if (sensor.sde == sde) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_special_effect == NULL - || sensor.set_special_effect(&sensor, sde) != 0) { - /* operation not supported */ - return -1; - } - - sensor.sde = sde; - return 0; -} - -int sensor_set_lens_correction(int enable, int radi, int coef) -{ - /* call the sensor specific function */ - if (sensor.set_lens_correction == NULL - || sensor.set_lens_correction(&sensor, enable, radi, coef) != 0) { - /* operation not supported */ - return -1; - } - - return 0; -} - -int sensor_ioctl(int request, ... /* arg */) -{ - int ret = -1; - - if (sensor.ioctl != NULL) { - va_list ap; - va_start(ap, request); - /* call the sensor specific function */ - ret = sensor.ioctl(&sensor, request, ap); - va_end(ap); - } - - return ret; -} - -int sensor_set_vsync_callback(vsync_cb_t vsync_cb) -{ - sensor.vsync_callback = vsync_cb; - if (sensor.vsync_callback == NULL) { - // Disable VSYNC EXTI IRQ - } else { - // Enable VSYNC EXTI IRQ - } - return 0; -} - -int sensor_set_frame_callback(frame_cb_t vsync_cb) -{ - sensor.frame_callback = vsync_cb; - return 0; -} - -int sensor_set_color_palette(const uint16_t *color_palette) -{ - sensor.color_palette = color_palette; - return 0; -} - -const uint16_t *sensor_get_color_palette() -{ - return sensor.color_palette; -} - -void VsyncExtiCallback() -{ - if (sensor.vsync_callback != NULL) { - //sensor.vsync_callback(HAL_GPIO_ReadPin(DCMI_VSYNC_PORT, DCMI_VSYNC_PIN)); - } -} - -// To make the user experience better we automatically shrink the size of the MAIN_FB() to fit -// within the RAM we have onboard the system. -void sensor_check_buffsize() -{ - if (MAIN_FB()->n_buffers != 1) { - framebuffer_set_buffers(1); - } - - uint32_t size = framebuffer_get_buffer_size(); - uint32_t bpp; - - switch (sensor.pixformat) { - case PIXFORMAT_GRAYSCALE: - case PIXFORMAT_BAYER: - bpp = 1; - break; - case PIXFORMAT_RGB565: - case PIXFORMAT_YUV422: - bpp = 2; - break; - // If the pixformat is NULL/JPEG there we can't do anything to check if it fits before hand. - default: - return; - } - - // MAIN_FB() fits, we are done. - if ((MAIN_FB()->u * MAIN_FB()->v * bpp) <= size) { - return; - } - - if (sensor.pixformat == PIXFORMAT_RGB565) { - // Switch to bayer for the quick 2x savings. - sensor_set_pixformat(PIXFORMAT_BAYER); - bpp = 1; - - // MAIN_FB() fits, we are done (bpp is 1). - if (MAIN_FB()->u * MAIN_FB()->v <= size) { - return; - } - } - - int window_w = MAIN_FB()->u; - int window_h = MAIN_FB()->v; - - // We need to shrink the frame buffer. We can do this by cropping. So, we will subtract columns - // and rows from the frame buffer until it fits within the frame buffer. - int max = IM_MAX(window_w, window_h); - int min = IM_MIN(window_w, window_h); - float aspect_ratio = max / ((float) min); - float r = aspect_ratio, best_r = r; - int c = 1, best_c = c; - float best_err = FLT_MAX; - - // Find the width/height ratio that's within 1% of the aspect ratio with a loop limit. - for (int i = 100; i; i--) { - float err = fast_fabsf(r - fast_roundf(r)); - - if (err <= best_err) { - best_err = err; - best_r = r; - best_c = c; - } - - if (best_err <= 0.01f) { - break; - } - - r += aspect_ratio; - c += 1; - } - - // Select the larger geometry to map the aspect ratio to. - int u_sub, v_sub; - - if (window_w > window_h) { - u_sub = fast_roundf(best_r); - v_sub = best_c; - } else { - u_sub = best_c; - v_sub = fast_roundf(best_r); - } - - // Crop the frame buffer while keeping the aspect ratio and keeping the width/height even. - while (((MAIN_FB()->u * MAIN_FB()->v * bpp) > size) || (MAIN_FB()->u % 2) || (MAIN_FB()->v % 2)) { - MAIN_FB()->u -= u_sub; - MAIN_FB()->v -= v_sub; - } - - // Center the new window using the previous offset and keep the offset even. - MAIN_FB()->x += (window_w - MAIN_FB()->u) / 2; - MAIN_FB()->y += (window_h - MAIN_FB()->v) / 2; - - if (MAIN_FB()->x % 2) { - MAIN_FB()->x -= 1; - } - - if (MAIN_FB()->y % 2) { - MAIN_FB()->y -= 1; - } -} - // This is the default snapshot function, which can be replaced in sensor_init functions. int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) { @@ -1072,6 +173,15 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) // Note: This doesn't run unless the IDE is connected and the framebuffer is enabled. framebuffer_update_jpeg_buffer(); + // This driver supports a single buffer. + if (MAIN_FB()->n_buffers != 1) { + framebuffer_set_buffers(1); + } + + if (sensor_check_framebuffer_size() != 0) { + return -1; + } + framebuffer_free_current_buffer(); vbuffer_t *buffer = framebuffer_get_tail(FB_NO_FLAGS); diff --git a/src/omv/ports/rp2/omv_portconfig.cmake b/src/omv/ports/rp2/omv_portconfig.cmake index 1eebe3cc2..8414ceb68 100644 --- a/src/omv/ports/rp2/omv_portconfig.cmake +++ b/src/omv/ports/rp2/omv_portconfig.cmake @@ -97,6 +97,7 @@ target_sources(${MICROPY_TARGET} PRIVATE ${TOP_DIR}/${OMV_DIR}/common/trace.c ${TOP_DIR}/${OMV_DIR}/common/mutex.c ${TOP_DIR}/${OMV_DIR}/common/usbdbg.c + ${TOP_DIR}/${OMV_DIR}/common/sensor_utils.c ${TOP_DIR}/${OMV_DIR}/sensors/ov2640.c ${TOP_DIR}/${OMV_DIR}/sensors/ov5640.c diff --git a/src/omv/ports/rp2/sensor.c b/src/omv/ports/rp2/sensor.c index 9005b2894..0b8992873 100644 --- a/src/omv/ports/rp2/sensor.c +++ b/src/omv/ports/rp2/sensor.c @@ -9,23 +9,12 @@ * Sensor abstraction layer for nRF port. */ #if MICROPY_PY_SENSOR -#include #include #include #include #include "py/mphal.h" - #include "cambus.h" #include "sensor.h" -#include "ov2640.h" -#include "ov5640.h" -#include "ov7725.h" -#include "ov7690.h" -#include "ov7670.h" -#include "ov9650.h" -#include "mt9v034.h" -#include "lepton.h" -#include "hm01b0.h" #include "framebuffer.h" #include "pico/time.h" @@ -38,56 +27,11 @@ #include "unaligned_memcpy.h" #include "dcmi.pio.h" -sensor_t sensor = {0}; -extern void __fatal_error(const char *msg); -static void dma_irq_handler(); +// Sensor struct. +sensor_t sensor = {}; -const int resolution[][2] = { - {0, 0 }, - // C/SIF Resolutions - {88, 72 }, /* QQCIF */ - {176, 144 }, /* QCIF */ - {352, 288 }, /* CIF */ - {88, 60 }, /* QQSIF */ - {176, 120 }, /* QSIF */ - {352, 240 }, /* SIF */ - // VGA Resolutions - {40, 30 }, /* QQQQVGA */ - {80, 60 }, /* QQQVGA */ - {160, 120 }, /* QQVGA */ - {320, 240 }, /* QVGA */ - {640, 480 }, /* VGA */ - {30, 20 }, /* HQQQQVGA */ - {60, 40 }, /* HQQQVGA */ - {120, 80 }, /* HQQVGA */ - {240, 160 }, /* HQVGA */ - {480, 320 }, /* HVGA */ - // FFT Resolutions - {64, 32 }, /* 64x32 */ - {64, 64 }, /* 64x64 */ - {128, 64 }, /* 128x64 */ - {128, 128 }, /* 128x128 */ - // Himax Resolutions - {160, 160 }, /* 160x160 */ - {320, 320 }, /* 320x320 */ - // Other - {128, 160 }, /* LCD */ - {128, 160 }, /* QQVGA2 */ - {720, 480 }, /* WVGA */ - {752, 480 }, /* WVGA2 */ - {800, 600 }, /* SVGA */ - {1024, 768 }, /* XGA */ - {1280, 768 }, /* WXGA */ - {1280, 1024}, /* SXGA */ - {1280, 960 }, /* SXGAM */ - {1600, 1200}, /* UXGA */ - {1280, 720 }, /* HD */ - {1920, 1080}, /* FHD */ - {2560, 1440}, /* QHD */ - {2048, 1536}, /* QXGA */ - {2560, 1600}, /* WQXGA */ - {2592, 1944}, /* WQXGA2 */ -}; +static void dma_irq_handler(); +extern void __fatal_error(const char *msg); static void sensor_dma_config(int w, int h, int bpp, uint32_t *capture_buf, bool rev_bytes) { @@ -141,230 +85,28 @@ int sensor_init() DCMI_RESET_HIGH(); #endif - /* Do a power cycle */ - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(10); - - DCMI_PWDN_LOW(); - mp_hal_delay_ms(10); - - // Configure the sensor external clock (XCLK) to XCLK_FREQ. - #if (OMV_XCLK_SOURCE == OMV_XCLK_TIM) - // Configure external clock timer. - if (sensor_set_xclk_frequency(OMV_XCLK_FREQUENCY) != 0) { - // Timer problem - return -1; - } - #elif (OMV_XCLK_SOURCE == OMV_XCLK_OSC) - // An external oscillator is used for the sensor clock. - // Nothing to do. - #else - #error "OMV_XCLK_SOURCE is not set!" - #endif - - /* Reset the sesnor state */ + // Reset the sesnor state memset(&sensor, 0, sizeof(sensor_t)); - /* Some sensors have different reset polarities, and we can't know which sensor - is connected before initializing cambus and probing the sensor, which in turn - requires pulling the sensor out of the reset state. So we try to probe the - sensor with both polarities to determine line state. */ - sensor.pwdn_pol = ACTIVE_HIGH; - sensor.reset_pol = ACTIVE_HIGH; - - /* Reset the sensor */ - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - - // Initialize the camera bus. - cambus_init(&sensor.bus, ISC_I2C_ID, ISC_I2C_SPEED); - mp_hal_delay_ms(10); - - /* Probe the sensor */ - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - /* Sensor has been held in reset, - so the reset line is active low */ - sensor.reset_pol = ACTIVE_LOW; - - /* Pull the sensor out of the reset state */ - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - - /* Probe again to set the slave addr */ - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - sensor.pwdn_pol = ACTIVE_LOW; - - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(10); - - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - sensor.reset_pol = ACTIVE_HIGH; - - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - return -2; - } - } - } - } - - // Clear sensor chip ID. - sensor.chip_id = 0; - // Set default snapshot function. + // Some sensors need to call snapshot from init. sensor.snapshot = sensor_snapshot; - switch (sensor.slv_addr) { - #if (OMV_ENABLE_OV2640 == 1) - case OV2640_SLV_ADDR: // Or OV9650. - cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); - break; - #endif // (OMV_ENABLE_OV2640 == 1) - - #if (OMV_ENABLE_OV5640 == 1) - case OV5640_SLV_ADDR: - cambus_readb2(&sensor.bus, sensor.slv_addr, OV5640_CHIP_ID, &sensor.chip_id); - break; - #endif // (OMV_ENABLE_OV5640 == 1) - - #if (OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) - case OV7725_SLV_ADDR: // Or OV7690 or OV7670. - cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) - - #if (OMV_ENABLE_MT9V034 == 1) - case MT9V034_SLV_ADDR: - cambus_readb(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_MT9V034 == 1) - - #if (OMV_ENABLE_MT9M114 == 1) - case MT9M114_SLV_ADDR: - cambus_readw2(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id_w); - break; - #endif // (OMV_ENABLE_MT9M114 == 1) - - #if (OMV_ENABLE_LEPTON == 1) - case LEPTON_SLV_ADDR: - sensor.chip_id = LEPTON_ID; - break; - #endif // (OMV_ENABLE_LEPTON == 1) - - #if (OMV_ENABLE_HM01B0 == 1) - case HM01B0_SLV_ADDR: - cambus_readb2(&sensor.bus, sensor.slv_addr, HIMAX_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_HM01B0 == 1) - default: - return -3; - break; + // Configure the sensor external clock (XCLK). + if (sensor_set_xclk_frequency(OMV_XCLK_FREQUENCY) != 0) { + // Failed to initialize the sensor clock. + return -1; } - switch (sensor.chip_id) { - #if (OMV_ENABLE_OV2640 == 1) - case OV2640_ID: - if (sensor_set_xclk_frequency(OV2640_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov2640_init(&sensor); - break; - #endif // (OMV_ENABLE_OV2640 == 1) - - #if (OMV_ENABLE_OV5640 == 1) - case OV5640_ID: - if (sensor_set_xclk_frequency(OV5640_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov5640_init(&sensor); - break; - #endif // (OMV_ENABLE_OV5640 == 1) - - #if (OMV_ENABLE_OV7670 == 1) - case OV7670_ID: - init_ret = ov7670_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7670 == 1) - - #if (OMV_ENABLE_OV7690 == 1) - case OV7690_ID: - if (sensor_set_xclk_frequency(OV7690_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov7690_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7690 == 1) - - #if (OMV_ENABLE_OV7725 == 1) - case OV7725_ID: - init_ret = ov7725_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7725 == 1) - - #if (OMV_ENABLE_OV9650 == 1) - case OV9650_ID: - init_ret = ov9650_init(&sensor); - break; - #endif // (OMV_ENABLE_OV9650 == 1) - - #if (OMV_ENABLE_MT9V034 == 1) - case MT9V034_ID: - if (sensor_set_xclk_frequency(MT9V034_XCLK_FREQ) != 0) { - return -3; - } - init_ret = mt9v034_init(&sensor); - break; - #endif //(OMV_ENABLE_MT9V034 == 1) - - #if (OMV_ENABLE_MT9M114 == 1) - case MT9M114_ID: - if (sensor_set_xclk_frequency(MT9M114_XCLK_FREQ) != 0) { - return -3; - } - init_ret = mt9m114_init(&sensor); - break; - #endif //(OMV_ENABLE_MT9M114 == 1) - - #if (OMV_ENABLE_LEPTON == 1) - case LEPTON_ID: - if (sensor_set_xclk_frequency(LEPTON_XCLK_FREQ) != 0) { - return -3; - } - init_ret = lepton_init(&sensor); - break; - #endif // (OMV_ENABLE_LEPTON == 1) - - #if (OMV_ENABLE_HM01B0 == 1) - case HM01B0_ID: - init_ret = hm01b0_init(&sensor); - break; - #endif //(OMV_ENABLE_HM01B0 == 1) - - default: - return -3; - break; - } - - if (init_ret != 0 ) { - // Sensor init failed. - return -4; + // Detect and initialize the image sensor. + if ((init_ret = sensor_probe_init()) != 0) { + // Sensor probe/init failed. + return init_ret; } // Set default color palette. sensor.color_palette = rainbow_table; - // Disable VSYNC IRQ and callback - sensor_set_vsync_callback(NULL); - // Set new DMA IRQ handler. // Disable IRQs. irq_set_enabled(DCMI_DMA_IRQ, false); @@ -385,6 +127,12 @@ int sensor_init() irq_set_enabled(DCMI_DMA_IRQ, true); + // Disable VSYNC IRQ and callback + sensor_set_vsync_callback(NULL); + + // Disable Frame callback. + sensor_set_frame_callback(NULL); + /* All good! */ sensor.detected = true; @@ -407,60 +155,6 @@ int sensor_abort() return 0; } -int sensor_reset() -{ - sensor_abort(); - - // Reset the sensor state - sensor.sde = 0; - sensor.pixformat = 0; - sensor.framesize = 0; - sensor.framerate = 0; - sensor.gainceiling = 0; - sensor.hmirror = false; - sensor.vflip = false; - sensor.transpose = false; - #if MICROPY_PY_IMU - sensor.auto_rotation = sensor.chip_id == OV7690_ID; - #else - sensor.auto_rotation = false; - #endif // MICROPY_PY_IMU - sensor.vsync_callback= NULL; - sensor.frame_callback= NULL; - - // Reset default color palette. - sensor.color_palette = rainbow_table; - - // Restore shutdown state on reset. - sensor_shutdown(false); - - // Hard-reset the sensor - if (sensor.reset_pol == ACTIVE_HIGH) { - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - DCMI_RESET_LOW(); - } else { - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - DCMI_RESET_HIGH(); - } - mp_hal_delay_ms(20); - - // Call sensor-specific reset function - if (sensor.reset(&sensor) != 0) { - return -1; - } - - // Reset framebuffers - framebuffer_reset_buffers(); - return 0; -} - -int sensor_get_id() -{ - return sensor.chip_id; -} - int sensor_set_xclk_frequency(uint32_t frequency) { uint32_t p = 4; @@ -487,496 +181,29 @@ int sensor_set_xclk_frequency(uint32_t frequency) return 0; } -bool sensor_is_detected() -{ - return sensor.detected; -} - -int sensor_sleep(int enable) -{ - if (sensor.sleep == NULL - || sensor.sleep(&sensor, enable) != 0) { - // Operation not supported - return -1; - } - return 0; -} - -int sensor_shutdown(int enable) -{ - int ret = 0; - if (enable) { - if (sensor.pwdn_pol == ACTIVE_HIGH) { - DCMI_PWDN_HIGH(); - } else { - DCMI_PWDN_LOW(); - } - } else { - if (sensor.pwdn_pol == ACTIVE_HIGH) { - DCMI_PWDN_LOW(); - } else { - DCMI_PWDN_HIGH(); - } - } - - mp_hal_delay_ms(10); - return ret; -} - -int sensor_read_reg(uint16_t reg_addr) -{ - if (sensor.read_reg == NULL) { - // Operation not supported - return -1; - } - return sensor.read_reg(&sensor, reg_addr); -} - -int sensor_write_reg(uint16_t reg_addr, uint16_t reg_data) -{ - if (sensor.write_reg == NULL) { - // Operation not supported - return -1; - } - return sensor.write_reg(&sensor, reg_addr, reg_data); -} - -int sensor_set_pixformat(pixformat_t pixformat) -{ - if (sensor.pixformat == pixformat) { - // No change - return 0; - } - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - if (sensor.set_pixformat == NULL - || sensor.set_pixformat(&sensor, pixformat) != 0) { - // Operation not supported - return -1; - } - - // wait for the camera to settle - mp_hal_delay_ms(100); - - // Set pixel format - sensor.pixformat = pixformat; - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - // Reconfigure PIO DCMI program. - return sensor_dcmi_config(pixformat); -} - -int sensor_set_framesize(framesize_t framesize) -{ - if (sensor.framesize == framesize) { - // No change - return 0; - } - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - // Call the sensor specific function - if (sensor.set_framesize == NULL - || sensor.set_framesize(&sensor, framesize) != 0) { - // Operation not supported - return -1; - } - - // wait for the camera to settle - mp_hal_delay_ms(100); - - // Set framebuffer size - sensor.framesize = framesize; - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - // Set MAIN FB x offset, y offset, width, height, backup width, and backup height. - MAIN_FB()->x = 0; - MAIN_FB()->y = 0; - MAIN_FB()->w = MAIN_FB()->u = resolution[framesize][0]; - MAIN_FB()->h = MAIN_FB()->v = resolution[framesize][1]; - - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); - return 0; -} - -int sensor_set_framerate(int framerate) -{ - if (sensor.framerate == framerate) { - // No change - return 0; - } - - // Call the sensor specific function - if (sensor.set_framerate == NULL - || sensor.set_framerate(&sensor, framerate) != 0) { - // Operation not supported - return -1; - } - - return 0; -} - int sensor_set_windowing(int x, int y, int w, int h) { return -1; } -int sensor_set_contrast(int level) -{ - if (sensor.set_contrast != NULL) { - return sensor.set_contrast(&sensor, level); - } - return -1; -} - -int sensor_set_brightness(int level) -{ - if (sensor.set_brightness != NULL) { - return sensor.set_brightness(&sensor, level); - } - return -1; -} - -int sensor_set_saturation(int level) -{ - if (sensor.set_saturation != NULL) { - return sensor.set_saturation(&sensor, level); - } - return -1; -} - -int sensor_set_gainceiling(gainceiling_t gainceiling) -{ - if (sensor.gainceiling == gainceiling) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_gainceiling == NULL - || sensor.set_gainceiling(&sensor, gainceiling) != 0) { - /* operation not supported */ - return -1; - } - - sensor.gainceiling = gainceiling; - return 0; -} - -int sensor_set_quality(int qs) -{ - /* call the sensor specific function */ - if (sensor.set_quality == NULL - || sensor.set_quality(&sensor, qs) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_colorbar(int enable) -{ - /* call the sensor specific function */ - if (sensor.set_colorbar == NULL - || sensor.set_colorbar(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_gain(int enable, float gain_db, float gain_db_ceiling) -{ - /* call the sensor specific function */ - if (sensor.set_auto_gain == NULL - || sensor.set_auto_gain(&sensor, enable, gain_db, gain_db_ceiling) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_gain_db(float *gain_db) -{ - /* call the sensor specific function */ - if (sensor.get_gain_db == NULL - || sensor.get_gain_db(&sensor, gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_exposure(int enable, int exposure_us) -{ - /* call the sensor specific function */ - if (sensor.set_auto_exposure == NULL - || sensor.set_auto_exposure(&sensor, enable, exposure_us) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_exposure_us(int *exposure_us) -{ - /* call the sensor specific function */ - if (sensor.get_exposure_us == NULL - || sensor.get_exposure_us(&sensor, exposure_us) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db) -{ - /* call the sensor specific function */ - if (sensor.set_auto_whitebal == NULL - || sensor.set_auto_whitebal(&sensor, enable, r_gain_db, g_gain_db, b_gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_gain_db) -{ - /* call the sensor specific function */ - if (sensor.get_rgb_gain_db == NULL - || sensor.get_rgb_gain_db(&sensor, r_gain_db, g_gain_db, b_gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_hmirror(int enable) -{ - if (sensor.hmirror == ((bool) enable)) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_hmirror == NULL - || sensor.set_hmirror(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - sensor.hmirror = enable; - mp_hal_delay_ms(100); // wait for the camera to settle - return 0; -} - -bool sensor_get_hmirror() -{ - return sensor.hmirror; -} - -int sensor_set_vflip(int enable) -{ - if (sensor.vflip == ((bool) enable)) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_vflip == NULL - || sensor.set_vflip(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - sensor.vflip = enable; - mp_hal_delay_ms(100); // wait for the camera to settle - return 0; -} - -bool sensor_get_vflip() -{ - return sensor.vflip; -} - -int sensor_set_transpose(bool enable) -{ - if (sensor.transpose == enable) { - /* no change */ - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - sensor.transpose = enable; - return 0; -} - -bool sensor_get_transpose() -{ - return sensor.transpose; -} - -int sensor_set_auto_rotation(bool enable) -{ - if (sensor.auto_rotation == enable) { - /* no change */ - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - sensor.auto_rotation = enable; - return 0; -} - -bool sensor_get_auto_rotation() -{ - return sensor.auto_rotation; -} - -int sensor_set_framebuffers(int count) -{ - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - return framebuffer_set_buffers(count); -} - -int sensor_set_special_effect(sde_t sde) -{ - if (sensor.sde == sde) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_special_effect == NULL - || sensor.set_special_effect(&sensor, sde) != 0) { - /* operation not supported */ - return -1; - } - - sensor.sde = sde; - return 0; -} - -int sensor_set_lens_correction(int enable, int radi, int coef) -{ - /* call the sensor specific function */ - if (sensor.set_lens_correction == NULL - || sensor.set_lens_correction(&sensor, enable, radi, coef) != 0) { - /* operation not supported */ - return -1; - } - - return 0; -} - -int sensor_ioctl(int request, ... /* arg */) -{ - int ret = -1; - - if (sensor.ioctl != NULL) { - va_list ap; - va_start(ap, request); - /* call the sensor specific function */ - ret = sensor.ioctl(&sensor, request, ap); - va_end(ap); - } - - return ret; -} - -int sensor_set_vsync_callback(vsync_cb_t vsync_cb) -{ - sensor.vsync_callback = vsync_cb; - if (sensor.vsync_callback == NULL) { - // Disable VSYNC EXTI IRQ - } else { - // Enable VSYNC EXTI IRQ - } - return 0; -} - -int sensor_set_frame_callback(frame_cb_t vsync_cb) -{ - sensor.frame_callback = vsync_cb; - return 0; -} - -int sensor_set_color_palette(const uint16_t *color_palette) -{ - sensor.color_palette = color_palette; - return 0; -} - -const uint16_t *sensor_get_color_palette() -{ - return sensor.color_palette; -} - -void VsyncExtiCallback() -{ - if (sensor.vsync_callback != NULL) { - //sensor.vsync_callback(HAL_GPIO_ReadPin(DCMI_VSYNC_PORT, DCMI_VSYNC_PIN)); - } -} - -// To make the user experience better we automatically shrink the size of the MAIN_FB() to fit -// within the RAM we have onboard the system. -int sensor_check_buffsize() -{ - uint32_t bpp; - uint32_t size = framebuffer_get_buffer_size(); - - switch (sensor.pixformat) { - case PIXFORMAT_GRAYSCALE: - case PIXFORMAT_BAYER: - bpp = 1; - break; - case PIXFORMAT_RGB565: - case PIXFORMAT_YUV422: - bpp = 2; - break; - default: - return -1; - } - - // This driver doesn't support windowing or anything like that. - if ((MAIN_FB()->u * MAIN_FB()->v * bpp) > size) { - return -1; - } - - return 0; -} - static void dma_irq_handler() { - // Clear the interrupt request. - dma_irqn_acknowledge_channel(DCMI_DMA, DCMI_DMA_CHANNEL); + if (dma_irqn_get_channel_status(DCMI_DMA, DCMI_DMA_CHANNEL)) { + // Clear the interrupt request. + dma_irqn_acknowledge_channel(DCMI_DMA, DCMI_DMA_CHANNEL); - framebuffer_get_tail(FB_NO_FLAGS); - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); - if (buffer != NULL) { - // Set next buffer and retrigger the DMA channel. - dma_channel_set_write_addr(DCMI_DMA_CHANNEL, buffer->data, true); + framebuffer_get_tail(FB_NO_FLAGS); + vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + if (buffer != NULL) { + // Set next buffer and retrigger the DMA channel. + dma_channel_set_write_addr(DCMI_DMA_CHANNEL, buffer->data, true); - // Unblock the state machine - pio_sm_restart(DCMI_PIO, DCMI_SM); - pio_sm_clear_fifos(DCMI_PIO, DCMI_SM); - pio_sm_put_blocking(DCMI_PIO, DCMI_SM, (MAIN_FB()->v - 1)); - pio_sm_put_blocking(DCMI_PIO, DCMI_SM, (MAIN_FB()->u * MAIN_FB()->bpp) - 1); + // Unblock the state machine + pio_sm_restart(DCMI_PIO, DCMI_SM); + pio_sm_clear_fifos(DCMI_PIO, DCMI_SM); + pio_sm_put_blocking(DCMI_PIO, DCMI_SM, (MAIN_FB()->v - 1)); + pio_sm_put_blocking(DCMI_PIO, DCMI_SM, (MAIN_FB()->u * MAIN_FB()->bpp) - 1); + } } } @@ -986,7 +213,7 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) // Compress the framebuffer for the IDE preview. framebuffer_update_jpeg_buffer(); - if (sensor_check_buffsize() != 0) { + if (sensor_check_framebuffer_size() != 0) { return -1; } @@ -1020,6 +247,11 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) sensor_dma_config(MAIN_FB()->u, MAIN_FB()->v, MAIN_FB()->bpp, (void *) buffer->data, (SENSOR_HW_FLAGS_GET(sensor, SENSOR_HW_FLAGS_RGB565_REV) && MAIN_FB()->bpp == 2)); + + // Re-enable the state machine. + pio_sm_clear_fifos(DCMI_PIO, DCMI_SM); + pio_sm_set_enabled(DCMI_PIO, DCMI_SM, true); + // Unblock the state machine pio_sm_put_blocking(DCMI_PIO, DCMI_SM, (MAIN_FB()->v - 1)); pio_sm_put_blocking(DCMI_PIO, DCMI_SM, (MAIN_FB()->u * MAIN_FB()->bpp) - 1); diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index 785060678..3212d6794 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -138,6 +138,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/common/, \ trace.o \ mutex.o \ usbdbg.o \ + sensor_utils.o \ ) FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/sensors/, \ @@ -505,6 +506,7 @@ UVC_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/common/, \ array.o \ trace.o \ mutex.o \ + sensor_utils.o \ ) UVC_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/sensors/, \ diff --git a/src/omv/ports/stm32/sensor.c b/src/omv/ports/stm32/sensor.c index d78d0f63d..8d7ea3797 100644 --- a/src/omv/ports/stm32/sensor.c +++ b/src/omv/ports/stm32/sensor.c @@ -15,18 +15,6 @@ #include "irq.h" #include "cambus.h" #include "sensor.h" -#include "ov2640.h" -#include "ov5640.h" -#include "ov7725.h" -#include "ov7670.h" -#include "ov7690.h" -#include "ov9650.h" -#include "mt9v034.h" -#include "mt9m114.h" -#include "lepton.h" -#include "hm01b0.h" -#include "paj6100.h" -#include "gc2145.h" #include "framebuffer.h" #include "omv_boardconfig.h" #include "unaligned_memcpy.h" @@ -42,10 +30,6 @@ #define OMV_ENABLE_SENSOR_MDMA_TOTAL_OFFLOAD 1 #endif -#if (OMV_ENABLE_PAJ6100 == 1) -#define OMV_ENABLE_NONI2CIS -#endif - sensor_t sensor = {}; static TIM_HandleTypeDef TIMHandle = {.Instance = DCMI_TIM}; static DMA_HandleTypeDef DMAHandle = {.Instance = DMA2_Stream1}; @@ -64,53 +48,6 @@ static bool drop_frame = false; extern uint8_t _line_buf; -const int resolution[][2] = { - {0, 0 }, - // C/SIF Resolutions - {88, 72 }, /* QQCIF */ - {176, 144 }, /* QCIF */ - {352, 288 }, /* CIF */ - {88, 60 }, /* QQSIF */ - {176, 120 }, /* QSIF */ - {352, 240 }, /* SIF */ - // VGA Resolutions - {40, 30 }, /* QQQQVGA */ - {80, 60 }, /* QQQVGA */ - {160, 120 }, /* QQVGA */ - {320, 240 }, /* QVGA */ - {640, 480 }, /* VGA */ - {30, 20 }, /* HQQQQVGA */ - {60, 40 }, /* HQQQVGA */ - {120, 80 }, /* HQQVGA */ - {240, 160 }, /* HQVGA */ - {480, 320 }, /* HVGA */ - // FFT Resolutions - {64, 32 }, /* 64x32 */ - {64, 64 }, /* 64x64 */ - {128, 64 }, /* 128x64 */ - {128, 128 }, /* 128x128 */ - // Himax Resolutions - {160, 160 }, /* 160x160 */ - {320, 320 }, /* 320x320 */ - // Other - {128, 160 }, /* LCD */ - {128, 160 }, /* QQVGA2 */ - {720, 480 }, /* WVGA */ - {752, 480 }, /* WVGA2 */ - {800, 600 }, /* SVGA */ - {1024, 768 }, /* XGA */ - {1280, 768 }, /* WXGA */ - {1280, 1024}, /* SXGA */ - {1280, 960 }, /* SXGAM */ - {1600, 1200}, /* UXGA */ - {1280, 720 }, /* HD */ - {1920, 1080}, /* FHD */ - {2560, 1440}, /* QHD */ - {2048, 1536}, /* QXGA */ - {2560, 1600}, /* WQXGA */ - {2592, 1944}, /* WQXGA2 */ -}; - void DCMI_IRQHandler(void) { HAL_DCMI_IRQHandler(&DCMIHandle); } @@ -131,7 +68,7 @@ void ISC_SPI_DMA_IRQHandler(void) } #endif // ISC_SPI -static int sesnsor_dma_config() +static int sensor_dma_config() { // DMA Stream configuration #if defined(MCU_SERIES_H7) @@ -164,19 +101,6 @@ static int sesnsor_dma_config() return 0; } -// Returns true if a crop is being applied to the frame buffer. -static bool cropped() -{ - if (sensor.framesize == FRAMESIZE_INVALID) { - return false; - } - - return MAIN_FB()->x // needs to be zero if not being cropped. - || MAIN_FB()->y // needs to be zero if not being cropped. - || (MAIN_FB()->u != resolution[sensor.framesize][0]) // should be equal to the resolution if not cropped. - || (MAIN_FB()->v != resolution[sensor.framesize][1]); // should be equal to the resolution if not cropped. -} - void sensor_init0() { sensor_abort(); @@ -196,300 +120,37 @@ int sensor_init() { int init_ret = 0; - /* Do a power cycle */ - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(10); - - DCMI_PWDN_LOW(); - mp_hal_delay_ms(10); - - // Configure the sensor external clock (XCLK) to XCLK_FREQ. - // - // Max pixclk is 2.5 * HCLK: - // STM32F427@180MHz PCLK = 71.9999MHz - // STM32F769@216MHz PCLK = 86.4000MHz - // - // OV2640: - // The sensor's internal PLL (when CLKRC=0x80) doubles the XCLK_FREQ - // (XCLK=XCLK_FREQ*2), and the unscaled PIXCLK output is XCLK_FREQ*4 - // - // OV7725 PCLK when prescalar is enabled (CLKRC[6]=0): - // Internal clock = Input clock × PLL multiplier / [(CLKRC[5:0] + 1) × 2] - // - // OV7725 PCLK when prescalar is disabled (CLKRC[6]=1): - // Internal clock = Input clock × PLL multiplier - // - #if (OMV_XCLK_SOURCE == OMV_XCLK_TIM) - // Configure external clock timer. - if (sensor_set_xclk_frequency(OMV_XCLK_FREQUENCY) != 0) { - // Timer problem - return -1; - } - #elif (OMV_XCLK_SOURCE == OMV_XCLK_MCO) - // Pass through the MCO1 clock with source input set to HSE (12MHz). - // Note MCO1 is multiplexed on OPENMV2/TIM1 only. - HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1); - #elif (OMV_XCLK_SOURCE == OMV_XCLK_OSC) - // An external oscillator is used for the sensor clock. - // Nothing to do. - #else - #error "OMV_XCLK_SOURCE is not set!" - #endif - - /* Reset the sesnor state */ + // Reset the sesnor state memset(&sensor, 0, sizeof(sensor_t)); - /* Some sensors have different reset polarities, and we can't know which sensor - is connected before initializing cambus and probing the sensor, which in turn - requires pulling the sensor out of the reset state. So we try to probe the - sensor with both polarities to determine line state. */ - sensor.pwdn_pol = ACTIVE_HIGH; - sensor.reset_pol = ACTIVE_HIGH; - - /* Reset the sensor */ - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - - // Initialize the camera bus. - cambus_init(&sensor.bus, ISC_I2C_ID, ISC_I2C_SPEED); - mp_hal_delay_ms(10); - - /* Probe the sensor */ - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - /* Sensor has been held in reset, - so the reset line is active low */ - sensor.reset_pol = ACTIVE_LOW; - - /* Pull the sensor out of the reset state */ - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - - /* Probe again to set the slave addr */ - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - sensor.pwdn_pol = ACTIVE_LOW; - - DCMI_PWDN_HIGH(); - mp_hal_delay_ms(10); - - sensor.slv_addr = cambus_scan(&sensor.bus); - if (sensor.slv_addr == 0) { - sensor.reset_pol = ACTIVE_HIGH; - - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - - sensor.slv_addr = cambus_scan(&sensor.bus); - #ifndef OMV_ENABLE_NONI2CIS - if (sensor.slv_addr == 0) { - return -2; - } - #endif - } - } - } - - // Clear sensor chip ID. - sensor.chip_id_w = 0; - // Set default snapshot function. + // Some sensors need to call snapshot from init. sensor.snapshot = sensor_snapshot; - switch (sensor.slv_addr) { - #if (OMV_ENABLE_PAJ6100 == 1) - case 0: - if (paj6100_detect(&sensor)) { - // Found PixArt PAJ6100 - sensor.chip_id_w = PAJ6100_ID; - sensor.pwdn_pol = ACTIVE_LOW; - sensor.reset_pol = ACTIVE_LOW; - break; - } - // Okay, there is not any sensor be detected. - return -2; - #endif - - #if (OMV_ENABLE_OV2640 == 1) - case OV2640_SLV_ADDR: // Or OV9650. - cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); - break; - #endif // (OMV_ENABLE_OV2640 == 1) - - #if (OMV_ENABLE_OV5640 == 1) - case OV5640_SLV_ADDR: - cambus_readb2(&sensor.bus, sensor.slv_addr, OV5640_CHIP_ID, &sensor.chip_id); - break; - #endif // (OMV_ENABLE_OV5640 == 1) - - #if (OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) - case OV7725_SLV_ADDR: // Or OV7690 or OV7670. - cambus_readb(&sensor.bus, sensor.slv_addr, OV_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_OV7725 == 1) || (OMV_ENABLE_OV7670 == 1) || (OMV_ENABLE_OV7690 == 1) - - #if (OMV_ENABLE_MT9V034 == 1) - case MT9V034_SLV_ADDR: - cambus_readb(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_MT9V034 == 1) - - #if (OMV_ENABLE_MT9M114 == 1) - case MT9M114_SLV_ADDR: - cambus_readw2(&sensor.bus, sensor.slv_addr, ON_CHIP_ID, &sensor.chip_id_w); - break; - #endif // (OMV_ENABLE_MT9M114 == 1) - - #if (OMV_ENABLE_LEPTON == 1) - case LEPTON_SLV_ADDR: - sensor.chip_id = LEPTON_ID; - break; - #endif // (OMV_ENABLE_LEPTON == 1) - - #if (OMV_ENABLE_HM01B0 == 1) - case HM01B0_SLV_ADDR: - cambus_readb2(&sensor.bus, sensor.slv_addr, HIMAX_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_HM01B0 == 1) - - #if (OMV_ENABLE_GC2145 == 1) - case GC2145_SLV_ADDR: - cambus_readb(&sensor.bus, sensor.slv_addr, GC_CHIP_ID, &sensor.chip_id); - break; - #endif //(OMV_ENABLE_GC2145 == 1) - - default: - return -3; - break; - } - switch (sensor.chip_id_w) { - #if (OMV_ENABLE_OV2640 == 1) - case OV2640_ID: - if (sensor_set_xclk_frequency(OV2640_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov2640_init(&sensor); - break; - #endif // (OMV_ENABLE_OV2640 == 1) - - #if (OMV_ENABLE_OV5640 == 1) - case OV5640_ID: - if (sensor_set_xclk_frequency(OV5640_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov5640_init(&sensor); - break; - #endif // (OMV_ENABLE_OV5640 == 1) - - #if (OMV_ENABLE_OV7670 == 1) - case OV7670_ID: - if (sensor_set_xclk_frequency(OV7670_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov7670_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7670 == 1) - - #if (OMV_ENABLE_OV7690 == 1) - case OV7690_ID: - if (sensor_set_xclk_frequency(OV7690_XCLK_FREQ) != 0) { - return -3; - } - init_ret = ov7690_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7690 == 1) - - #if (OMV_ENABLE_OV7725 == 1) - case OV7725_ID: - init_ret = ov7725_init(&sensor); - break; - #endif // (OMV_ENABLE_OV7725 == 1) - - #if (OMV_ENABLE_OV9650 == 1) - case OV9650_ID: - init_ret = ov9650_init(&sensor); - break; - #endif // (OMV_ENABLE_OV9650 == 1) - - #if (OMV_ENABLE_MT9V034 == 1) - case MT9V034_ID: - if (sensor_set_xclk_frequency(MT9V034_XCLK_FREQ) != 0) { - return -3; - } - init_ret = mt9v034_init(&sensor); - break; - #endif //(OMV_ENABLE_MT9V034 == 1) - - #if (OMV_ENABLE_MT9M114 == 1) - case MT9M114_ID: - if (sensor_set_xclk_frequency(MT9M114_XCLK_FREQ) != 0) { - return -3; - } - init_ret = mt9m114_init(&sensor); - break; - #endif //(OMV_ENABLE_MT9M114 == 1) - - #if (OMV_ENABLE_LEPTON == 1) - case LEPTON_ID: - if (sensor_set_xclk_frequency(LEPTON_XCLK_FREQ) != 0) { - return -3; - } - init_ret = lepton_init(&sensor); - break; - #endif // (OMV_ENABLE_LEPTON == 1) - - #if (OMV_ENABLE_HM01B0 == 1) - case HM01B0_ID: - init_ret = hm01b0_init(&sensor); - break; - #endif //(OMV_ENABLE_HM01B0 == 1) - - #if (OMV_ENABLE_GC2145 == 1) - case GC2145_ID: - if (sensor_set_xclk_frequency(GC2145_XCLK_FREQ) != 0) { - return -3; - } - init_ret = gc2145_init(&sensor); - break; - #endif //(OMV_ENABLE_GC2145 == 1) - - #if (OMV_ENABLE_PAJ6100 == 1) - case PAJ6100_ID: - if (sensor_set_xclk_frequency(PAJ6100_XCLK_FREQ) != 0) { - return -3; - } - init_ret = paj6100_init(&sensor); - break; - #endif // (OMV_ENABLE_PAJ6100 == 1) - - default: - return -3; - break; + // Configure the sensor external clock (XCLK). + if (sensor_set_xclk_frequency(OMV_XCLK_FREQUENCY) != 0) { + // Failed to initialize the sensor clock. + return -1; } - if (init_ret != 0 ) { - // Sensor init failed. - return -4; + // Detect and initialize the image sensor. + if ((init_ret = sensor_probe_init()) != 0) { + // Sensor probe/init failed. + return init_ret; } - /* Configure the DCMI DMA Stream */ - if (sesnsor_dma_config() != 0) { + // Configure the DCMI DMA Stream + if (sensor_dma_config() != 0) { // DMA problem return -5; } // Configure the DCMI interface. - if (sensor_dcmi_config(PIXFORMAT_INVALID) != 0) { + if (sensor_dcmi_config(PIXFORMAT_INVALID) != 0){ // DCMI config failed return -6; } - // Disable VSYNC EXTI IRQ - HAL_NVIC_DisableIRQ(DCMI_VSYNC_IRQN); - // Clear fb_enabled flag // This is executed only once to initialize the FB enabled flag. JPEG_FB()->enabled = 0; @@ -571,65 +232,6 @@ int sensor_abort() return 0; } -int sensor_reset() -{ - sensor_abort(); - - // Reset the sensor state - sensor.sde = 0; - sensor.pixformat = 0; - sensor.framesize = 0; - sensor.framerate = 0; - sensor.last_frame_ms = 0; - sensor.last_frame_ms_valid = false; - sensor.gainceiling = 0; - sensor.hmirror = false; - sensor.vflip = false; - sensor.transpose = false; - #if MICROPY_PY_IMU - sensor.auto_rotation = sensor.chip_id == OV7690_ID; - #else - sensor.auto_rotation = false; - #endif // MICROPY_PY_IMU - sensor.vsync_callback = NULL; - sensor.frame_callback = NULL; - - // Reset default color palette. - sensor.color_palette = rainbow_table; - - sensor.disable_full_flush = false; - - // Restore shutdown state on reset. - sensor_shutdown(false); - - // Hard-reset the sensor - if (sensor.reset_pol == ACTIVE_HIGH) { - DCMI_RESET_HIGH(); - mp_hal_delay_ms(10); - DCMI_RESET_LOW(); - } else { - DCMI_RESET_LOW(); - mp_hal_delay_ms(10); - DCMI_RESET_HIGH(); - } - mp_hal_delay_ms(20); - - // Call sensor-specific reset function - if (sensor.reset(&sensor) != 0) { - return -1; - } - - // Disable VSYNC EXTI IRQ - HAL_NVIC_DisableIRQ(DCMI_VSYNC_IRQN); - - return 0; -} - -int sensor_get_id() -{ - return sensor.chip_id_w; -} - uint32_t sensor_get_xclk_frequency() { return (DCMI_TIM_PCLK_FREQ() * 2) / (TIMHandle.Init.Period + 1); @@ -675,25 +277,16 @@ int sensor_set_xclk_frequency(uint32_t frequency) || (HAL_TIM_PWM_Start(&TIMHandle, DCMI_TIM_CHANNEL) != HAL_OK)) { return -1; } + #elif (OMV_XCLK_SOURCE == OMV_XCLK_MCO) + // Pass through the MCO1 clock with source input set to HSE (12MHz). + // Note MCO1 is multiplexed on OPENMV2/TIM1 only. + HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1); + #elif (OMV_XCLK_SOURCE == OMV_XCLK_OSC) + // An external oscillator is used for the sensor clock. + // Configure and enable external oscillator if needed. + #else + #error "OMV_XCLK_SOURCE is not set!" #endif // (OMV_XCLK_SOURCE == OMV_XCLK_TIM) - - return 0; -} - -bool sensor_is_detected() -{ - return sensor.detected; -} - -int sensor_sleep(int enable) -{ - sensor_abort(); - - if (sensor.sleep == NULL - || sensor.sleep(&sensor, enable) != 0) { - // Operation not supported - return -1; - } return 0; } @@ -723,454 +316,6 @@ int sensor_shutdown(int enable) return ret; } -int sensor_read_reg(uint16_t reg_addr) -{ - if (sensor.read_reg == NULL) { - // Operation not supported - return -1; - } - return sensor.read_reg(&sensor, reg_addr); -} - -int sensor_write_reg(uint16_t reg_addr, uint16_t reg_data) -{ - if (sensor.write_reg == NULL) { - // Operation not supported - return -1; - } - return sensor.write_reg(&sensor, reg_addr, reg_data); -} - -int sensor_set_pixformat(pixformat_t pixformat) -{ - if (sensor.pixformat == pixformat) { - // No change - return 0; - } - - // sensor_check_buffsize() will switch from PIXFORMAT_BAYER to PIXFORMAT_RGB565 to try to fit - // the MAIN_FB() in RAM as a first step optimization. If the user tries to switch back to RGB565 - // and that would be bigger than the RAM buffer we would just switch back. - // - // So, just short-circuit doing any work. - // - // This code is explicitly here to allow users to set the resolution to RGB565 and have it - // switch to BAYER only once even though they are setting the resolution to RGB565 repeatedly - // in a loop. Only RGB565->BAYER has this problem and needs this fix because of sensor_check_buffsize(). - uint32_t size = framebuffer_get_buffer_size(); - if ((sensor.pixformat == PIXFORMAT_BAYER) - && (pixformat == PIXFORMAT_RGB565) - && (MAIN_FB()->u * MAIN_FB()->v * 2 > size) - && (MAIN_FB()->u * MAIN_FB()->v * 1 <= size)) { - // No change - return 0; - } - - // Cropping and transposing (and thus auto rotation) don't work in JPEG mode. - if ((pixformat == PIXFORMAT_JPEG) && (cropped() || sensor.transpose || sensor.auto_rotation)) { - return -1; - } - - sensor_abort(); - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - if (sensor.set_pixformat == NULL - || sensor.set_pixformat(&sensor, pixformat) != 0) { - // Operation not supported - return -1; - } - - mp_hal_delay_ms(100); // wait for the camera to settle - - // Set pixel format - sensor.pixformat = pixformat; - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); - - // Re-configure DCMI. - return sensor_dcmi_config(pixformat); -} - -int sensor_set_framesize(framesize_t framesize) -{ - if (sensor.framesize == framesize) { - // No change - return 0; - } - - sensor_abort(); - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - // Call the sensor specific function - if (sensor.set_framesize == NULL - || sensor.set_framesize(&sensor, framesize) != 0) { - // Operation not supported - return -1; - } - - mp_hal_delay_ms(100); // wait for the camera to settle - - // Set framebuffer size - sensor.framesize = framesize; - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - // Set MAIN FB x offset, y offset, width, height, backup width, and backup height. - MAIN_FB()->x = 0; - MAIN_FB()->y = 0; - MAIN_FB()->w = MAIN_FB()->u = resolution[framesize][0]; - MAIN_FB()->h = MAIN_FB()->v = resolution[framesize][1]; - - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); - - return 0; -} - -int sensor_set_framerate(int framerate) -{ - if (sensor.framerate == framerate) { - // No change - return 0; - } - - if (framerate < 0) { - return -1; - } - - // Call the sensor specific function (does not fail if function is not set) - if (sensor.set_framerate != NULL) { - if (sensor.set_framerate(&sensor, framerate) != 0) { - // Operation not supported - return -1; - } - } - - // Set framerate - sensor.framerate = framerate; - return 0; -} - -int sensor_set_windowing(int x, int y, int w, int h) -{ - if ((MAIN_FB()->x == x) && (MAIN_FB()->y == y) && (MAIN_FB()->u == w) && (MAIN_FB()->v == h)) { - // No change - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - sensor_abort(); - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - // Skip the first frame. - MAIN_FB()->bpp = -1; - - MAIN_FB()->x = x; - MAIN_FB()->y = y; - MAIN_FB()->w = MAIN_FB()->u = w; - MAIN_FB()->h = MAIN_FB()->v = h; - - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); - - return 0; -} - -int sensor_set_contrast(int level) -{ - if (sensor.set_contrast != NULL) { - return sensor.set_contrast(&sensor, level); - } - return -1; -} - -int sensor_set_brightness(int level) -{ - if (sensor.set_brightness != NULL) { - return sensor.set_brightness(&sensor, level); - } - return -1; -} - -int sensor_set_saturation(int level) -{ - if (sensor.set_saturation != NULL) { - return sensor.set_saturation(&sensor, level); - } - return -1; -} - -int sensor_set_gainceiling(gainceiling_t gainceiling) -{ - if (sensor.gainceiling == gainceiling) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_gainceiling == NULL - || sensor.set_gainceiling(&sensor, gainceiling) != 0) { - /* operation not supported */ - return -1; - } - - sensor.gainceiling = gainceiling; - return 0; -} - -int sensor_set_quality(int qs) -{ - /* call the sensor specific function */ - if (sensor.set_quality == NULL - || sensor.set_quality(&sensor, qs) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_colorbar(int enable) -{ - /* call the sensor specific function */ - if (sensor.set_colorbar == NULL - || sensor.set_colorbar(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_gain(int enable, float gain_db, float gain_db_ceiling) -{ - /* call the sensor specific function */ - if (sensor.set_auto_gain == NULL - || sensor.set_auto_gain(&sensor, enable, gain_db, gain_db_ceiling) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_gain_db(float *gain_db) -{ - /* call the sensor specific function */ - if (sensor.get_gain_db == NULL - || sensor.get_gain_db(&sensor, gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_exposure(int enable, int exposure_us) -{ - /* call the sensor specific function */ - if (sensor.set_auto_exposure == NULL - || sensor.set_auto_exposure(&sensor, enable, exposure_us) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_exposure_us(int *exposure_us) -{ - /* call the sensor specific function */ - if (sensor.get_exposure_us == NULL - || sensor.get_exposure_us(&sensor, exposure_us) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db) -{ - /* call the sensor specific function */ - if (sensor.set_auto_whitebal == NULL - || sensor.set_auto_whitebal(&sensor, enable, r_gain_db, g_gain_db, b_gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_gain_db) -{ - /* call the sensor specific function */ - if (sensor.get_rgb_gain_db == NULL - || sensor.get_rgb_gain_db(&sensor, r_gain_db, g_gain_db, b_gain_db) != 0) { - /* operation not supported */ - return -1; - } - return 0; -} - -int sensor_set_hmirror(int enable) -{ - if (sensor.hmirror == ((bool) enable)) { - /* no change */ - return 0; - } - - sensor_abort(); - - /* call the sensor specific function */ - if (sensor.set_hmirror == NULL - || sensor.set_hmirror(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - sensor.hmirror = enable; - mp_hal_delay_ms(100); // wait for the camera to settle - return 0; -} - -bool sensor_get_hmirror() -{ - return sensor.hmirror; -} - -int sensor_set_vflip(int enable) -{ - if (sensor.vflip == ((bool) enable)) { - /* no change */ - return 0; - } - - sensor_abort(); - - /* call the sensor specific function */ - if (sensor.set_vflip == NULL - || sensor.set_vflip(&sensor, enable) != 0) { - /* operation not supported */ - return -1; - } - sensor.vflip = enable; - mp_hal_delay_ms(100); // wait for the camera to settle - return 0; -} - -bool sensor_get_vflip() -{ - return sensor.vflip; -} - -int sensor_set_transpose(bool enable) -{ - if (sensor.transpose == enable) { - /* no change */ - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - sensor_abort(); - - sensor.transpose = enable; - return 0; -} - -bool sensor_get_transpose() -{ - return sensor.transpose; -} - -int sensor_set_auto_rotation(bool enable) -{ - if (sensor.auto_rotation == enable) { - /* no change */ - return 0; - } - - if (sensor.pixformat == PIXFORMAT_JPEG) { - return -1; - } - - sensor_abort(); - - sensor.auto_rotation = enable; - return 0; -} - -bool sensor_get_auto_rotation() -{ - return sensor.auto_rotation; -} - -int sensor_set_framebuffers(int count) -{ - sensor_abort(); - - // Flush previous frame. - framebuffer_update_jpeg_buffer(); - - return framebuffer_set_buffers(count); -} - -int sensor_set_special_effect(sde_t sde) -{ - if (sensor.sde == sde) { - /* no change */ - return 0; - } - - /* call the sensor specific function */ - if (sensor.set_special_effect == NULL - || sensor.set_special_effect(&sensor, sde) != 0) { - /* operation not supported */ - return -1; - } - - sensor.sde = sde; - return 0; -} - -int sensor_set_lens_correction(int enable, int radi, int coef) -{ - /* call the sensor specific function */ - if (sensor.set_lens_correction == NULL - || sensor.set_lens_correction(&sensor, enable, radi, coef) != 0) { - /* operation not supported */ - return -1; - } - - return 0; -} - -int sensor_ioctl(int request, ... /* arg */) -{ - sensor_abort(); - - int ret = -1; - - if (sensor.ioctl != NULL) { - va_list ap; - va_start(ap, request); - /* call the sensor specific function */ - ret = sensor.ioctl(&sensor, request, ap); - va_end(ap); - } - - return ret; -} - int sensor_set_vsync_callback(vsync_cb_t vsync_cb) { sensor.vsync_callback = vsync_cb; @@ -1185,23 +330,6 @@ int sensor_set_vsync_callback(vsync_cb_t vsync_cb) return 0; } -int sensor_set_frame_callback(frame_cb_t vsync_cb) -{ - sensor.frame_callback = vsync_cb; - return 0; -} - -int sensor_set_color_palette(const uint16_t *color_palette) -{ - sensor.color_palette = color_palette; - return 0; -} - -const uint16_t *sensor_get_color_palette() -{ - return sensor.color_palette; -} - void DCMI_VsyncExtiCallback() { __HAL_GPIO_EXTI_CLEAR_FLAG(1 << DCMI_VSYNC_IRQ_LINE); @@ -1210,36 +338,6 @@ void DCMI_VsyncExtiCallback() } } -static uint32_t get_src_bytes_per_pixel() -{ - switch (sensor.pixformat) { - case PIXFORMAT_GRAYSCALE: - return sensor.gs_bpp; - case PIXFORMAT_RGB565: - case PIXFORMAT_YUV422: - return 2; - case PIXFORMAT_BAYER: - case PIXFORMAT_JPEG: - return 1; - default: - return 0; - } -} - -static uint32_t get_dst_bytes_per_pixel() -{ - switch (sensor.pixformat) { - case PIXFORMAT_GRAYSCALE: - case PIXFORMAT_BAYER: - return 1; - case PIXFORMAT_RGB565: - case PIXFORMAT_YUV422: - return 2; - default: - return 0; - } -} - // If we are cropping the image by more than 1 word in width we can align the line start to // a word address to improve copy performance. Do not crop by more than 1 word as this will // result in less time between DMA transfers complete interrupts on 16-byte boundaries. @@ -1256,91 +354,6 @@ static uint32_t get_dcmi_hw_crop(uint32_t bytes_per_pixel) return x_crop; } -// To make the user experience better we automatically shrink the size of the MAIN_FB() to fit -// within the RAM we have onboard the system. -static void sensor_check_buffsize() -{ - uint32_t size = framebuffer_get_buffer_size(); - uint32_t bpp = get_dst_bytes_per_pixel(); - - // If the pixformat is NULL/JPEG there we can't do anything to check if it fits before hand. - if (!bpp) { - return; - } - - // MAIN_FB() fits, we are done. - if ((MAIN_FB()->u * MAIN_FB()->v * bpp) <= size) { - return; - } - - if (sensor.pixformat == PIXFORMAT_RGB565) { - // Switch to bayer for the quick 2x savings. - sensor_set_pixformat(PIXFORMAT_BAYER); - bpp = 1; - - // MAIN_FB() fits, we are done (bpp is 1). - if ((MAIN_FB()->u * MAIN_FB()->v) <= size) { - return; - } - } - - int window_w = MAIN_FB()->u; - int window_h = MAIN_FB()->v; - - // We need to shrink the frame buffer. We can do this by cropping. So, we will subtract columns - // and rows from the frame buffer until it fits within the frame buffer. - int max = IM_MAX(window_w, window_h); - int min = IM_MIN(window_w, window_h); - float aspect_ratio = max / ((float) min); - float r = aspect_ratio, best_r = r; - int c = 1, best_c = c; - float best_err = FLT_MAX; - - // Find the width/height ratio that's within 1% of the aspect ratio with a loop limit. - for (int i = 100; i; i--) { - float err = fast_fabsf(r - fast_roundf(r)); - - if (err <= best_err) { - best_err = err; - best_r = r; - best_c = c; - } - - if (best_err <= 0.01f) { - break; - } - - r += aspect_ratio; - c += 1; - } - - // Select the larger geometry to map the aspect ratio to. - int u_sub, v_sub; - - if (window_w > window_h) { - u_sub = fast_roundf(best_r); - v_sub = best_c; - } else { - u_sub = best_c; - v_sub = fast_roundf(best_r); - } - - // Crop the frame buffer while keeping the aspect ratio and keeping the width/height even. - while (((MAIN_FB()->u * MAIN_FB()->v * bpp) > size) || (MAIN_FB()->u % 2) || (MAIN_FB()->v % 2)) { - MAIN_FB()->u -= u_sub; - MAIN_FB()->v -= v_sub; - } - - // Center the new window using the previous offset and keep the offset even. - MAIN_FB()->x += (window_w - MAIN_FB()->u) / 2; - MAIN_FB()->y += (window_h - MAIN_FB()->v) / 2; - if (MAIN_FB()->x % 2) MAIN_FB()->x -= 1; - if (MAIN_FB()->y % 2) MAIN_FB()->y -= 1; - - // Pickout a good buffer count for the user. - framebuffer_auto_adjust_buffers(); -} - // Stop allowing new data in on the end of the frame and let snapshot know that the frame has been // received. Note that DCMI_DMAConvCpltUser() is called before DCMI_IT_FRAME is enabled by // DCMI_DMAXferCplt() so this means that the last line of data is *always* transferred before @@ -1505,7 +518,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) } #endif - uint32_t bytes_per_pixel = get_src_bytes_per_pixel(); + uint32_t bytes_per_pixel = sensor_get_src_bpp(); uint8_t *src = ((uint8_t *) addr) + (MAIN_FB()->x * bytes_per_pixel) - get_dcmi_hw_crop(bytes_per_pixel); uint8_t *dst = buffer->data; @@ -1708,7 +721,7 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) // Make sure the raw frame fits into the FB. It will be switched from RGB565 to BAYER // first to save space before being cropped until it fits. - sensor_check_buffsize(); + sensor_auto_crop_framebuffer(); // The user may have changed the MAIN_FB width or height on the last image so we need // to restore that here. We don't have to restore bpp because that's taken care of @@ -1734,7 +747,7 @@ int sensor_snapshot(sensor_t *sensor, image_t *image, uint32_t flags) // need to wait till there's no frame happening before enabling. if (!(DCMI->CR & DCMI_CR_ENABLE)) { // Setup the size and address of the transfer - uint32_t bytes_per_pixel = get_src_bytes_per_pixel(); + uint32_t bytes_per_pixel = sensor_get_src_bpp(); // Error out if the pixformat is not set. if (!bytes_per_pixel) {