Move common image sensor driver code to a common file.

* Provide image sensor driver utility functions that can be used by all ports.
* Provide default (weak) functions that can be replaced by port-specific drivers.
This commit is contained in:
iabdalkader 2021-06-29 21:23:02 +02:00
parent 9ca4a06776
commit a700397b07
9 changed files with 1183 additions and 2733 deletions

View File

@ -23,6 +23,7 @@ SRCS += $(addprefix common/, \
trace.c \
mutex.c \
usbdbg.c \
sensor_utils.c \
)
SRCS += $(addprefix sensors/, \

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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/, \

View File

@ -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,596 +159,10 @@ 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.
@ -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);

View File

@ -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

View File

@ -9,23 +9,12 @@
* Sensor abstraction layer for nRF port.
*/
#if MICROPY_PY_SENSOR
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#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,482 +181,14 @@ 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()
{
if (dma_irqn_get_channel_status(DCMI_DMA, DCMI_DMA_CHANNEL)) {
// Clear the interrupt request.
dma_irqn_acknowledge_channel(DCMI_DMA, DCMI_DMA_CHANNEL);
@ -978,6 +204,7 @@ static void dma_irq_handler()
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);
}
}
}
// This is the default snapshot function, which can be replaced in sensor_init functions.
@ -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);

View File

@ -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/, \

File diff suppressed because it is too large Load Diff