diff --git a/common/fb_alloc.c b/common/fb_alloc.c index d4ad6cd2a..4ca2d561b 100644 --- a/common/fb_alloc.c +++ b/common/fb_alloc.c @@ -64,15 +64,17 @@ void fb_alloc_init0() { } uint32_t fb_avail() { - uint32_t temp = pointer - framebuffer_get_buffers_end() - sizeof(uint32_t); + framebuffer_t *fb = framebuffer_get(0); + uint32_t temp = pointer - framebuffer_get_buffers_end(fb) - sizeof(uint32_t); return (temp < sizeof(uint32_t)) ? 0 : temp; } void fb_alloc_mark() { + framebuffer_t *fb = framebuffer_get(0); char *new_pointer = pointer - sizeof(uint32_t); // Check if allocation overwrites the framebuffer pixels - if (new_pointer < framebuffer_get_buffers_end()) { + if (new_pointer < framebuffer_get_buffers_end(fb)) { nlr_jump(MP_OBJ_TO_PTR(mp_obj_new_exception_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Out of fast frame buffer stack memory")))); } @@ -135,6 +137,8 @@ void fb_alloc_free_till_mark_past_mark_permanent() { // returns null pointer without error if size==0 void *fb_alloc(uint32_t size, int hints) { + framebuffer_t *fb = framebuffer_get(0); + if (!size) { return NULL; } @@ -150,7 +154,7 @@ void *fb_alloc(uint32_t size, int hints) { char *new_pointer = result - sizeof(uint32_t); // Check if allocation overwrites the framebuffer pixels - if (new_pointer < framebuffer_get_buffers_end()) { + if (new_pointer < framebuffer_get_buffers_end(fb)) { fb_alloc_fail(); } @@ -194,7 +198,8 @@ void *fb_alloc0(uint32_t size, int hints) { } void *fb_alloc_all(uint32_t *size, int hints) { - uint32_t temp = pointer - framebuffer_get_buffers_end() - sizeof(uint32_t); + framebuffer_t *fb = framebuffer_get(0); + uint32_t temp = pointer - framebuffer_get_buffers_end(fb) - sizeof(uint32_t); if (temp < sizeof(uint32_t)) { *size = 0; diff --git a/common/omv_csi.c b/common/omv_csi.c index c8ec0fa57..473a21643 100644 --- a/common/omv_csi.c +++ b/common/omv_csi.c @@ -128,6 +128,8 @@ uint16_t resolution[][2] = { {2592, 1944}, /* WQXGA2 */ }; +omv_csi_t csi = {0}; + __weak void omv_csi_init0() { // Reset the csi state memset(&csi, 0, sizeof(omv_csi_t)); @@ -139,13 +141,13 @@ __weak int omv_csi_init() { return OMV_CSI_ERROR_CTL_UNSUPPORTED; } -__weak int omv_csi_abort(bool fifo_flush, bool in_irq) { +__weak int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { return OMV_CSI_ERROR_CTL_UNSUPPORTED; } __weak int omv_csi_reset() { // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Reset the csi state csi.sde = 0; @@ -198,13 +200,13 @@ __weak int omv_csi_reset() { omv_i2c_enable(&csi.i2c_bus, true); // Call csi-specific reset function - if (csi.reset != NULL - && csi.reset(&csi) != 0) { + if (csi.reset != NULL && + csi.reset(&csi) != 0) { return OMV_CSI_ERROR_CTL_FAILED; } // Reset framebuffers - framebuffer_flush_buffers(true); + framebuffer_flush_buffers(csi.fb, true); return 0; } @@ -594,7 +596,7 @@ __weak bool omv_csi_is_detected() { __weak int omv_csi_sleep(int enable) { // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Call the sensor specific function. if (csi.sleep != NULL && @@ -609,7 +611,7 @@ __weak int omv_csi_shutdown(int enable) { int ret = 0; // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); #if defined(OMV_CSI_POWER_PIN) if (enable) { @@ -671,11 +673,11 @@ __weak int omv_csi_set_pixformat(pixformat_t pixformat) { // 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 color and (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(); + uint32_t size = framebuffer_get_buffer_size(csi.fb); if ((csi.pixformat == PIXFORMAT_BAYER) && ((pixformat == PIXFORMAT_RGB565) || (pixformat == PIXFORMAT_YUV422)) && - (MAIN_FB()->u * MAIN_FB()->v * 2 > size) && - (MAIN_FB()->u * MAIN_FB()->v * 1 <= size)) { + (csi.fb->u * csi.fb->v * 2 > size) && + (csi.fb->u * csi.fb->v * 1 <= size)) { return 0; } @@ -686,10 +688,10 @@ __weak int omv_csi_set_pixformat(pixformat_t pixformat) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Flush previous frame. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(csi.fb); // Check if the control is supported. if (csi.set_pixformat == NULL) { @@ -709,7 +711,7 @@ __weak int omv_csi_set_pixformat(pixformat_t pixformat) { csi.pixformat = pixformat; // Reset pixel format to skip the first frame. - MAIN_FB()->pixfmt = PIXFORMAT_INVALID; + csi.fb->pixfmt = PIXFORMAT_INVALID; // Auto-adjust the number of frame buffers. omv_csi_set_framebuffers(-1); @@ -725,10 +727,10 @@ __weak int omv_csi_set_framesize(omv_csi_framesize_t framesize) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Flush previous frame. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(csi.fb); // Call the sensor specific function if (csi.set_framesize == NULL) { @@ -747,16 +749,16 @@ __weak int omv_csi_set_framesize(omv_csi_framesize_t framesize) { csi.framesize = framesize; // Set x and y offsets. - MAIN_FB()->x = 0; - MAIN_FB()->y = 0; + csi.fb->x = 0; + csi.fb->y = 0; // Set width and height. - MAIN_FB()->w = resolution[framesize][0]; - MAIN_FB()->h = resolution[framesize][1]; + csi.fb->w = resolution[framesize][0]; + csi.fb->h = resolution[framesize][1]; // Set backup width and height. - MAIN_FB()->u = resolution[framesize][0]; - MAIN_FB()->v = resolution[framesize][1]; + csi.fb->u = resolution[framesize][0]; + csi.fb->v = resolution[framesize][1]; // Reset pixel format to skip the first frame. - MAIN_FB()->pixfmt = PIXFORMAT_INVALID; + csi.fb->pixfmt = PIXFORMAT_INVALID; // Auto-adjust the number of frame buffers. omv_csi_set_framebuffers(-1); @@ -776,8 +778,8 @@ __weak int omv_csi_set_framerate(int framerate) { } // If the csi implements framerate control use it. - if (csi.set_framerate != NULL - && csi.set_framerate(&csi, framerate) != 0) { + if (csi.set_framerate != NULL && + csi.set_framerate(&csi, framerate) != 0) { return OMV_CSI_ERROR_CTL_FAILED; } else { // Otherwise use software framerate control. @@ -807,10 +809,10 @@ __weak void omv_csi_throttle_framerate() { __weak bool omv_csi_get_cropped() { if (csi.framesize != OMV_CSI_FRAMESIZE_INVALID) { - return (MAIN_FB()->x != 0) || - (MAIN_FB()->y != 0) || - (MAIN_FB()->u != resolution[csi.framesize][0]) || - (MAIN_FB()->v != resolution[csi.framesize][1]); + return (csi.fb->x != 0) || + (csi.fb->y != 0) || + (csi.fb->u != resolution[csi.framesize][0]) || + (csi.fb->v != resolution[csi.framesize][1]); } return false; } @@ -848,8 +850,8 @@ __weak uint32_t omv_csi_get_dst_bpp() { __weak int omv_csi_set_windowing(int x, int y, int w, int h) { // Check if the value has changed. - if ((MAIN_FB()->x == x) && (MAIN_FB()->y == y) && - (MAIN_FB()->u == w) && (MAIN_FB()->v == h)) { + if ((csi.fb->x == x) && (csi.fb->y == y) && + (csi.fb->u == w) && (csi.fb->v == h)) { return 0; } @@ -858,22 +860,22 @@ __weak int omv_csi_set_windowing(int x, int y, int w, int h) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Flush previous frame. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(csi.fb); // Set x and y offsets. - MAIN_FB()->x = x; - MAIN_FB()->y = y; + csi.fb->x = x; + csi.fb->y = y; // Set width and height. - MAIN_FB()->w = w; - MAIN_FB()->h = h; + csi.fb->w = w; + csi.fb->h = h; // Set backup width and height. - MAIN_FB()->u = w; - MAIN_FB()->v = h; + csi.fb->u = w; + csi.fb->v = h; // Reset pixel format to skip the first frame. - MAIN_FB()->pixfmt = PIXFORMAT_INVALID; + csi.fb->pixfmt = PIXFORMAT_INVALID; // Auto-adjust the number of frame buffers. omv_csi_set_framebuffers(-1); @@ -1093,7 +1095,7 @@ __weak int omv_csi_set_hmirror(int enable) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Check if the control is supported. if (csi.set_hmirror == NULL) { @@ -1127,7 +1129,7 @@ __weak int omv_csi_set_vflip(int enable) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Check if the control is supported. if (csi.set_vflip == NULL) { @@ -1161,7 +1163,7 @@ __weak int omv_csi_set_transpose(bool enable) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); if ((csi.pixformat == PIXFORMAT_YUV422) || (csi.pixformat == PIXFORMAT_JPEG)) { return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED; @@ -1184,7 +1186,7 @@ __weak int omv_csi_set_auto_rotation(bool enable) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Operation not supported on JPEG images. if ((csi.pixformat == PIXFORMAT_YUV422) || (csi.pixformat == PIXFORMAT_JPEG)) { @@ -1202,10 +1204,10 @@ __weak bool omv_csi_get_auto_rotation() { __weak int omv_csi_set_framebuffers(int count) { // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Flush previous frame. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(csi.fb); if (csi.pixformat == PIXFORMAT_INVALID) { return OMV_CSI_ERROR_INVALID_PIXFORMAT; @@ -1217,12 +1219,12 @@ __weak int omv_csi_set_framebuffers(int count) { #if OMV_CSI_HW_CROP_ENABLE // If hardware cropping is supported, use window size. - MAIN_FB()->frame_size = MAIN_FB()->u * MAIN_FB()->v * 2; + csi.fb->frame_size = csi.fb->u * csi.fb->v * 2; #else // Otherwise, use the real frame size. - MAIN_FB()->frame_size = resolution[csi.framesize][0] * resolution[csi.framesize][1] * 2; + csi.fb->frame_size = resolution[csi.framesize][0] * resolution[csi.framesize][1] * 2; #endif - return framebuffer_set_buffers(count); + return framebuffer_set_buffers(csi.fb, count); } __weak int omv_csi_set_special_effect(omv_csi_sde_t sde) { @@ -1264,7 +1266,7 @@ __weak int omv_csi_set_lens_correction(int enable, int radi, int coef) { __weak int omv_csi_ioctl(int request, ... /* arg */) { // Disable any ongoing frame capture. if (request & OMV_CSI_IOCTL_FLAGS_ABORT) { - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); } // Check if the control is supported. @@ -1302,21 +1304,21 @@ __weak const uint16_t *omv_csi_get_color_palette() { __weak int omv_csi_check_framebuffer_size() { uint32_t bpp = omv_csi_get_dst_bpp(); - uint32_t size = framebuffer_get_buffer_size(); - return (((MAIN_FB()->u * MAIN_FB()->v * bpp) <= size) ? 0 : -1); + uint32_t size = framebuffer_get_buffer_size(csi.fb); + return (((csi.fb->u * csi.fb->v * bpp) <= size) ? 0 : -1); } __weak int omv_csi_auto_crop_framebuffer() { uint32_t bpp = omv_csi_get_dst_bpp(); - uint32_t size = framebuffer_get_buffer_size(); + uint32_t size = framebuffer_get_buffer_size(csi.fb); // 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) { + // csi.fb fits, we are done. + if ((csi.fb->u * csi.fb->v * bpp) <= size) { return 0; } @@ -1325,14 +1327,14 @@ __weak int omv_csi_auto_crop_framebuffer() { omv_csi_set_pixformat(PIXFORMAT_BAYER); bpp = 1; - // MAIN_FB() fits, we are done (bpp is 1). - if ((MAIN_FB()->u * MAIN_FB()->v) <= size) { + // csi.fb fits, we are done (bpp is 1). + if ((csi.fb->u * csi.fb->v) <= size) { return 0; } } - int window_w = MAIN_FB()->u; - int window_h = MAIN_FB()->v; + int window_w = csi.fb->u; + int window_h = csi.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. @@ -1373,20 +1375,20 @@ __weak int omv_csi_auto_crop_framebuffer() { } // 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; + while (((csi.fb->u * csi.fb->v * bpp) > size) || (csi.fb->u % 2) || (csi.fb->v % 2)) { + csi.fb->u -= u_sub; + csi.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; + csi.fb->x += (window_w - csi.fb->u) / 2; + csi.fb->y += (window_h - csi.fb->v) / 2; - if (MAIN_FB()->x % 2) { - MAIN_FB()->x -= 1; + if (csi.fb->x % 2) { + csi.fb->x -= 1; } - if (MAIN_FB()->y % 2) { - MAIN_FB()->y -= 1; + if (csi.fb->y % 2) { + csi.fb->y -= 1; } // Auto-adjust the number of frame buffers. @@ -1395,13 +1397,13 @@ __weak int omv_csi_auto_crop_framebuffer() { } #define copy_transposed_line(dstp, srcp) \ - for (int i = MAIN_FB()->u, h = MAIN_FB()->v; i; i--) { \ + for (int i = csi.fb->u, h = csi.fb->v; i; i--) { \ *dstp = *srcp++; \ dstp += h; \ } #define copy_transposed_line_rev16(dstp, srcp) \ - for (int i = MAIN_FB()->u, h = MAIN_FB()->v; i; i--) { \ + for (int i = csi.fb->u, h = csi.fb->v; i; i--) { \ *dstp = __REV16(*srcp++); \ dstp += h; \ } @@ -1421,7 +1423,7 @@ __weak int omv_csi_copy_line(void *dma, uint8_t *src, uint8_t *dst) { } #endif if (!csi.transpose) { - unaligned_memcpy(dst, src, MAIN_FB()->u); + unaligned_memcpy(dst, src, csi.fb->u); } else { copy_transposed_line(dst, src); } @@ -1435,14 +1437,14 @@ __weak int omv_csi_copy_line(void *dma, uint8_t *src, uint8_t *dst) { if (csi.mono_bpp == 1) { // 1BPP GRAYSCALE. if (!csi.transpose) { - unaligned_memcpy(dst, src, MAIN_FB()->u); + unaligned_memcpy(dst, src, csi.fb->u); } else { copy_transposed_line(dst, src); } } else { // Extract Y channel from YUV. if (!csi.transpose) { - unaligned_2_to_1_memcpy(dst, src16, MAIN_FB()->u); + unaligned_2_to_1_memcpy(dst, src16, csi.fb->u); } else { copy_transposed_line(dst, src16); } @@ -1460,14 +1462,14 @@ __weak int omv_csi_copy_line(void *dma, uint8_t *src, uint8_t *dst) { } else if ((csi.pixformat == PIXFORMAT_RGB565 && csi.rgb_swap) || (csi.pixformat == PIXFORMAT_YUV422 && csi.yuv_swap)) { if (!csi.transpose) { - unaligned_memcpy_rev16(dst16, src16, MAIN_FB()->u); + unaligned_memcpy_rev16(dst16, src16, csi.fb->u); } else { copy_transposed_line_rev16(dst16, src16); } #endif } else { if (!csi.transpose) { - unaligned_memcpy(dst16, src16, MAIN_FB()->u * sizeof(uint16_t)); + unaligned_memcpy(dst16, src16, csi.fb->u * sizeof(uint16_t)); } else { copy_transposed_line(dst16, src16); } diff --git a/common/omv_csi.h b/common/omv_csi.h index 08f6a84df..cf415cf28 100644 --- a/common/omv_csi.h +++ b/common/omv_csi.h @@ -35,6 +35,7 @@ #include #include "omv_i2c.h" #include "imlib.h" +#include "framebuffer.h" #define OV2640_SLV_ADDR (0x60) #define OV5640_SLV_ADDR (0x78) @@ -320,6 +321,8 @@ typedef struct _omv_csi { omv_i2c_t i2c_bus; // SCCB/I2C bus. + framebuffer_t *fb; // Frame buffer pointer + #ifdef OMV_CSI_PORT_BITS // Additional port-specific members like device base pointer, // dma handles, more I/Os etc... are included directly here, @@ -376,7 +379,7 @@ int omv_csi_probe_init(uint32_t bus_id, uint32_t bus_speed); int omv_csi_config(omv_csi_config_t config); // Abort frame capture and disable IRQs, DMA etc.. -int omv_csi_abort(bool fifo_flush, bool in_irq); +int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq); // Reset the sensor to its default state. int omv_csi_reset(); diff --git a/common/usbdbg.c b/common/usbdbg.c index bd8763c4c..9770bd20b 100644 --- a/common/usbdbg.c +++ b/common/usbdbg.c @@ -319,7 +319,9 @@ void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback) { case USBDBG_TEMPLATE_SAVE: { #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) image_t image; - framebuffer_init_image(&image); + framebuffer_t *fb = framebuffer_get(0); + + framebuffer_init_image(fb, &image); size = MIN(128, size); char buffer[size]; @@ -338,7 +340,9 @@ void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback) { #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) \ && defined(IMLIB_ENABLE_KEYPOINTS) image_t image; - framebuffer_init_image(&image); + framebuffer_t *fb = framebuffer_get(0); + + framebuffer_init_image(fb, &image); size = MIN(128, size); char buffer[size]; diff --git a/drivers/sensors/boson.c b/drivers/sensors/boson.c index 14d1b8db4..9adbcdca7 100644 --- a/drivers/sensors/boson.c +++ b/drivers/sensors/boson.c @@ -143,13 +143,13 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { int num_pixels = resolution[boson_framesize][0] * resolution[boson_framesize][1]; - if (csi->color_palette && (framebuffer_get_buffer_size() >= (num_pixels * sizeof(uint16_t)))) { + if (csi->color_palette && (framebuffer_get_buffer_size(csi->fb) >= (num_pixels * sizeof(uint16_t)))) { for (int32_t i = num_pixels - 1; i >= 0; i--) { ((uint16_t *) image->data)[i] = csi->color_palette[image->data[i]]; } image->pixfmt = PIXFORMAT_RGB565; - MAIN_FB()->pixfmt = PIXFORMAT_RGB565; + csi->fb->pixfmt = PIXFORMAT_RGB565; } return ret; diff --git a/drivers/sensors/genx320.c b/drivers/sensors/genx320.c index ddf5846f5..25abe787e 100644 --- a/drivers/sensors/genx320.c +++ b/drivers/sensors/genx320.c @@ -244,7 +244,7 @@ static int set_framerate(omv_csi_t *csi, int framerate) { } // Disable any ongoing frame capture. - omv_csi_abort(true, false); + omv_csi_abort(csi, true, false); psee_sensor_write(EHC_INTEGRATION_PERIOD, us); psee_sensor_write(CPI_PACKET_TIME_CONTROL, ACTIVE_SENSOR_WIDTH << CPI_PACKET_TIME_CONTROL_PERIOD_Pos | @@ -345,7 +345,7 @@ static void disable_hot_pixels(uint8_t *histogram) { } #endif // (OMV_GENX320_CAL_ENABLE == 1) -static void snapshot_post_process(image_t *image) { +static void snapshot_post_process(omv_csi_t *csi, image_t *image) { #if (OMV_GENX320_EHC_ENABLE == 1) for (uint32_t i = 0; i < ACTIVE_SENSOR_SIZE; i++) { image->data[i] = __USAT((((int8_t *) image->data)[i] * contrast) + brightness, UINT8_T_BITS); @@ -383,12 +383,13 @@ static void snapshot_post_process(image_t *image) { fb_free(); #endif // (OMV_GENX320_EHC_ENABLE == 1) - if (csi.color_palette && (framebuffer_get_buffer_size() >= (ACTIVE_SENSOR_SIZE * sizeof(uint16_t)))) { + if (csi->color_palette && + (framebuffer_get_buffer_size(csi->fb) >= (ACTIVE_SENSOR_SIZE * sizeof(uint16_t)))) { for (int32_t i = ACTIVE_SENSOR_SIZE - 1; i >= 0; i--) { - ((uint16_t *) image->data)[i] = csi.color_palette[image->data[i]]; + ((uint16_t *) image->data)[i] = csi->color_palette[image->data[i]]; } image->pixfmt = PIXFORMAT_RGB565; - MAIN_FB()->pixfmt = PIXFORMAT_RGB565; + csi->fb->pixfmt = PIXFORMAT_RGB565; } } @@ -440,7 +441,7 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } #endif // (OMV_GENX320_EHC_ENABLE == 1) - snapshot_post_process(image); + snapshot_post_process(csi, image); } disable_hot_pixels(histogram); @@ -456,7 +457,7 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return ret; } - snapshot_post_process(image); + snapshot_post_process(csi, image); return ret; } diff --git a/drivers/sensors/lepton.c b/drivers/sensors/lepton.c index bd29ef99c..86000ded5 100644 --- a/drivers/sensors/lepton.c +++ b/drivers/sensors/lepton.c @@ -380,10 +380,11 @@ static int reset(omv_csi_t *csi) { } static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { - framebuffer_update_jpeg_buffer(); + framebuffer_t *fb = csi->fb; + framebuffer_update_jpeg_buffer(fb); - if (MAIN_FB()->n_buffers != 1) { - framebuffer_set_buffers(1); + if (fb->n_buffers != 1) { + framebuffer_set_buffers(fb, 1); } if (csi->pixformat == PIXFORMAT_INVALID) { @@ -402,8 +403,8 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; } - framebuffer_free_current_buffer(); - vbuffer_t *buffer = framebuffer_get_tail(FB_NO_FLAGS); + framebuffer_free_current_buffer(fb); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_NO_FLAGS); if (!buffer) { return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; @@ -422,11 +423,11 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } } - MAIN_FB()->w = MAIN_FB()->u; - MAIN_FB()->h = MAIN_FB()->v; - MAIN_FB()->pixfmt = csi->pixformat; + fb->w = fb->u; + fb->h = fb->v; + fb->pixfmt = csi->pixformat; - framebuffer_init_image(image); + framebuffer_init_image(fb, image); float x_scale = resolution[csi->framesize][0] / ((float) lepton.h_res); float y_scale = resolution[csi->framesize][1] / ((float) lepton.v_res); @@ -445,13 +446,13 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } for (int y = y_offset, yy = fast_ceilf(lepton.v_res * scale) + y_offset; y < yy; y++) { - if ((MAIN_FB()->y <= y) && (y < (MAIN_FB()->y + MAIN_FB()->v))) { + if ((fb->y <= y) && (y < (fb->y + fb->v))) { // user window cropping uint16_t *row_ptr = _vospi_buf + (fast_floorf(y * scale_inv) * lepton.h_res); for (int x = x_offset, xx = fast_ceilf(lepton.h_res * scale) + x_offset; x < xx; x++) { - if ((MAIN_FB()->x <= x) && (x < (MAIN_FB()->x + MAIN_FB()->u))) { + if ((fb->x <= x) && (x < (fb->x + fb->u))) { // user window cropping // Value is the 14/16-bit value from the FLIR IR camera. @@ -469,14 +470,14 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { (lepton.max_temp - lepton.min_temp)), 8); } - int t_x = x - MAIN_FB()->x; - int t_y = y - MAIN_FB()->y; + int t_x = x - fb->x; + int t_y = y - fb->y; if (lepton.hmirror) { - t_x = MAIN_FB()->u - t_x - 1; + t_x = fb->u - t_x - 1; } if (lepton.vflip) { - t_y = MAIN_FB()->v - t_y - 1; + t_y = fb->v - t_y - 1; } switch (csi->pixformat) { diff --git a/drivers/sensors/softcsi.c b/drivers/sensors/softcsi.c index 73d10b348..06be8630d 100644 --- a/drivers/sensors/softcsi.c +++ b/drivers/sensors/softcsi.c @@ -62,17 +62,16 @@ static int set_vflip(omv_csi_t *csi, int enable) { } static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { - uint32_t w = MAIN_FB()->u; - uint32_t h = MAIN_FB()->v; + framebuffer_t *fb = csi->fb; if (!image) { return 0; } - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(fb); - if (MAIN_FB()->n_buffers != 1) { - framebuffer_set_buffers(1); + if (fb->n_buffers != 1) { + framebuffer_set_buffers(fb, 1); } if (csi->pixformat == PIXFORMAT_INVALID) { @@ -87,24 +86,23 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; } - framebuffer_free_current_buffer(); - vbuffer_t *buffer = framebuffer_get_tail(FB_NO_FLAGS); + framebuffer_free_current_buffer(fb); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_NO_FLAGS); if (!buffer) { return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; } - if (!csi->transpose) { - MAIN_FB()->w = w; - MAIN_FB()->h = h; + fb->w = fb->u; + fb->h = fb->v; } else { - MAIN_FB()->w = h; - MAIN_FB()->h = w; + fb->w = fb->v; + fb->h = fb->u; } - MAIN_FB()->pixfmt = csi->pixformat; - framebuffer_init_image(image); + fb->pixfmt = csi->pixformat; + framebuffer_init_image(fb, image); static uint32_t step = 0; uint32_t offset = (step / 4); diff --git a/lib/imlib/framebuffer.c b/lib/imlib/framebuffer.c index 76b8a8548..f0869a6e9 100644 --- a/lib/imlib/framebuffer.c +++ b/lib/imlib/framebuffer.c @@ -33,27 +33,216 @@ #define FB_ALIGN_SIZE_ROUND_UP(x) FB_ALIGN_SIZE_ROUND_DOWN(((x) + FRAMEBUFFER_ALIGNMENT - 1)) #define OMV_JPEG_BUFFER_SIZE_MAX ((&_jpeg_memory_end - &_jpeg_memory_start) - sizeof(jpegbuffer_t)) -extern char _fb_memory_start; -extern char _fb_memory_end; -framebuffer_t *framebuffer = (framebuffer_t *) &_fb_memory_start; +extern char _fb_memory_start[]; +extern char _fb_memory_end[]; +static framebuffer_t *framebuffer = (framebuffer_t *) &_fb_memory_start; extern char _jpeg_memory_start; extern char _jpeg_memory_end; -jpegbuffer_t *jpeg_framebuffer = (jpegbuffer_t *) &_jpeg_memory_start; +jpegbuffer_t *jpegbuffer = (jpegbuffer_t *) &_jpeg_memory_start; -void fb_set_streaming_enabled(bool enable) { - framebuffer->streaming_enabled = enable; +void framebuffer_init0() { + // Save fb_enabled flag state + int fb_enabled = jpegbuffer->enabled; + + // Clear framebuffers + memset(framebuffer, 0, sizeof(*framebuffer)); + memset(jpegbuffer, 0, sizeof(*jpegbuffer)); + + mutex_init0(&jpegbuffer->lock); + + // Enable streaming. + framebuffer->streaming_enabled = true; // controlled by the OpenMV Cam. + + // Set default quality + jpegbuffer->quality = ((OMV_JPEG_QUALITY_HIGH - OMV_JPEG_QUALITY_LOW) / 2) + OMV_JPEG_QUALITY_LOW; + + // Set fb_enabled + jpegbuffer->enabled = fb_enabled; // controlled by the IDE. + + // Setup buffering. + framebuffer_set_buffers(framebuffer, 1); } -bool fb_get_streaming_enabled() { - return framebuffer->streaming_enabled; +void framebuffer_init_image(framebuffer_t *fb, image_t *img) { + if (img != NULL) { + img->w = fb->w; + img->h = fb->h; + img->size = fb->size; + img->pixfmt = fb->pixfmt; + img->pixels = framebuffer_get_buffer(fb, fb->head)->data; + } } -int fb_encode_for_ide_new_size(image_t *img) { - return (((img->size * 8) + 5) / 6) + 2; +void framebuffer_init_from_image(framebuffer_t *fb, image_t *img) { + fb->w = img->w; + fb->h = img->h; + fb->size = img->size; + fb->pixfmt = img->pixfmt; } -void fb_encode_for_ide(uint8_t *ptr, image_t *img) { +static void jpegbuffer_init_from_image(framebuffer_t *fb, image_t *img) { + if (img == NULL) { + jpegbuffer->w = 0; + jpegbuffer->h = 0; + jpegbuffer->size = 0; + } else { + jpegbuffer->w = img->w; + jpegbuffer->h = img->h; + jpegbuffer->size = img->size; + } +} + +void framebuffer_update_jpeg_buffer(framebuffer_t *fb) { + static int overflow_count = 0; + + image_t main_fb_src; + framebuffer_init_image(fb, &main_fb_src); + image_t *src = &main_fb_src; + + if (src->pixfmt != PIXFORMAT_INVALID && + fb->streaming_enabled && jpegbuffer->enabled) { + if (src->is_compressed) { + bool does_not_fit = false; + + if (mutex_try_lock_alternate(&jpegbuffer->lock, MUTEX_TID_OMV)) { + if (OMV_JPEG_BUFFER_SIZE_MAX < src->size) { + jpegbuffer_init_from_image(fb, NULL); + does_not_fit = true; + } else { + jpegbuffer_init_from_image(fb, src); + memcpy(jpegbuffer->pixels, src->pixels, src->size); + } + + mutex_unlock(&jpegbuffer->lock, MUTEX_TID_OMV); + } + + if (does_not_fit) { + printf("Warning: JPEG/PNG too big! Trying framebuffer transfer using fallback method!\n"); + int new_size = framebuffer_encoded_size(fb, src); + fb_alloc_mark(); + uint8_t *temp = fb_alloc(new_size, FB_ALLOC_NO_HINT); + framebuffer_encode(fb, temp, src); + (MP_PYTHON_PRINTER)->print_strn((MP_PYTHON_PRINTER)->data, (const char *) temp, new_size); + fb_alloc_free_till_mark(); + } + } else if (src->pixfmt != PIXFORMAT_INVALID) { + if (mutex_try_lock_alternate(&jpegbuffer->lock, MUTEX_TID_OMV)) { + image_t dst = { + .w = src->w, + .h = src->h, + .pixfmt = PIXFORMAT_JPEG, + .size = OMV_JPEG_BUFFER_SIZE_MAX, + .pixels = jpegbuffer->pixels + }; + + bool compress = true; + bool overflow = false; + + #if OMV_RAW_PREVIEW_ENABLE + if (src->is_mutable) { + // Down-scale the frame (if necessary) and send the raw frame. + dst.size = src->bpp; + dst.pixfmt = src->pixfmt; + if (src->w <= OMV_RAW_PREVIEW_WIDTH && src->h <= OMV_RAW_PREVIEW_HEIGHT) { + if (image_size(&dst) <= OMV_JPEG_BUFFER_SIZE_MAX) { + memcpy(dst.pixels, src->pixels, image_size(src)); + compress = false; + } + } else { + float x_scale = OMV_RAW_PREVIEW_WIDTH / (float) src->w; + float y_scale = OMV_RAW_PREVIEW_HEIGHT / (float) src->h; + float scale = IM_MIN(x_scale, y_scale); + dst.w = fast_floorf(src->w * scale); + dst.h = fast_floorf(src->h * scale); + if (image_size(&dst) <= OMV_JPEG_BUFFER_SIZE_MAX) { + imlib_draw_image(&dst, src, 0, 0, scale, scale, NULL, -1, 255, NULL, NULL, + IMAGE_HINT_BILINEAR | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL, NULL); + compress = false; + } + } + } + #endif + + if (compress) { + // For all other formats, send a compressed frame. + overflow = jpeg_compress(src, &dst, jpegbuffer->quality, false, JPEG_SUBSAMPLING_AUTO); + } + + if (overflow) { + // JPEG buffer overflowed, reduce JPEG quality for the next frame + // and skip the current frame. The IDE doesn't receive this frame. + if (jpegbuffer->quality > 1) { + // Keep this quality for the next n frames + overflow_count = 60; + jpegbuffer->quality = IM_MAX(1, (jpegbuffer->quality / 2)); + } + + jpegbuffer_init_from_image(fb, NULL); + } else { + if (overflow_count) { + overflow_count--; + } + + // Dynamically adjust our quality if the image is huge. + bool big_frame_buffer = image_size(src) > OMV_JPEG_QUALITY_THRESHOLD; + int jpeg_quality_max = big_frame_buffer ? OMV_JPEG_QUALITY_LOW : OMV_JPEG_QUALITY_HIGH; + + // No buffer overflow, increase quality up to max quality based on frame size... + if ((!overflow_count) && (jpegbuffer->quality < jpeg_quality_max)) { + jpegbuffer->quality++; + } + + jpegbuffer_init_from_image(fb, &dst); + } + + mutex_unlock(&jpegbuffer->lock, MUTEX_TID_OMV); + } + } + } +} + +framebuffer_t *framebuffer_get(size_t id) { + return framebuffer; +} + +int32_t framebuffer_get_x(framebuffer_t *fb) { + return fb->x; +} + +int32_t framebuffer_get_y(framebuffer_t *fb) { + return fb->y; +} + +int32_t framebuffer_get_u(framebuffer_t *fb) { + return fb->u; +} + +int32_t framebuffer_get_v(framebuffer_t *fb) { + return fb->v; +} + +int32_t framebuffer_get_width(framebuffer_t *fb) { + return fb->w; +} + +int32_t framebuffer_get_height(framebuffer_t *fb) { + return fb->h; +} + +int32_t framebuffer_get_depth(framebuffer_t *fb) { + return fb->bpp; +} + +void framebuffer_set_streaming(framebuffer_t *fb, bool enable) { + fb->streaming_enabled = enable; +} + +bool framebuffer_get_streaming(framebuffer_t *fb) { + return fb->streaming_enabled; +} + +void framebuffer_encode(framebuffer_t *fb, uint8_t *ptr, image_t *img) { *ptr++ = 0xFE; for (int i = 0, j = (img->size / 3) * 3; i < j; i += 3) { @@ -88,211 +277,26 @@ void fb_encode_for_ide(uint8_t *ptr, image_t *img) { *ptr++ = 0xFE; } -void framebuffer_init0() { - // Save fb_enabled flag state - int fb_enabled = JPEG_FB()->enabled; - - // Clear framebuffers - memset(MAIN_FB(), 0, sizeof(*MAIN_FB())); - memset(JPEG_FB(), 0, sizeof(*JPEG_FB())); - - mutex_init0(&JPEG_FB()->lock); - - // Enable streaming. - MAIN_FB()->streaming_enabled = true; // controlled by the OpenMV Cam. - - // Set default quality - JPEG_FB()->quality = ((OMV_JPEG_QUALITY_HIGH - OMV_JPEG_QUALITY_LOW) / 2) + OMV_JPEG_QUALITY_LOW; - - // Set fb_enabled - JPEG_FB()->enabled = fb_enabled; // controlled by the IDE. - - // Setup buffering. - framebuffer_set_buffers(1); -} - -void framebuffer_init_image(image_t *img) { - if (img != NULL) { - img->w = framebuffer->w; - img->h = framebuffer->h; - img->size = framebuffer->size; - img->pixfmt = framebuffer->pixfmt; - img->pixels = framebuffer_get_buffer(framebuffer->head)->data; - } -} - -void framebuffer_init_from_image(image_t *img) { - framebuffer->w = img->w; - framebuffer->h = img->h; - framebuffer->size = img->size; - framebuffer->pixfmt = img->pixfmt; -} - -static void jpegbuffer_init_from_image(image_t *img) { - if (img == NULL) { - jpeg_framebuffer->w = 0; - jpeg_framebuffer->h = 0; - jpeg_framebuffer->size = 0; - } else { - jpeg_framebuffer->w = img->w; - jpeg_framebuffer->h = img->h; - jpeg_framebuffer->size = img->size; - } -} - -void framebuffer_update_jpeg_buffer() { - static int overflow_count = 0; - - image_t main_fb_src; - framebuffer_init_image(&main_fb_src); - image_t *src = &main_fb_src; - - if (src->pixfmt != PIXFORMAT_INVALID && - framebuffer->streaming_enabled && jpeg_framebuffer->enabled) { - if (src->is_compressed) { - bool does_not_fit = false; - - if (mutex_try_lock_alternate(&jpeg_framebuffer->lock, MUTEX_TID_OMV)) { - if (OMV_JPEG_BUFFER_SIZE_MAX < src->size) { - jpegbuffer_init_from_image(NULL); - does_not_fit = true; - } else { - jpegbuffer_init_from_image(src); - memcpy(jpeg_framebuffer->pixels, src->pixels, src->size); - } - - mutex_unlock(&jpeg_framebuffer->lock, MUTEX_TID_OMV); - } - - if (does_not_fit) { - printf("Warning: JPEG/PNG too big! Trying framebuffer transfer using fallback method!\n"); - int new_size = fb_encode_for_ide_new_size(src); - fb_alloc_mark(); - uint8_t *temp = fb_alloc(new_size, FB_ALLOC_NO_HINT); - fb_encode_for_ide(temp, src); - (MP_PYTHON_PRINTER)->print_strn((MP_PYTHON_PRINTER)->data, (const char *) temp, new_size); - fb_alloc_free_till_mark(); - } - } else if (src->pixfmt != PIXFORMAT_INVALID) { - if (mutex_try_lock_alternate(&jpeg_framebuffer->lock, MUTEX_TID_OMV)) { - image_t dst = { - .w = src->w, - .h = src->h, - .pixfmt = PIXFORMAT_JPEG, - .size = OMV_JPEG_BUFFER_SIZE_MAX, - .pixels = jpeg_framebuffer->pixels - }; - - bool compress = true; - bool overflow = false; - - #if OMV_RAW_PREVIEW_ENABLE - if (src->is_mutable) { - // Down-scale the frame (if necessary) and send the raw frame. - dst.size = src->bpp; - dst.pixfmt = src->pixfmt; - if (src->w <= OMV_RAW_PREVIEW_WIDTH && src->h <= OMV_RAW_PREVIEW_HEIGHT) { - if (image_size(&dst) <= OMV_JPEG_BUFFER_SIZE_MAX) { - memcpy(dst.pixels, src->pixels, image_size(src)); - compress = false; - } - } else { - float x_scale = OMV_RAW_PREVIEW_WIDTH / (float) src->w; - float y_scale = OMV_RAW_PREVIEW_HEIGHT / (float) src->h; - float scale = IM_MIN(x_scale, y_scale); - dst.w = fast_floorf(src->w * scale); - dst.h = fast_floorf(src->h * scale); - if (image_size(&dst) <= OMV_JPEG_BUFFER_SIZE_MAX) { - imlib_draw_image(&dst, src, 0, 0, scale, scale, NULL, -1, 255, NULL, NULL, - IMAGE_HINT_BILINEAR | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL, NULL); - compress = false; - } - } - } - #endif - - if (compress) { - // For all other formats, send a compressed frame. - overflow = jpeg_compress(src, &dst, jpeg_framebuffer->quality, false, JPEG_SUBSAMPLING_AUTO); - } - - if (overflow) { - // JPEG buffer overflowed, reduce JPEG quality for the next frame - // and skip the current frame. The IDE doesn't receive this frame. - if (jpeg_framebuffer->quality > 1) { - // Keep this quality for the next n frames - overflow_count = 60; - jpeg_framebuffer->quality = IM_MAX(1, (jpeg_framebuffer->quality / 2)); - } - - jpegbuffer_init_from_image(NULL); - } else { - if (overflow_count) { - overflow_count--; - } - - // Dynamically adjust our quality if the image is huge. - bool big_frame_buffer = image_size(src) > OMV_JPEG_QUALITY_THRESHOLD; - int jpeg_quality_max = big_frame_buffer ? OMV_JPEG_QUALITY_LOW : OMV_JPEG_QUALITY_HIGH; - - // No buffer overflow, increase quality up to max quality based on frame size... - if ((!overflow_count) && (jpeg_framebuffer->quality < jpeg_quality_max)) { - jpeg_framebuffer->quality++; - } - - jpegbuffer_init_from_image(&dst); - } - - mutex_unlock(&jpeg_framebuffer->lock, MUTEX_TID_OMV); - } - } - } -} - -int32_t framebuffer_get_x() { - return framebuffer->x; -} - -int32_t framebuffer_get_y() { - return framebuffer->y; -} - -int32_t framebuffer_get_u() { - return framebuffer->u; -} - -int32_t framebuffer_get_v() { - return framebuffer->v; -} - -int32_t framebuffer_get_width() { - return framebuffer->w; -} - -int32_t framebuffer_get_height() { - return framebuffer->h; -} - -int32_t framebuffer_get_depth() { - return framebuffer->bpp; +int framebuffer_encoded_size(framebuffer_t *fb, image_t *img) { + return (((img->size * 8) + 5) / 6) + 2; } // Returns the current frame buffer size, factoring in the space taken by fb_alloc. -static uint32_t framebuffer_max_buffer_size() { - uint32_t fb_total_size = FB_ALIGN_SIZE_ROUND_DOWN(&_fb_memory_end - (char *) framebuffer->data); - uint32_t fb_avail_size = FB_ALIGN_SIZE_ROUND_DOWN(fb_alloc_stack_pointer() - (char *) framebuffer->data); +static uint32_t framebuffer_max_buffer_size(framebuffer_t *fb) { + uint32_t fb_total_size = FB_ALIGN_SIZE_ROUND_DOWN((char *) &_fb_memory_end - (char *) fb->data); + uint32_t fb_avail_size = FB_ALIGN_SIZE_ROUND_DOWN(fb_alloc_stack_pointer() - (char *) fb->data); return IM_MIN(fb_total_size, fb_avail_size); } -uint32_t framebuffer_get_buffer_size() { +uint32_t framebuffer_get_buffer_size(framebuffer_t *fb) { uint32_t size; - if (framebuffer->n_buffers == 1) { + if (fb->n_buffers == 1) { // With only 1 vbuffer the frame buffer size can change given fb_alloc(). - size = framebuffer_max_buffer_size(); + size = framebuffer_max_buffer_size(fb); } else { // Whatever the raw size was when the number of buffers were set is locked in. - size = framebuffer->buff_size; + size = fb->buff_size; } // Remove the size of the state header plus alignment padding. @@ -304,28 +308,29 @@ uint32_t framebuffer_get_buffer_size() { // Each raw frame buffer is split into two parts. The vbuffer_t struct followed by // padding and then the pixel array starting at the next 32-byte offset. -vbuffer_t *framebuffer_get_buffer(int32_t index) { - uint32_t offset = (sizeof(vbuffer_t) + framebuffer_get_buffer_size()) * index; - return (vbuffer_t *) (framebuffer->data + offset); +vbuffer_t *framebuffer_get_buffer(framebuffer_t *fb, int32_t index) { + uint32_t fbsize = framebuffer_get_buffer_size(fb); + uint32_t offset = (sizeof(vbuffer_t) + fbsize) * index; + return (vbuffer_t *) (fb->data + offset); } -void framebuffer_flush_buffers(bool fifo_flush) { +void framebuffer_flush_buffers(framebuffer_t *fb, bool fifo_flush) { if (fifo_flush) { // Drop all frame buffers. - for (uint32_t i = 0; i < framebuffer->n_buffers; i++) { - memset(framebuffer_get_buffer(i), 0, sizeof(vbuffer_t)); + for (uint32_t i = 0; i < fb->n_buffers; i++) { + memset(framebuffer_get_buffer(fb, i), 0, sizeof(vbuffer_t)); } } // Move the tail pointer to the head which empties the virtual fifo while keeping the same // position of the current frame for the rest of the code. - framebuffer->tail = framebuffer->head; - framebuffer->check_head = true; - framebuffer->sampled_head = 0; + fb->tail = fb->head; + fb->check_head = true; + fb->sampled_head = 0; } -int framebuffer_set_buffers(int32_t n_buffers) { - uint32_t avail_size = FB_ALIGN_SIZE_ROUND_DOWN(framebuffer_max_buffer_size()); - uint32_t frame_size = FB_ALIGN_SIZE_ROUND_UP(framebuffer->frame_size + sizeof(vbuffer_t)); +int framebuffer_set_buffers(framebuffer_t *fb, int32_t n_buffers) { + uint32_t avail_size = FB_ALIGN_SIZE_ROUND_DOWN(framebuffer_max_buffer_size(fb)); + uint32_t frame_size = FB_ALIGN_SIZE_ROUND_UP(fb->frame_size + sizeof(vbuffer_t)); uint32_t vbuff_size = (n_buffers == 1) ? avail_size : frame_size; uint32_t vbuff_count = IM_MIN((avail_size / vbuff_size), (n_buffers == -1) ? 3 : (uint32_t) n_buffers); @@ -333,141 +338,142 @@ int framebuffer_set_buffers(int32_t n_buffers) { return -1; } - framebuffer->head = 0; - framebuffer->buff_size = vbuff_size; - framebuffer->n_buffers = vbuff_count; - framebuffer->pixfmt = PIXFORMAT_INVALID; + fb->head = 0; + fb->buff_size = vbuff_size; + fb->n_buffers = vbuff_count; + fb->pixfmt = PIXFORMAT_INVALID; - framebuffer_flush_buffers(true); + framebuffer_flush_buffers(fb, true); return 0; } // Returns the real size of bytes in the frame buffer. -static uint32_t framebuffer_total_buffer_size() { - if (framebuffer->n_buffers == 1) { +static uint32_t framebuffer_total_buffer_size(framebuffer_t *fb) { + if (fb->n_buffers == 1) { // Allow fb_alloc to use frame buffer space up until the image size. image_t img; - framebuffer_init_image(&img); + framebuffer_init_image(fb, &img); return sizeof(vbuffer_t) + FB_ALIGN_SIZE_ROUND_UP(image_size(&img)); } else { - // fb_alloc may only use up to the size of all the virtual buffers... - return (sizeof(vbuffer_t) + framebuffer_get_buffer_size()) * framebuffer->n_buffers; + // fb_alloc may only use up to the size of all the virtual buffers. + uint32_t fbsize = framebuffer_get_buffer_size(fb); + return (sizeof(vbuffer_t) + fbsize) * fb->n_buffers; } } -void framebuffer_free_current_buffer() { - vbuffer_t *buffer = framebuffer_get_buffer(framebuffer->head); +void framebuffer_free_current_buffer(framebuffer_t *fb) { + vbuffer_t *buffer = framebuffer_get_buffer(fb, fb->head); #ifdef __DCACHE_PRESENT // Make sure all cached CPU writes are discarded before returning the buffer. - SCB_InvalidateDCache_by_Addr(buffer->data, framebuffer_get_buffer_size()); + SCB_InvalidateDCache_by_Addr(buffer->data, framebuffer_get_buffer_size(fb)); #endif // Invalidate frame. - framebuffer->pixfmt = PIXFORMAT_INVALID; + fb->pixfmt = PIXFORMAT_INVALID; // Allow frame to be updated in single buffer mode... - if (framebuffer->n_buffers == 1) { + if (fb->n_buffers == 1) { buffer->waiting_for_data = true; } } -void framebuffer_setup_buffers() { +void framebuffer_setup_buffers(framebuffer_t *fb) { #ifdef __DCACHE_PRESENT - for (int32_t i = 0; i < framebuffer->n_buffers; i++) { - if (i != framebuffer->head) { - vbuffer_t *buffer = framebuffer_get_buffer(i); + for (int32_t i = 0; i < fb->n_buffers; i++) { + if (i != fb->head) { + vbuffer_t *buffer = framebuffer_get_buffer(fb, i); // Make sure all cached CPU writes are discarded before returning the buffer. - SCB_InvalidateDCache_by_Addr(buffer->data, framebuffer_get_buffer_size()); + SCB_InvalidateDCache_by_Addr(buffer->data, framebuffer_get_buffer_size(fb)); } } #endif } -vbuffer_t *framebuffer_get_head(framebuffer_flags_t flags) { - int32_t new_head = (framebuffer->head + 1) % framebuffer->n_buffers; +vbuffer_t *framebuffer_get_head(framebuffer_t *fb, framebuffer_flags_t flags) { + int32_t new_head = (fb->head + 1) % fb->n_buffers; // Single Buffer Mode. - if (framebuffer->n_buffers == 1) { - if (framebuffer_get_buffer(framebuffer->head)->waiting_for_data) { + if (fb->n_buffers == 1) { + if (framebuffer_get_buffer(fb, fb->head)->waiting_for_data) { return NULL; } // Double Buffer Mode. - } else if (framebuffer->n_buffers == 2) { - if (framebuffer->head == framebuffer->tail) { + } else if (fb->n_buffers == 2) { + if (fb->head == fb->tail) { return NULL; } // Triple Buffer Mode. - } else if (framebuffer->n_buffers == 3) { - int32_t sampled_tail = framebuffer->tail; - if (framebuffer->head == sampled_tail) { + } else if (fb->n_buffers == 3) { + int32_t sampled_tail = fb->tail; + if (fb->head == sampled_tail) { return NULL; } else { new_head = sampled_tail; } // Video FIFO Mode. } else { - if (framebuffer->head == framebuffer->tail) { + if (fb->head == fb->tail) { return NULL; } } if (!(flags & FB_PEEK)) { - framebuffer->head = new_head; + fb->head = new_head; } - vbuffer_t *buffer = framebuffer_get_buffer(new_head); + vbuffer_t *buffer = framebuffer_get_buffer(fb, new_head); #ifdef __DCACHE_PRESENT if (flags & FB_INVALIDATE) { // Make sure any cached CPU reads are dropped before returning the buffer. - SCB_InvalidateDCache_by_Addr(buffer->data, framebuffer_get_buffer_size()); + SCB_InvalidateDCache_by_Addr(buffer->data, framebuffer_get_buffer_size(fb)); } #endif return buffer; } -vbuffer_t *framebuffer_get_tail(framebuffer_flags_t flags) { +vbuffer_t *framebuffer_get_tail(framebuffer_t *fb, framebuffer_flags_t flags) { // Sample head on the first line of a new frame. - if (framebuffer->check_head) { - framebuffer->check_head = false; - framebuffer->sampled_head = framebuffer->head; + if (fb->check_head) { + fb->check_head = false; + fb->sampled_head = fb->head; } - int32_t new_tail = (framebuffer->tail + 1) % framebuffer->n_buffers; + int32_t new_tail = (fb->tail + 1) % fb->n_buffers; // Single Buffer Mode. - if (framebuffer->n_buffers == 1) { - if (!framebuffer_get_buffer(new_tail)->waiting_for_data) { + if (fb->n_buffers == 1) { + if (!framebuffer_get_buffer(fb, new_tail)->waiting_for_data) { // Setup to check head again. - framebuffer->check_head = true; + fb->check_head = true; return NULL; } // Double Buffer Mode. - } else if (framebuffer->n_buffers == 2) { - if (new_tail == framebuffer->sampled_head) { + } else if (fb->n_buffers == 2) { + if (new_tail == fb->sampled_head) { // Setup to check head again. - framebuffer->check_head = true; + fb->check_head = true; return NULL; } // Triple Buffer Mode. - } else if (framebuffer->n_buffers == 3) { + } else if (fb->n_buffers == 3) { // For triple buffering we are never writing where tail or head // (which may instantly update to be equal to tail) is. - if (new_tail == framebuffer->sampled_head) { - new_tail = (new_tail + 1) % framebuffer->n_buffers; + if (new_tail == fb->sampled_head) { + new_tail = (new_tail + 1) % fb->n_buffers; } // Video FIFO Mode. } else { - if (new_tail == framebuffer->sampled_head) { + if (new_tail == fb->sampled_head) { // Setup to check head again. - framebuffer->check_head = true; + fb->check_head = true; return NULL; } } - vbuffer_t *buffer = framebuffer_get_buffer(new_tail); + vbuffer_t *buffer = framebuffer_get_buffer(fb, new_tail); // Reset on start versus the end so offset and jpeg_buffer_overflow are valid after FB_COMMIT. if (buffer->reset_state) { @@ -481,18 +487,18 @@ vbuffer_t *framebuffer_get_tail(framebuffer_flags_t flags) { buffer->reset_state = true; // Mark the frame buffer ready in single buffer mode. - if (framebuffer->n_buffers == 1) { + if (fb->n_buffers == 1) { buffer->waiting_for_data = false; } - framebuffer->tail = new_tail; + fb->tail = new_tail; // Setup to check head again. - framebuffer->check_head = true; + fb->check_head = true; } return buffer; } -char *framebuffer_get_buffers_end() { - return (char *) (framebuffer->data + framebuffer_total_buffer_size()); +char *framebuffer_get_buffers_end(framebuffer_t *fb) { + return (char *) (fb->data + framebuffer_total_buffer_size(fb)); } diff --git a/lib/imlib/framebuffer.h b/lib/imlib/framebuffer.h index d919ce91c..117f0b7a9 100644 --- a/lib/imlib/framebuffer.h +++ b/lib/imlib/framebuffer.h @@ -53,8 +53,6 @@ typedef struct framebuffer { OMV_ATTR_ALIGNED(uint8_t data[], FRAMEBUFFER_ALIGNMENT); } framebuffer_t; -extern framebuffer_t *framebuffer; - typedef enum { FB_NO_FLAGS = (0 << 0), FB_PEEK = (1 << 0), // If set, will not move the head/tail. @@ -81,72 +79,73 @@ typedef struct jpegbuffer { OMV_ATTR_ALIGNED(uint8_t pixels[], FRAMEBUFFER_ALIGNMENT); } jpegbuffer_t; -extern jpegbuffer_t *jpeg_framebuffer; - -// Force fb streaming to the IDE off. -void fb_set_streaming_enabled(bool enable); -bool fb_get_streaming_enabled(); - -// Encode jpeg data for transmission over a text channel. -int fb_encode_for_ide_new_size(image_t *img); -void fb_encode_for_ide(uint8_t *ptr, image_t *img); +extern jpegbuffer_t *jpegbuffer; void framebuffer_init0(); -int32_t framebuffer_get_x(); -int32_t framebuffer_get_y(); -int32_t framebuffer_get_u(); -int32_t framebuffer_get_v(); +framebuffer_t *framebuffer_get(size_t id); -int32_t framebuffer_get_width(); -int32_t framebuffer_get_height(); -int32_t framebuffer_get_depth(); +int32_t framebuffer_get_x(framebuffer_t *fb); +int32_t framebuffer_get_y(framebuffer_t *fb); +int32_t framebuffer_get_u(framebuffer_t *fb); +int32_t framebuffer_get_v(framebuffer_t *fb); + +int32_t framebuffer_get_width(framebuffer_t *fb); +int32_t framebuffer_get_height(framebuffer_t *fb); +int32_t framebuffer_get_depth(framebuffer_t *fb); + +// Force fb streaming to the IDE off. +void framebuffer_set_streaming(framebuffer_t *fb, bool enable); +bool framebuffer_get_streaming(framebuffer_t *fb); + +// Encode jpeg data for transmission over a text channel. +void framebuffer_encode(framebuffer_t *fb, uint8_t *ptr, image_t *img); +int framebuffer_encoded_size(framebuffer_t *fb, image_t *img); // Return the number of bytes in the current buffer. -uint32_t framebuffer_get_buffer_size(); +uint32_t framebuffer_get_buffer_size(framebuffer_t *fb); // Return the state of a buffer. -vbuffer_t *framebuffer_get_buffer(int32_t index); +vbuffer_t *framebuffer_get_buffer(framebuffer_t *fb, int32_t index); // Initializes an image from the frame buffer. -void framebuffer_init_image(image_t *img); +void framebuffer_init_image(framebuffer_t *fb, image_t *img); // Sets the frame buffer from an image. -void framebuffer_init_from_image(image_t *img); +void framebuffer_init_from_image(framebuffer_t *fb, image_t *img); // Compress src image to the JPEG buffer if src is mutable, otherwise copy src to the JPEG buffer // if the src is JPEG and fits in the JPEG buffer, or encode and stream src image to the IDE if not. -void framebuffer_update_jpeg_buffer(); +void framebuffer_update_jpeg_buffer(framebuffer_t *fb); // Clear the framebuffer FIFO. If fifo_flush is true, reset and discard all framebuffers, // otherwise, retain the last frame in the fifo. -void framebuffer_flush_buffers(bool fifo_flush); +void framebuffer_flush_buffers(framebuffer_t *fb, bool fifo_flush); // Set the number of virtual buffers in the frame buffer. // If n_buffers = -1 the number of virtual buffers will be set to 3 each if possible. // If n_buffers = 1 the whole framebuffer is used. In this case, `frame_size` is ignored. -int framebuffer_set_buffers(int32_t n_buffers); +int framebuffer_set_buffers(framebuffer_t *fb, int32_t n_buffers); // Call when done with the current vbuffer to mark it as free. -void framebuffer_free_current_buffer(); +void framebuffer_free_current_buffer(framebuffer_t *fb); // Call to do any heavy setup before frame capture. -void framebuffer_setup_buffers(); +void framebuffer_setup_buffers(framebuffer_t *fb); // Sets the current frame buffer to the latest virtual frame buffer. // Returns the buffer if it is ready or NULL if not... // Pass FB_PEEK to get the next buffer but not take it. -vbuffer_t *framebuffer_get_head(framebuffer_flags_t flags); +vbuffer_t *framebuffer_get_head(framebuffer_t *fb, framebuffer_flags_t flags); // Return the next vbuffer to store image data to or NULL if none. // Pass FB_PEEK to get the next buffer but not commit it. -vbuffer_t *framebuffer_get_tail(framebuffer_flags_t flags); +vbuffer_t *framebuffer_get_tail(framebuffer_t *fb, framebuffer_flags_t flags); // Returns a pointer to the end of the framebuffer(s). -char *framebuffer_get_buffers_end(); +char *framebuffer_get_buffers_end(framebuffer_t *fb); -// Use these macros to get a pointer to main or JPEG framebuffer. -#define MAIN_FB() (framebuffer) -#define JPEG_FB() (jpeg_framebuffer) +// Use this macro to get a pointer to the JPEG buffer. +#define JPEG_FB() (jpegbuffer) #endif /* __FRAMEBUFFER_H__ */ diff --git a/modules/py_csi.c b/modules/py_csi.c index ef877cfee..619ac2707 100644 --- a/modules/py_csi.c +++ b/modules/py_csi.c @@ -131,7 +131,7 @@ static mp_obj_t py_omv_csi_shutdown(mp_obj_t enable) { static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_shutdown_obj, py_omv_csi_shutdown); static mp_obj_t py_omv_csi_flush() { - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(csi.fb); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_flush_obj, py_omv_csi_flush); @@ -194,12 +194,13 @@ static mp_obj_t py_omv_csi_height() { static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_height_obj, py_omv_csi_height); static mp_obj_t py_omv_csi_get_fb() { - if (framebuffer_get_depth() < 0) { + image_t image; + + if (framebuffer_get_depth(csi.fb) < 0) { return mp_const_none; } - image_t image; - framebuffer_init_image(&image); + framebuffer_init_image(csi.fb, &image); return py_image_from_struct(&image); } static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_fb_obj, py_omv_csi_get_fb); @@ -210,7 +211,8 @@ static mp_obj_t py_omv_csi_get_id() { static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_id_obj, py_omv_csi_get_id); static mp_obj_t py_omv_csi_get_frame_available() { - return mp_obj_new_bool(framebuffer->tail != framebuffer->head); + framebuffer_t *fb = framebuffer_get(0); + return mp_obj_new_bool(fb->tail != fb->head); } static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_frame_available_obj, py_omv_csi_get_frame_available); @@ -351,10 +353,10 @@ static mp_obj_t py_omv_csi_get_windowing() { omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMESIZE); } - return mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(framebuffer_get_x()), - mp_obj_new_int(framebuffer_get_y()), - mp_obj_new_int(framebuffer_get_u()), - mp_obj_new_int(framebuffer_get_v())}); + return mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(framebuffer_get_x(csi.fb)), + mp_obj_new_int(framebuffer_get_y(csi.fb)), + mp_obj_new_int(framebuffer_get_u(csi.fb)), + mp_obj_new_int(framebuffer_get_v(csi.fb))}); } static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_windowing_obj, py_omv_csi_get_windowing); @@ -651,8 +653,9 @@ static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_auto_rotation_obj, py_omv_csi_ge static mp_obj_t py_omv_csi_set_framebuffers(mp_obj_t count) { mp_int_t c = mp_obj_get_int(count); + framebuffer_t *fb = framebuffer_get(0); - if (framebuffer->n_buffers == c) { + if (fb->n_buffers == c) { return mp_const_none; } @@ -670,7 +673,8 @@ static mp_obj_t py_omv_csi_set_framebuffers(mp_obj_t count) { static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_framebuffers_obj, py_omv_csi_set_framebuffers); static mp_obj_t py_omv_csi_get_framebuffers() { - return mp_obj_new_int(framebuffer->n_buffers); + framebuffer_t *fb = framebuffer_get(0); + return mp_obj_new_int(fb->n_buffers); } static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_framebuffers_obj, py_omv_csi_get_framebuffers); diff --git a/modules/py_fir.c b/modules/py_fir.c index 10261c001..a9abf43b7 100644 --- a/modules/py_fir.c +++ b/modules/py_fir.c @@ -1107,7 +1107,8 @@ mp_obj_t py_fir_snapshot(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a fb_alloc_free_till_mark(); if (args[ARG_copy_to_fb].u_bool) { - framebuffer_update_jpeg_buffer(); + framebuffer_t *fb = framebuffer_get(0); + framebuffer_update_jpeg_buffer(fb); } return py_image_from_struct(&dst_img); } diff --git a/modules/py_gif.c b/modules/py_gif.c index 6528f230b..82cc0df95 100644 --- a/modules/py_gif.c +++ b/modules/py_gif.c @@ -129,10 +129,12 @@ static mp_obj_t py_gif_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + framebuffer_t *fb = framebuffer_get(0); py_gif_obj_t *gif = mp_obj_malloc_with_finaliser(py_gif_obj_t, &py_gif_type); - gif->width = (args[ARG_width].u_int == -1) ? framebuffer_get_width() : args[ARG_width].u_int; - gif->height = (args[ARG_height].u_int == -1) ? framebuffer_get_height() : args[ARG_height].u_int; - gif->color = (args[ARG_color].u_int == -1) ? (framebuffer_get_depth() >= 2) : args[ARG_color].u_bool; + + gif->width = (args[ARG_width].u_int == -1) ? framebuffer_get_width(fb) : args[ARG_width].u_int; + gif->height = (args[ARG_height].u_int == -1) ? framebuffer_get_height(fb) : args[ARG_height].u_int; + gif->color = (args[ARG_color].u_int == -1) ? (framebuffer_get_depth(fb) >= 2) : args[ARG_color].u_bool; gif->loop = args[ARG_loop].u_bool; file_open(&gif->fp, path, false, FA_WRITE | FA_CREATE_ALWAYS); diff --git a/modules/py_helper.c b/modules/py_helper.c index 6a9ee0f65..12ee83600 100644 --- a/modules/py_helper.c +++ b/modules/py_helper.c @@ -547,24 +547,27 @@ const uint8_t *py_helper_keyword_alpha_palette(size_t n_args, const mp_obj_t *ar } bool py_helper_is_equal_to_framebuffer(image_t *img) { - return framebuffer_get_buffer(framebuffer->head)->data == img->data; + framebuffer_t *fb = framebuffer_get(0); + return framebuffer_get_buffer(fb, fb->head)->data == img->data; } void py_helper_update_framebuffer(image_t *img) { if (py_helper_is_equal_to_framebuffer(img)) { - framebuffer_init_from_image(img); + framebuffer_init_from_image(framebuffer_get(0), img); } } void py_helper_set_to_framebuffer(image_t *img) { + framebuffer_t *fb = framebuffer_get(0); + #if MICROPY_PY_CSI omv_csi_set_framebuffers(1); #else - framebuffer_set_buffers(1); + framebuffer_set_buffers(fb, 1); #endif - PY_ASSERT_TRUE_MSG((image_size(img) <= framebuffer_get_buffer_size()), + PY_ASSERT_TRUE_MSG((image_size(img) <= framebuffer_get_buffer_size(fb)), "The image doesn't fit in the frame buffer!"); - framebuffer_init_from_image(img); - img->data = framebuffer_get_buffer(framebuffer->head)->data; + framebuffer_init_from_image(fb, img); + img->data = framebuffer_get_buffer(fb, fb->head)->data; } diff --git a/modules/py_image.c b/modules/py_image.c index 01f12f425..6de5b9f4c 100644 --- a/modules/py_image.c +++ b/modules/py_image.c @@ -1045,7 +1045,8 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, mp_rom_obj_t default_color_palet IMAGE_HINT_SCALE_ASPECT_IGNORE)) | IMAGE_HINT_BLACK_BACKGROUND; if (args[ARG_copy_to_fb].u_bool) { - framebuffer_update_jpeg_buffer(); + framebuffer_t *fb = framebuffer_get(0); + framebuffer_update_jpeg_buffer(fb); } image_t dst_img = { @@ -1136,7 +1137,8 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, mp_rom_obj_t default_color_palet } if (args[ARG_encode_for_ide].u_bool) { - dst_img.size = fb_encode_for_ide_new_size(&dst_img_tmp); + framebuffer_t *fb = framebuffer_get(0); + dst_img.size = framebuffer_encoded_size(fb, &dst_img_tmp); } else { dst_img.size = dst_img_tmp.size; } @@ -1152,15 +1154,17 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, mp_rom_obj_t default_color_palet image_xalloc(&dst_img, size); } else { // Convert in place. - bool fb = py_helper_is_equal_to_framebuffer(src_img); - size_t buf_size = fb ? framebuffer_get_buffer_size() : image_size(src_img); + framebuffer_t *fb = framebuffer_get(0); + bool is_fb = py_helper_is_equal_to_framebuffer(src_img); + size_t buf_size = is_fb ? framebuffer_get_buffer_size(fb) : image_size(src_img); PY_ASSERT_TRUE_MSG((size <= buf_size), "The image doesn't fit in the frame buffer!"); dst_img.data = src_img->data; } if (dst_img.is_compressed) { if (args[ARG_encode_for_ide].u_bool) { - fb_encode_for_ide(dst_img.data, &dst_img_tmp); + framebuffer_t *fb = framebuffer_get(0); + framebuffer_encode(fb, dst_img.data, &dst_img_tmp); } else if (dst_img.data != dst_img_tmp.data) { memcpy(dst_img.data, dst_img_tmp.data, dst_img.size); } @@ -1265,7 +1269,8 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(py_image_save_obj, 2, py_image_save); #endif //IMLIB_ENABLE_IMAGE_FILE_IO static mp_obj_t py_image_flush(mp_obj_t img_obj) { - framebuffer_update_jpeg_buffer(); + framebuffer_t *fb = framebuffer_get(0); + framebuffer_update_jpeg_buffer(fb); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(py_image_flush_obj, py_image_flush); @@ -6231,7 +6236,8 @@ mp_obj_t py_image_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw } if (args[ARG_copy_to_fb].u_bool) { - framebuffer_update_jpeg_buffer(); + framebuffer_t *fb = framebuffer_get(0); + framebuffer_update_jpeg_buffer(fb); } return py_image_from_struct(&image); } diff --git a/modules/py_imageio.c b/modules/py_imageio.c index 8df0f4cee..e4f9ac338 100644 --- a/modules/py_imageio.c +++ b/modules/py_imageio.c @@ -415,7 +415,8 @@ static mp_obj_t py_imageio_read(size_t n_args, const mp_obj_t *pos_args, mp_map_ py_helper_update_framebuffer(&image); if (args[ARG_copy_to_fb].u_bool) { - framebuffer_update_jpeg_buffer(); + framebuffer_t *fb = framebuffer_get(0); + framebuffer_update_jpeg_buffer(fb); } return py_image_from_struct(&image); } diff --git a/modules/py_mjpeg.c b/modules/py_mjpeg.c index 3b635d810..7a9e9cf84 100644 --- a/modules/py_mjpeg.c +++ b/modules/py_mjpeg.c @@ -188,14 +188,16 @@ static mp_obj_t py_mjpeg_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + framebuffer_t *fb = framebuffer_get(0); py_mjpeg_obj_t *mjpeg = mp_obj_malloc_with_finaliser(py_mjpeg_obj_t, &py_mjpeg_type); + mjpeg->frames = 0; mjpeg->bytes = 0; mjpeg->us_old = 0; mjpeg->us_avg = 0; mjpeg->closed = 0; - mjpeg->width = (args[ARG_width].u_int == -1) ? framebuffer_get_width() : args[ARG_width].u_int; - mjpeg->height = (args[ARG_height].u_int == -1) ? framebuffer_get_height() : args[ARG_height].u_int; + mjpeg->width = (args[ARG_width].u_int == -1) ? framebuffer_get_width(fb) : args[ARG_width].u_int; + mjpeg->height = (args[ARG_height].u_int == -1) ? framebuffer_get_height(fb) : args[ARG_height].u_int; file_open(&mjpeg->fp, path, false, FA_WRITE | FA_CREATE_ALWAYS); mjpeg_open(&mjpeg->fp, mjpeg->width, mjpeg->height); diff --git a/modules/py_omv.c b/modules/py_omv.c index d57211770..064e1504e 100644 --- a/modules/py_omv.c +++ b/modules/py_omv.c @@ -77,10 +77,12 @@ static mp_obj_t py_omv_debug_mode() { static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_debug_mode_obj, py_omv_debug_mode); static mp_obj_t py_omv_disable_fb(size_t n_args, const mp_obj_t *args) { + framebuffer_t *fb = framebuffer_get(0); + if (!n_args) { - return mp_obj_new_bool(!fb_get_streaming_enabled()); + return mp_obj_new_bool(!framebuffer_get_streaming(fb)); } - fb_set_streaming_enabled(!mp_obj_get_int(args[0])); + framebuffer_set_streaming(fb, !mp_obj_get_int(args[0])); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_disable_fb_obj, 0, 1, py_omv_disable_fb); diff --git a/modules/py_tof.c b/modules/py_tof.c index 8d564f8ae..4ed5467e3 100644 --- a/modules/py_tof.c +++ b/modules/py_tof.c @@ -589,7 +589,8 @@ mp_obj_t py_tof_snapshot(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a fb_alloc_free_till_mark(); if (args[ARG_copy_to_fb].u_bool) { - framebuffer_update_jpeg_buffer(); + framebuffer_t *fb = framebuffer_get(0); + framebuffer_update_jpeg_buffer(fb); } return py_image_from_struct(&dst_img); } diff --git a/ports/alif/omv_csi.c b/ports/alif/omv_csi.c index 8bace89ff..2b0df43f4 100644 --- a/ports/alif/omv_csi.c +++ b/ports/alif/omv_csi.c @@ -47,8 +47,6 @@ #include "omv_gpu.h" #include "omv_i2c.h" #include "omv_csi.h" - -#include "framebuffer.h" #include "unaligned_memcpy.h" // Bits missing from cpi.h @@ -71,15 +69,13 @@ #define CPI_ERROR_FLAGS (CAM_INTR_INFIFO_OVERRUN | \ CAM_INTR_OUTFIFO_OVERRUN | \ CAM_INTR_BRESP_ERR) -// csi struct. -omv_csi_t csi = {}; static CPI_Type *cpi_get_base_addr(omv_csi_t *csi) { return ((CPI_Type *) CPI_BASE); } void omv_csi_init0() { - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Re-init I2C to reset the bus state after soft reset, which // could have interrupted the bus in the middle of a transfer. @@ -115,8 +111,10 @@ int omv_csi_init() { // Reset the csi state memset(&csi, 0, sizeof(omv_csi_t)); + // Set default framebuffer + csi.fb = framebuffer_get(0); + // Set default snapshot function. - // Some sensors need to call snapshot from init. csi.snapshot = omv_csi_snapshot; // Configure the CSI external clock. @@ -190,24 +188,25 @@ int omv_csi_config(omv_csi_config_t config) { return 0; } -int omv_csi_abort(bool fifo_flush, bool in_irq) { - CPI_Type *cpi = cpi_get_base_addr(&csi); +int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { + CPI_Type *cpi = cpi_get_base_addr(csi); cpi->CAM_CTRL = 0; NVIC_DisableIRQ(CAM_IRQ_IRQn); cpi_disable_interrupt(cpi, CPI_IRQ_FLAGS); cpi_irq_handler_clear_intr_status(cpi, CPI_IRQ_FLAGS); - csi.first_line = false; - csi.drop_frame = false; - csi.last_frame_ms = 0; - csi.last_frame_ms_valid = false; + csi->first_line = false; + csi->drop_frame = false; + csi->last_frame_ms = 0; + csi->last_frame_ms_valid = false; - if (fifo_flush) { - framebuffer_flush_buffers(true); - } else if (!csi.disable_full_flush) { - framebuffer_flush_buffers(false); + if (csi->fb) { + if (fifo_flush) { + framebuffer_flush_buffers(csi->fb, true); + } else if (!csi->disable_full_flush) { + framebuffer_flush_buffers(csi->fb, false); + } } - return 0; } @@ -248,13 +247,11 @@ uint32_t omv_csi_get_fb_offset(omv_csi_t *csi) { // This is the default snapshot function, which can be replaced in omv_csi_init functions. int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { + framebuffer_t *fb = csi->fb; + static uint32_t frames = 0; static uint32_t r_stat, gb_stat, gr_stat, b_stat; - // Used to restore MAIN_FB's width and height. - uint32_t w = MAIN_FB()->u; - uint32_t h = MAIN_FB()->v; - CPI_Type *cpi = cpi_get_base_addr(csi); if (csi->pixformat == PIXFORMAT_INVALID) { @@ -270,14 +267,14 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { } // Compress the framebuffer for the IDE preview. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(fb); // Free the current FB head. - framebuffer_free_current_buffer(); + framebuffer_free_current_buffer(fb); // Reconfigure and restart the CSI transfer if it's not running. if (!(cpi->CAM_CTRL & CAM_CTRL_BUSY)) { - framebuffer_setup_buffers(); + framebuffer_setup_buffers(fb); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t line_size_bytes = resolution[csi->framesize][0] * bytes_per_pixel; @@ -290,7 +287,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { } // Get the destination buffer address. - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); // Check if buffer is not ready or is not 64-bit aligned. if ((!buffer) || (LocalToGlobal(buffer->data) & 0x7)) { @@ -341,12 +338,12 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { } #endif - vbuffer_t *buffer = framebuffer_get_head(FB_INVALIDATE); + vbuffer_t *buffer = framebuffer_get_head(fb, FB_INVALIDATE); // Wait for the DMA to finish the transfer. for (mp_uint_t ticks = mp_hal_ticks_ms(); buffer == NULL;) { MICROPY_EVENT_POLL_HOOK if ((mp_hal_ticks_ms() - ticks) > 3000) { - omv_csi_abort(true, false); + omv_csi_abort(csi, true, false); #if defined(OMV_CSI_FSYNC_PIN) if (csi->frame_sync) { @@ -356,7 +353,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { return OMV_CSI_ERROR_CAPTURE_TIMEOUT; } - buffer = framebuffer_get_head(FB_INVALIDATE); + buffer = framebuffer_get_head(fb, FB_INVALIDATE); } // We're done receiving data. @@ -366,31 +363,32 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { } #endif + // Restore the frame buffer width and height. if (!csi->transpose) { - MAIN_FB()->w = w; - MAIN_FB()->h = h; + fb->w = fb->u; + fb->h = fb->v; } else { - MAIN_FB()->w = h; - MAIN_FB()->h = w; + fb->w = fb->v; + fb->h = fb->u; } // Reset the frame buffer's pixel format. switch (csi->pixformat) { case PIXFORMAT_GRAYSCALE: - MAIN_FB()->pixfmt = PIXFORMAT_GRAYSCALE; + fb->pixfmt = PIXFORMAT_GRAYSCALE; break; case PIXFORMAT_RGB565: - MAIN_FB()->pixfmt = PIXFORMAT_RGB565; + fb->pixfmt = PIXFORMAT_RGB565; break; case PIXFORMAT_YUV422: { - MAIN_FB()->pixfmt = PIXFORMAT_YUV; - MAIN_FB()->subfmt_id = csi->yuv_format; - MAIN_FB()->pixfmt = imlib_yuv_shift(MAIN_FB()->pixfmt, MAIN_FB()->x); + fb->pixfmt = PIXFORMAT_YUV; + fb->subfmt_id = csi->yuv_format; + fb->pixfmt = imlib_yuv_shift(fb->pixfmt, fb->x); break; case PIXFORMAT_BAYER: - MAIN_FB()->pixfmt = PIXFORMAT_BAYER; - MAIN_FB()->subfmt_id = csi->cfa_format; - MAIN_FB()->pixfmt = imlib_bayer_shift(MAIN_FB()->pixfmt, MAIN_FB()->x, MAIN_FB()->y, csi->transpose); + fb->pixfmt = PIXFORMAT_BAYER; + fb->subfmt_id = csi->cfa_format; + fb->pixfmt = imlib_bayer_shift(fb->pixfmt, fb->x, fb->y, csi->transpose); break; } default: @@ -398,13 +396,13 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { } // Initialize a frame using the frame buffer. - framebuffer_init_image(dst_image); + framebuffer_init_image(fb, dst_image); // Set the frame's pixel format to bayer for raw sensors. if (csi->raw_output && csi->pixformat != PIXFORMAT_BAYER) { dst_image->pixfmt = PIXFORMAT_BAYER; dst_image->subfmt_id = csi->cfa_format; - dst_image->pixfmt = imlib_bayer_shift(dst_image->pixfmt, MAIN_FB()->x, MAIN_FB()->y, csi->transpose); + dst_image->pixfmt = imlib_bayer_shift(dst_image->pixfmt, fb->x, fb->y, csi->transpose); } // Crop first to reduce the frame size before debayering. @@ -421,8 +419,8 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { dst_cimage.pixels += omv_csi_get_fb_offset(csi); } - rectangle_t srect = { MAIN_FB()->x, MAIN_FB()->y, MAIN_FB()->u, MAIN_FB()->v }; - rectangle_t drect = { 0, 0, MAIN_FB()->u, MAIN_FB()->v }; + rectangle_t srect = { fb->x, fb->y, fb->u, fb->v }; + rectangle_t drect = { 0, 0, fb->u, fb->v }; if (omv_gpu_draw_image(&src_cimage, &srect, &dst_cimage, &drect, 255, NULL, NULL, 0) != 0) { return OMV_CSI_ERROR_IO_ERROR; } @@ -438,7 +436,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) { } // Set the target pixel format before debayer. - dst_image->pixfmt = MAIN_FB()->pixfmt; + dst_image->pixfmt = fb->pixfmt; // Update AWB stats every n frames. if ((frames++ % 100) == 0) { @@ -466,19 +464,19 @@ void CAM_IRQHandler(void) { if (status & CAM_INTR_INFIFO_OVERRUN) { mask |= CAM_INTR_INFIFO_OVERRUN; - omv_csi_abort(true, true); + omv_csi_abort(&csi, true, true); printf("INFIFO_OVERRUN\n"); } if (status & CAM_INTR_OUTFIFO_OVERRUN) { mask |= CAM_INTR_OUTFIFO_OVERRUN; - omv_csi_abort(true, true); + omv_csi_abort(&csi, true, true); printf("OUTFIFO_OVERRUN\n"); } if (status & CAM_INTR_BRESP_ERR) { mask |= CAM_INTR_BRESP_ERR; - omv_csi_abort(true, true); + omv_csi_abort(&csi, true, true); printf("BRESP_ERR %lu\n", cpi->CAM_AXI_ERR_STAT); } @@ -487,10 +485,12 @@ void CAM_IRQHandler(void) { cpi->CAM_CTRL = 0; if (!(status & CPI_ERROR_FLAGS)) { // Release the current framebuffer. - framebuffer_get_tail(FB_NO_FLAGS); + framebuffer_get_tail(csi.fb, FB_NO_FLAGS); } + // Get the current framebuffer (or new tail). - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + vbuffer_t *buffer = framebuffer_get_tail(csi.fb, FB_PEEK); + if (buffer != NULL) { cpi->CAM_CTRL = 0; cpi->CAM_CTRL |= CAM_CTRL_SW_RESET; @@ -498,6 +498,7 @@ void CAM_IRQHandler(void) { cpi_irq_handler_clear_intr_status(cpi, mask); cpi->CAM_CTRL = (CAM_CTRL_SNAPSHOT | CAM_CTRL_START | CAM_CTRL_FIFO_CLK_SEL); } + if (!(status & CPI_ERROR_FLAGS)) { if (csi.frame_callback) { csi.frame_callback(); diff --git a/ports/mimxrt/omv_csi.c b/ports/mimxrt/omv_csi.c index f8179c3ca..366081976 100644 --- a/ports/mimxrt/omv_csi.c +++ b/ports/mimxrt/omv_csi.c @@ -43,14 +43,11 @@ #include "omv_gpio.h" #include "omv_i2c.h" #include "omv_csi.h" -#include "framebuffer.h" #include "unaligned_memcpy.h" #define DMA_LENGTH_ALIGNMENT (8) #define MIN_EDMA_DST_INC (4) -// Sensor struct. -omv_csi_t csi = {}; extern uint8_t _line_buf[OMV_LINE_BUF_SIZE]; #define CSI_IRQ_FLAGS (CSI_CR1_SOF_INTEN_MASK \ @@ -58,7 +55,7 @@ extern uint8_t _line_buf[OMV_LINE_BUF_SIZE]; | CSI_CR1_FB1_DMA_DONE_INTEN_MASK) void omv_csi_init0() { - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Re-init I2C to reset the bus state after soft reset, which // could have interrupted the bus in the middle of a transfer. @@ -92,6 +89,9 @@ int omv_csi_init() { // Reset the csi state memset(&csi, 0, sizeof(omv_csi_t)); + // Set default framebuffer + csi.fb = framebuffer_get(0); + // Set default snapshot function. // Some sensors need to call snapshot from init. csi.snapshot = omv_csi_snapshot; @@ -168,20 +168,24 @@ int omv_csi_config(omv_csi_config_t config) { return 0; } -int omv_csi_abort(bool fifo_flush, bool in_irq) { +int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { NVIC_DisableIRQ(CSI_IRQn); CSI_DisableInterrupts(CSI, CSI_IRQ_FLAGS); CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK; CSI_REG_CR18(CSI) &= ~CSI_CR18_CSI_ENABLE_MASK; - csi.dest_inc = 0; - csi.first_line = false; - csi.drop_frame = false; - csi.last_frame_ms = 0; - csi.last_frame_ms_valid = false; - if (fifo_flush) { - framebuffer_flush_buffers(true); - } else if (!csi.disable_full_flush) { - framebuffer_flush_buffers(false); + + csi->dest_inc = 0; + csi->first_line = false; + csi->drop_frame = false; + csi->last_frame_ms = 0; + csi->last_frame_ms_valid = false; + + if (csi->fb) { + if (fifo_flush) { + framebuffer_flush_buffers(csi->fb, true); + } else if (!csi->disable_full_flush) { + framebuffer_flush_buffers(csi->fb, false); + } } return 0; } @@ -210,10 +214,12 @@ uint32_t omv_csi_get_xclk_frequency() { void omv_csi_sof_callback() { csi.first_line = false; csi.drop_frame = false; + // Get current framebuffer. - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + vbuffer_t *buffer = framebuffer_get_tail(csi.fb, FB_PEEK); + if (buffer == NULL) { - omv_csi_abort(false, true); + omv_csi_abort(&csi, false, true); } else if (buffer->offset < resolution[csi.framesize][1]) { // Missed a few lines, reset buffer state and continue. buffer->reset_state = true; @@ -232,15 +238,17 @@ int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed edma_handle_t *handle = dma; edma_transfer_config_t config; + framebuffer_t *fb = csi.fb; + EDMA_PrepareTransferConfig(&config, src, // srcAddr csi.src_size, // srcWidth csi.src_inc, // srcOffset dst, // destAddr transposed ? bpp : csi.dest_inc, // destWidth - transposed ? (MAIN_FB()->v * bpp) : csi.dest_inc, // destOffset - MAIN_FB()->u * bpp, // bytesEachRequest - MAIN_FB()->u * bpp); // transferBytes + transposed ? (fb->v * bpp) : csi.dest_inc, // destOffset + fb->u * bpp, // bytesEachRequest + fb->u * bpp); // transferBytes size_t retry = 3; status_t status = kStatus_EDMA_Busy; @@ -262,11 +270,13 @@ int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed #endif void omv_csi_line_callback(uint32_t addr) { + framebuffer_t *fb = csi.fb; + // Throttle frames to match the current frame rate. omv_csi_throttle_framerate(); // Get current framebuffer. - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); if (csi.pixformat == PIXFORMAT_JPEG) { if (csi.drop_frame) { @@ -288,7 +298,7 @@ void omv_csi_line_callback(uint32_t addr) { // uint16_t size = __REV16(*((uint16_t *) addr)); // Prevent a buffer overflow when writing the jpeg data. - if (buffer->offset + size > framebuffer_get_buffer_size()) { + if (buffer->offset + size > framebuffer_get_buffer_size(fb)) { buffer->jpeg_buffer_overflow = true; jpeg_end = true; } else { @@ -311,7 +321,7 @@ void omv_csi_line_callback(uint32_t addr) { // detect the end of the frame when there's no more jpeg data. if (jpeg_end) { // Release the current framebuffer. - framebuffer_get_tail(FB_NO_FLAGS); + framebuffer_get_tail(fb, FB_NO_FLAGS); CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK; if (csi.frame_callback) { csi.frame_callback(); @@ -329,10 +339,10 @@ void omv_csi_line_callback(uint32_t addr) { return; } - if ((MAIN_FB()->y <= buffer->offset) && (buffer->offset < (MAIN_FB()->y + MAIN_FB()->v))) { + if ((fb->y <= buffer->offset) && (buffer->offset < (fb->y + fb->v))) { // Copy from DMA buffer to framebuffer. uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); - uint8_t *src = ((uint8_t *) addr) + (MAIN_FB()->x * bytes_per_pixel); + uint8_t *src = ((uint8_t *) addr) + (fb->x * bytes_per_pixel); uint8_t *dst = buffer->data; // Adjust BPP for Grayscale. @@ -341,9 +351,9 @@ void omv_csi_line_callback(uint32_t addr) { } if (csi.transpose) { - dst += bytes_per_pixel * (buffer->offset - MAIN_FB()->y); + dst += bytes_per_pixel * (buffer->offset - fb->y); } else { - dst += MAIN_FB()->u * bytes_per_pixel * (buffer->offset - MAIN_FB()->y); + dst += fb->u * bytes_per_pixel * (buffer->offset - fb->y); } #if defined(OMV_CSI_DMA) @@ -358,7 +368,7 @@ void omv_csi_line_callback(uint32_t addr) { if (++buffer->offset == resolution[csi.framesize][1]) { // Release the current framebuffer. - framebuffer_get_tail(FB_NO_FLAGS); + framebuffer_get_tail(fb, FB_NO_FLAGS); CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK; if (csi.frame_callback) { csi.frame_callback(); @@ -368,8 +378,9 @@ void omv_csi_line_callback(uint32_t addr) { #if defined(OMV_CSI_DMA) static void edma_config(omv_csi_t *csi, uint32_t bytes_per_pixel) { - uint32_t line_offset_bytes = MAIN_FB()->x * bytes_per_pixel; - uint32_t line_width_bytes = MAIN_FB()->u * bytes_per_pixel; + framebuffer_t *fb = fb; + uint32_t line_offset_bytes = fb->x * bytes_per_pixel; + uint32_t line_width_bytes = fb->u * bytes_per_pixel; // YUV422 Source -> Y Destination if ((csi->pixformat == PIXFORMAT_GRAYSCALE) && (csi->mono_bpp == 2)) { @@ -407,9 +418,11 @@ static void edma_config(omv_csi_t *csi, uint32_t bytes_per_pixel) { #endif int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { - // Used to restore MAIN_FB's width and height. - uint32_t w = MAIN_FB()->u; - uint32_t h = MAIN_FB()->v; + framebuffer_t *fb = csi->fb; + + // Used to restore the frame buffer width and height. + uint32_t w = fb->u; + uint32_t h = fb->v; if (csi->pixformat == PIXFORMAT_INVALID) { return OMV_CSI_ERROR_INVALID_PIXFORMAT; @@ -424,14 +437,14 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } // Compress the framebuffer for the IDE preview. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(fb); // Free the current FB head. - framebuffer_free_current_buffer(); + framebuffer_free_current_buffer(fb); // If the DMA is not active, reconfigure and restart the CSI transfer. if (!(CSI->CR18 & CSI_CR18_CSI_ENABLE_MASK)) { - framebuffer_setup_buffers(); + framebuffer_setup_buffers(fb); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t dma_line_bytes = resolution[csi->framesize][0] * bytes_per_pixel; @@ -452,7 +465,9 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { edma_config(csi, bytes_per_pixel); for (int i = 0; i < OMV_CSI_DMA_CHANNEL_COUNT; i++) { EDMA_CreateHandle(&csi->dma_channels[i], OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL_START + i); - EDMA_DisableChannelInterrupts(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL_START + i, kEDMA_MajorInterruptEnable); + EDMA_DisableChannelInterrupts(OMV_CSI_DMA, + OMV_CSI_DMA_CHANNEL_START + i, + kEDMA_MajorInterruptEnable); } } #endif @@ -492,12 +507,12 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } #endif - vbuffer_t *buffer = framebuffer_get_head(fb_flags); + vbuffer_t *buffer = framebuffer_get_head(fb, fb_flags); // Wait for the DMA to finish the transfer. for (mp_uint_t ticks = mp_hal_ticks_ms(); buffer == NULL;) { MICROPY_EVENT_POLL_HOOK if ((mp_hal_ticks_ms() - ticks) > OMV_CSI_TIMEOUT_MS) { - omv_csi_abort(true, false); + omv_csi_abort(csi, true, false); #if defined(OMV_CSI_FSYNC_PIN) if (csi->frame_sync) { @@ -507,7 +522,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return OMV_CSI_ERROR_CAPTURE_TIMEOUT; } - buffer = framebuffer_get_head(fb_flags); + buffer = framebuffer_get_head(fb, fb_flags); } // We're done receiving data. @@ -523,30 +538,30 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } if (!csi->transpose) { - MAIN_FB()->w = w; - MAIN_FB()->h = h; + fb->w = w; + fb->h = h; } else { - MAIN_FB()->w = h; - MAIN_FB()->h = w; + fb->w = h; + fb->h = w; } // Fix the BPP. switch (csi->pixformat) { case PIXFORMAT_GRAYSCALE: - MAIN_FB()->pixfmt = PIXFORMAT_GRAYSCALE; + fb->pixfmt = PIXFORMAT_GRAYSCALE; break; case PIXFORMAT_RGB565: - MAIN_FB()->pixfmt = PIXFORMAT_RGB565; + fb->pixfmt = PIXFORMAT_RGB565; break; case PIXFORMAT_BAYER: - MAIN_FB()->pixfmt = PIXFORMAT_BAYER; - MAIN_FB()->subfmt_id = csi->cfa_format; - MAIN_FB()->pixfmt = imlib_bayer_shift(MAIN_FB()->pixfmt, MAIN_FB()->x, MAIN_FB()->y, csi->transpose); + fb->pixfmt = PIXFORMAT_BAYER; + fb->subfmt_id = csi->cfa_format; + fb->pixfmt = imlib_bayer_shift(fb->pixfmt, fb->x, fb->y, csi->transpose); break; case PIXFORMAT_YUV422: { - MAIN_FB()->pixfmt = PIXFORMAT_YUV; - MAIN_FB()->subfmt_id = csi->yuv_format; - MAIN_FB()->pixfmt = imlib_yuv_shift(MAIN_FB()->pixfmt, MAIN_FB()->x); + fb->pixfmt = PIXFORMAT_YUV; + fb->subfmt_id = csi->yuv_format; + fb->pixfmt = imlib_yuv_shift(fb->pixfmt, fb->x); break; } case PIXFORMAT_JPEG: { @@ -559,8 +574,8 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // OV2640 JPEG TODO } // Clean trailing data after 0xFFD9 at the end of the jpeg byte stream. - MAIN_FB()->pixfmt = PIXFORMAT_JPEG; - MAIN_FB()->size = jpeg_clean_trailing_bytes(size, buffer->data); + fb->pixfmt = PIXFORMAT_JPEG; + fb->size = jpeg_clean_trailing_bytes(size, buffer->data); break; } default: @@ -568,7 +583,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } // Set the user image. - framebuffer_init_image(image); + framebuffer_init_image(fb, image); return 0; } #endif diff --git a/ports/nrf/omv_csi.c b/ports/nrf/omv_csi.c index 21da85427..2f0f23c1e 100644 --- a/ports/nrf/omv_csi.c +++ b/ports/nrf/omv_csi.c @@ -29,15 +29,11 @@ #include "py/mphal.h" #include "omv_i2c.h" #include "omv_csi.h" -#include "framebuffer.h" #include "omv_boardconfig.h" #include "unaligned_memcpy.h" #include "nrf_i2s.h" #include "hal/nrf_gpio.h" -// Sensor struct. -omv_csi_t csi = {}; - static uint32_t _vsyncMask; static uint32_t _hrefMask; static uint32_t _pclkMask; @@ -81,8 +77,10 @@ int omv_csi_init() { // Reset the csi state memset(&csi, 0, sizeof(omv_csi_t)); + // Set default framebuffer + csi.fb = framebuffer_get(0); + // Set default snapshot function. - // Some sensors need to call snapshot from init. csi.snapshot = omv_csi_snapshot; // Configure the csi external clock (XCLK). @@ -178,31 +176,33 @@ int omv_csi_set_windowing(int x, int y, int w, int h) { // This is the default snapshot function, which can be replaced in omv_csi_init functions. int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { + framebuffer_t *fb = csi->fb; + // Compress the framebuffer for the IDE preview, only if it's not the first frame, // the framebuffer is enabled and the image sensor does not support JPEG encoding. // Note: This doesn't run unless the IDE is connected and the framebuffer is enabled. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(fb); // This driver supports a single buffer. - if (MAIN_FB()->n_buffers != 1) { - framebuffer_set_buffers(1); + if (fb->n_buffers != 1) { + framebuffer_set_buffers(fb, 1); } - if (omv_csi_check_framebuffer_size() != 0) { + if (omv_csi_check_framebuffer_size(fb) != 0) { return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; } - framebuffer_free_current_buffer(); - framebuffer_setup_buffers(); - vbuffer_t *buffer = framebuffer_get_tail(FB_NO_FLAGS); + framebuffer_free_current_buffer(fb); + framebuffer_setup_buffers(fb); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_NO_FLAGS); if (!buffer) { return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; } uint8_t *b = buffer->data; - uint32_t _width = MAIN_FB()->w; - uint32_t _height = MAIN_FB()->h; + uint32_t _width = fb->w; + uint32_t _height = fb->h; int bytesPerRow = _width * 2; // Always read 2 BPP bool _grayscale = (csi->pixformat == PIXFORMAT_GRAYSCALE); @@ -249,15 +249,15 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } // Set framebuffer pixel format. - MAIN_FB()->pixfmt = csi->pixformat; + fb->pixfmt = csi->pixformat; // Swap bytes if set. - if ((MAIN_FB()->pixfmt == PIXFORMAT_RGB565 && csi->rgb_swap) || - (MAIN_FB()->pixfmt == PIXFORMAT_YUV422 && csi->yuv_swap)) { + if ((fb->pixfmt == PIXFORMAT_RGB565 && csi->rgb_swap) || + (fb->pixfmt == PIXFORMAT_YUV422 && csi->yuv_swap)) { unaligned_memcpy_rev16(buffer->data, buffer->data, _width * _height); } // Set the user image. - framebuffer_init_image(image); + framebuffer_init_image(fb, image); return 0; } diff --git a/ports/rp2/omv_csi.c b/ports/rp2/omv_csi.c index 2837092ee..78ee75cc9 100644 --- a/ports/rp2/omv_csi.c +++ b/ports/rp2/omv_csi.c @@ -30,7 +30,6 @@ #include "py/mphal.h" #include "omv_i2c.h" #include "omv_csi.h" -#include "framebuffer.h" #include "pico/time.h" #include "pico/stdlib.h" @@ -42,9 +41,6 @@ #include "unaligned_memcpy.h" #include "dcmi.pio.h" -// Sensor struct. -omv_csi_t csi = {}; - static void dma_irq_handler(); extern void __fatal_error(const char *msg); @@ -101,8 +97,10 @@ int omv_csi_init() { // Reset the csi state memset(&csi, 0, sizeof(omv_csi_t)); + // Set default framebuffer + csi.fb = framebuffer_get(0); + // Set default snapshot function. - // Some sensors need to call snapshot from init. csi.snapshot = omv_csi_snapshot; // Configure the csi external clock (XCLK). @@ -152,7 +150,7 @@ int omv_csi_init() { return 0; } -int omv_csi_abort(bool fifo_flush, bool in_irq) { +int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { // Disable DMA channel dma_channel_abort(OMV_CSI_DMA_CHANNEL); dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, false); @@ -161,8 +159,10 @@ int omv_csi_abort(bool fifo_flush, bool in_irq) { pio_sm_set_enabled(OMV_CSI_PIO, OMV_CSI_SM, false); pio_sm_clear_fifos(OMV_CSI_PIO, OMV_CSI_SM); - // Clear bpp flag. - MAIN_FB()->pixfmt = PIXFORMAT_INVALID; + if (csi->fb) { + // Clear bpp flag. + csi->fb->pixfmt = PIXFORMAT_INVALID; + } return 0; } @@ -197,12 +197,14 @@ int omv_csi_set_windowing(int x, int y, int w, int h) { } static void dma_irq_handler() { + framebuffer_t *fb = csi.fb; + if (dma_irqn_get_channel_status(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL)) { // Clear the interrupt request. dma_irqn_acknowledge_channel(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL); - framebuffer_get_tail(FB_NO_FLAGS); - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + framebuffer_get_tail(fb, FB_NO_FLAGS); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); if (buffer != NULL) { // Set next buffer and retrigger the DMA channel. dma_channel_set_write_addr(OMV_CSI_DMA_CHANNEL, buffer->data, true); @@ -210,69 +212,71 @@ static void dma_irq_handler() { // Unblock the state machine pio_sm_restart(OMV_CSI_PIO, OMV_CSI_SM); pio_sm_clear_fifos(OMV_CSI_PIO, OMV_CSI_SM); - pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (MAIN_FB()->v - 1)); - pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (MAIN_FB()->u * MAIN_FB()->bpp) - 1); + pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (fb->v - 1)); + pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (fb->u * fb->bpp) - 1); } } } int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { + framebuffer_t *fb = csi->fb; + // Compress the framebuffer for the IDE preview. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(fb); if (omv_csi_check_framebuffer_size() != 0) { return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; } // Free the current FB head. - framebuffer_free_current_buffer(); + framebuffer_free_current_buffer(fb); // Set framebuffer pixel format. if (csi->pixformat == PIXFORMAT_INVALID) { return OMV_CSI_ERROR_INVALID_PIXFORMAT; } - MAIN_FB()->pixfmt = csi->pixformat; + fb->pixfmt = csi->pixformat; - vbuffer_t *buffer = framebuffer_get_head(FB_NO_FLAGS); + vbuffer_t *buffer = framebuffer_get_head(fb, FB_NO_FLAGS); // If there's no ready buffer in the fifo, and the DMA is Not currently // transferring a new buffer, reconfigure and restart the DMA transfer. if (buffer == NULL && !dma_channel_is_busy(OMV_CSI_DMA_CHANNEL)) { - framebuffer_setup_buffers(); + framebuffer_setup_buffers(fb); - buffer = framebuffer_get_tail(FB_PEEK); + buffer = framebuffer_get_tail(fb, FB_PEEK); if (buffer == NULL) { return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; } // Configure the DMA on the first frame, for later frames only the write is changed. - omv_csi_dma_config(MAIN_FB()->u, MAIN_FB()->v, MAIN_FB()->bpp, - (void *) buffer->data, (csi->rgb_swap && MAIN_FB()->bpp == 2)); - + omv_csi_dma_config(fb->u, fb->v, fb->bpp, + (void *) buffer->data, + (csi->rgb_swap && fb->bpp == 2)); // Re-enable the state machine. pio_sm_clear_fifos(OMV_CSI_PIO, OMV_CSI_SM); pio_sm_set_enabled(OMV_CSI_PIO, OMV_CSI_SM, true); // Unblock the state machine - pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (MAIN_FB()->v - 1)); - pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (MAIN_FB()->u * MAIN_FB()->bpp) - 1); + pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (fb->v - 1)); + pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (fb->u * fb->bpp) - 1); } // Wait for the DMA to finish the transfer. for (mp_uint_t ticks = mp_hal_ticks_ms(); buffer == NULL;) { - buffer = framebuffer_get_head(FB_NO_FLAGS); + buffer = framebuffer_get_head(fb, FB_NO_FLAGS); if ((mp_hal_ticks_ms() - ticks) > 3000) { - omv_csi_abort(true, false); + omv_csi_abort(csi, true, false); return OMV_CSI_ERROR_CAPTURE_TIMEOUT; } } - MAIN_FB()->w = MAIN_FB()->u; - MAIN_FB()->h = MAIN_FB()->v; + fb->w = fb->u; + fb->h = fb->v; // Set the user image. - framebuffer_init_image(image); + framebuffer_init_image(fb, image); return 0; } #endif diff --git a/ports/stm32/omv_csi.c b/ports/stm32/omv_csi.c index 4787359e3..b211aa262 100644 --- a/ports/stm32/omv_csi.c +++ b/ports/stm32/omv_csi.c @@ -35,7 +35,6 @@ #include #include "py/mphal.h" #include "irq.h" -#include "framebuffer.h" #include "omv_boardconfig.h" #include "unaligned_memcpy.h" #include "omv_gpio.h" @@ -47,7 +46,6 @@ #define DMA_MAX_XFER_SIZE (0xFFFFU * 4U) #define DMA_LENGTH_ALIGNMENT (16) -omv_csi_t csi = {}; extern uint8_t _line_buf; extern uint32_t hal_get_exti_gpio(uint32_t line); @@ -107,7 +105,7 @@ static int omv_csi_dma_config() { } void omv_csi_init0() { - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); // Re-init i2c bus to reset the bus state after soft reset, which // could have interrupted the bus in the middle of a transfer. @@ -139,8 +137,10 @@ int omv_csi_init() { // Reset the csi state memset(&csi, 0, sizeof(omv_csi_t)); + // Set default framebuffer + csi.fb = framebuffer_get(0); + // Set default snapshot function. - // Some sensors need to call snapshot from init. csi.snapshot = omv_csi_snapshot; // Configure the csi external clock (XCLK). @@ -222,35 +222,41 @@ int omv_csi_config(omv_csi_config_t config) { } // Stop the DCMI from generating more DMA requests, and disable the DMA. -int omv_csi_abort(bool fifo_flush, bool in_irq) { +int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { if (DCMI->CR & DCMI_CR_ENABLE) { DCMI->CR &= ~DCMI_CR_ENABLE; + if (in_irq) { - HAL_DMA_Abort_IT(&csi.dma); + HAL_DMA_Abort_IT(&csi->dma); } else { - HAL_DMA_Abort(&csi.dma); + HAL_DMA_Abort(&csi->dma); } HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); + #if defined(OMV_MDMA_CHANNEL_DCMI_0) if (!in_irq) { - HAL_MDMA_Abort(&csi.mdma0); - HAL_MDMA_Abort(&csi.mdma1); + HAL_MDMA_Abort(&csi->mdma0); + HAL_MDMA_Abort(&csi->mdma1); } - HAL_MDMA_DeInit(&csi.mdma0); - HAL_MDMA_DeInit(&csi.mdma1); + HAL_MDMA_DeInit(&csi->mdma0); + HAL_MDMA_DeInit(&csi->mdma1); #endif - __HAL_DCMI_DISABLE_IT(&csi.dcmi, DCMI_IT_FRAME); - __HAL_DCMI_CLEAR_FLAG(&csi.dcmi, DCMI_FLAG_FRAMERI); - csi.first_line = false; - csi.drop_frame = false; - csi.last_frame_ms = 0; - csi.last_frame_ms_valid = false; + + __HAL_DCMI_DISABLE_IT(&csi->dcmi, DCMI_IT_FRAME); + __HAL_DCMI_CLEAR_FLAG(&csi->dcmi, DCMI_FLAG_FRAMERI); + + csi->first_line = false; + csi->drop_frame = false; + csi->last_frame_ms = 0; + csi->last_frame_ms_valid = false; } - if (fifo_flush) { - framebuffer_flush_buffers(true); - } else if (!csi.disable_full_flush) { - framebuffer_flush_buffers(false); + if (csi->fb) { + if (fifo_flush) { + framebuffer_flush_buffers(csi->fb, true); + } else if (!csi->disable_full_flush) { + framebuffer_flush_buffers(csi->fb, false); + } } return 0; @@ -325,7 +331,7 @@ int omv_csi_set_clk_frequency(uint32_t frequency) { int omv_csi_shutdown(int enable) { int ret = 0; - omv_csi_abort(true, false); + omv_csi_abort(&csi, true, false); if (enable) { #if defined(OMV_CSI_POWER_PIN) @@ -377,8 +383,9 @@ int omv_csi_set_vsync_callback(vsync_cb_t vsync_cb) { // 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. static uint32_t get_dcmi_hw_crop(uint32_t bytes_per_pixel) { - uint32_t byte_x_offset = (MAIN_FB()->x * bytes_per_pixel) % sizeof(uint32_t); - uint32_t width_remainder = (resolution[csi.framesize][0] - (MAIN_FB()->x + MAIN_FB()->u)) * bytes_per_pixel; + framebuffer_t *fb = csi.fb; + uint32_t byte_x_offset = (fb->x * bytes_per_pixel) % sizeof(uint32_t); + uint32_t width_remainder = (resolution[csi.framesize][0] - (fb->x + fb->u)) * bytes_per_pixel; uint32_t x_crop = 0; if (byte_x_offset && (width_remainder >= (sizeof(uint32_t) - byte_x_offset))) { @@ -389,6 +396,8 @@ static uint32_t get_dcmi_hw_crop(uint32_t bytes_per_pixel) { } void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { + framebuffer_t *fb = csi.fb; + #if defined(OMV_MDMA_CHANNEL_DCMI_0) // Clear out any stale flags. DMA2->LIFCR = DMA_FLAG_TCIF1_5 | DMA_FLAG_HTIF1_5; @@ -401,14 +410,14 @@ void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { if (csi.drop_frame) { csi.drop_frame = false; // Reset the buffer's state if the frame was dropped. - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); if (buffer) { buffer->reset_state = true; } return; } - framebuffer_get_tail(FB_NO_FLAGS); + framebuffer_get_tail(fb, FB_NO_FLAGS); if (csi.frame_callback) { csi.frame_callback(); @@ -417,6 +426,7 @@ void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { #if defined(OMV_MDMA_CHANNEL_DCMI_0) int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed) { + framebuffer_t *fb = csi.fb; MDMA_HandleTypeDef *handle = dma; // Drop the frame if MDMA is not keeping up as the image will be corrupted. @@ -431,8 +441,8 @@ int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed HAL_MDMA_Start(handle, (uint32_t) src, (uint32_t) dst, - transposed ? bpp : (MAIN_FB()->u * bpp), - transposed ? MAIN_FB()->u : 1); + transposed ? bpp : (fb->u * bpp), + transposed ? fb->u : 1); return 0; } #endif @@ -442,6 +452,8 @@ int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed // Using line buffers allows performing post-processing before writing the frame to the // framebuffer, and help hide external RAM latency. void DCMI_DMAConvCpltUser(uint32_t addr) { + framebuffer_t *fb = csi.fb; + // Throttle frames to match the current frame rate. omv_csi_throttle_framerate(); @@ -454,9 +466,9 @@ void DCMI_DMAConvCpltUser(uint32_t addr) { return; } - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); if (buffer == NULL) { - omv_csi_abort(false, true); + omv_csi_abort(&csi, false, true); return; } @@ -471,7 +483,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) { // valid data length, followed by image data and optional padding (0xFF). `offset` holds the // total size. uint16_t size = __REV16(*((uint16_t *) addr)); - if (buffer->offset + size > framebuffer_get_buffer_size()) { + if (buffer->offset + size > framebuffer_get_buffer_size(fb)) { buffer->jpeg_buffer_overflow = true; return; } @@ -490,7 +502,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) { #endif uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); - uint8_t *src = ((uint8_t *) addr) + (MAIN_FB()->x * bytes_per_pixel) - get_dcmi_hw_crop(bytes_per_pixel); + uint8_t *src = ((uint8_t *) addr) + (fb->x * bytes_per_pixel) - get_dcmi_hw_crop(bytes_per_pixel); uint8_t *dst = buffer->data; if (csi.pixformat == PIXFORMAT_GRAYSCALE) { @@ -502,7 +514,7 @@ void DCMI_DMAConvCpltUser(uint32_t addr) { if (!csi.transpose) { // NOTE: MDMA is started here, not in FRAME/VSYNC callbacks, to maximize the time before // the frame has to be dropped. - uint32_t line_width_bytes = MAIN_FB()->u * bytes_per_pixel; + uint32_t line_width_bytes = fb->u * bytes_per_pixel; // mdma0 will copy this line of the image to the final destination. __HAL_UNLOCK(&csi.mdma0); csi.mdma0.State = HAL_MDMA_STATE_READY; @@ -512,14 +524,14 @@ void DCMI_DMAConvCpltUser(uint32_t addr) { __HAL_UNLOCK(&csi.mdma1); csi.mdma1.State = HAL_MDMA_STATE_READY; HAL_MDMA_Start(&csi.mdma1, (uint32_t) src, (uint32_t) (dst + line_width_bytes), - line_width_bytes, MAIN_FB()->v - 1); + line_width_bytes, fb->v - 1); HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); return; } #endif if (!csi.transpose) { - dst += MAIN_FB()->u * bytes_per_pixel * buffer->offset++; + dst += fb->u * bytes_per_pixel * buffer->offset++; } else { dst += bytes_per_pixel * buffer->offset++; } @@ -535,6 +547,8 @@ void DCMI_DMAConvCpltUser(uint32_t addr) { #if defined(OMV_MDMA_CHANNEL_DCMI_0) // Configures an MDMA channel to completely offload the CPU in copying one line of pixels. static void mdma_config(MDMA_InitTypeDef *init, omv_csi_t *csi, uint32_t bytes_per_pixel) { + framebuffer_t *fb = csi->fb; + init->Request = MDMA_REQUEST_SW; init->TransferTriggerMode = MDMA_REPEAT_BLOCK_TRANSFER; init->Priority = MDMA_PRIORITY_VERY_HIGH; @@ -553,12 +567,12 @@ static void mdma_config(MDMA_InitTypeDef *init, omv_csi_t *csi, uint32_t bytes_p init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; } - uint32_t line_offset_bytes = (MAIN_FB()->x * bytes_per_pixel) - get_dcmi_hw_crop(bytes_per_pixel); - uint32_t line_width_bytes = MAIN_FB()->u * bytes_per_pixel; + uint32_t line_offset_bytes = (fb->x * bytes_per_pixel) - get_dcmi_hw_crop(bytes_per_pixel); + uint32_t line_width_bytes = fb->u * bytes_per_pixel; if (csi->transpose) { line_width_bytes = bytes_per_pixel; - init->DestBlockAddressOffset = (MAIN_FB()->v - 1) * bytes_per_pixel; + init->DestBlockAddressOffset = (fb->v - 1) * bytes_per_pixel; } // YUV422 Source -> Y Destination @@ -609,28 +623,29 @@ static void mdma_config(MDMA_InitTypeDef *init, omv_csi_t *csi, uint32_t bytes_p // This is the default snapshot function, which can be replaced in omv_csi_init functions. int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { uint32_t length = 0; + framebuffer_t *fb = csi->fb; // Compress the framebuffer for the IDE preview, if not the first frame, the // framebuffer is enabled, and the image sensor doesn't support JPEG encoding. - framebuffer_update_jpeg_buffer(); + framebuffer_update_jpeg_buffer(fb); // Ensure that 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. omv_csi_auto_crop_framebuffer(); - // Restore MAIN_FB width and height if they were changed before. BPP is restored later. + // Restore frame buffer width and height if they were changed before. BPP is restored later. // Note that JPEG compression is done first on the framebuffer with the user settings. - uint32_t w = MAIN_FB()->u; - uint32_t h = MAIN_FB()->v; + uint32_t w = fb->u; + uint32_t h = fb->v; // If DCMI_DMAConvCpltUser() happens before framebuffer_free_current_buffer(); below then the // transfer is stopped and it will be re-enabled again right afterwards in the single vbuffer // case. We know the transfer was stopped by checking DCMI_CR_ENABLE. - framebuffer_free_current_buffer(); + framebuffer_free_current_buffer(fb); // Configure and start the capture. if (!(DCMI->CR & DCMI_CR_ENABLE)) { - framebuffer_setup_buffers(); + framebuffer_setup_buffers(fb); // Setup the size and address of the transfer uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); @@ -660,7 +675,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } // Get the destination buffer address. - vbuffer_t *buffer = framebuffer_get_tail(FB_PEEK); + vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3) && (!buffer)) { return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; @@ -679,7 +694,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { csi->mdma1.Init.Request = MDMA_REQUEST_DMA2_Stream1_TC; csi->mdma1.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; // We setup MDMA to repeatedly reset itself to transfer the same line buffer. - csi->mdma1.Init.SourceBlockAddressOffset = -(MAIN_FB()->u * bytes_per_pixel); + csi->mdma1.Init.SourceBlockAddressOffset = -(fb->u * bytes_per_pixel); HAL_MDMA_Init(&csi->mdma1); HAL_MDMA_ConfigPostRequestMask(&csi->mdma1, (uint32_t) &DMA2->LIFCR, DMA_FLAG_TCIF1_5); @@ -692,7 +707,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { HAL_DCMI_DisableCrop(&csi->dcmi); if (csi->pixformat != PIXFORMAT_JPEG) { // Vertically crop the image. Horizontal cropping is done in software. - HAL_DCMI_ConfigCrop(&csi->dcmi, x_crop, MAIN_FB()->y, dma_line_width_bytes - 1, h - 1); + HAL_DCMI_ConfigCrop(&csi->dcmi, x_crop, fb->y, dma_line_width_bytes - 1, h - 1); HAL_DCMI_EnableCrop(&csi->dcmi); } @@ -706,7 +721,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // differing only in size, with an interrupt after every half of the transfer. if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { // Start a one-shot transfer to the framebuffer, used only for JPEG mode 3. - uint32_t size = framebuffer_get_buffer_size(); + uint32_t size = framebuffer_get_buffer_size(fb); length = IM_MIN(size, (DMA_MAX_XFER_SIZE * 2U)); HAL_DCMI_Start_DMA(&csi->dcmi, DCMI_MODE_SNAPSHOT, (uint32_t) buffer->data, length / sizeof(uint32_t)); @@ -753,10 +768,10 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // Wait for a frame to be ready. vbuffer_t *buffer = NULL; - for (uint32_t tick_start = HAL_GetTick(); !(buffer = framebuffer_get_head(fb_flags)); ) { + for (uint32_t tick_start = HAL_GetTick(); !(buffer = framebuffer_get_head(fb, fb_flags)); ) { __WFI(); if ((HAL_GetTick() - tick_start) > OMV_CSI_TIMEOUT_MS) { - omv_csi_abort(true, false); + omv_csi_abort(csi, true, false); #if defined(OMV_CSI_FSYNC_PIN) if (csi->frame_sync) { @@ -770,7 +785,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // In JPEG 3 mode, the transfer must be aborted as it waits for data indefinitely. if ((csi->pixformat == PIXFORMAT_JPEG) && (csi->jpg_format == 3)) { - omv_csi_abort(true, false); + omv_csi_abort(csi, true, false); } // We're done receiving data. @@ -787,30 +802,30 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { // Prepare the frame buffer w/h/bpp values given the image type. if (!csi->transpose) { - MAIN_FB()->w = w; - MAIN_FB()->h = h; + fb->w = w; + fb->h = h; } else { - MAIN_FB()->w = h; - MAIN_FB()->h = w; + fb->w = h; + fb->h = w; } // Fix the BPP. switch (csi->pixformat) { case PIXFORMAT_GRAYSCALE: - MAIN_FB()->pixfmt = PIXFORMAT_GRAYSCALE; + fb->pixfmt = PIXFORMAT_GRAYSCALE; break; case PIXFORMAT_RGB565: - MAIN_FB()->pixfmt = PIXFORMAT_RGB565; + fb->pixfmt = PIXFORMAT_RGB565; break; case PIXFORMAT_BAYER: - MAIN_FB()->pixfmt = PIXFORMAT_BAYER; - MAIN_FB()->subfmt_id = csi->cfa_format; - MAIN_FB()->pixfmt = imlib_bayer_shift(MAIN_FB()->pixfmt, MAIN_FB()->x, MAIN_FB()->y, csi->transpose); + fb->pixfmt = PIXFORMAT_BAYER; + fb->subfmt_id = csi->cfa_format; + fb->pixfmt = imlib_bayer_shift(fb->pixfmt, fb->x, fb->y, csi->transpose); break; case PIXFORMAT_YUV422: { - MAIN_FB()->pixfmt = PIXFORMAT_YUV; - MAIN_FB()->subfmt_id = csi->yuv_format; - MAIN_FB()->pixfmt = imlib_yuv_shift(MAIN_FB()->pixfmt, MAIN_FB()->x); + fb->pixfmt = PIXFORMAT_YUV; + fb->subfmt_id = csi->yuv_format; + fb->pixfmt = imlib_yuv_shift(fb->pixfmt, fb->x); break; } case PIXFORMAT_JPEG: { @@ -828,8 +843,8 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } } // Clean trailing data after 0xFFD9 at the end of the jpeg byte stream. - MAIN_FB()->pixfmt = PIXFORMAT_JPEG; - MAIN_FB()->size = jpeg_clean_trailing_bytes(size, buffer->data); + fb->pixfmt = PIXFORMAT_JPEG; + fb->size = jpeg_clean_trailing_bytes(size, buffer->data); break; } default: @@ -837,6 +852,6 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } // Set the user image. - framebuffer_init_image(image); + framebuffer_init_image(fb, image); return 0; }