From 9124d765a9396ec81062e8bf1843f347dec99199 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Tue, 8 Jun 2021 21:16:53 -0700 Subject: [PATCH] set_frame_rate now works for any FPS --- src/omv/common/sensor.h | 2 + src/omv/ports/nrf/sensor.c | 43 +++++++------ src/omv/ports/stm32/sensor.c | 114 ++++++++++++++++++++++++++++------- src/omv/sensors/hm01b0.c | 42 ++++++------- 4 files changed, 136 insertions(+), 65 deletions(-) diff --git a/src/omv/common/sensor.h b/src/omv/common/sensor.h index a3f0695bd..176feb979 100644 --- a/src/omv/common/sensor.h +++ b/src/omv/common/sensor.h @@ -175,6 +175,8 @@ typedef struct _sensor { pixformat_t pixformat; // Pixel format framesize_t framesize; // Frame size int framerate; // Frame rate + uint32_t last_frame_ms; // Last sampled frame timestamp in milliseconds. + bool last_frame_ms_valid; // Last sampled frame timestamp in milliseconds valid. gainceiling_t gainceiling; // AGC gainceiling bool hmirror; // Horizontal Mirror bool vflip; // Vertical Flip diff --git a/src/omv/ports/nrf/sensor.c b/src/omv/ports/nrf/sensor.c index 5982959ca..dd8dde006 100644 --- a/src/omv/ports/nrf/sensor.c +++ b/src/omv/ports/nrf/sensor.c @@ -410,24 +410,26 @@ int sensor_reset() framebuffer_reset_buffers(); // 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; + 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; + sensor.auto_rotation = sensor.chip_id == OV7690_ID; #else - sensor.auto_rotation = false; + sensor.auto_rotation = false; #endif // MICROPY_PY_IMU - sensor.vsync_callback= NULL; - sensor.frame_callback= NULL; + sensor.vsync_callback = NULL; + sensor.frame_callback = NULL; // Reset default color palette. - sensor.color_palette = rainbow_table; + sensor.color_palette = rainbow_table; // Restore shutdown state on reset. sensor_shutdown(false); @@ -602,13 +604,20 @@ int sensor_set_framerate(int framerate) return 0; } - // Call the sensor specific function - if (sensor.set_framerate == NULL - || sensor.set_framerate(&sensor, framerate) != 0) { - // Operation not supported + 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; } diff --git a/src/omv/ports/stm32/sensor.c b/src/omv/ports/stm32/sensor.c index 713f8f214..4959c3868 100644 --- a/src/omv/ports/stm32/sensor.c +++ b/src/omv/ports/stm32/sensor.c @@ -50,6 +50,8 @@ static DCMI_HandleTypeDef DCMIHandle = {.Instance = DCMI}; static MDMA_HandleTypeDef DCMI_MDMA_Handle0 = {.Instance = MDMA_Channel0}; static MDMA_HandleTypeDef DCMI_MDMA_Handle1 = {.Instance = MDMA_Channel1}; #endif +static bool first_line = false; +static bool drop_frame = false; extern uint8_t _line_buf; @@ -232,6 +234,10 @@ static void dcmi_abort() #endif __HAL_DCMI_DISABLE_IT(&DCMIHandle, DCMI_IT_FRAME); __HAL_DCMI_CLEAR_FLAG(&DCMIHandle, DCMI_FLAG_FRAMERI); + first_line = false; + drop_frame = false; + sensor.last_frame_ms = 0; + sensor.last_frame_ms_valid = false; } framebuffer_reset_buffers(); @@ -559,24 +565,26 @@ int sensor_reset() dcmi_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; + 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; + sensor.auto_rotation = sensor.chip_id == OV7690_ID; #else - sensor.auto_rotation = false; + sensor.auto_rotation = false; #endif // MICROPY_PY_IMU - sensor.vsync_callback= NULL; - sensor.frame_callback= NULL; + sensor.vsync_callback = NULL; + sensor.frame_callback = NULL; // Reset default color palette. - sensor.color_palette = rainbow_table; + sensor.color_palette = rainbow_table; // Restore shutdown state on reset. sensor_shutdown(false); @@ -719,6 +727,9 @@ int sensor_set_pixformat(pixformat_t pixformat) // Skip the first frame. MAIN_FB()->bpp = -1; + // Pickout a good buffer count for the user. + framebuffer_auto_adjust_buffers(); + // Change the JPEG mode. return dcmi_config((pixformat == PIXFORMAT_JPEG) ? DCMI_JPEG_ENABLE : DCMI_JPEG_DISABLE); } @@ -769,13 +780,20 @@ int sensor_set_framerate(int framerate) return 0; } - // Call the sensor specific function - if (sensor.set_framerate == NULL - || sensor.set_framerate(&sensor, framerate) != 0) { - // Operation not supported + 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; } @@ -792,6 +810,7 @@ int sensor_set_windowing(int x, int y, int w, int h) dcmi_abort(); + // Flush previous frame. framebuffer_update_jpeg_buffer(); // Skip the first frame. @@ -945,6 +964,8 @@ int sensor_set_hmirror(int enable) return 0; } + dcmi_abort(); + /* call the sensor specific function */ if (sensor.set_hmirror == NULL || sensor.set_hmirror(&sensor, enable) != 0) { @@ -968,6 +989,8 @@ int sensor_set_vflip(int enable) return 0; } + dcmi_abort(); + /* call the sensor specific function */ if (sensor.set_vflip == NULL || sensor.set_vflip(&sensor, enable) != 0) { @@ -995,6 +1018,8 @@ int sensor_set_transpose(bool enable) return -1; } + dcmi_abort(); + sensor.transpose = enable; return 0; } @@ -1015,6 +1040,8 @@ int sensor_set_auto_rotation(bool enable) return -1; } + dcmi_abort(); + sensor.auto_rotation = enable; return 0; } @@ -1257,18 +1284,26 @@ static void sensor_check_buffsize() // moving the tail to the next buffer. void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { - framebuffer_get_tail(FB_NO_FLAGS); - - if (sensor.frame_callback) { - sensor.frame_callback(); - } - + // This can be executed at any time since this interrupt has a higher priority than DMA2_Stream1_IRQn. #if (OMV_ENABLE_SENSOR_MDMA_TOTAL_OFFLOAD == 1) // Clear out any stale flags. DMA2->LIFCR = DMA_FLAG_TCIF1_5 | DMA_FLAG_HTIF1_5; // Re-enable the DMA IRQ to catch the next start line. HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn); #endif + + // Reset DCMI_DMAConvCpltUser frame drop state. + first_line = false; + if (drop_frame) { + drop_frame = false; + return; + } + + framebuffer_get_tail(FB_NO_FLAGS); + + if (sensor.frame_callback) { + sensor.frame_callback(); + } } #if (OMV_ENABLE_SENSOR_MDMA == 1) @@ -1296,6 +1331,35 @@ static void mdma_memcpy(vbuffer_t *buffer, void *dst, void *src, int bpp, bool t // DMA transfers the next line to the other half of the line buffer. void DCMI_DMAConvCpltUser(uint32_t addr) { + if (!first_line) { + first_line = true; + uint32_t tick = HAL_GetTick(); + uint32_t framerate_ms = IM_DIV(1000, sensor.framerate); + + // Drops frames to match the frame rate requested by the user. The frame is NOT copied to + // SRAM/SDRAM when dropping to save CPU cycles/energy that would be wasted. + // If framerate is zero then this does nothing... + if (sensor.last_frame_ms_valid && ((tick - sensor.last_frame_ms) < framerate_ms)) { + drop_frame = true; + } else if (sensor.last_frame_ms_valid) { + sensor.last_frame_ms += framerate_ms; + } else { + sensor.last_frame_ms = tick; + sensor.last_frame_ms_valid = true; + } + } + + if (drop_frame) { + // If we're dropping a frame in full offload mode it's safe to disable this interrupt saving + // ourselves from having to service the DMA complete callback. + #if (OMV_ENABLE_SENSOR_MDMA_TOTAL_OFFLOAD == 1) + if (!sensor.transpose) { + HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); + } + #endif + return; + } + vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); // If snapshot was not already waiting to receive data then we have missed this frame and have @@ -1309,6 +1373,10 @@ void DCMI_DMAConvCpltUser(uint32_t addr) #endif __HAL_DCMI_DISABLE_IT(&DCMIHandle, DCMI_IT_FRAME); __HAL_DCMI_CLEAR_FLAG(&DCMIHandle, DCMI_FLAG_FRAMERI); + first_line = false; + drop_frame = false; + sensor.last_frame_ms = 0; + sensor.last_frame_ms_valid = false; // Reset the queue of frames when we start dropping frames. framebuffer_flush_buffers(); return; diff --git a/src/omv/sensors/hm01b0.c b/src/omv/sensors/hm01b0.c index bbe9260c6..1658a29da 100644 --- a/src/omv/sensors/hm01b0.c +++ b/src/omv/sensors/hm01b0.c @@ -49,15 +49,15 @@ static const uint16_t default_regs[][2] = { {0x3064, 0x00}, {0x3065, 0x04}, // pad pull 0 {ANA_Register_17, 0x00}, // Disable internal oscillator - + {BLC_CFG, 0x43}, // BLC_on, IIR - + {0x1001, 0x43}, // BLC dithering en {0x1002, 0x43}, // blc_darkpixel_thd {0x0350, 0x7F}, // Dgain Control {BLI_EN, 0x01}, // BLI enable {0x1003, 0x00}, // BLI Target [Def: 0x20] - + {DPC_CTRL, 0x01}, // DPC option 0: DPC off 1 : mono 3 : bayer1 5 : bayer2 {0x1009, 0xA0}, // cluster hot pixel th {0x100A, 0x60}, // cluster cold pixel th @@ -77,7 +77,7 @@ static const uint16_t default_regs[][2] = { {0x2014, 0x58}, {0x2017, 0x00}, {0x2018, 0x9B}, - + {AE_CTRL, 0x01}, //Automatic Exposure {AE_TARGET_MEAN, 0x64}, //AE target mean [Def: 0x3C] {AE_MIN_MEAN, 0x0A}, //AE min target mean [Def: 0x0A] @@ -88,16 +88,16 @@ static const uint16_t default_regs[][2] = { {MAX_AGAIN_FULL, 0x04}, //Maximum Analog gain in full frame mode [Def: 0x03] {MAX_AGAIN_BIN2, 0x04}, //Maximum Analog gain in bin2 mode [Def: 0x04] {MAX_DGAIN, 0xC0}, - + {INTEGRATION_H, 0x01}, //Integration H [Def: 0x01] {INTEGRATION_L, 0x08}, //Integration L [Def: 0x08] {ANALOG_GAIN, 0x00}, //Analog Global Gain [Def: 0x00] {DAMPING_FACTOR, 0x20}, //Damping Factor [Def: 0x20] {DIGITAL_GAIN_H, 0x01}, //Digital Gain High [Def: 0x01] {DIGITAL_GAIN_L, 0x00}, //Digital Gain Low [Def: 0x00] - + {FS_CTRL, 0x00}, //Flicker Control - + {FS_60HZ_H, 0x00}, {FS_60HZ_L, 0x3C}, {FS_50HZ_H, 0x00}, @@ -148,7 +148,7 @@ static int reset(sensor_t *sensor) // Set PCLK polarity. ret |= cambus_writeb2(&sensor->bus, sensor->slv_addr, PCLK_POLARITY, (0x20 | PCLK_FALLING_EDGE)); - + // Set mode to streaming ret |= cambus_writeb2(&sensor->bus, sensor->slv_addr, MODE_SELECT, HIMAX_MODE_STREAMING); @@ -258,7 +258,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) default: if (w>320 || h>320) ret = -1; - + } return ret; @@ -275,22 +275,14 @@ static int set_framerate(sensor_t *sensor, int framerate) highres = true; } - switch (framerate) { - case 15: - osc_div = (highres == true) ? 0x01 : 0x00; - break; - case 30: - osc_div = (highres == true) ? 0x02 : 0x01; - break; - case 60: - osc_div = (highres == true) ? 0x03 : 0x02; - break; - case 120: - // Set to the max possible FPS at this resolution. - osc_div = 0x03; - break; - default: - return -1; + if (framerate <= 15) { + osc_div = (highres == true) ? 0x01 : 0x00; + } else if (framerate <= 30) { + osc_div = (highres == true) ? 0x02 : 0x01; + } else if (framerate <= 60) { + osc_div = (highres == true) ? 0x03 : 0x02; + } else { + osc_div = 0x03; // Set to the max possible FPS at this resolution. } return cambus_writeb2(&sensor->bus, sensor->slv_addr, OSC_CLK_DIV, 0x08 | osc_div); }