Merge pull request #2669 from openmv/add_csi_fb
Some checks are pending
🔥 Firmware Build / build-firmware (ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions

imlib: Refactor framebuffer API to accept a context.
This commit is contained in:
Ibrahim Abdelkader 2025-04-27 09:20:12 +03:00 committed by GitHub
commit 96d0676cc2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 750 additions and 674 deletions

View File

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

View File

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

View File

@ -35,6 +35,7 @@
#include <stdarg.h>
#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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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__ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,6 @@
#include <stdbool.h>
#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;
}