mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
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
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:
commit
96d0676cc2
@ -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;
|
||||
|
150
common/omv_csi.c
150
common/omv_csi.c
@ -128,6 +128,8 @@ uint16_t resolution[][2] = {
|
||||
{2592, 1944}, /* WQXGA2 */
|
||||
};
|
||||
|
||||
omv_csi_t csi = {0};
|
||||
|
||||
__weak void omv_csi_init0() {
|
||||
// Reset the csi state
|
||||
memset(&csi, 0, sizeof(omv_csi_t));
|
||||
@ -139,13 +141,13 @@ __weak int omv_csi_init() {
|
||||
return OMV_CSI_ERROR_CTL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
__weak int omv_csi_abort(bool fifo_flush, bool in_irq) {
|
||||
__weak int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
|
||||
return OMV_CSI_ERROR_CTL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
__weak int omv_csi_reset() {
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Reset the csi state
|
||||
csi.sde = 0;
|
||||
@ -198,13 +200,13 @@ __weak int omv_csi_reset() {
|
||||
omv_i2c_enable(&csi.i2c_bus, true);
|
||||
|
||||
// Call csi-specific reset function
|
||||
if (csi.reset != NULL
|
||||
&& csi.reset(&csi) != 0) {
|
||||
if (csi.reset != NULL &&
|
||||
csi.reset(&csi) != 0) {
|
||||
return OMV_CSI_ERROR_CTL_FAILED;
|
||||
}
|
||||
|
||||
// Reset framebuffers
|
||||
framebuffer_flush_buffers(true);
|
||||
framebuffer_flush_buffers(csi.fb, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -594,7 +596,7 @@ __weak bool omv_csi_is_detected() {
|
||||
|
||||
__weak int omv_csi_sleep(int enable) {
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Call the sensor specific function.
|
||||
if (csi.sleep != NULL &&
|
||||
@ -609,7 +611,7 @@ __weak int omv_csi_shutdown(int enable) {
|
||||
int ret = 0;
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
#if defined(OMV_CSI_POWER_PIN)
|
||||
if (enable) {
|
||||
@ -671,11 +673,11 @@ __weak int omv_csi_set_pixformat(pixformat_t pixformat) {
|
||||
// Some sensor drivers automatically switch to BAYER to reduce the frame size if it does not fit in RAM.
|
||||
// If the current format is BAYER (1BPP), and the target format is color and (2BPP), and the frame does not
|
||||
// fit in RAM it will just be switched back again to BAYER, so we keep the current format unchanged.
|
||||
uint32_t size = framebuffer_get_buffer_size();
|
||||
uint32_t size = framebuffer_get_buffer_size(csi.fb);
|
||||
if ((csi.pixformat == PIXFORMAT_BAYER) &&
|
||||
((pixformat == PIXFORMAT_RGB565) || (pixformat == PIXFORMAT_YUV422)) &&
|
||||
(MAIN_FB()->u * MAIN_FB()->v * 2 > size) &&
|
||||
(MAIN_FB()->u * MAIN_FB()->v * 1 <= size)) {
|
||||
(csi.fb->u * csi.fb->v * 2 > size) &&
|
||||
(csi.fb->u * csi.fb->v * 1 <= size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -686,10 +688,10 @@ __weak int omv_csi_set_pixformat(pixformat_t pixformat) {
|
||||
}
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Flush previous frame.
|
||||
framebuffer_update_jpeg_buffer();
|
||||
framebuffer_update_jpeg_buffer(csi.fb);
|
||||
|
||||
// Check if the control is supported.
|
||||
if (csi.set_pixformat == NULL) {
|
||||
@ -709,7 +711,7 @@ __weak int omv_csi_set_pixformat(pixformat_t pixformat) {
|
||||
csi.pixformat = pixformat;
|
||||
|
||||
// Reset pixel format to skip the first frame.
|
||||
MAIN_FB()->pixfmt = PIXFORMAT_INVALID;
|
||||
csi.fb->pixfmt = PIXFORMAT_INVALID;
|
||||
|
||||
// Auto-adjust the number of frame buffers.
|
||||
omv_csi_set_framebuffers(-1);
|
||||
@ -725,10 +727,10 @@ __weak int omv_csi_set_framesize(omv_csi_framesize_t framesize) {
|
||||
}
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Flush previous frame.
|
||||
framebuffer_update_jpeg_buffer();
|
||||
framebuffer_update_jpeg_buffer(csi.fb);
|
||||
|
||||
// Call the sensor specific function
|
||||
if (csi.set_framesize == NULL) {
|
||||
@ -747,16 +749,16 @@ __weak int omv_csi_set_framesize(omv_csi_framesize_t framesize) {
|
||||
csi.framesize = framesize;
|
||||
|
||||
// Set x and y offsets.
|
||||
MAIN_FB()->x = 0;
|
||||
MAIN_FB()->y = 0;
|
||||
csi.fb->x = 0;
|
||||
csi.fb->y = 0;
|
||||
// Set width and height.
|
||||
MAIN_FB()->w = resolution[framesize][0];
|
||||
MAIN_FB()->h = resolution[framesize][1];
|
||||
csi.fb->w = resolution[framesize][0];
|
||||
csi.fb->h = resolution[framesize][1];
|
||||
// Set backup width and height.
|
||||
MAIN_FB()->u = resolution[framesize][0];
|
||||
MAIN_FB()->v = resolution[framesize][1];
|
||||
csi.fb->u = resolution[framesize][0];
|
||||
csi.fb->v = resolution[framesize][1];
|
||||
// Reset pixel format to skip the first frame.
|
||||
MAIN_FB()->pixfmt = PIXFORMAT_INVALID;
|
||||
csi.fb->pixfmt = PIXFORMAT_INVALID;
|
||||
|
||||
// Auto-adjust the number of frame buffers.
|
||||
omv_csi_set_framebuffers(-1);
|
||||
@ -776,8 +778,8 @@ __weak int omv_csi_set_framerate(int framerate) {
|
||||
}
|
||||
|
||||
// If the csi implements framerate control use it.
|
||||
if (csi.set_framerate != NULL
|
||||
&& csi.set_framerate(&csi, framerate) != 0) {
|
||||
if (csi.set_framerate != NULL &&
|
||||
csi.set_framerate(&csi, framerate) != 0) {
|
||||
return OMV_CSI_ERROR_CTL_FAILED;
|
||||
} else {
|
||||
// Otherwise use software framerate control.
|
||||
@ -807,10 +809,10 @@ __weak void omv_csi_throttle_framerate() {
|
||||
|
||||
__weak bool omv_csi_get_cropped() {
|
||||
if (csi.framesize != OMV_CSI_FRAMESIZE_INVALID) {
|
||||
return (MAIN_FB()->x != 0) ||
|
||||
(MAIN_FB()->y != 0) ||
|
||||
(MAIN_FB()->u != resolution[csi.framesize][0]) ||
|
||||
(MAIN_FB()->v != resolution[csi.framesize][1]);
|
||||
return (csi.fb->x != 0) ||
|
||||
(csi.fb->y != 0) ||
|
||||
(csi.fb->u != resolution[csi.framesize][0]) ||
|
||||
(csi.fb->v != resolution[csi.framesize][1]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -848,8 +850,8 @@ __weak uint32_t omv_csi_get_dst_bpp() {
|
||||
|
||||
__weak int omv_csi_set_windowing(int x, int y, int w, int h) {
|
||||
// Check if the value has changed.
|
||||
if ((MAIN_FB()->x == x) && (MAIN_FB()->y == y) &&
|
||||
(MAIN_FB()->u == w) && (MAIN_FB()->v == h)) {
|
||||
if ((csi.fb->x == x) && (csi.fb->y == y) &&
|
||||
(csi.fb->u == w) && (csi.fb->v == h)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -858,22 +860,22 @@ __weak int omv_csi_set_windowing(int x, int y, int w, int h) {
|
||||
}
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Flush previous frame.
|
||||
framebuffer_update_jpeg_buffer();
|
||||
framebuffer_update_jpeg_buffer(csi.fb);
|
||||
|
||||
// Set x and y offsets.
|
||||
MAIN_FB()->x = x;
|
||||
MAIN_FB()->y = y;
|
||||
csi.fb->x = x;
|
||||
csi.fb->y = y;
|
||||
// Set width and height.
|
||||
MAIN_FB()->w = w;
|
||||
MAIN_FB()->h = h;
|
||||
csi.fb->w = w;
|
||||
csi.fb->h = h;
|
||||
// Set backup width and height.
|
||||
MAIN_FB()->u = w;
|
||||
MAIN_FB()->v = h;
|
||||
csi.fb->u = w;
|
||||
csi.fb->v = h;
|
||||
// Reset pixel format to skip the first frame.
|
||||
MAIN_FB()->pixfmt = PIXFORMAT_INVALID;
|
||||
csi.fb->pixfmt = PIXFORMAT_INVALID;
|
||||
|
||||
// Auto-adjust the number of frame buffers.
|
||||
omv_csi_set_framebuffers(-1);
|
||||
@ -1093,7 +1095,7 @@ __weak int omv_csi_set_hmirror(int enable) {
|
||||
}
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Check if the control is supported.
|
||||
if (csi.set_hmirror == NULL) {
|
||||
@ -1127,7 +1129,7 @@ __weak int omv_csi_set_vflip(int enable) {
|
||||
}
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Check if the control is supported.
|
||||
if (csi.set_vflip == NULL) {
|
||||
@ -1161,7 +1163,7 @@ __weak int omv_csi_set_transpose(bool enable) {
|
||||
}
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
if ((csi.pixformat == PIXFORMAT_YUV422) || (csi.pixformat == PIXFORMAT_JPEG)) {
|
||||
return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED;
|
||||
@ -1184,7 +1186,7 @@ __weak int omv_csi_set_auto_rotation(bool enable) {
|
||||
}
|
||||
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Operation not supported on JPEG images.
|
||||
if ((csi.pixformat == PIXFORMAT_YUV422) || (csi.pixformat == PIXFORMAT_JPEG)) {
|
||||
@ -1202,10 +1204,10 @@ __weak bool omv_csi_get_auto_rotation() {
|
||||
|
||||
__weak int omv_csi_set_framebuffers(int count) {
|
||||
// Disable any ongoing frame capture.
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
|
||||
// Flush previous frame.
|
||||
framebuffer_update_jpeg_buffer();
|
||||
framebuffer_update_jpeg_buffer(csi.fb);
|
||||
|
||||
if (csi.pixformat == PIXFORMAT_INVALID) {
|
||||
return OMV_CSI_ERROR_INVALID_PIXFORMAT;
|
||||
@ -1217,12 +1219,12 @@ __weak int omv_csi_set_framebuffers(int count) {
|
||||
|
||||
#if OMV_CSI_HW_CROP_ENABLE
|
||||
// If hardware cropping is supported, use window size.
|
||||
MAIN_FB()->frame_size = MAIN_FB()->u * MAIN_FB()->v * 2;
|
||||
csi.fb->frame_size = csi.fb->u * csi.fb->v * 2;
|
||||
#else
|
||||
// Otherwise, use the real frame size.
|
||||
MAIN_FB()->frame_size = resolution[csi.framesize][0] * resolution[csi.framesize][1] * 2;
|
||||
csi.fb->frame_size = resolution[csi.framesize][0] * resolution[csi.framesize][1] * 2;
|
||||
#endif
|
||||
return framebuffer_set_buffers(count);
|
||||
return framebuffer_set_buffers(csi.fb, count);
|
||||
}
|
||||
|
||||
__weak int omv_csi_set_special_effect(omv_csi_sde_t sde) {
|
||||
@ -1264,7 +1266,7 @@ __weak int omv_csi_set_lens_correction(int enable, int radi, int coef) {
|
||||
__weak int omv_csi_ioctl(int request, ... /* arg */) {
|
||||
// Disable any ongoing frame capture.
|
||||
if (request & OMV_CSI_IOCTL_FLAGS_ABORT) {
|
||||
omv_csi_abort(true, false);
|
||||
omv_csi_abort(&csi, true, false);
|
||||
}
|
||||
|
||||
// Check if the control is supported.
|
||||
@ -1302,21 +1304,21 @@ __weak const uint16_t *omv_csi_get_color_palette() {
|
||||
|
||||
__weak int omv_csi_check_framebuffer_size() {
|
||||
uint32_t bpp = omv_csi_get_dst_bpp();
|
||||
uint32_t size = framebuffer_get_buffer_size();
|
||||
return (((MAIN_FB()->u * MAIN_FB()->v * bpp) <= size) ? 0 : -1);
|
||||
uint32_t size = framebuffer_get_buffer_size(csi.fb);
|
||||
return (((csi.fb->u * csi.fb->v * bpp) <= size) ? 0 : -1);
|
||||
}
|
||||
|
||||
__weak int omv_csi_auto_crop_framebuffer() {
|
||||
uint32_t bpp = omv_csi_get_dst_bpp();
|
||||
uint32_t size = framebuffer_get_buffer_size();
|
||||
uint32_t size = framebuffer_get_buffer_size(csi.fb);
|
||||
|
||||
// If the pixformat is NULL/JPEG there we can't do anything to check if it fits before hand.
|
||||
if (!bpp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// MAIN_FB() fits, we are done.
|
||||
if ((MAIN_FB()->u * MAIN_FB()->v * bpp) <= size) {
|
||||
// csi.fb fits, we are done.
|
||||
if ((csi.fb->u * csi.fb->v * bpp) <= size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1325,14 +1327,14 @@ __weak int omv_csi_auto_crop_framebuffer() {
|
||||
omv_csi_set_pixformat(PIXFORMAT_BAYER);
|
||||
bpp = 1;
|
||||
|
||||
// MAIN_FB() fits, we are done (bpp is 1).
|
||||
if ((MAIN_FB()->u * MAIN_FB()->v) <= size) {
|
||||
// csi.fb fits, we are done (bpp is 1).
|
||||
if ((csi.fb->u * csi.fb->v) <= size) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int window_w = MAIN_FB()->u;
|
||||
int window_h = MAIN_FB()->v;
|
||||
int window_w = csi.fb->u;
|
||||
int window_h = csi.fb->v;
|
||||
|
||||
// We need to shrink the frame buffer. We can do this by cropping. So, we will subtract columns
|
||||
// and rows from the frame buffer until it fits within the frame buffer.
|
||||
@ -1373,20 +1375,20 @@ __weak int omv_csi_auto_crop_framebuffer() {
|
||||
}
|
||||
|
||||
// Crop the frame buffer while keeping the aspect ratio and keeping the width/height even.
|
||||
while (((MAIN_FB()->u * MAIN_FB()->v * bpp) > size) || (MAIN_FB()->u % 2) || (MAIN_FB()->v % 2)) {
|
||||
MAIN_FB()->u -= u_sub;
|
||||
MAIN_FB()->v -= v_sub;
|
||||
while (((csi.fb->u * csi.fb->v * bpp) > size) || (csi.fb->u % 2) || (csi.fb->v % 2)) {
|
||||
csi.fb->u -= u_sub;
|
||||
csi.fb->v -= v_sub;
|
||||
}
|
||||
|
||||
// Center the new window using the previous offset and keep the offset even.
|
||||
MAIN_FB()->x += (window_w - MAIN_FB()->u) / 2;
|
||||
MAIN_FB()->y += (window_h - MAIN_FB()->v) / 2;
|
||||
csi.fb->x += (window_w - csi.fb->u) / 2;
|
||||
csi.fb->y += (window_h - csi.fb->v) / 2;
|
||||
|
||||
if (MAIN_FB()->x % 2) {
|
||||
MAIN_FB()->x -= 1;
|
||||
if (csi.fb->x % 2) {
|
||||
csi.fb->x -= 1;
|
||||
}
|
||||
if (MAIN_FB()->y % 2) {
|
||||
MAIN_FB()->y -= 1;
|
||||
if (csi.fb->y % 2) {
|
||||
csi.fb->y -= 1;
|
||||
}
|
||||
|
||||
// Auto-adjust the number of frame buffers.
|
||||
@ -1395,13 +1397,13 @@ __weak int omv_csi_auto_crop_framebuffer() {
|
||||
}
|
||||
|
||||
#define copy_transposed_line(dstp, srcp) \
|
||||
for (int i = MAIN_FB()->u, h = MAIN_FB()->v; i; i--) { \
|
||||
for (int i = csi.fb->u, h = csi.fb->v; i; i--) { \
|
||||
*dstp = *srcp++; \
|
||||
dstp += h; \
|
||||
}
|
||||
|
||||
#define copy_transposed_line_rev16(dstp, srcp) \
|
||||
for (int i = MAIN_FB()->u, h = MAIN_FB()->v; i; i--) { \
|
||||
for (int i = csi.fb->u, h = csi.fb->v; i; i--) { \
|
||||
*dstp = __REV16(*srcp++); \
|
||||
dstp += h; \
|
||||
}
|
||||
@ -1421,7 +1423,7 @@ __weak int omv_csi_copy_line(void *dma, uint8_t *src, uint8_t *dst) {
|
||||
}
|
||||
#endif
|
||||
if (!csi.transpose) {
|
||||
unaligned_memcpy(dst, src, MAIN_FB()->u);
|
||||
unaligned_memcpy(dst, src, csi.fb->u);
|
||||
} else {
|
||||
copy_transposed_line(dst, src);
|
||||
}
|
||||
@ -1435,14 +1437,14 @@ __weak int omv_csi_copy_line(void *dma, uint8_t *src, uint8_t *dst) {
|
||||
if (csi.mono_bpp == 1) {
|
||||
// 1BPP GRAYSCALE.
|
||||
if (!csi.transpose) {
|
||||
unaligned_memcpy(dst, src, MAIN_FB()->u);
|
||||
unaligned_memcpy(dst, src, csi.fb->u);
|
||||
} else {
|
||||
copy_transposed_line(dst, src);
|
||||
}
|
||||
} else {
|
||||
// Extract Y channel from YUV.
|
||||
if (!csi.transpose) {
|
||||
unaligned_2_to_1_memcpy(dst, src16, MAIN_FB()->u);
|
||||
unaligned_2_to_1_memcpy(dst, src16, csi.fb->u);
|
||||
} else {
|
||||
copy_transposed_line(dst, src16);
|
||||
}
|
||||
@ -1460,14 +1462,14 @@ __weak int omv_csi_copy_line(void *dma, uint8_t *src, uint8_t *dst) {
|
||||
} else if ((csi.pixformat == PIXFORMAT_RGB565 && csi.rgb_swap) ||
|
||||
(csi.pixformat == PIXFORMAT_YUV422 && csi.yuv_swap)) {
|
||||
if (!csi.transpose) {
|
||||
unaligned_memcpy_rev16(dst16, src16, MAIN_FB()->u);
|
||||
unaligned_memcpy_rev16(dst16, src16, csi.fb->u);
|
||||
} else {
|
||||
copy_transposed_line_rev16(dst16, src16);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (!csi.transpose) {
|
||||
unaligned_memcpy(dst16, src16, MAIN_FB()->u * sizeof(uint16_t));
|
||||
unaligned_memcpy(dst16, src16, csi.fb->u * sizeof(uint16_t));
|
||||
} else {
|
||||
copy_transposed_line(dst16, src16);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user