Merge pull request #1356 from kwagyeman/kwabena/make_frame_rate_control_work

Add working frame rate control for all sensors to STM32
This commit is contained in:
Ibrahim Abd Elkader 2021-06-09 19:41:00 +02:00 committed by GitHub
commit a7f2a6fcab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 136 additions and 65 deletions

View File

@ -180,6 +180,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

View File

@ -415,24 +415,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;
sensor.disable_full_flush = false;
@ -609,13 +611,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;
}

View File

@ -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;
@ -236,6 +238,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();
@ -563,24 +569,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;
sensor.disable_full_flush = false;
@ -725,6 +733,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);
}
@ -775,13 +786,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;
}
@ -798,6 +816,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.
@ -951,6 +970,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) {
@ -974,6 +995,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) {
@ -1001,6 +1024,8 @@ int sensor_set_transpose(bool enable)
return -1;
}
dcmi_abort();
sensor.transpose = enable;
return 0;
}
@ -1021,6 +1046,8 @@ int sensor_set_auto_rotation(bool enable)
return -1;
}
dcmi_abort();
sensor.auto_rotation = enable;
return 0;
}
@ -1263,18 +1290,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)
@ -1302,6 +1337,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
@ -1315,6 +1379,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.
if (!sensor.disable_full_flush) {
framebuffer_flush_buffers();

View File

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