Merge pull request #2720 from kwagyeman/kwabena/framebuffer_lepton
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_N6) (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

drivers/sensors/lepton: Update lepton driver with asynchronous fb.
This commit is contained in:
Ibrahim Abdelkader 2025-06-24 21:19:02 +03:00 committed by GitHub
commit acc5edb0fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 213 additions and 222 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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