diff --git a/boards/ARDUINO_GIGA/omv_boardconfig.h b/boards/ARDUINO_GIGA/omv_boardconfig.h index cbf379a2b..1df679c13 100644 --- a/boards/ARDUINO_GIGA/omv_boardconfig.h +++ b/boards/ARDUINO_GIGA/omv_boardconfig.h @@ -141,8 +141,6 @@ #define OMV_FB_OVERLAY_SIZE (448K) // Fast fb_alloc memory size. #define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer. #define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data). -#define OMV_VOSPI_MEMORY DTCM // VoSPI buffer memory. -#define OMV_VOSPI_SIZE (38K) #define OMV_DMA_MEMORY SRAM3 // Misc DMA buffers memory. #define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers. #define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers. diff --git a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h index 6ae50cc65..1f7ecde85 100644 --- a/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h +++ b/boards/ARDUINO_NICLA_VISION/omv_boardconfig.h @@ -139,6 +139,7 @@ #define OMV_GC_BLOCK2_SIZE (276K) #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data #define OMV_LINE_BUF_SIZE (3 * 1024) // Image line buffer round(640 * 2BPP * 2 buffers). +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory map. #define OMV_FLASH_ORIGIN 0x08000000 diff --git a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h index 35d42a800..1b0c6f476 100644 --- a/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h +++ b/boards/ARDUINO_PORTENTA_H7/omv_boardconfig.h @@ -139,8 +139,6 @@ #define OMV_FB_OVERLAY_SIZE (480K) // Fast fb_alloc memory size. #define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer. #define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data). -#define OMV_VOSPI_MEMORY SRAM4 // VoSPI buffer memory. -#define OMV_VOSPI_SIZE (38K) #define OMV_DMA_MEMORY SRAM3 // Misc DMA buffers memory. #define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers. #define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers. @@ -156,6 +154,7 @@ #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data #define OMV_SDRAM_SIZE (8 * 1024 * 1024) // This needs to be here for UVC firmware. #define OMV_LINE_BUF_SIZE (11 * 1024) // Image line buffer round(2592 * 2BPP * 2 buffers). +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory map. #define OMV_FLASH_ORIGIN 0x08000000 diff --git a/boards/OPENMV2/omv_boardconfig.h b/boards/OPENMV2/omv_boardconfig.h index 1128ad77f..c63d94dd9 100644 --- a/boards/OPENMV2/omv_boardconfig.h +++ b/boards/OPENMV2/omv_boardconfig.h @@ -93,6 +93,7 @@ #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data #define OMV_FFS_BUF_SIZE (16K) // Flash filesystem cache #define OMV_LINE_BUF_SIZE (2 * 1024) // Image line buffer round(320 * 2BPP * 2 buffers). +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory map. #define OMV_FLASH_ORIGIN 0x08000000 diff --git a/boards/OPENMV3/omv_boardconfig.h b/boards/OPENMV3/omv_boardconfig.h index 0d027d9ee..fd3541710 100644 --- a/boards/OPENMV3/omv_boardconfig.h +++ b/boards/OPENMV3/omv_boardconfig.h @@ -92,6 +92,7 @@ #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data #define OMV_FFS_BUF_SIZE (32K) // Flash filesystem cache #define OMV_LINE_BUF_SIZE (3 * 1024) // Image line buffer round(640 * 2BPP * 2 buffers). +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory map. #define OMV_FLASH_ORIGIN 0x08000000 diff --git a/boards/OPENMV4/omv_boardconfig.h b/boards/OPENMV4/omv_boardconfig.h index a1121e51a..b51a2fc85 100644 --- a/boards/OPENMV4/omv_boardconfig.h +++ b/boards/OPENMV4/omv_boardconfig.h @@ -138,15 +138,14 @@ #define OMV_FB_ALLOC_SIZE (80K) // minimum fb alloc size #define OMV_JPEG_MEMORY SRAM0 // JPEG buffer memory. #define OMV_JPEG_SIZE (32K) // IDE JPEG buffer (header + data). -#define OMV_VOSPI_MEMORY SRAM4 // VoSPI buffer memory. -#define OMV_VOSPI_SIZE (38K) #define OMV_DMA_MEMORY SRAM2 // Misc DMA buffers memory. #define OMV_GC_BLOCK0_MEMORY SRAM4 // Main GC block. -#define OMV_GC_BLOCK0_SIZE (26K) +#define OMV_GC_BLOCK0_SIZE (64K) #define OMV_GC_BLOCK1_MEMORY SRAM1 // Extra GC block 0. #define OMV_GC_BLOCK1_SIZE (267K) #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data #define OMV_LINE_BUF_SIZE (3 * 1024) // Image line buffer round(640 * 2BPP * 2 buffers). +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory map. #define OMV_FLASH_ORIGIN 0x08000000 diff --git a/boards/OPENMV4P/omv_boardconfig.h b/boards/OPENMV4P/omv_boardconfig.h index c664d2a81..9627461ad 100644 --- a/boards/OPENMV4P/omv_boardconfig.h +++ b/boards/OPENMV4P/omv_boardconfig.h @@ -126,8 +126,6 @@ #define OMV_FB_OVERLAY_SIZE (496K) // Fast fb_alloc memory size. #define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer. #define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data). -#define OMV_VOSPI_MEMORY SRAM4 // VoSPI buffer memory. -#define OMV_VOSPI_SIZE (38K) #define OMV_DMA_MEMORY SRAM3 // Misc DMA buffers memory. #define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers. #define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers. @@ -139,6 +137,7 @@ #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data #define OMV_SDRAM_SIZE (32 * 1024 * 1024) // This needs to be here for UVC firmware. #define OMV_LINE_BUF_SIZE (11 * 1024) // Image line buffer round(2592 * 2BPP * 2 buffers). +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory map. #define OMV_FLASH_ORIGIN 0x08000000 diff --git a/boards/OPENMVPT/omv_boardconfig.h b/boards/OPENMVPT/omv_boardconfig.h index f95daf25c..7f5e345ce 100644 --- a/boards/OPENMVPT/omv_boardconfig.h +++ b/boards/OPENMVPT/omv_boardconfig.h @@ -117,8 +117,6 @@ #define OMV_FB_OVERLAY_SIZE (496K) // Fast fb_alloc memory size. #define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer. #define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data). -#define OMV_VOSPI_MEMORY SRAM4 // VoSPI buffer memory. -#define OMV_VOSPI_SIZE (38K) #define OMV_DMA_MEMORY SRAM3 // DMA buffers memory. #define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers. #define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers. @@ -130,6 +128,7 @@ #define OMV_SDRAM_SIZE (64 * 1024 * 1024) // This needs to be here for UVC firmware. #define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data #define OMV_LINE_BUF_SIZE (11 * 1024) // Image line buffer round(2592 * 2BPP * 2 buffers). +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory map. #define OMV_FLASH_ORIGIN 0x08000000 diff --git a/boards/OPENMV_N6/omv_boardconfig.h b/boards/OPENMV_N6/omv_boardconfig.h index 875fdfeab..107feeb2a 100644 --- a/boards/OPENMV_N6/omv_boardconfig.h +++ b/boards/OPENMV_N6/omv_boardconfig.h @@ -153,14 +153,14 @@ #define OMV_FB_ALLOC_SIZE (11M) // minimum fb_alloc size #define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer. #define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data). -#define OMV_VOSPI_MEMORY SRAM1 // VoSPI buffer memory. -#define OMV_VOSPI_SIZE (64K) #define OMV_DMA_MEMORY SRAM1 // Misc DMA buffers memory. +#define OMV_DMA_MEMORY_D2 SRAM7 // Domain 2 DMA buffers. #define OMV_GC_BLOCK0_MEMORY SRAM2 // Main GC block #define OMV_GC_BLOCK0_SIZE (1M) #define OMV_GC_BLOCK1_MEMORY DRAM // Main GC block #define OMV_GC_BLOCK1_SIZE (24M) #define OMV_MSC_BUF_SIZE (4K) // USB MSC bot data +#define OMV_VOSPI_DMA_BUFFER ".d2_dma_buffer" // Memory map. #define OMV_DTCM_ORIGIN 0x30000000 diff --git a/boards/OPENMV_RT1060/omv_boardconfig.h b/boards/OPENMV_RT1060/omv_boardconfig.h index 687be0dc3..8fd690c79 100644 --- a/boards/OPENMV_RT1060/omv_boardconfig.h +++ b/boards/OPENMV_RT1060/omv_boardconfig.h @@ -84,17 +84,16 @@ #define OMV_FB_ALLOC_SIZE (10M) // minimum fb alloc size #define OMV_FB_OVERLAY_MEMORY OCRM1 // Fast fb_alloc memory. #define OMV_FB_OVERLAY_SIZE (512K) -#define OMV_VOSPI_MEMORY OCRM2 // VoSPI buffer memory. -#define OMV_VOSPI_SIZE (38K) #define OMV_DMA_MEMORY DTCM // Misc DMA buffers memory. #define OMV_GC_BLOCK0_MEMORY OCRM2 // Extra GC block 0. -#define OMV_GC_BLOCK0_SIZE (26K) +#define OMV_GC_BLOCK0_SIZE (64K) #define OMV_GC_BLOCK1_MEMORY DTCM // Main GC block #define OMV_GC_BLOCK1_SIZE (288K) #define OMV_GC_BLOCK2_MEMORY DRAM // Extra GC block 1. #define OMV_GC_BLOCK2_SIZE (8M) #define OMV_RAMFUNC_MEMORY ITCM2 // RAM code memory. #define OMV_LINE_BUF_SIZE (11 * 1024) // Image line buffer. +#define OMV_VOSPI_DMA_BUFFER ".dma_buffer" // Memory configuration. #define OMV_DTCM_ORIGIN 0x20000000 diff --git a/common/common.ld.S b/common/common.ld.S index 7ac266baf..e048dbf0a 100644 --- a/common/common.ld.S +++ b/common/common.ld.S @@ -133,17 +133,6 @@ PROVIDE(__stack = __StackTop); } >OMV_JPEG_MEMORY #endif -/* VOSPI framebuffer memory */ -#if defined(OMV_VOSPI_MEMORY) -.vospi_memory (NOLOAD) : -{ - . = ALIGN(4); - _vospi_buf = .; - . = . + OMV_VOSPI_SIZE; - . = ALIGN(4); -} >OMV_VOSPI_MEMORY -#endif - /* GPU memory */ #if defined(OMV_GPU_MEMORY) .gpu_memory (NOLOAD) : diff --git a/common/vospi.c b/common/vospi.c index 1807e8327..03de484ea 100644 --- a/common/vospi.c +++ b/common/vospi.c @@ -60,7 +60,7 @@ typedef enum { typedef struct _vospi_state { int pid; int sid; - uint16_t *framebuffer; + framebuffer_t *fb; bool lepton_3; omv_spi_t spi_bus; volatile uint32_t flags; @@ -68,7 +68,7 @@ typedef struct _vospi_state { static vospi_state_t vospi; -static uint16_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED_DMA(vospi_buf[VOSPI_BUFFER_SIZE]), ".dma_buffer"); +static uint16_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED_DMA(vospi_buf[VOSPI_BUFFER_SIZE]), OMV_VOSPI_DMA_BUFFER); static void vospi_callback(omv_spi_t *spi, void *userdata, void *buf); static void vospi_resync() { @@ -143,33 +143,39 @@ void vospi_callback(omv_spi_t *spi, void *userdata, void *buf) { return; } - memcpy(vospi.framebuffer - + (vospi.pid * VOSPI_PID_SIZE_PIXELS) - + (vospi.sid * VOSPI_SID_SIZE_PIXELS), - base + VOSPI_HEADER_WORDS, VOSPI_PID_SIZE_PIXELS * sizeof(uint16_t)); + vbuffer_t *buffer = framebuffer_get_tail(vospi.fb, FB_PEEK); - vospi.pid += 1; - if (vospi.pid == VOSPI_PIDS_PER_SID) { - vospi.pid = 0; + if (buffer) { + memcpy(((uint16_t *) buffer->data) + + (vospi.pid * VOSPI_PID_SIZE_PIXELS) + + (vospi.sid * VOSPI_SID_SIZE_PIXELS), + base + VOSPI_HEADER_WORDS, VOSPI_PID_SIZE_PIXELS * sizeof(uint16_t)); - // For the FLIR Lepton 3 we have to receive all the pids in all the segments. - if (vospi.lepton_3) { - vospi.sid += 1; - if (vospi.sid == VOSPI_SIDS_PER_FRAME) { - vospi.sid = 0; - vospi.flags &= ~VOSPI_FLAGS_CAPTURE; + vospi.pid += 1; + if (vospi.pid == VOSPI_PIDS_PER_SID) { + vospi.pid = 0; + + // For the FLIR Lepton 3 we have to receive all the pids in all the segments. + if (vospi.lepton_3) { + vospi.sid += 1; + if (vospi.sid == VOSPI_SIDS_PER_FRAME) { + vospi.sid = 0; + framebuffer_get_tail(vospi.fb, FB_NO_FLAGS); + } + // For the FLIR Lepton 1/2 we just have to receive all the pids. + } else { + framebuffer_get_tail(vospi.fb, FB_NO_FLAGS); } - // For the FLIR Lepton 1/2 we just have to receive all the pids. - } else { - vospi.flags &= ~VOSPI_FLAGS_CAPTURE; } + } else { + vospi.flags &= ~VOSPI_FLAGS_CAPTURE; } } -int vospi_init(uint32_t n_packets, void *buffer) { +int vospi_init(uint32_t n_packets, framebuffer_t *fb) { memset(&vospi, 0, sizeof(vospi_state_t)); vospi.lepton_3 = n_packets > VOSPI_PIDS_PER_SID; - vospi.framebuffer = buffer; + vospi.fb = fb; // resync on first snapshot. vospi.flags = VOSPI_FLAGS_RESYNC; @@ -183,15 +189,26 @@ int vospi_init(uint32_t n_packets, void *buffer) { spi_config.clk_pha = OMV_SPI_CPHA_2EDGE; spi_config.dma_flags = OMV_SPI_DMA_CIRCULAR | OMV_SPI_DMA_DOUBLE; - if (omv_spi_init(&vospi.spi_bus, &spi_config) != 0) { - return -1; - } - return 0; + return omv_spi_init(&vospi.spi_bus, &spi_config); +} + +int vospi_deinit() { + return omv_spi_deinit(&vospi.spi_bus); +} + +bool vospi_active(void) { + return vospi.flags & VOSPI_FLAGS_CAPTURE; } int vospi_snapshot(uint32_t timeout_ms) { - // Restart counters to capture a new frame. - vospi.flags |= VOSPI_FLAGS_CAPTURE; + framebuffer_free_current_buffer(vospi.fb); + + if (!(vospi.flags & VOSPI_FLAGS_CAPTURE)) { + framebuffer_setup_buffers(vospi.fb); + + // Restart counters to capture a new frame. + vospi.flags |= VOSPI_FLAGS_CAPTURE; + } // Snapshot start tick mp_uint_t tick_start = mp_hal_ticks_ms(); @@ -202,6 +219,10 @@ int vospi_snapshot(uint32_t timeout_ms) { vospi_resync(); } + if (timeout_ms == 0) { + return -1; + } + if ((mp_hal_ticks_ms() - tick_start) > timeout_ms) { omv_spi_transfer_abort(&vospi.spi_bus); vospi.pid = 0; @@ -211,9 +232,8 @@ int vospi_snapshot(uint32_t timeout_ms) { } MICROPY_EVENT_POLL_HOOK - } while (vospi.flags & VOSPI_FLAGS_CAPTURE); + } while (!framebuffer_get_head(vospi.fb, FB_NO_FLAGS)); return 0; } - #endif diff --git a/common/vospi.h b/common/vospi.h index 15c6e7144..d4820a95c 100644 --- a/common/vospi.h +++ b/common/vospi.h @@ -25,6 +25,9 @@ */ #ifndef __VOSPI_H__ #define __VOSPI_H__ -int vospi_init(uint32_t n_packets, void *buffer); +#include "framebuffer.h" +int vospi_init(uint32_t n_packets, framebuffer_t *fb); +int vospi_deinit(); +bool vospi_active(void); int vospi_snapshot(uint32_t timeout_ms); #endif // __VOSPI_H__ diff --git a/drivers/sensors/lepton.c b/drivers/sensors/lepton.c index b90f228e3..3a74f1142 100644 --- a/drivers/sensors/lepton.c +++ b/drivers/sensors/lepton.c @@ -69,7 +69,6 @@ typedef struct lepton_state { LEP_CAMERA_PORT_DESC_T port; } lepton_state_t; -extern uint16_t _vospi_buf[]; static lepton_state_t lepton; static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mode); @@ -202,7 +201,7 @@ static int ioctl(omv_csi_t *csi, int request, va_list ap) { } case OMV_CSI_IOCTL_LEPTON_GET_RESOLUTION: { int *resolution = va_arg(ap, int *); - *resolution = 14; + *resolution = lepton.radiometry ? 16 : 14; break; } case OMV_CSI_IOCTL_LEPTON_RUN_COMMAND: { @@ -359,21 +358,21 @@ static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mo } static int reset(omv_csi_t *csi) { - static bool vospi_initialized = false; + vospi_deinit(); memset(&lepton, 0, sizeof(lepton_state_t)); lepton.min_temp = LEPTON_MIN_TEMP_DEFAULT; lepton.max_temp = LEPTON_MAX_TEMP_DEFAULT; + // Extra delay after power-on + mp_hal_delay_ms(1000); + if (lepton_reset(csi, false, false) != 0) { return OMV_CSI_ERROR_CTL_FAILED; } - if (vospi_initialized == false) { - if (vospi_init(lepton.v_res, _vospi_buf) != 0) { - return OMV_CSI_ERROR_CTL_FAILED; - } - vospi_initialized = true; + if (csi->fb && vospi_init(lepton.v_res, csi->fb) != 0) { + return OMV_CSI_ERROR_CTL_FAILED; } return 0; @@ -383,10 +382,6 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { framebuffer_t *fb = csi->fb; framebuffer_update_jpeg_buffer(fb); - if (fb->n_buffers != 1) { - framebuffer_set_buffers(fb, 1); - } - if (csi->pixformat == PIXFORMAT_INVALID) { return OMV_CSI_ERROR_INVALID_PIXFORMAT; } @@ -399,15 +394,12 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return OMV_CSI_ERROR_INVALID_FRAMESIZE; } - if (omv_csi_check_framebuffer_size(csi) == -1) { - return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; + if (resolution[csi->framesize][0] < lepton.h_res || resolution[csi->framesize][1] < lepton.v_res) { + return OMV_CSI_ERROR_INVALID_FRAMESIZE; } - framebuffer_free_current_buffer(fb); - vbuffer_t *buffer = framebuffer_get_tail(fb, FB_NO_FLAGS); - - if (!buffer) { - return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; + if (omv_csi_check_framebuffer_size(csi) == -1) { + return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; } for (int i = 0; i < LEPTON_SNAPSHOT_RETRY; i++) { @@ -423,20 +415,18 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { } } - fb->w = fb->u; - fb->h = fb->v; + if (!csi->transpose) { + fb->w = fb->u; + fb->h = fb->v; + } else { + fb->w = fb->v; + fb->h = fb->u; + } + fb->pixfmt = csi->pixformat; - 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); - // MAX == KeepAspectRationByExpanding - MIN == KeepAspectRatio - float scale = IM_MAX(x_scale, y_scale), scale_inv = 1.0f / scale; - int x_offset = (resolution[csi->framesize][0] - (lepton.h_res * scale)) / 2; - int y_offset = (resolution[csi->framesize][1] - (lepton.v_res * scale)) / 2; - // The code below upscales the source image to the requested frame size - // and then crops it to the window set by the user. + image_t fb_image; + framebuffer_init_image(fb, &fb_image); LEP_SYS_FPA_TEMPERATURE_KELVIN_T kelvin; if (lepton.measurement_mode && (!lepton.radiometry)) { @@ -444,60 +434,38 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { return OMV_CSI_ERROR_IO_ERROR; } } + + fb_alloc_mark(); + image_t temp = { + .w = (!csi->transpose) ? lepton.h_res : lepton.v_res, + .h = (!csi->transpose) ? lepton.v_res : lepton.h_res, + .pixfmt = PIXFORMAT_GRAYSCALE, + .data = fb_alloc(lepton.h_res * lepton.v_res, FB_ALLOC_CACHE_ALIGN), + }; - for (int y = y_offset, yy = fast_ceilf(lepton.v_res * scale) + y_offset; y < yy; y++) { - if ((fb->y <= y) && (y < (fb->y + fb->v))) { - // user window cropping + // When not in measurment mode set the min and max temperatures such that 0-255 values from the + // sensor, which are grayscale pixels 0-255, are not clipped when interpreted as Kelvin values. + float min = -273.15f; // in Celsius -> 0.0f in Kelvin + float max = -270.6f; // in Celsius -> 2.55f in Kelvin - 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 ((fb->x <= x) && (x < (fb->x + fb->u))) { - // user window cropping - - // Value is the 14/16-bit value from the FLIR IR camera. - // However, with AGC enabled only the bottom 8-bits are non-zero. - int value = row_ptr[fast_floorf(x * scale_inv)]; - - if (lepton.measurement_mode) { - // Need to convert 14/16-bits to 8-bits ourselves... - if (!lepton.radiometry) { - value = (value - 8192) + kelvin; - } - float celsius = (value * 0.01f) - 273.15f; - celsius = IM_CLAMP(celsius, lepton.min_temp, lepton.max_temp); - value = __USAT(IM_DIV(((celsius - lepton.min_temp) * 255), - (lepton.max_temp - lepton.min_temp)), 8); - } - - int t_x = x - fb->x; - int t_y = y - fb->y; - - if (lepton.hmirror) { - t_x = fb->u - t_x - 1; - } - if (lepton.vflip) { - t_y = fb->v - t_y - 1; - } - - switch (csi->pixformat) { - case PIXFORMAT_GRAYSCALE: { - IMAGE_PUT_GRAYSCALE_PIXEL(image, t_x, t_y, value & 0xFF); - break; - } - case PIXFORMAT_RGB565: { - IMAGE_PUT_RGB565_PIXEL(image, t_x, t_y, csi->color_palette[value & 0xFF]); - break; - } - default: { - break; - } - } - } - } - } + // When in measurment mode the lepton provides 14-bit or 16-bit values that must be clamped + // between the min and max temperatures in celsius and scaled to 0-255 values. + if (lepton.measurement_mode) { + min = lepton.min_temp; + max = lepton.max_temp; } + imlib_fill_image_from_lepton(&temp, lepton.h_res, lepton.v_res, (uint16_t *) fb_image.data, min, max, + false, (!lepton.measurement_mode) || lepton.radiometry, kelvin, + lepton.hmirror, lepton.vflip, csi->transpose); + + imlib_draw_image(&fb_image, &temp, 0, 0, 1.0f, 1.0f, NULL, -1, 255, + (csi->pixformat == PIXFORMAT_RGB565) ? csi->color_palette : NULL, NULL, + IMAGE_HINT_BILINEAR | IMAGE_HINT_CENTER | IMAGE_HINT_SCALE_ASPECT_EXPAND, + NULL, NULL, NULL); + + fb_alloc_free_till_mark(); + framebuffer_init_image(fb, image); return 0; } diff --git a/lib/imlib/imlib.c b/lib/imlib/imlib.c index 43afe8b4d..ad2765721 100644 --- a/lib/imlib/imlib.c +++ b/lib/imlib/imlib.c @@ -456,6 +456,76 @@ void imlib_fill_image_from_float(image_t *img, int w, int h, float *data, float } } +// This function fills a grayscale image from an array of lepton 8/14/16-bit values that are scaled +// between min and max. The image w*h must equal the floating point array w*h. +void imlib_fill_image_from_lepton(image_t *img, int w, int h, uint16_t *data, float min, float max, + bool auto_range, bool radiometric, int kelvin_offset, + bool mirror, bool flip, bool transpose) { + int new_min; + int new_max; + + if (auto_range) { + new_min = INT_MAX; + new_max = INT_MIN; + + for (int i = 0; i < w * h; i++) { + int temp = data[i]; + + if (!radiometric) { + temp = (temp - 8192) + kelvin_offset; + } + + if (temp < new_min) { + new_min = temp; + } + + if (temp > new_max) { + new_max = temp; + } + } + } else { + float tmp = min; + min = (min < max) ? min : max; + max = (max > tmp) ? max : tmp; + new_min = fast_roundf((min + 273.15f) * 100.f); // to kelvin + new_max = fast_roundf((max + 273.15f) * 100.f); // to kelvin + } + + float diff = 255.f / (new_max - new_min); + + for (int y = 0; y < h; y++) { + int y_dst = flip ? (h - 1 - y) : y; + const uint16_t *raw_row = data + (y * w); + uint8_t *row_pointer = ((uint8_t *) img->data) + (y_dst * w); + uint8_t *t_row_pointer = ((uint8_t *) img->data) + y_dst; + + for (int x = 0; x < w; x++) { + int x_dst = mirror ? (w - 1 - x) : x; + int raw = raw_row[x]; + + if (!radiometric) { + raw = (raw - 8192) + kelvin_offset; + } + + if (raw < new_min) { + raw = new_min; + } + + if (raw > new_max) { + raw = new_max; + } + + int pixel = __USAT(fast_roundf((raw - new_min) * diff), 8); + + if (!transpose) { + row_pointer[x_dst] = pixel; + } else { + t_row_pointer[x_dst * h] = pixel; + } + } + } +} + int8_t imlib_rgb565_to_l(uint16_t pixel) { float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)]; float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)]; diff --git a/lib/imlib/imlib.h b/lib/imlib/imlib.h index 91f09de98..618311a58 100644 --- a/lib/imlib/imlib.h +++ b/lib/imlib/imlib.h @@ -1187,6 +1187,9 @@ void imlib_deinit_all(); // Generic Helper Functions void imlib_fill_image_from_float(image_t *img, int w, int h, float *data, float min, float max, bool mirror, bool flip, bool dst_transpose, bool src_transpose); +void imlib_fill_image_from_lepton(image_t *img, int w, int h, uint16_t *data, float min, float max, + bool auto_range, bool radiometric, int kelvin_offset, + bool mirror, bool flip, bool transpose); // Bayer Image Processing pixformat_t imlib_bayer_shift(pixformat_t pixfmt, int x, int y, bool transpose); diff --git a/modules/py_fir.c b/modules/py_fir.c index a9abf43b7..edfda7e93 100644 --- a/modules/py_fir.c +++ b/modules/py_fir.c @@ -1089,9 +1089,10 @@ mp_obj_t py_fir_snapshot(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a #if (OMV_FIR_LEPTON_ENABLE == 1) case FIR_LEPTON: { bool auto_range = args[ARG_scale].u_obj == mp_const_none; - fir_lepton_fill_image(&src_img, fir_width, fir_height, auto_range, min, max, - args[ARG_hmirror].u_bool, args[ARG_vflip].u_bool, - args[ARG_transpose].u_bool, args[ARG_timeout].u_int); + imlib_fill_image_from_lepton(&src_img, fir_width, fir_height, + fir_lepton_get_frame(args[ARG_timeout].u_int), min, max, auto_range, + fir_lepton_get_radiometry_enabled(), fir_lepton_get_temperature(), + args[ARG_hmirror].u_bool, args[ARG_vflip].u_bool, args[ARG_transpose].u_bool); break; } #endif diff --git a/modules/py_fir_lepton.c b/modules/py_fir_lepton.c index 9891f466b..c5e8948c0 100644 --- a/modules/py_fir_lepton.c +++ b/modules/py_fir_lepton.c @@ -81,7 +81,7 @@ static soft_timer_entry_t flir_lepton_spi_rx_timer = {}; static int fir_lepton_spi_rx_cb_tail = 0; static int fir_lepton_spi_rx_cb_expected_pid = 0; static int fir_lepton_spi_rx_cb_expected_sid = 0; -static uint16_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED_DMA(fir_lepton_buf[VOSPI_BUFFER_SIZE]), ".dma_buffer"); +static uint16_t OMV_ATTR_SECTION(OMV_ATTR_ALIGNED_DMA(fir_lepton_buf[VOSPI_BUFFER_SIZE]), OMV_VOSPI_DMA_BUFFER); static void fir_lepton_spi_callback(omv_spi_t *spi, void *userdata, void *buf); static mp_obj_t fir_lepton_spi_resync_callback(mp_obj_t unused) { @@ -390,7 +390,7 @@ int fir_lepton_init(omv_i2c_t *bus, int *w, int *h, int *refresh, int *resolutio fir_lepton_rad_en = rad == LEP_RAD_ENABLE; *w = flir_w; *h = flir_h; - *refresh = fir_lepton_3 ? 27 : 9; + *refresh = fir_lepton_3 ? 9 : 27; *resolution = fir_lepton_rad_en ? 16 : 14; #if defined(OMV_FIR_LEPTON_VSYNC_PIN) @@ -441,7 +441,7 @@ mp_obj_t fir_lepton_get_frame_available() { return mp_obj_new_bool(framebuffer_tail != framebuffer_head); } -static const uint16_t *fir_lepton_get_frame(int timeout) { +uint16_t *fir_lepton_get_frame(int timeout) { int sampled_framebuffer_tail = framebuffer_tail; if (timeout >= 0) { @@ -464,7 +464,11 @@ static const uint16_t *fir_lepton_get_frame(int timeout) { return framebuffers[sampled_framebuffer_tail]; } -static int fir_lepton_get_temperature() { +bool fir_lepton_get_radiometry_enabled() { + return fir_lepton_rad_en; +} + +int fir_lepton_get_temperature() { LEP_SYS_FPA_TEMPERATURE_KELVIN_T kelvin; if (LEP_GetSysFpaTemperatureKelvin(&fir_lepton_handle, &kelvin) != LEP_OK) { @@ -529,78 +533,6 @@ mp_obj_t fir_lepton_read_ir(int w, int h, bool mirror, bool flip, bool transpose return mp_obj_new_tuple(4, tuple); } -void fir_lepton_fill_image(image_t *img, int w, int h, bool auto_range, float min, float max, - bool mirror, bool flip, bool transpose, int timeout) { - int kelvin = fir_lepton_get_temperature(); - const uint16_t *data = fir_lepton_get_frame(timeout); - int new_min; - int new_max; - - if (auto_range) { - new_min = INT_MAX; - new_max = INT_MIN; - - for (int i = 0, ii = w * h; i < ii; i++) { - int temp = data[i]; - - if (!fir_lepton_rad_en) { - temp = (temp - 8192) + kelvin; - } - - if (temp < new_min) { - new_min = temp; - } - - if (temp > new_max) { - new_max = temp; - } - } - } else { - float tmp = min; - min = (min < max) ? min : max; - max = (max > tmp) ? max : tmp; - new_min = fast_roundf((min + 273.15f) * 100.f); // to kelvin - new_max = fast_roundf((max + 273.15f) * 100.f); // to kelvin - } - - float diff = 255.f / (new_max - new_min); - int w_1 = w - 1; - int h_1 = h - 1; - - for (int y = 0; y < h; y++) { - int y_dst = flip ? (h_1 - y) : y; - const uint16_t *raw_row = data + (y * w); - uint8_t *row_pointer = ((uint8_t *) img->data) + (y_dst * w); - uint8_t *t_row_pointer = ((uint8_t *) img->data) + y_dst; - - for (int x = 0; x < w; x++) { - int x_dst = mirror ? (w_1 - x) : x; - int raw = raw_row[x]; - - if (!fir_lepton_rad_en) { - raw = (raw - 8192) + kelvin; - } - - if (raw < new_min) { - raw = new_min; - } - - if (raw > new_max) { - raw = new_max; - } - - int pixel = fast_roundf((raw - new_min) * diff); - pixel = __USAT(pixel, 8); - - if (!transpose) { - row_pointer[x_dst] = pixel; - } else { - t_row_pointer[x_dst * h] = pixel; - } - } - } -} - void fir_lepton_trigger_ffc(int timeout) { if (LEP_RunSysFFCNormalization(&fir_lepton_handle) != LEP_OK) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("FFC Error!")); diff --git a/modules/py_fir_lepton.h b/modules/py_fir_lepton.h index 2cae91929..d02bad003 100644 --- a/modules/py_fir_lepton.h +++ b/modules/py_fir_lepton.h @@ -31,9 +31,10 @@ void fir_lepton_register_vsync_cb(mp_obj_t cb); mp_obj_t fir_lepton_get_radiometry(); void fir_lepton_register_frame_cb(mp_obj_t cb); mp_obj_t fir_lepton_get_frame_available(); +uint16_t *fir_lepton_get_frame(int timeout); +bool fir_lepton_get_radiometry_enabled(); +int fir_lepton_get_temperature(); mp_obj_t fir_lepton_read_ta(); mp_obj_t fir_lepton_read_ir(int w, int h, bool mirror, bool flip, bool transpose, int timeout); -void fir_lepton_fill_image(image_t *img, int w, int h, bool auto_range, float min, float max, - bool mirror, bool flip, bool transpose, int timeout); void fir_lepton_trigger_ffc(int timeout); #endif // __PY_FIR_LEPTON_H__ diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 0db6537e6..8c20cd7b6 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -75,6 +75,7 @@ #include "file_utils.h" #include "mp_utils.h" #include "mimxrt_hal.h" +#include "vospi.h" int main(void) { bool first_soft_reset = true; @@ -222,6 +223,9 @@ soft_reset: soft_reset_exit: mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + #if OMV_ENABLE_VOSPI || OMV_LEPTON_ENABLE + vospi_deinit(); + #endif #if MICROPY_PY_MACHINE_CAN machine_can_irq_deinit(); #endif diff --git a/ports/stm32/main.c b/ports/stm32/main.c index b1328d9ba..ca744fb05 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -88,6 +88,7 @@ #include "fb_alloc.h" #include "dma_alloc.h" #include "file_utils.h" +#include "vospi.h" #include "py_image.h" #include "py_fir.h" @@ -373,6 +374,9 @@ soft_reset_exit: // soft reset mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + #if OMV_ENABLE_VOSPI || OMV_LEPTON_ENABLE + vospi_deinit(); + #endif #if MICROPY_PY_LWIP systick_disable_dispatch(SYSTICK_DISPATCH_LWIP); #endif diff --git a/ports/stm32/omv_spi.c b/ports/stm32/omv_spi.c index 686d716fa..60c61e930 100644 --- a/ports/stm32/omv_spi.c +++ b/ports/stm32/omv_spi.c @@ -95,7 +95,7 @@ DEFINE_SPI_INSTANCE(6) static omv_spi_t *omv_spi_descr_all[6] = { NULL }; #if defined(STM32N6) -static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(omv_dma_nodes_all[6 * 2], 32), ".dma_buffer"); +static DMA_NodeTypeDef OMV_ATTR_SECTION(OMV_ATTR_ALIGNED(omv_dma_nodes_all[6 * 2], 32), OMV_VOSPI_DMA_BUFFER); #endif static uint32_t omv_spi_clocksource(SPI_TypeDef *spi) { @@ -330,8 +330,8 @@ static int omv_spi_dma_init(omv_spi_t *spi, uint32_t direction, omv_spi_config_t dma_init->Direction = direction; dma_init->SrcInc = (direction == DMA_PERIPH_TO_MEMORY) ? DMA_SINC_FIXED : DMA_SINC_INCREMENTED; dma_init->DestInc = (direction == DMA_MEMORY_TO_PERIPH) ? DMA_DINC_FIXED : DMA_DINC_INCREMENTED; - dma_init->SrcDataWidth = (config->datasize == 8) ? DMA_SRC_DATAWIDTH_BYTE : DMA_SRC_DATAWIDTH_WORD; - dma_init->DestDataWidth = (config->datasize == 8) ? DMA_DEST_DATAWIDTH_BYTE : DMA_DEST_DATAWIDTH_WORD; + dma_init->SrcDataWidth = DMA_SRC_DATAWIDTH_WORD; + dma_init->DestDataWidth = DMA_DEST_DATAWIDTH_WORD; dma_init->SrcBurstLength = 1; dma_init->DestBurstLength = 1; dma_init->TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; @@ -368,7 +368,7 @@ static int omv_spi_dma_init(omv_spi_t *spi, uint32_t direction, omv_spi_config_t return -1; } - dma_descr->InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT; + dma_descr->InitLinkedList.Priority = DMA_HIGH_PRIORITY; dma_descr->InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION; dma_descr->InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR; dma_descr->InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;