Merge pull request #2691 from openmv/n6_port_dual_csi
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

ports/all: Dual CSI support.
This commit is contained in:
Ibrahim Abdelkader 2025-06-25 22:27:25 +03:00 committed by GitHub
commit 190aecc45c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 3161 additions and 1397 deletions

View File

@ -138,6 +138,11 @@ MPY_CFLAGS += -DMICROPY_PY_CSI=1
MPY_MKARGS += MICROPY_PY_CSI=1 MPY_MKARGS += MICROPY_PY_CSI=1
endif endif
ifeq ($(MICROPY_PY_CSI_NG), 1)
MPY_CFLAGS += -DMICROPY_PY_CSI_NG=1
MPY_MKARGS += MICROPY_PY_CSI_NG=1
endif
ifeq ($(MICROPY_PY_FIR), 1) ifeq ($(MICROPY_PY_FIR), 1)
MPY_CFLAGS += -DMICROPY_PY_FIR=1 MPY_CFLAGS += -DMICROPY_PY_FIR=1
MPY_MKARGS += MICROPY_PY_FIR=1 MPY_MKARGS += MICROPY_PY_FIR=1

View File

@ -15,6 +15,7 @@ OMV_BOARD_CFLAGS = -DCORE_CM7 \
OMV_ENABLE_BL=0 OMV_ENABLE_BL=0
OMV_ENABLE_UVC=1 OMV_ENABLE_UVC=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_LWIP = 1 MICROPY_PY_LWIP = 1

View File

@ -14,6 +14,7 @@ OMV_BOARD_CFLAGS = -DNRF52_SERIES \
-DCONFIG_GPIO_AS_PINRESET -DCONFIG_GPIO_AS_PINRESET
DFU_DEVICE=0x0483:0xdf11 DFU_DEVICE=0x0483:0xdf11
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB=1 MICROPY_PY_ULAB=1
MICROPY_PY_AUDIO=1 MICROPY_PY_AUDIO=1

View File

@ -73,7 +73,7 @@ PIXEL_LOOP:
.wrap .wrap
% c-sdk { % c-sdk {
int omv_csi_config(omv_csi_config_t config) { int omv_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_PIXFORMAT) { if (config == OMV_CSI_CONFIG_PIXFORMAT) {
uint offset; uint offset;
pio_sm_config config; pio_sm_config config;
@ -86,7 +86,7 @@ int omv_csi_config(omv_csi_config_t config) {
} }
pio_sm_set_consecutive_pindirs(OMV_CSI_PIO, OMV_CSI_SM, OMV_CSI_D0_PIN, 7, false); pio_sm_set_consecutive_pindirs(OMV_CSI_PIO, OMV_CSI_SM, OMV_CSI_D0_PIN, 7, false);
if (csi.pixformat == PIXFORMAT_GRAYSCALE) { if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
offset = pio_add_program(OMV_CSI_PIO, &dcmi_odd_byte_program); offset = pio_add_program(OMV_CSI_PIO, &dcmi_odd_byte_program);
config = dcmi_odd_byte_program_get_default_config(offset); config = dcmi_odd_byte_program_get_default_config(offset);
} else { } else {

View File

@ -1,4 +1,5 @@
set(MICROPY_PY_CSI 1) set(MICROPY_PY_CSI 1)
set(MICROPY_PY_CSI_NG 1)
set(MICROPY_PY_FIR 1) set(MICROPY_PY_FIR 1)
set(MICROPY_PY_ULAB 1) set(MICROPY_PY_ULAB 1)
set(MICROPY_PY_NINAW10 1) set(MICROPY_PY_NINAW10 1)

View File

@ -17,6 +17,7 @@ OMV_ENABLE_BL = 0
OMV_ENABLE_UVC = 1 OMV_ENABLE_UVC = 1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_WINC1500 = 0 MICROPY_PY_WINC1500 = 0

View File

@ -16,6 +16,7 @@ OMV_ENABLE_BL=0
OMV_ENABLE_UVC=1 OMV_ENABLE_UVC=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_WINC1500 = 0 MICROPY_PY_WINC1500 = 0

View File

@ -11,5 +11,6 @@ OMV_ENABLE_BL=1
OMV_ENABLE_UVC=1 OMV_ENABLE_UVC=1
OMV_BOARD_CFLAGS=-DUSE_USB_FS OMV_BOARD_CFLAGS=-DUSE_USB_FS
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_WINC1500 = 1 MICROPY_PY_WINC1500 = 1
$(error "This camera is no longer supported :O Have fun hacking the code $(BRACKET)") $(error "This camera is no longer supported :O Have fun hacking the code $(BRACKET)")

View File

@ -11,6 +11,7 @@ OMV_ENABLE_BL=1
OMV_ENABLE_UVC=1 OMV_ENABLE_UVC=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_WINC1500 = 1 MICROPY_PY_WINC1500 = 1
MICROPY_PY_DISPLAY = 1 MICROPY_PY_DISPLAY = 1

View File

@ -11,6 +11,7 @@ OMV_ENABLE_BL=1
OMV_ENABLE_UVC=1 OMV_ENABLE_UVC=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_WINC1500 = 1 MICROPY_PY_WINC1500 = 1

View File

@ -12,6 +12,7 @@ OMV_ENABLE_BL=1
OMV_ENABLE_UVC=1 OMV_ENABLE_UVC=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_WINC1500 = 1 MICROPY_PY_WINC1500 = 1

View File

@ -13,6 +13,7 @@ OMV_ENABLE_UVC=1
OMV_GENX320_ENABLE=1 OMV_GENX320_ENABLE=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_WINC1500 = 1 MICROPY_PY_WINC1500 = 1

View File

@ -12,6 +12,7 @@ OMV_ENABLE_BL=1
OMV_ENABLE_UVC=1 OMV_ENABLE_UVC=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_WINC1500 = 1 MICROPY_PY_WINC1500 = 1

View File

@ -249,6 +249,7 @@ extern unsigned char OMV_BOARD_UID_ADDR[12]; // Unique address.
// CSI I2C bus // CSI I2C bus
#define OMV_CSI_I2C_ID (0) #define OMV_CSI_I2C_ID (0)
#define OMV_CSI_I2C_SPEED (OMV_I2C_SPEED_FULL) #define OMV_CSI_I2C_SPEED (OMV_I2C_SPEED_FULL)
#define OMV_CSI_I2C_REINIT (0)
// FIR I2C bus // FIR I2C bus
#define OMV_FIR_I2C_ID (1) #define OMV_FIR_I2C_ID (1)

View File

@ -14,6 +14,7 @@ OMV_HSE_VALUE=12000000
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
OMV_TOF_VL53L8CX_ENABLE=1 OMV_TOF_VL53L8CX_ENABLE=1
MICROPY_PY_CSI = $(CORE_M55_HP) MICROPY_PY_CSI = $(CORE_M55_HP)
MICROPY_PY_CSI_NG = $(CORE_M55_HP)
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_TOF = 1 MICROPY_PY_TOF = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1

View File

@ -52,6 +52,7 @@
#define OMV_LEPTON_ENABLE (1) #define OMV_LEPTON_ENABLE (1)
#define OMV_PAG7936_ENABLE (1) #define OMV_PAG7936_ENABLE (1)
#define OMV_PAG7936_MIPI_CSI2 (1) #define OMV_PAG7936_MIPI_CSI2 (1)
#define OMV_SOFTCSI_ENABLE (0)
#define OMV_PS5520_ENABLE (1) #define OMV_PS5520_ENABLE (1)
#define OMV_GENX320_EHC_ENABLE (1) #define OMV_GENX320_EHC_ENABLE (1)
#define OMV_GENX320_HSYNC_VALUE (0x1) #define OMV_GENX320_HSYNC_VALUE (0x1)
@ -279,6 +280,7 @@
#define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI) #define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI)
#define OMV_CSI_DMA_MEMCPY_ENABLE (0) #define OMV_CSI_DMA_MEMCPY_ENABLE (0)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)
#define OMV_CSI_MAX_DEVICES (3)
#define OMV_CSI_D0_PIN (&omv_pin_A1_DCMI) #define OMV_CSI_D0_PIN (&omv_pin_A1_DCMI)
#define OMV_CSI_D1_PIN (&omv_pin_A10_DCMI) #define OMV_CSI_D1_PIN (&omv_pin_A10_DCMI)

View File

@ -27,6 +27,7 @@ OMV_BOSON_ENABLE=1
OMV_GENX320_ENABLE=1 OMV_GENX320_ENABLE=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_IMU = 1 MICROPY_PY_IMU = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1

View File

@ -202,6 +202,7 @@
#define OMV_CSI_DMA_MEMCPY_ENABLE (1) #define OMV_CSI_DMA_MEMCPY_ENABLE (1)
#define OMV_CSI_HW_SWAP_ENABLE (1) #define OMV_CSI_HW_SWAP_ENABLE (1)
#define OMV_CSI_HW_CROP_ENABLE (1) #define OMV_CSI_HW_CROP_ENABLE (1)
#define OMV_CSI_MAX_DEVICES (2)
#define OMV_CSI_D0_PIN (&omv_pin_DCMI_D0) #define OMV_CSI_D0_PIN (&omv_pin_DCMI_D0)
#define OMV_CSI_D1_PIN (&omv_pin_DCMI_D1) #define OMV_CSI_D1_PIN (&omv_pin_DCMI_D1)

View File

@ -12,6 +12,7 @@ OMV_BOSON_ENABLE=1
OMV_GENX320_ENABLE=1 OMV_GENX320_ENABLE=1
OMV_LEPTON_SDK_ENABLE=1 OMV_LEPTON_SDK_ENABLE=1
MICROPY_PY_CSI = 1 MICROPY_PY_CSI = 1
MICROPY_PY_CSI_NG = 1
MICROPY_PY_FIR = 1 MICROPY_PY_FIR = 1
MICROPY_PY_ULAB = 1 MICROPY_PY_ULAB = 1
MICROPY_PY_LWIP = 1 MICROPY_PY_LWIP = 1

View File

@ -57,7 +57,7 @@ PIXEL_LOOP:
.wrap .wrap
% c-sdk { % c-sdk {
int omv_csi_config(omv_csi_config_t config) { int omv_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_PIXFORMAT) { if (config == OMV_CSI_CONFIG_PIXFORMAT) {
uint offset; uint offset;
pio_sm_config config; pio_sm_config config;
@ -70,7 +70,7 @@ int omv_csi_config(omv_csi_config_t config) {
} }
pio_sm_set_consecutive_pindirs(OMV_CSI_PIO, OMV_CSI_SM, OMV_CSI_D0_PIN, 8, false); pio_sm_set_consecutive_pindirs(OMV_CSI_PIO, OMV_CSI_SM, OMV_CSI_D0_PIN, 8, false);
if (csi.pixformat == PIXFORMAT_GRAYSCALE) { if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
offset = pio_add_program(OMV_CSI_PIO, &dcmi_odd_byte_program); offset = pio_add_program(OMV_CSI_PIO, &dcmi_odd_byte_program);
config = dcmi_odd_byte_program_get_default_config(offset); config = dcmi_odd_byte_program_get_default_config(offset);
} else { } else {

View File

@ -1,4 +1,5 @@
set(MICROPY_PY_CSI 1) set(MICROPY_PY_CSI 1)
set(MICROPY_PY_CSI_NG 1)
set(MICROPY_PY_FIR 1) set(MICROPY_PY_FIR 1)
set(MICROPY_PY_ULAB 1) set(MICROPY_PY_ULAB 1)
set(MICROPY_PY_NINAW10 0) set(MICROPY_PY_NINAW10 0)

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,7 @@
#define FROGEYE2020_SLV_ADDR (0x6E) #define FROGEYE2020_SLV_ADDR (0x6E)
#define PAG7920_SLV_ADDR (0x80) #define PAG7920_SLV_ADDR (0x80)
#define PAG7936_SLV_ADDR (0x80) #define PAG7936_SLV_ADDR (0x80)
#define SOFTCSI_SLV_ADDR (0x7f) #define PAG7936_SLV_ADDR_ALT (0x2A)
#define PS5520_SLV_ADDR (0x90) #define PS5520_SLV_ADDR (0x90)
// Chip ID Registers // Chip ID Registers
@ -104,6 +104,10 @@
#define OMV_CSI_CLK_SOURCE_TIM (1U) #define OMV_CSI_CLK_SOURCE_TIM (1U)
#define OMV_CSI_CLK_SOURCE_OSC (2U) #define OMV_CSI_CLK_SOURCE_OSC (2U)
#ifndef OMV_CSI_MAX_DEVICES
#define OMV_CSI_MAX_DEVICES (1)
#endif
typedef enum { typedef enum {
OMV_CSI_ACTIVE_LOW = 0, OMV_CSI_ACTIVE_LOW = 0,
OMV_CSI_ACTIVE_HIGH = 1 OMV_CSI_ACTIVE_HIGH = 1
@ -189,6 +193,11 @@ typedef enum {
OMV_CSI_IOCTL_FLAGS_ABORT = (1 << 8), OMV_CSI_IOCTL_FLAGS_ABORT = (1 << 8),
} omv_csi_ioctl_flags_t; } omv_csi_ioctl_flags_t;
typedef enum {
OMV_CSI_CAPTURE_FLAGS_UPDATE = (1 << 0),
OMV_CSI_CAPTURE_FLAGS_NBLOCK = (1 << 1),
} omv_csi_capture_flags_t;
typedef enum { typedef enum {
OMV_CSI_IOCTL_SET_READOUT_WINDOW = 0x00 | OMV_CSI_IOCTL_FLAGS_ABORT, OMV_CSI_IOCTL_SET_READOUT_WINDOW = 0x00 | OMV_CSI_IOCTL_FLAGS_ABORT,
OMV_CSI_IOCTL_GET_READOUT_WINDOW = 0x01, OMV_CSI_IOCTL_GET_READOUT_WINDOW = 0x01,
@ -250,6 +259,7 @@ typedef enum {
OMV_CSI_ERROR_FRAMEBUFFER_ERROR = -18, OMV_CSI_ERROR_FRAMEBUFFER_ERROR = -18,
OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW = -19, OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW = -19,
OMV_CSI_ERROR_JPEG_OVERFLOW = -20, OMV_CSI_ERROR_JPEG_OVERFLOW = -20,
OMV_CSI_ERROR_WOULD_BLOCK = -21,
} omv_csi_error_t; } omv_csi_error_t;
#if (OMV_GENX320_ENABLE == 1) #if (OMV_GENX320_ENABLE == 1)
@ -270,10 +280,13 @@ typedef enum {
} omv_csi_genx320_bias_t; } omv_csi_genx320_bias_t;
#endif #endif
typedef void (*vsync_cb_t) (uint32_t vsync);
typedef void (*frame_cb_t) ();
typedef struct _omv_csi omv_csi_t; typedef struct _omv_csi omv_csi_t;
typedef struct _omv_csi_callback_t {
void (*fun) (void *);
void *arg;
} omv_csi_cb_t;
typedef struct _omv_csi { typedef struct _omv_csi {
uint32_t chip_id; // Sensor ID 32 bits. uint32_t chip_id; // Sensor ID 32 bits.
uint8_t slv_addr; // Sensor I2C slave address. uint8_t slv_addr; // Sensor I2C slave address.
@ -296,14 +309,15 @@ typedef struct _omv_csi {
uint32_t cfa_format : 3; // CFA format/pattern. uint32_t cfa_format : 3; // CFA format/pattern.
uint32_t mipi_if : 1; // CSI-2 interface. uint32_t mipi_if : 1; // CSI-2 interface.
uint32_t mipi_brate : 12; // CSI-2 interface bitrate. uint32_t mipi_brate : 12; // CSI-2 interface bitrate.
uint32_t auxiliary : 1; // Indicates that the sensor can be used in dual-CSI config.
}; };
const uint16_t *color_palette; // Color palette used for color lookup. const uint16_t *color_palette; // Color palette used for color lookup.
bool disable_delays; // Set to true to disable all sensor settling time delays. bool disable_delays; // Set to true to disable all sensor settling time delays.
bool disable_full_flush; // Turn off default frame buffer flush policy when full. bool disable_full_flush; // Turn off default frame buffer flush policy when full.
vsync_cb_t vsync_callback; // VSYNC callback. omv_csi_cb_t vsync_cb; // VSYNC callback
frame_cb_t frame_callback; // Frame callback. omv_csi_cb_t frame_cb; // Frame callback
// Sensor state // Sensor state
omv_csi_sde_t sde; // Special digital effects omv_csi_sde_t sde; // Special digital effects
@ -322,19 +336,22 @@ typedef struct _omv_csi {
bool detected; // Set to true when the sensor is initialized. bool detected; // Set to true when the sensor is initialized.
omv_i2c_t *i2c; // SCCB/I2C bus. omv_i2c_t *i2c; // SCCB/I2C bus.
framebuffer_t *fb; // Frame buffer pointer framebuffer_t *fb; // Frame buffer pointer
#ifdef OMV_CSI_PORT_BITS #ifdef OMV_CSI_PORT_BITS
// Additional port-specific members like device base pointer, // Additional port-specific members like device base pointer,
// dma handles, more I/Os etc... are included directly here, // DMA handles, and additional I/Os, are included directly here.
// so that they can be accessible from this struct.
OMV_CSI_PORT_BITS OMV_CSI_PORT_BITS
#endif #endif
// Can be used by port or image sensor drivers to store private
// data, internal state, or additional operations.
void *priv;
// Sensor function pointers // Sensor function pointers
int (*reset) (omv_csi_t *csi); int (*reset) (omv_csi_t *csi);
int (*sleep) (omv_csi_t *csi, int enable); int (*sleep) (omv_csi_t *csi, int enable);
int (*match) (omv_csi_t *csi, size_t id);
int (*read_reg) (omv_csi_t *csi, uint16_t reg_addr); int (*read_reg) (omv_csi_t *csi, uint16_t reg_addr);
int (*write_reg) (omv_csi_t *csi, uint16_t reg_addr, uint16_t reg_data); int (*write_reg) (omv_csi_t *csi, uint16_t reg_addr, uint16_t reg_data);
int (*set_pixformat) (omv_csi_t *csi, pixformat_t pixformat); int (*set_pixformat) (omv_csi_t *csi, pixformat_t pixformat);
@ -359,36 +376,44 @@ typedef struct _omv_csi {
int (*set_special_effect) (omv_csi_t *csi, omv_csi_sde_t sde); int (*set_special_effect) (omv_csi_t *csi, omv_csi_sde_t sde);
int (*set_lens_correction) (omv_csi_t *csi, int enable, int radi, int coef); int (*set_lens_correction) (omv_csi_t *csi, int enable, int radi, int coef);
int (*ioctl) (omv_csi_t *csi, int request, va_list ap); int (*ioctl) (omv_csi_t *csi, int request, va_list ap);
int (*config) (omv_csi_t *csi, omv_csi_config_t config);
int (*abort) (omv_csi_t *csi, bool fifo_flush, bool in_irq);
int (*snapshot) (omv_csi_t *csi, image_t *image, uint32_t flags); int (*snapshot) (omv_csi_t *csi, image_t *image, uint32_t flags);
} omv_csi_t; } omv_csi_t;
extern omv_csi_t csi; // CSI array
extern omv_i2c_t csi_i2c; extern omv_csi_t csi_all[OMV_CSI_MAX_DEVICES];
// Resolution table // Resolution table
extern uint16_t resolution[][2]; extern uint16_t resolution[][2];
// Initialize the sensor state. // Resets the sensor state on soft-reboots.
void omv_csi_init0(); void omv_csi_init0();
// Initialize the sensor and probe the image sensor. // Initializes CSI struct with default ops.
int omv_csi_init(); int omv_csi_init();
// Return CSI instance.
// If id == -1, return the main csi, otherwise look up csi by chip-id.
omv_csi_t *omv_csi_get(int id);
// Detect and initialize the image sensor. // Detect and initialize the image sensor.
int omv_csi_probe_init(uint32_t bus_id, uint32_t bus_speed); int omv_csi_probe(omv_i2c_t *i2c);
// Match CSI chip ID with provided ID.
int omv_csi_match(omv_csi_t *csi, size_t id);
// This function is called after a setting that may require reconfiguring // This function is called after a setting that may require reconfiguring
// the hardware changes, such as window size, frame size, or pixel format. // the hardware changes, such as window size, frame size, or pixel format.
int omv_csi_config(omv_csi_config_t config); int omv_csi_config(omv_csi_t *csi, omv_csi_config_t config);
// Abort frame capture and disable IRQs, DMA etc.. // Abort frame capture and disable IRQs, DMA etc..
int omv_csi_abort(omv_csi_t *csi, 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. // Reset the sensor to its default state.
int omv_csi_reset(); int omv_csi_reset(omv_csi_t *csi, bool hard);
// Return csi PID. int omv_csi_get_id(omv_csi_t *csi);
int omv_csi_get_id();
// Returns the xclk freq in hz. // Returns the xclk freq in hz.
uint32_t omv_csi_get_clk_frequency(); uint32_t omv_csi_get_clk_frequency();
@ -397,144 +422,144 @@ uint32_t omv_csi_get_clk_frequency();
int omv_csi_set_clk_frequency(uint32_t frequency); int omv_csi_set_clk_frequency(uint32_t frequency);
// Return true if the sensor was detected and initialized. // Return true if the sensor was detected and initialized.
bool omv_csi_is_detected(); bool omv_csi_is_detected(omv_csi_t *csi);
// Sleep mode. // Sleep mode.
int omv_csi_sleep(int enable); int omv_csi_sleep(omv_csi_t *csi, int enable);
// Shutdown mode. // Shutdown mode.
int omv_csi_shutdown(int enable); int omv_csi_shutdown(omv_csi_t *csi, int enable);
// Read a csi register. // Read a csi register.
int omv_csi_read_reg(uint16_t reg_addr); int omv_csi_read_reg(omv_csi_t *csi, uint16_t reg_addr);
// Write a csi register. // Write a csi register.
int omv_csi_write_reg(uint16_t reg_addr, uint16_t reg_data); int omv_csi_write_reg(omv_csi_t *csi, uint16_t reg_addr, uint16_t reg_data);
// Set the sensor pixel format. // Set the sensor pixel format.
int omv_csi_set_pixformat(pixformat_t pixformat); int omv_csi_set_pixformat(omv_csi_t *csi, pixformat_t pixformat);
// Set the sensor frame size. // Set the sensor frame size.
int omv_csi_set_framesize(omv_csi_framesize_t framesize); int omv_csi_set_framesize(omv_csi_t *csi, omv_csi_framesize_t framesize);
// Set the sensor frame rate. // Set the sensor frame rate.
int omv_csi_set_framerate(int framerate); int omv_csi_set_framerate(omv_csi_t *csi, int framerate);
// Return the number of bytes per pixel to read from the image sensor. // Return the number of bytes per pixel to read from the image sensor.
uint32_t omv_csi_get_src_bpp(); uint32_t omv_csi_get_src_bpp(omv_csi_t *csi);
// Return the number of bytes per pixel to write to memory. // Return the number of bytes per pixel to write to memory.
uint32_t omv_csi_get_dst_bpp(); uint32_t omv_csi_get_dst_bpp(omv_csi_t *csi);
// Returns true if a crop is being applied to the frame buffer. // Returns true if a crop is being applied to the frame buffer.
bool omv_csi_get_cropped(); bool omv_csi_get_cropped(omv_csi_t *csi);
// Set window size. // Set window size.
int omv_csi_set_windowing(int x, int y, int w, int h); int omv_csi_set_windowing(omv_csi_t *csi, int x, int y, int w, int h);
// Set the sensor contrast level (from -3 to +3). // Set the sensor contrast level (from -3 to +3).
int omv_csi_set_contrast(int level); int omv_csi_set_contrast(omv_csi_t *csi, int level);
// Set the sensor brightness level (from -3 to +3). // Set the sensor brightness level (from -3 to +3).
int omv_csi_set_brightness(int level); int omv_csi_set_brightness(omv_csi_t *csi, int level);
// Set the sensor saturation level (from -3 to +3). // Set the sensor saturation level (from -3 to +3).
int omv_csi_set_saturation(int level); int omv_csi_set_saturation(omv_csi_t *csi, int level);
// Set the sensor AGC gain ceiling. // Set the sensor AGC gain ceiling.
// Note: This function has no effect when AGC (Automatic Gain Control) is disabled. // Note: This function has no effect when AGC (Automatic Gain Control) is disabled.
int omv_csi_set_gainceiling(omv_csi_gainceiling_t gainceiling); int omv_csi_set_gainceiling(omv_csi_t *csi, omv_csi_gainceiling_t gainceiling);
// Set the quantization scale factor, controls JPEG quality (quality 0-255). // Set the quantization scale factor, controls JPEG quality (quality 0-255).
int omv_csi_set_quality(int qs); int omv_csi_set_quality(omv_csi_t *csi, int qs);
// Enable/disable the colorbar mode. // Enable/disable the colorbar mode.
int omv_csi_set_colorbar(int enable); int omv_csi_set_colorbar(omv_csi_t *csi, int enable);
// Enable auto gain or set value manually. // Enable auto gain or set value manually.
int omv_csi_set_auto_gain(int enable, float gain_db, float gain_db_ceiling); int omv_csi_set_auto_gain(omv_csi_t *csi, int enable, float gain_db, float gain_db_ceiling);
// Get the gain value. // Get the gain value.
int omv_csi_get_gain_db(float *gain_db); int omv_csi_get_gain_db(omv_csi_t *csi, float *gain_db);
// Enable auto exposure or set value manually. // Enable auto exposure or set value manually.
int omv_csi_set_auto_exposure(int enable, int exposure_us); int omv_csi_set_auto_exposure(omv_csi_t *csi, int enable, int exposure_us);
// Get the exposure value. // Get the exposure value.
int omv_csi_get_exposure_us(int *get_exposure_us); int omv_csi_get_exposure_us(omv_csi_t *csi, int *get_exposure_us);
// Enable auto white balance or set value manually. // Enable auto white balance or set value manually.
int omv_csi_set_auto_whitebal(int enable, float r_gain_db, float g_gain_db, float b_gain_db); int omv_csi_set_auto_whitebal(omv_csi_t *csi, int enable, float r_gain_db, float g_gain_db, float b_gain_db);
// Get the rgb gain values. // Get the rgb gain values.
int omv_csi_get_rgb_gain_db(float *r_gain_db, float *g_gain_db, float *b_gain_db); int omv_csi_get_rgb_gain_db(omv_csi_t *csi, float *r_gain_db, float *g_gain_db, float *b_gain_db);
// Enable auto blc (black level calibration) or set from previous calibration. // Enable auto blc (black level calibration) or set from previous calibration.
int omv_csi_set_auto_blc(int enable, int *regs); int omv_csi_set_auto_blc(omv_csi_t *csi, int enable, int *regs);
// Get black level valibration register values. // Get black level valibration register values.
int omv_csi_get_blc_regs(int *regs); int omv_csi_get_blc_regs(omv_csi_t *csi, int *regs);
// Enable/disable the hmirror mode. // Enable/disable the hmirror mode.
int omv_csi_set_hmirror(int enable); int omv_csi_set_hmirror(omv_csi_t *csi, int enable);
// Get hmirror status. // Get hmirror status.
bool omv_csi_get_hmirror(); bool omv_csi_get_hmirror(omv_csi_t *csi);
// Enable/disable the vflip mode. // Enable/disable the vflip mode.
int omv_csi_set_vflip(int enable); int omv_csi_set_vflip(omv_csi_t *csi, int enable);
// Get vflip status. // Get vflip status.
bool omv_csi_get_vflip(); bool omv_csi_get_vflip(omv_csi_t *csi);
// Enable/disable the transpose mode. // Enable/disable the transpose mode.
int omv_csi_set_transpose(bool enable); int omv_csi_set_transpose(omv_csi_t *csi, bool enable);
// Get transpose mode state. // Get transpose mode state.
bool omv_csi_get_transpose(); bool omv_csi_get_transpose(omv_csi_t *csi);
// Enable/disable the auto rotation mode. // Enable/disable the auto rotation mode.
int omv_csi_set_auto_rotation(bool enable); int omv_csi_set_auto_rotation(omv_csi_t *csi, bool enable);
// Get transpose mode state. // Get transpose mode state.
bool omv_csi_get_auto_rotation(); bool omv_csi_get_auto_rotation(omv_csi_t *csi);
// Set the number of virtual frame buffers. // Set the number of virtual frame buffers.
int omv_csi_set_framebuffers(int count); int omv_csi_set_framebuffers(omv_csi_t *csi, int count);
// Drop the next frame to match the current frame rate. // Drop the next frame to match the current frame rate.
void omv_csi_throttle_framerate(); void omv_csi_throttle_framerate(omv_csi_t *csi);
// Set special digital effects (SDE). // Set special digital effects (SDE).
int omv_csi_set_special_effect(omv_csi_sde_t sde); int omv_csi_set_special_effect(omv_csi_t *csi, omv_csi_sde_t sde);
// Set lens shading correction // Set lens shading correction
int omv_csi_set_lens_correction(int enable, int radi, int coef); int omv_csi_set_lens_correction(omv_csi_t *csi, int enable, int radi, int coef);
// IOCTL function // IOCTL function
int omv_csi_ioctl(int request, ...); int omv_csi_ioctl(omv_csi_t *csi, int request, ...);
// Set vsync callback function. // Set vsync callback function.
int omv_csi_set_vsync_callback(vsync_cb_t vsync_cb); int omv_csi_set_vsync_callback(omv_csi_t *csi, omv_csi_cb_t cb);
// Set frame callback function. // Set frame callback function.
int omv_csi_set_frame_callback(frame_cb_t vsync_cb); int omv_csi_set_frame_callback(omv_csi_t *csi, omv_csi_cb_t cb);
// Set color palette // Set color palette
int omv_csi_set_color_palette(const uint16_t *color_palette); int omv_csi_set_color_palette(omv_csi_t *csi, const uint16_t *color_palette);
// Get color palette // Get color palette
const uint16_t *omv_csi_get_color_palette(); const uint16_t *omv_csi_get_color_palette(omv_csi_t *csi);
// Return true if the current frame size/format fits in RAM. // Return true if the current frame size/format fits in RAM.
int omv_csi_check_framebuffer_size(); int omv_csi_check_framebuffer_size(omv_csi_t *csi);
// Auto-crop frame buffer until it fits in RAM (may switch pixel format to BAYER). // Auto-crop frame buffer until it fits in RAM (may switch pixel format to BAYER).
int omv_csi_auto_crop_framebuffer(); int omv_csi_auto_crop_framebuffer(omv_csi_t *csi);
// Copy a single line buffer to its destination. The copying process is // Copy a single line buffer to its destination. The copying process is
// DMA-accelerated, if available, and falls back to slow software if not. // DMA-accelerated, if available, and falls back to slow software if not.
int omv_csi_copy_line(void *dma, uint8_t *src, uint8_t *dst); int omv_csi_copy_line(omv_csi_t *csi, void *dma, uint8_t *src, uint8_t *dst);
// Default snapshot function. // Default snapshot function.
int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags); int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags);

View File

@ -33,7 +33,7 @@
#include "py/runtime.h" #include "py/runtime.h"
#include "imlib.h" #include "imlib.h"
#if MICROPY_PY_CSI #if MICROPY_PY_CSI || MICROPY_PY_CSI_NG
#include "omv_i2c.h" #include "omv_i2c.h"
#include "omv_csi.h" #include "omv_csi.h"
#endif #endif
@ -146,8 +146,9 @@ void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
case USBDBG_SENSOR_ID: { case USBDBG_SENSOR_ID: {
uint32_t buffer = 0xFF; uint32_t buffer = 0xFF;
#if MICROPY_PY_CSI #if MICROPY_PY_CSI
if (omv_csi_is_detected() == true) { omv_csi_t *csi = omv_csi_get(-1);
buffer = omv_csi_get_id(); if (omv_csi_is_detected(csi) == true) {
buffer = omv_csi_get_id(csi);
} }
#endif #endif
cmd = USBDBG_NONE; cmd = USBDBG_NONE;
@ -363,21 +364,24 @@ void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback) {
struct { struct {
int32_t name; int32_t name;
int32_t value; int32_t value;
} } attr;
attr;
omv_csi_t *csi = omv_csi_get(-1);
read_callback(&attr, sizeof(attr)); read_callback(&attr, sizeof(attr));
switch (attr.name) { switch (attr.name) {
case OMV_CSI_ATTR_CONTRAST: case OMV_CSI_ATTR_CONTRAST:
omv_csi_set_contrast(attr.value); omv_csi_set_contrast(csi, attr.value);
break; break;
case OMV_CSI_ATTR_BRIGHTNESS: case OMV_CSI_ATTR_BRIGHTNESS:
omv_csi_set_brightness(attr.value); omv_csi_set_brightness(csi, attr.value);
break; break;
case OMV_CSI_ATTR_SATURATION: case OMV_CSI_ATTR_SATURATION:
omv_csi_set_saturation(attr.value); omv_csi_set_saturation(csi, attr.value);
break; break;
case OMV_CSI_ATTR_GAINCEILING: case OMV_CSI_ATTR_GAINCEILING:
omv_csi_set_gainceiling(attr.value); omv_csi_set_gainceiling(csi, attr.value);
break; break;
default: default:
break; break;

View File

@ -45,7 +45,7 @@
#define LEPTON_BOOT_TIMEOUT (1000) #define LEPTON_BOOT_TIMEOUT (1000)
#define LEPTON_SNAPSHOT_RETRY (3) #define LEPTON_SNAPSHOT_RETRY (3)
#define LEPTON_SNAPSHOT_TIMEOUT (10000) #define LEPTON_SNAPSHOT_TIMEOUT (5000)
// Min/Max temperatures in Celsius. // Min/Max temperatures in Celsius.
#define LEPTON_MIN_TEMP_NORM (-10.0f) #define LEPTON_MIN_TEMP_NORM (-10.0f)
@ -73,18 +73,6 @@ static lepton_state_t lepton;
static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mode); static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mode);
static int sleep(omv_csi_t *csi, int enable) {
if (enable) {
omv_gpio_write(OMV_CSI_POWER_PIN, 0);
mp_hal_delay_ms(100);
} else {
omv_gpio_write(OMV_CSI_POWER_PIN, 1);
mp_hal_delay_ms(100);
}
return 0;
}
static int read_reg(omv_csi_t *csi, uint16_t reg_addr) { static int read_reg(omv_csi_t *csi, uint16_t reg_addr) {
uint16_t reg_data; uint16_t reg_data;
if (omv_i2c_readw2(csi->i2c, csi->slv_addr, reg_addr, &reg_data)) { if (omv_i2c_readw2(csi->i2c, csi->slv_addr, reg_addr, &reg_data)) {
@ -281,39 +269,49 @@ static int ioctl(omv_csi_t *csi, int request, va_list ap) {
} }
static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mode) { static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mode) {
omv_gpio_write(OMV_CSI_POWER_PIN, 0);
mp_hal_delay_ms(10);
omv_gpio_write(OMV_CSI_POWER_PIN, 1);
mp_hal_delay_ms(10);
omv_gpio_write(OMV_CSI_RESET_PIN, 0);
mp_hal_delay_ms(10);
omv_gpio_write(OMV_CSI_RESET_PIN, 1);
mp_hal_delay_ms(1000);
LEP_RAD_ENABLE_E rad; LEP_RAD_ENABLE_E rad;
LEP_AGC_ROI_T roi; LEP_AGC_ROI_T roi;
memset(&lepton.port, 0, sizeof(LEP_CAMERA_PORT_DESC_T)); memset(&lepton.port, 0, sizeof(LEP_CAMERA_PORT_DESC_T));
if (!csi->auxiliary) {
omv_gpio_write(OMV_CSI_POWER_PIN, 0);
mp_hal_delay_ms(10);
omv_gpio_write(OMV_CSI_POWER_PIN, 1);
mp_hal_delay_ms(10);
omv_gpio_write(OMV_CSI_RESET_PIN, 0);
mp_hal_delay_ms(10);
omv_gpio_write(OMV_CSI_RESET_PIN, 1);
mp_hal_delay_ms(1000);
}
for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) { for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) {
if (LEP_OpenPort(csi->i2c, LEP_CCI_TWI, 0, &lepton.port) == LEP_OK) { if (LEP_OpenPort(csi->i2c, LEP_CCI_TWI, 0, &lepton.port) == LEP_OK) {
break; break;
} }
if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) { if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) {
return -1; return -1;
} }
} }
if (csi->auxiliary) {
LEP_RunOemReboot(&lepton.port);
mp_hal_delay_ms(1000);
}
for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) { for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(1)) {
LEP_SDK_BOOT_STATUS_E status; LEP_SDK_BOOT_STATUS_E status;
if (LEP_GetCameraBootStatus(&lepton.port, &status) != LEP_OK) { if (LEP_GetCameraBootStatus(&lepton.port, &status) != LEP_OK) {
return -1; return -1;
} }
if (status == LEP_BOOT_STATUS_BOOTED) { if (status == LEP_BOOT_STATUS_BOOTED) {
break; break;
} }
if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) { if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) {
return -1; return -1;
} }
@ -324,9 +322,11 @@ static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mo
if (LEP_DirectReadRegister(&lepton.port, LEP_I2C_STATUS_REG, &status) != LEP_OK) { if (LEP_DirectReadRegister(&lepton.port, LEP_I2C_STATUS_REG, &status) != LEP_OK) {
return -1; return -1;
} }
if (!(status & LEP_I2C_STATUS_BUSY_BIT_MASK)) { if (!(status & LEP_I2C_STATUS_BUSY_BIT_MASK)) {
break; break;
} }
if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) { if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) {
return -1; return -1;
} }
@ -357,6 +357,14 @@ static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mo
return 0; return 0;
} }
static int sleep(omv_csi_t *csi, int enable) {
return 0;
}
static int match(omv_csi_t *csi, size_t id) {
return (id == LEPTON_ID) || ((id >> 8) == LEPTON_ID);
}
static int reset(omv_csi_t *csi) { static int reset(omv_csi_t *csi) {
vospi_deinit(); vospi_deinit();
@ -380,7 +388,23 @@ static int reset(omv_csi_t *csi) {
static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
framebuffer_t *fb = csi->fb; framebuffer_t *fb = csi->fb;
framebuffer_update_jpeg_buffer(fb);
if (flags & OMV_CSI_CAPTURE_FLAGS_NBLOCK) {
if (!vospi_active()) {
// Start the capture without blocking
vospi_snapshot(0);
return OMV_CSI_ERROR_WOULD_BLOCK;
}
// If capture is running and no frames return.
if (!framebuffer_get_head(fb, FB_PEEK)) {
return OMV_CSI_ERROR_WOULD_BLOCK;
}
}
if (flags & OMV_CSI_CAPTURE_FLAGS_UPDATE) {
framebuffer_update_jpeg_buffer(fb);
}
if (csi->pixformat == PIXFORMAT_INVALID) { if (csi->pixformat == PIXFORMAT_INVALID) {
return OMV_CSI_ERROR_INVALID_PIXFORMAT; return OMV_CSI_ERROR_INVALID_PIXFORMAT;
@ -472,6 +496,9 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
int lepton_init(omv_csi_t *csi) { int lepton_init(omv_csi_t *csi) {
csi->reset = reset; csi->reset = reset;
csi->sleep = sleep; csi->sleep = sleep;
csi->config = NULL;
csi->abort = NULL;
csi->match = match;
csi->snapshot = snapshot; csi->snapshot = snapshot;
csi->read_reg = read_reg; csi->read_reg = read_reg;
csi->write_reg = write_reg; csi->write_reg = write_reg;
@ -495,12 +522,16 @@ int lepton_init(omv_csi_t *csi) {
csi->set_lens_correction = set_lens_correction; csi->set_lens_correction = set_lens_correction;
csi->ioctl = ioctl; csi->ioctl = ioctl;
csi->auxiliary = 1;
csi->vsync_pol = 1; csi->vsync_pol = 1;
csi->hsync_pol = 0; csi->hsync_pol = 0;
csi->pixck_pol = 0; csi->pixck_pol = 0;
csi->frame_sync = 0; csi->frame_sync = 0;
csi->mono_bpp = 1; csi->mono_bpp = 1;
// Extra delay after power-on
mp_hal_delay_ms(1000);
if (reset(csi) != 0) { if (reset(csi) != 0) {
return -1; return -1;
} }

View File

@ -68,7 +68,9 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
return 0; return 0;
} }
framebuffer_update_jpeg_buffer(fb); if (flags & OMV_CSI_CAPTURE_FLAGS_UPDATE) {
framebuffer_update_jpeg_buffer(fb);
}
if (fb->n_buffers != 1) { if (fb->n_buffers != 1) {
framebuffer_set_buffers(fb, 1); framebuffer_set_buffers(fb, 1);
@ -149,12 +151,15 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
int softcsi_init(omv_csi_t *csi) { int softcsi_init(omv_csi_t *csi) {
csi->reset = reset; csi->reset = reset;
csi->abort = NULL;
csi->config = NULL;
csi->set_pixformat = set_pixformat; csi->set_pixformat = set_pixformat;
csi->set_framesize = set_framesize; csi->set_framesize = set_framesize;
csi->set_hmirror = set_hmirror; csi->set_hmirror = set_hmirror;
csi->set_vflip = set_vflip; csi->set_vflip = set_vflip;
csi->snapshot = snapshot; csi->snapshot = snapshot;
csi->auxiliary = 1;
csi->vsync_pol = 1; csi->vsync_pol = 1;
csi->hsync_pol = 0; csi->hsync_pol = 0;
csi->pixck_pol = 0; csi->pixck_pol = 0;

View File

@ -42,26 +42,28 @@ extern char _jpeg_memory_end;
jpegbuffer_t *jpegbuffer = (jpegbuffer_t *) &_jpeg_memory_start; jpegbuffer_t *jpegbuffer = (jpegbuffer_t *) &_jpeg_memory_start;
void framebuffer_init0() { void framebuffer_init0() {
// Save fb_enabled flag state // Save enable flag.
int fb_enabled = jpegbuffer->enabled; int fb_enabled = jpegbuffer->enabled;
uint32_t fb_size = (char *) &_fb_memory_end - (char *) framebuffer->data;
// Clear framebuffers // Initialize frame buffer.
memset(framebuffer, 0, sizeof(*framebuffer)); framebuffer_init_fb(framebuffer, fb_size, false);
// Initialize jpeg buffer.
memset(jpegbuffer, 0, sizeof(*jpegbuffer)); memset(jpegbuffer, 0, sizeof(*jpegbuffer));
mutex_init0(&jpegbuffer->lock); mutex_init0(&jpegbuffer->lock);
jpegbuffer->enabled = fb_enabled;
// 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; jpegbuffer->quality = ((OMV_JPEG_QUALITY_HIGH - OMV_JPEG_QUALITY_LOW) / 2) + OMV_JPEG_QUALITY_LOW;
}
// Set fb_enabled void framebuffer_init_fb(framebuffer_t *fb, size_t size, bool dynamic) {
jpegbuffer->enabled = fb_enabled; // controlled by the IDE. // Clear framebuffers
memset(fb, 0, sizeof(*fb));
// Setup buffering. fb->raw_size = size;
framebuffer_set_buffers(framebuffer, 1); fb->streaming_enabled = true;
fb->dynamic = dynamic;
framebuffer_set_buffers(fb, 1);
} }
void framebuffer_init_image(framebuffer_t *fb, image_t *img) { void framebuffer_init_image(framebuffer_t *fb, image_t *img) {
@ -283,8 +285,14 @@ int framebuffer_encoded_size(framebuffer_t *fb, image_t *img) {
// Returns the current frame buffer size, factoring in the space taken by fb_alloc. // Returns the current frame buffer size, factoring in the space taken by fb_alloc.
static uint32_t framebuffer_max_buffer_size(framebuffer_t *fb) { 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_total_size = FB_ALIGN_SIZE_ROUND_DOWN(fb->raw_size);
uint32_t fb_avail_size = FB_ALIGN_SIZE_ROUND_DOWN(fb_alloc_stack_pointer() - (char *) fb->data); uint32_t fb_avail_size = FB_ALIGN_SIZE_ROUND_DOWN(fb_alloc_stack_pointer() - (char *) fb->data);
// No fb_alloc on dynamic FBs.
if (fb->dynamic) {
fb_avail_size = fb_total_size;
}
return IM_MIN(fb_total_size, fb_avail_size); return IM_MIN(fb_total_size, fb_avail_size);
} }

View File

@ -43,6 +43,7 @@ typedef struct framebuffer {
int32_t u, v; int32_t u, v;
PIXFORMAT_STRUCT; PIXFORMAT_STRUCT;
int32_t streaming_enabled; int32_t streaming_enabled;
uint32_t raw_size;
uint32_t buff_size; uint32_t buff_size;
uint32_t n_buffers; uint32_t n_buffers;
uint32_t frame_size; uint32_t frame_size;
@ -50,6 +51,7 @@ typedef struct framebuffer {
volatile int32_t tail; volatile int32_t tail;
bool check_head; bool check_head;
int32_t sampled_head; int32_t sampled_head;
bool dynamic;
OMV_ATTR_ALIGNED(uint8_t data[], FRAMEBUFFER_ALIGNMENT); OMV_ATTR_ALIGNED(uint8_t data[], FRAMEBUFFER_ALIGNMENT);
} framebuffer_t; } framebuffer_t;
@ -108,6 +110,9 @@ uint32_t framebuffer_get_buffer_size(framebuffer_t *fb);
// Return the state of a buffer. // Return the state of a buffer.
vbuffer_t *framebuffer_get_buffer(framebuffer_t *fb, int32_t index); vbuffer_t *framebuffer_get_buffer(framebuffer_t *fb, int32_t index);
// Initializes a frame buffer instance.
void framebuffer_init_fb(framebuffer_t *fb, size_t size, bool dynamic);
// Initializes an image from the frame buffer. // Initializes an image from the frame buffer.
void framebuffer_init_image(framebuffer_t *fb, image_t *img); void framebuffer_init_image(framebuffer_t *fb, image_t *img);

View File

@ -284,7 +284,7 @@ void image_init(image_t *ptr, int w, int h, pixformat_t pixfmt, uint32_t size, v
ptr->pixels = pixels; ptr->pixels = pixels;
} }
void image_copy(image_t *dst, image_t *src) { void image_copy(image_t *dst, image_t *src, bool deep) {
memcpy(dst, src, sizeof(image_t)); memcpy(dst, src, sizeof(image_t));
} }

View File

@ -570,7 +570,7 @@ typedef struct image {
void image_xalloc(image_t *img, size_t size); void image_xalloc(image_t *img, size_t size);
void image_xalloc0(image_t *img, size_t size); void image_xalloc0(image_t *img, size_t size);
void image_init(image_t *ptr, int w, int h, pixformat_t pixfmt, uint32_t size, void *pixels); void image_init(image_t *ptr, int w, int h, pixformat_t pixfmt, uint32_t size, void *pixels);
void image_copy(image_t *dst, image_t *src); void image_copy(image_t *dst, image_t *src, bool deep);
size_t image_line_size(image_t *ptr); size_t image_line_size(image_t *ptr);
size_t image_size(image_t *ptr); size_t image_size(image_t *ptr);
bool image_get_mask_pixel(image_t *ptr, int x, int y); bool image_get_mask_pixel(image_t *ptr, int x, int y);

View File

@ -38,6 +38,8 @@
#if MICROPY_PY_CSI #if MICROPY_PY_CSI
#include "omv_csi.h" #include "omv_csi.h"
#include "omv_gpio.h"
#include "imlib.h" #include "imlib.h"
#include "xalloc.h" #include "xalloc.h"
#include "py_assert.h" #include "py_assert.h"
@ -50,7 +52,6 @@
#include "py_helper.h" #include "py_helper.h"
#include "framebuffer.h" #include "framebuffer.h"
extern omv_csi_t csi;
static mp_obj_t vsync_callback = mp_const_none; static mp_obj_t vsync_callback = mp_const_none;
static mp_obj_t frame_callback = mp_const_none; static mp_obj_t frame_callback = mp_const_none;
@ -58,8 +59,8 @@ static mp_obj_t frame_callback = mp_const_none;
#define omv_csi_print_error(op) printf("\x1B[31mWARNING: %s control is not supported by this image sensor.\x1B[0m\n", op); #define omv_csi_print_error(op) printf("\x1B[31mWARNING: %s control is not supported by this image sensor.\x1B[0m\n", op);
#if MICROPY_PY_IMU #if MICROPY_PY_IMU
static void do_auto_rotation(int pitch_deadzone, int roll_activezone) { static void do_auto_rotation(omv_csi_t *csi, int pitch_deadzone, int roll_activezone) {
if (omv_csi_get_auto_rotation()) { if (omv_csi_get_auto_rotation(csi)) {
float pitch = py_imu_pitch_rotation(); float pitch = py_imu_pitch_rotation();
if (((pitch <= (90 - pitch_deadzone)) || ((90 + pitch_deadzone) < pitch)) if (((pitch <= (90 - pitch_deadzone)) || ((90 + pitch_deadzone) < pitch))
&& ((pitch <= (270 - pitch_deadzone)) || ((270 + pitch_deadzone) < pitch))) { && ((pitch <= (270 - pitch_deadzone)) || ((270 + pitch_deadzone) < pitch))) {
@ -67,24 +68,24 @@ static void do_auto_rotation(int pitch_deadzone, int roll_activezone) {
float roll = py_imu_roll_rotation(); float roll = py_imu_roll_rotation();
if (((360 - roll_activezone) <= roll) || (roll < (0 + roll_activezone)) ) { if (((360 - roll_activezone) <= roll) || (roll < (0 + roll_activezone)) ) {
// center is 0/360, upright // center is 0/360, upright
omv_csi_set_hmirror(false); omv_csi_set_hmirror(csi, false);
omv_csi_set_vflip(false); omv_csi_set_vflip(csi, false);
omv_csi_set_transpose(false); omv_csi_set_transpose(csi, false);
} else if (((270 - roll_activezone) <= roll) && (roll < (270 + roll_activezone))) { } else if (((270 - roll_activezone) <= roll) && (roll < (270 + roll_activezone))) {
// center is 270, rotated right // center is 270, rotated right
omv_csi_set_hmirror(true); omv_csi_set_hmirror(csi, true);
omv_csi_set_vflip(false); omv_csi_set_vflip(csi, false);
omv_csi_set_transpose(true); omv_csi_set_transpose(csi, true);
} else if (((180 - roll_activezone) <= roll) && (roll < (180 + roll_activezone))) { } else if (((180 - roll_activezone) <= roll) && (roll < (180 + roll_activezone))) {
// center is 180, upside down // center is 180, upside down
omv_csi_set_hmirror(true); omv_csi_set_hmirror(csi, true);
omv_csi_set_vflip(true); omv_csi_set_vflip(csi, true);
omv_csi_set_transpose(false); omv_csi_set_transpose(csi, false);
} else if (((90 - roll_activezone) <= roll) && (roll < (90 + roll_activezone))) { } else if (((90 - roll_activezone) <= roll) && (roll < (90 + roll_activezone))) {
// center is 90, rotated left // center is 90, rotated left
omv_csi_set_hmirror(false); omv_csi_set_hmirror(csi, false);
omv_csi_set_vflip(true); omv_csi_set_vflip(csi, true);
omv_csi_set_transpose(true); omv_csi_set_transpose(csi, true);
} }
} }
} }
@ -92,10 +93,11 @@ static void do_auto_rotation(int pitch_deadzone, int roll_activezone) {
#endif // MICROPY_PY_IMU #endif // MICROPY_PY_IMU
static mp_obj_t py_omv_csi__init__() { static mp_obj_t py_omv_csi__init__() {
omv_csi_t *csi = omv_csi_get(-1);
// This is the module init function, not the sensor init function. // This is the module init function, not the sensor init function.
// This gets called when the module is imported, so it's a good // This gets called when the module is imported, so it's a good
// place to check if the sensor was detected or not. // place to check if the sensor was detected or not.
if (omv_csi_is_detected() == false) { if (omv_csi_is_detected(csi) == false) {
omv_csi_raise_error(OMV_CSI_ERROR_ISC_UNDETECTED); omv_csi_raise_error(OMV_CSI_ERROR_ISC_UNDETECTED);
} }
return mp_const_none; return mp_const_none;
@ -103,14 +105,16 @@ static mp_obj_t py_omv_csi__init__() {
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi__init__obj, py_omv_csi__init__); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi__init__obj, py_omv_csi__init__);
static mp_obj_t py_omv_csi_reset() { static mp_obj_t py_omv_csi_reset() {
int error = omv_csi_reset(); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_reset(csi, true);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
#if MICROPY_PY_IMU #if MICROPY_PY_IMU
// +-10 degree dead-zone around pitch 90/270. // +-10 degree dead-zone around pitch 90/270.
// +-45 degree active-zone around roll 0/90/180/270/360. // +-45 degree active-zone around roll 0/90/180/270/360.
do_auto_rotation(10, 45); do_auto_rotation(csi, 10, 45);
// We're setting the dead-zone on pitch because roll readings are invalid there. // We're setting the dead-zone on pitch because roll readings are invalid there.
// We're setting the full range on roll to set the initial state. // We're setting the full range on roll to set the initial state.
#endif // MICROPY_PY_IMU #endif // MICROPY_PY_IMU
@ -119,34 +123,41 @@ static mp_obj_t py_omv_csi_reset() {
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_reset_obj, py_omv_csi_reset); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_reset_obj, py_omv_csi_reset);
static mp_obj_t py_omv_csi_sleep(mp_obj_t enable) { static mp_obj_t py_omv_csi_sleep(mp_obj_t enable) {
PY_ASSERT_FALSE_MSG(omv_csi_sleep(mp_obj_is_true(enable)) != 0, "Sleep Failed"); omv_csi_t *csi = omv_csi_get(-1);
PY_ASSERT_FALSE_MSG(omv_csi_sleep(csi, mp_obj_is_true(enable)) != 0, "Sleep Failed");
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_sleep_obj, py_omv_csi_sleep); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_sleep_obj, py_omv_csi_sleep);
static mp_obj_t py_omv_csi_shutdown(mp_obj_t enable) { static mp_obj_t py_omv_csi_shutdown(mp_obj_t enable) {
PY_ASSERT_FALSE_MSG(omv_csi_shutdown(mp_obj_is_true(enable)) != 0, "Shutdown Failed"); omv_csi_t *csi = omv_csi_get(-1);
PY_ASSERT_FALSE_MSG(omv_csi_shutdown(csi, mp_obj_is_true(enable)) != 0, "Shutdown Failed");
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_shutdown_obj, py_omv_csi_shutdown); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_shutdown_obj, py_omv_csi_shutdown);
static mp_obj_t py_omv_csi_flush() { static mp_obj_t py_omv_csi_flush() {
framebuffer_update_jpeg_buffer(csi.fb); omv_csi_t *csi = omv_csi_get(-1);
framebuffer_update_jpeg_buffer(csi->fb);
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_flush_obj, py_omv_csi_flush); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_flush_obj, py_omv_csi_flush);
static mp_obj_t py_omv_csi_snapshot(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { static mp_obj_t py_omv_csi_snapshot(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
omv_csi_t *csi = omv_csi_get(-1);
#if MICROPY_PY_IMU #if MICROPY_PY_IMU
// +-10 degree dead-zone around pitch 90/270. // +-10 degree dead-zone around pitch 90/270.
// +-35 degree active-zone around roll 0/90/180/270/360. // +-35 degree active-zone around roll 0/90/180/270/360.
do_auto_rotation(10, 35); do_auto_rotation(csi, 10, 35);
// We're setting the dead-zone on pitch because roll readings are invalid there. // We're setting the dead-zone on pitch because roll readings are invalid there.
// We're not setting the full range on roll to prevent oscillation. // We're not setting the full range on roll to prevent oscillation.
#endif // MICROPY_PY_IMU #endif // MICROPY_PY_IMU
mp_obj_t image = py_image(0, 0, 0, 0, 0); mp_obj_t image = py_image(0, 0, 0, 0, 0);
int error = csi.snapshot(&csi, (image_t *) py_image_cobj(image), 0); uint32_t flags = OMV_CSI_CAPTURE_FLAGS_UPDATE;
int error = omv_csi_snapshot(csi, (image_t *) py_image_cobj(image), flags);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -184,34 +195,39 @@ static mp_obj_t py_omv_csi_skip_frames(size_t n_args, const mp_obj_t *args, mp_m
static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_skip_frames_obj, 0, py_omv_csi_skip_frames); static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_skip_frames_obj, 0, py_omv_csi_skip_frames);
static mp_obj_t py_omv_csi_width() { static mp_obj_t py_omv_csi_width() {
return mp_obj_new_int(resolution[csi.framesize][0]); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_int(resolution[csi->framesize][0]);
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_width_obj, py_omv_csi_width); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_width_obj, py_omv_csi_width);
static mp_obj_t py_omv_csi_height() { static mp_obj_t py_omv_csi_height() {
return mp_obj_new_int(resolution[csi.framesize][1]); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_int(resolution[csi->framesize][1]);
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_height_obj, 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() { static mp_obj_t py_omv_csi_get_fb() {
image_t image; image_t image;
omv_csi_t *csi = omv_csi_get(-1);
if (framebuffer_get_depth(csi.fb) < 0) { if (framebuffer_get_depth(csi->fb) < 0) {
return mp_const_none; return mp_const_none;
} }
framebuffer_init_image(csi.fb, &image); framebuffer_init_image(csi->fb, &image);
return py_image_from_struct(&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); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_fb_obj, py_omv_csi_get_fb);
static mp_obj_t py_omv_csi_get_id() { static mp_obj_t py_omv_csi_get_id() {
return mp_obj_new_int(omv_csi_get_id()); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_int(omv_csi_get_id(csi));
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_id_obj, 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() { static mp_obj_t py_omv_csi_get_frame_available() {
framebuffer_t *fb = framebuffer_get(0); omv_csi_t *csi = omv_csi_get(-1);
framebuffer_t *fb = csi->fb;
return mp_obj_new_bool(fb->tail != fb->head); 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); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_frame_available_obj, py_omv_csi_get_frame_available);
@ -245,7 +261,8 @@ static mp_obj_t py_omv_csi_dealloc_extra_fb() {
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_dealloc_extra_fb_obj, py_omv_csi_dealloc_extra_fb); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_dealloc_extra_fb_obj, py_omv_csi_dealloc_extra_fb);
static mp_obj_t py_omv_csi_set_pixformat(mp_obj_t pixformat) { static mp_obj_t py_omv_csi_set_pixformat(mp_obj_t pixformat) {
int error = omv_csi_set_pixformat(mp_obj_get_int(pixformat)); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_set_pixformat(csi, mp_obj_get_int(pixformat));
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -254,15 +271,17 @@ static mp_obj_t py_omv_csi_set_pixformat(mp_obj_t pixformat) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_pixformat_obj, py_omv_csi_set_pixformat); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_pixformat_obj, py_omv_csi_set_pixformat);
static mp_obj_t py_omv_csi_get_pixformat() { static mp_obj_t py_omv_csi_get_pixformat() {
if (csi.pixformat == PIXFORMAT_INVALID) { omv_csi_t *csi = omv_csi_get(-1);
if (csi->pixformat == PIXFORMAT_INVALID) {
omv_csi_raise_error(OMV_CSI_ERROR_INVALID_PIXFORMAT); omv_csi_raise_error(OMV_CSI_ERROR_INVALID_PIXFORMAT);
} }
return mp_obj_new_int(csi.pixformat); return mp_obj_new_int(csi->pixformat);
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_pixformat_obj, py_omv_csi_get_pixformat); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_pixformat_obj, py_omv_csi_get_pixformat);
static mp_obj_t py_omv_csi_set_framesize(mp_obj_t framesize) { static mp_obj_t py_omv_csi_set_framesize(mp_obj_t framesize) {
int error = omv_csi_set_framesize(mp_obj_get_int(framesize)); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_set_framesize(csi, mp_obj_get_int(framesize));
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -271,15 +290,17 @@ static mp_obj_t py_omv_csi_set_framesize(mp_obj_t framesize) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_framesize_obj, py_omv_csi_set_framesize); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_framesize_obj, py_omv_csi_set_framesize);
static mp_obj_t py_omv_csi_get_framesize() { static mp_obj_t py_omv_csi_get_framesize() {
if (csi.framesize == OMV_CSI_FRAMESIZE_INVALID) { omv_csi_t *csi = omv_csi_get(-1);
if (csi->framesize == OMV_CSI_FRAMESIZE_INVALID) {
omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMESIZE); omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMESIZE);
} }
return mp_obj_new_int(csi.framesize); return mp_obj_new_int(csi->framesize);
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_framesize_obj, py_omv_csi_get_framesize); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_framesize_obj, py_omv_csi_get_framesize);
static mp_obj_t py_omv_csi_set_framerate(mp_obj_t framerate) { static mp_obj_t py_omv_csi_set_framerate(mp_obj_t framerate) {
int error = omv_csi_set_framerate(mp_obj_get_int(framerate)); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_set_framerate(csi, mp_obj_get_int(framerate));
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -288,23 +309,25 @@ static mp_obj_t py_omv_csi_set_framerate(mp_obj_t framerate) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_framerate_obj, py_omv_csi_set_framerate); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_framerate_obj, py_omv_csi_set_framerate);
static mp_obj_t py_omv_csi_get_framerate() { static mp_obj_t py_omv_csi_get_framerate() {
if (csi.framerate == 0) { omv_csi_t *csi = omv_csi_get(-1);
if (csi->framerate == 0) {
omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMERATE); omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMERATE);
} }
return mp_obj_new_int(csi.framerate); return mp_obj_new_int(csi->framerate);
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_framerate_obj, py_omv_csi_get_framerate); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_framerate_obj, py_omv_csi_get_framerate);
static mp_obj_t py_omv_csi_set_windowing(size_t n_args, const mp_obj_t *args) { static mp_obj_t py_omv_csi_set_windowing(size_t n_args, const mp_obj_t *args) {
if (csi.framesize == OMV_CSI_FRAMESIZE_INVALID) { omv_csi_t *csi = omv_csi_get(-1);
if (csi->framesize == OMV_CSI_FRAMESIZE_INVALID) {
omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMESIZE); omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMESIZE);
} }
rectangle_t temp; rectangle_t temp;
temp.x = 0; temp.x = 0;
temp.y = 0; temp.y = 0;
temp.w = resolution[csi.framesize][0]; temp.w = resolution[csi->framesize][0];
temp.h = resolution[csi.framesize][1]; temp.h = resolution[csi->framesize][1];
mp_obj_t *array = (mp_obj_t *) args; mp_obj_t *array = (mp_obj_t *) args;
mp_uint_t array_len = n_args; mp_uint_t array_len = n_args;
@ -339,7 +362,7 @@ static mp_obj_t py_omv_csi_set_windowing(size_t n_args, const mp_obj_t *args) {
rectangle_intersected(&r, &temp); rectangle_intersected(&r, &temp);
int error = omv_csi_set_windowing(r.x, r.y, r.w, r.h); int error = omv_csi_set_windowing(csi, r.x, r.y, r.w, r.h);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -349,18 +372,20 @@ static mp_obj_t py_omv_csi_set_windowing(size_t n_args, const mp_obj_t *args) {
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_set_windowing_obj, 1, 4, py_omv_csi_set_windowing); static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_set_windowing_obj, 1, 4, py_omv_csi_set_windowing);
static mp_obj_t py_omv_csi_get_windowing() { static mp_obj_t py_omv_csi_get_windowing() {
if (csi.framesize == OMV_CSI_FRAMESIZE_INVALID) { omv_csi_t *csi = omv_csi_get(-1);
if (csi->framesize == OMV_CSI_FRAMESIZE_INVALID) {
omv_csi_raise_error(OMV_CSI_ERROR_INVALID_FRAMESIZE); 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(csi.fb)), 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_y(csi->fb)),
mp_obj_new_int(framebuffer_get_u(csi.fb)), mp_obj_new_int(framebuffer_get_u(csi->fb)),
mp_obj_new_int(framebuffer_get_v(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); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_windowing_obj, py_omv_csi_get_windowing);
static mp_obj_t py_omv_csi_set_gainceiling(mp_obj_t gainceiling) { static mp_obj_t py_omv_csi_set_gainceiling(mp_obj_t gainceiling) {
omv_csi_t *csi = omv_csi_get(-1);
omv_csi_gainceiling_t gain; omv_csi_gainceiling_t gain;
switch (mp_obj_get_int(gainceiling)) { switch (mp_obj_get_int(gainceiling)) {
case 2: case 2:
@ -389,7 +414,7 @@ static mp_obj_t py_omv_csi_set_gainceiling(mp_obj_t gainceiling) {
break; break;
} }
if (omv_csi_set_gainceiling(gain) != 0) { if (omv_csi_set_gainceiling(csi, gain) != 0) {
return mp_const_false; return mp_const_false;
} }
return mp_const_true; return mp_const_true;
@ -397,7 +422,8 @@ static mp_obj_t py_omv_csi_set_gainceiling(mp_obj_t gainceiling) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_gainceiling_obj, py_omv_csi_set_gainceiling); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_gainceiling_obj, py_omv_csi_set_gainceiling);
static mp_obj_t py_omv_csi_set_brightness(mp_obj_t brightness) { static mp_obj_t py_omv_csi_set_brightness(mp_obj_t brightness) {
if (omv_csi_set_brightness(mp_obj_get_int(brightness)) != 0) { omv_csi_t *csi = omv_csi_get(-1);
if (omv_csi_set_brightness(csi, mp_obj_get_int(brightness)) != 0) {
return mp_const_false; return mp_const_false;
} }
return mp_const_true; return mp_const_true;
@ -405,7 +431,8 @@ static mp_obj_t py_omv_csi_set_brightness(mp_obj_t brightness) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_brightness_obj, py_omv_csi_set_brightness); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_brightness_obj, py_omv_csi_set_brightness);
static mp_obj_t py_omv_csi_set_contrast(mp_obj_t contrast) { static mp_obj_t py_omv_csi_set_contrast(mp_obj_t contrast) {
if (omv_csi_set_contrast(mp_obj_get_int(contrast)) != 0) { omv_csi_t *csi = omv_csi_get(-1);
if (omv_csi_set_contrast(csi, mp_obj_get_int(contrast)) != 0) {
return mp_const_false; return mp_const_false;
} }
return mp_const_true; return mp_const_true;
@ -413,7 +440,8 @@ static mp_obj_t py_omv_csi_set_contrast(mp_obj_t contrast) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_contrast_obj, py_omv_csi_set_contrast); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_contrast_obj, py_omv_csi_set_contrast);
static mp_obj_t py_omv_csi_set_saturation(mp_obj_t saturation) { static mp_obj_t py_omv_csi_set_saturation(mp_obj_t saturation) {
if (omv_csi_set_saturation(mp_obj_get_int(saturation)) != 0) { omv_csi_t *csi = omv_csi_get(-1);
if (omv_csi_set_saturation(csi, mp_obj_get_int(saturation)) != 0) {
return mp_const_false; return mp_const_false;
} }
return mp_const_true; return mp_const_true;
@ -421,12 +449,13 @@ static mp_obj_t py_omv_csi_set_saturation(mp_obj_t saturation) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_saturation_obj, py_omv_csi_set_saturation); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_saturation_obj, py_omv_csi_set_saturation);
static mp_obj_t py_omv_csi_set_quality(mp_obj_t qs) { static mp_obj_t py_omv_csi_set_quality(mp_obj_t qs) {
omv_csi_t *csi = omv_csi_get(-1);
int q = mp_obj_get_int(qs); int q = mp_obj_get_int(qs);
PY_ASSERT_TRUE((q >= 0 && q <= 100)); PY_ASSERT_TRUE((q >= 0 && q <= 100));
q = 100 - q; //invert quality q = 100 - q; //invert quality
q = 255 * q / 100; //map to 0->255 q = 255 * q / 100; //map to 0->255
if (omv_csi_set_quality(q) != 0) { if (omv_csi_set_quality(csi, q) != 0) {
return mp_const_false; return mp_const_false;
} }
return mp_const_true; return mp_const_true;
@ -434,7 +463,8 @@ static mp_obj_t py_omv_csi_set_quality(mp_obj_t qs) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_quality_obj, py_omv_csi_set_quality); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_quality_obj, py_omv_csi_set_quality);
static mp_obj_t py_omv_csi_set_colorbar(mp_obj_t enable) { static mp_obj_t py_omv_csi_set_colorbar(mp_obj_t enable) {
if (omv_csi_set_colorbar(mp_obj_is_true(enable)) != 0) { omv_csi_t *csi = omv_csi_get(-1);
if (omv_csi_set_colorbar(csi, mp_obj_is_true(enable)) != 0) {
return mp_const_false; return mp_const_false;
} }
return mp_const_true; return mp_const_true;
@ -449,6 +479,7 @@ static mp_obj_t py_omv_csi_set_auto_gain(size_t n_args, const mp_obj_t *pos_args
}; };
// Parse args. // Parse args.
omv_csi_t *csi = omv_csi_get(-1);
int enable = mp_obj_get_int(pos_args[0]); int enable = mp_obj_get_int(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 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); mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@ -456,7 +487,7 @@ static mp_obj_t py_omv_csi_set_auto_gain(size_t n_args, const mp_obj_t *pos_args
float gain_db = py_helper_arg_to_float(args[ARG_gain_db].u_obj, NAN); float gain_db = py_helper_arg_to_float(args[ARG_gain_db].u_obj, NAN);
float gain_db_ceiling = py_helper_arg_to_float(args[ARG_gain_db_ceiling].u_obj, NAN); float gain_db_ceiling = py_helper_arg_to_float(args[ARG_gain_db_ceiling].u_obj, NAN);
int error = omv_csi_set_auto_gain(enable, gain_db, gain_db_ceiling); int error = omv_csi_set_auto_gain(csi, enable, gain_db, gain_db_ceiling);
if (error != 0) { if (error != 0) {
if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) { if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
@ -468,8 +499,9 @@ static mp_obj_t py_omv_csi_set_auto_gain(size_t n_args, const mp_obj_t *pos_args
static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_set_auto_gain_obj, 1, py_omv_csi_set_auto_gain); static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_set_auto_gain_obj, 1, py_omv_csi_set_auto_gain);
static mp_obj_t py_omv_csi_get_gain_db() { static mp_obj_t py_omv_csi_get_gain_db() {
omv_csi_t *csi = omv_csi_get(-1);
float gain_db; float gain_db;
int error = omv_csi_get_gain_db(&gain_db); int error = omv_csi_get_gain_db(csi, &gain_db);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -484,11 +516,12 @@ static mp_obj_t py_omv_csi_set_auto_exposure(size_t n_args, const mp_obj_t *pos_
}; };
// Parse args. // Parse args.
omv_csi_t *csi = omv_csi_get(-1);
int enable = mp_obj_get_int(pos_args[0]); int enable = mp_obj_get_int(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 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); mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
int error = omv_csi_set_auto_exposure(enable, args[ARG_exposure_us].u_int); int error = omv_csi_set_auto_exposure(csi, enable, args[ARG_exposure_us].u_int);
if (error != 0) { if (error != 0) {
if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) { if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
@ -501,7 +534,8 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_set_auto_exposure_obj, 1, py_omv_cs
static mp_obj_t py_omv_csi_get_exposure_us() { static mp_obj_t py_omv_csi_get_exposure_us() {
int exposure_us; int exposure_us;
int error = omv_csi_get_exposure_us(&exposure_us); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_get_exposure_us(csi, &exposure_us);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -516,6 +550,7 @@ static mp_obj_t py_omv_csi_set_auto_whitebal(size_t n_args, const mp_obj_t *pos_
}; };
// Parse args. // Parse args.
omv_csi_t *csi = omv_csi_get(-1);
int enable = mp_obj_get_int(pos_args[0]); int enable = mp_obj_get_int(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 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); mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@ -523,7 +558,7 @@ static mp_obj_t py_omv_csi_set_auto_whitebal(size_t n_args, const mp_obj_t *pos_
float rgb_gain_db[3] = {NAN, NAN, NAN}; float rgb_gain_db[3] = {NAN, NAN, NAN};
py_helper_arg_to_float_array(args[ARG_rgb_gain_db].u_obj, rgb_gain_db, 3); py_helper_arg_to_float_array(args[ARG_rgb_gain_db].u_obj, rgb_gain_db, 3);
int error = omv_csi_set_auto_whitebal(enable, rgb_gain_db[0], rgb_gain_db[1], rgb_gain_db[2]); int error = omv_csi_set_auto_whitebal(csi, enable, rgb_gain_db[0], rgb_gain_db[1], rgb_gain_db[2]);
if (error != 0) { if (error != 0) {
if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) { if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
@ -536,7 +571,8 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_set_auto_whitebal_obj, 1, py_omv_cs
static mp_obj_t py_omv_csi_get_rgb_gain_db() { static mp_obj_t py_omv_csi_get_rgb_gain_db() {
float r_gain_db = 0.0, g_gain_db = 0.0, b_gain_db = 0.0; float r_gain_db = 0.0, g_gain_db = 0.0, b_gain_db = 0.0;
int error = omv_csi_get_rgb_gain_db(&r_gain_db, &g_gain_db, &b_gain_db); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_get_rgb_gain_db(csi, &r_gain_db, &g_gain_db, &b_gain_db);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -558,18 +594,20 @@ static mp_obj_t py_omv_csi_set_auto_blc(size_t n_args, const mp_obj_t *pos_args,
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
omv_csi_t *csi = omv_csi_get(-1);
int enable = args[ARG_enable].u_int; int enable = args[ARG_enable].u_int;
int regs[csi.blc_size]; int regs[csi->blc_size];
bool regs_present = args[ARG_regs].u_obj != mp_const_none; bool regs_present = args[ARG_regs].u_obj != mp_const_none;
if (regs_present) { if (regs_present) {
mp_obj_t *arg_array; mp_obj_t *arg_array;
mp_obj_get_array_fixed_n(args[ARG_regs].u_obj, csi.blc_size, &arg_array); mp_obj_get_array_fixed_n(args[ARG_regs].u_obj, csi->blc_size, &arg_array);
for (uint32_t i = 0; i < csi.blc_size; i++) { for (uint32_t i = 0; i < csi->blc_size; i++) {
regs[i] = mp_obj_get_int(arg_array[i]); regs[i] = mp_obj_get_int(arg_array[i]);
} }
} }
int error = omv_csi_set_auto_blc(enable, regs_present ? regs : NULL); int error = omv_csi_set_auto_blc(csi, enable, regs_present ? regs : NULL);
if (error != 0) { if (error != 0) {
if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) { if (error != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
@ -581,14 +619,16 @@ static mp_obj_t py_omv_csi_set_auto_blc(size_t n_args, const mp_obj_t *pos_args,
static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_set_auto_blc_obj, 1, py_omv_csi_set_auto_blc); static MP_DEFINE_CONST_FUN_OBJ_KW(py_omv_csi_set_auto_blc_obj, 1, py_omv_csi_set_auto_blc);
static mp_obj_t py_omv_csi_get_blc_regs() { static mp_obj_t py_omv_csi_get_blc_regs() {
int regs[csi.blc_size]; omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_get_blc_regs(regs); int regs[csi->blc_size];
int error = omv_csi_get_blc_regs(csi, regs);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
mp_obj_list_t *l = mp_obj_new_list(csi.blc_size, NULL); mp_obj_list_t *l = mp_obj_new_list(csi->blc_size, NULL);
for (uint32_t i = 0; i < csi.blc_size; i++) { for (uint32_t i = 0; i < csi->blc_size; i++) {
l->items[i] = mp_obj_new_int(regs[i]); l->items[i] = mp_obj_new_int(regs[i]);
} }
return l; return l;
@ -596,7 +636,8 @@ static mp_obj_t py_omv_csi_get_blc_regs() {
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_blc_regs_obj, py_omv_csi_get_blc_regs); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_blc_regs_obj, py_omv_csi_get_blc_regs);
static mp_obj_t py_omv_csi_set_hmirror(mp_obj_t enable) { static mp_obj_t py_omv_csi_set_hmirror(mp_obj_t enable) {
int error = omv_csi_set_hmirror(mp_obj_is_true(enable)); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_set_hmirror(csi, mp_obj_is_true(enable));
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -605,12 +646,14 @@ static mp_obj_t py_omv_csi_set_hmirror(mp_obj_t enable) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_hmirror_obj, py_omv_csi_set_hmirror); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_hmirror_obj, py_omv_csi_set_hmirror);
static mp_obj_t py_omv_csi_get_hmirror() { static mp_obj_t py_omv_csi_get_hmirror() {
return mp_obj_new_bool(omv_csi_get_hmirror()); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_bool(omv_csi_get_hmirror(csi));
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_hmirror_obj, py_omv_csi_get_hmirror); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_hmirror_obj, py_omv_csi_get_hmirror);
static mp_obj_t py_omv_csi_set_vflip(mp_obj_t enable) { static mp_obj_t py_omv_csi_set_vflip(mp_obj_t enable) {
int error = omv_csi_set_vflip(mp_obj_is_true(enable)); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_set_vflip(csi, mp_obj_is_true(enable));
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -619,12 +662,14 @@ static mp_obj_t py_omv_csi_set_vflip(mp_obj_t enable) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_vflip_obj, py_omv_csi_set_vflip); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_vflip_obj, py_omv_csi_set_vflip);
static mp_obj_t py_omv_csi_get_vflip() { static mp_obj_t py_omv_csi_get_vflip() {
return mp_obj_new_bool(omv_csi_get_vflip()); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_bool(omv_csi_get_vflip(csi));
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_vflip_obj, py_omv_csi_get_vflip); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_vflip_obj, py_omv_csi_get_vflip);
static mp_obj_t py_omv_csi_set_transpose(mp_obj_t enable) { static mp_obj_t py_omv_csi_set_transpose(mp_obj_t enable) {
int error = omv_csi_set_transpose(mp_obj_is_true(enable)); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_set_transpose(csi, mp_obj_is_true(enable));
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -633,12 +678,14 @@ static mp_obj_t py_omv_csi_set_transpose(mp_obj_t enable) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_transpose_obj, py_omv_csi_set_transpose); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_transpose_obj, py_omv_csi_set_transpose);
static mp_obj_t py_omv_csi_get_transpose() { static mp_obj_t py_omv_csi_get_transpose() {
return mp_obj_new_bool(omv_csi_get_transpose()); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_bool(omv_csi_get_transpose(csi));
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_transpose_obj, py_omv_csi_get_transpose); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_transpose_obj, py_omv_csi_get_transpose);
static mp_obj_t py_omv_csi_set_auto_rotation(mp_obj_t enable) { static mp_obj_t py_omv_csi_set_auto_rotation(mp_obj_t enable) {
int error = omv_csi_set_auto_rotation(mp_obj_is_true(enable)); omv_csi_t *csi = omv_csi_get(-1);
int error = omv_csi_set_auto_rotation(csi, mp_obj_is_true(enable));
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -647,15 +694,16 @@ static mp_obj_t py_omv_csi_set_auto_rotation(mp_obj_t enable) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_auto_rotation_obj, py_omv_csi_set_auto_rotation); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_auto_rotation_obj, py_omv_csi_set_auto_rotation);
static mp_obj_t py_omv_csi_get_auto_rotation() { static mp_obj_t py_omv_csi_get_auto_rotation() {
return mp_obj_new_bool(omv_csi_get_auto_rotation()); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_bool(omv_csi_get_auto_rotation(csi));
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_auto_rotation_obj, py_omv_csi_get_auto_rotation); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_auto_rotation_obj, py_omv_csi_get_auto_rotation);
static mp_obj_t py_omv_csi_set_framebuffers(mp_obj_t count) { static mp_obj_t py_omv_csi_set_framebuffers(mp_obj_t count) {
omv_csi_t *csi = omv_csi_get(-1);
mp_int_t c = mp_obj_get_int(count); mp_int_t c = mp_obj_get_int(count);
framebuffer_t *fb = framebuffer_get(0);
if (fb->n_buffers == c) { if (c == csi->fb->n_buffers) {
return mp_const_none; return mp_const_none;
} }
@ -663,7 +711,7 @@ static mp_obj_t py_omv_csi_set_framebuffers(mp_obj_t count) {
omv_csi_raise_error(OMV_CSI_ERROR_INVALID_ARGUMENT); omv_csi_raise_error(OMV_CSI_ERROR_INVALID_ARGUMENT);
} }
int error = omv_csi_set_framebuffers(c); int error = omv_csi_set_framebuffers(csi, c);
if (error != 0) { if (error != 0) {
omv_csi_raise_error(error); omv_csi_raise_error(error);
} }
@ -673,33 +721,36 @@ 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_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() { static mp_obj_t py_omv_csi_get_framebuffers() {
framebuffer_t *fb = framebuffer_get(0); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_int(fb->n_buffers); return mp_obj_new_int(csi->fb->n_buffers);
} }
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_framebuffers_obj, py_omv_csi_get_framebuffers); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_framebuffers_obj, py_omv_csi_get_framebuffers);
static mp_obj_t py_omv_csi_disable_delays(size_t n_args, const mp_obj_t *args) { static mp_obj_t py_omv_csi_disable_delays(size_t n_args, const mp_obj_t *args) {
omv_csi_t *csi = omv_csi_get(-1);
if (!n_args) { if (!n_args) {
return mp_obj_new_bool(csi.disable_delays); return mp_obj_new_bool(csi->disable_delays);
} }
csi.disable_delays = mp_obj_get_int(args[0]); csi->disable_delays = mp_obj_get_int(args[0]);
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_disable_delays_obj, 0, 1, py_omv_csi_disable_delays); static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_disable_delays_obj, 0, 1, py_omv_csi_disable_delays);
static mp_obj_t py_omv_csi_disable_full_flush(size_t n_args, const mp_obj_t *args) { static mp_obj_t py_omv_csi_disable_full_flush(size_t n_args, const mp_obj_t *args) {
omv_csi_t *csi = omv_csi_get(-1);
if (!n_args) { if (!n_args) {
return mp_obj_new_bool(csi.disable_full_flush); return mp_obj_new_bool(csi->disable_full_flush);
} }
csi.disable_full_flush = mp_obj_get_int(args[0]); csi->disable_full_flush = mp_obj_get_int(args[0]);
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_disable_full_flush_obj, 0, 1, py_omv_csi_disable_full_flush); static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_disable_full_flush_obj, 0, 1, py_omv_csi_disable_full_flush);
static mp_obj_t py_omv_csi_set_special_effect(mp_obj_t sde) { static mp_obj_t py_omv_csi_set_special_effect(mp_obj_t sde) {
if (omv_csi_set_special_effect(mp_obj_get_int(sde)) != 0) { omv_csi_t *csi = omv_csi_get(-1);
if (omv_csi_set_special_effect(csi, mp_obj_get_int(sde)) != 0) {
return mp_const_false; return mp_const_false;
} }
return mp_const_true; return mp_const_true;
@ -707,7 +758,8 @@ static mp_obj_t py_omv_csi_set_special_effect(mp_obj_t sde) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_special_effect_obj, py_omv_csi_set_special_effect); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_special_effect_obj, py_omv_csi_set_special_effect);
static mp_obj_t py_omv_csi_set_lens_correction(mp_obj_t enable, mp_obj_t radi, mp_obj_t coef) { static mp_obj_t py_omv_csi_set_lens_correction(mp_obj_t enable, mp_obj_t radi, mp_obj_t coef) {
if (omv_csi_set_lens_correction(mp_obj_is_true(enable), omv_csi_t *csi = omv_csi_get(-1);
if (omv_csi_set_lens_correction(csi, mp_obj_is_true(enable),
mp_obj_get_int(radi), mp_obj_get_int(coef)) != 0) { mp_obj_get_int(radi), mp_obj_get_int(coef)) != 0) {
return mp_const_false; return mp_const_false;
} }
@ -715,45 +767,58 @@ static mp_obj_t py_omv_csi_set_lens_correction(mp_obj_t enable, mp_obj_t radi, m
} }
static MP_DEFINE_CONST_FUN_OBJ_3(py_omv_csi_set_lens_correction_obj, py_omv_csi_set_lens_correction); static MP_DEFINE_CONST_FUN_OBJ_3(py_omv_csi_set_lens_correction_obj, py_omv_csi_set_lens_correction);
static void omv_csi_vsync_callback(uint32_t vsync) { static void omv_csi_vsync_callback(void *data) {
if (mp_obj_is_callable(vsync_callback)) { if (mp_obj_is_callable(vsync_callback)) {
mp_call_function_1(vsync_callback, mp_obj_new_int(vsync)); uint32_t vsync_state = 0;
#ifdef OMV_CSI_VSYNC_PIN
vsync_state = omv_gpio_read(OMV_CSI_VSYNC_PIN);
#endif
mp_call_function_1(vsync_callback, mp_obj_new_int(vsync_state));
} }
} }
static mp_obj_t py_omv_csi_set_vsync_callback(mp_obj_t vsync_callback_obj) { static mp_obj_t py_omv_csi_set_vsync_callback(mp_obj_t vsync_callback_obj) {
omv_csi_cb_t cb;
omv_csi_t *csi = omv_csi_get(-1);
if (!mp_obj_is_callable(vsync_callback_obj)) { if (!mp_obj_is_callable(vsync_callback_obj)) {
vsync_callback = mp_const_none; vsync_callback = mp_const_none;
omv_csi_set_vsync_callback(NULL); cb = (omv_csi_cb_t) { NULL, NULL };
} else { } else {
vsync_callback = vsync_callback_obj; vsync_callback = vsync_callback_obj;
omv_csi_set_vsync_callback(omv_csi_vsync_callback); cb = (omv_csi_cb_t) { omv_csi_vsync_callback, NULL };
} }
omv_csi_set_vsync_callback(csi, cb);
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_vsync_callback_obj, py_omv_csi_set_vsync_callback); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_vsync_callback_obj, py_omv_csi_set_vsync_callback);
static void omv_csi_frame_callback() { static void omv_csi_frame_callback(void *data) {
if (mp_obj_is_callable(frame_callback)) { if (mp_obj_is_callable(frame_callback)) {
mp_call_function_0(frame_callback); mp_call_function_0(frame_callback);
} }
} }
static mp_obj_t py_omv_csi_set_frame_callback(mp_obj_t frame_callback_obj) { static mp_obj_t py_omv_csi_set_frame_callback(mp_obj_t frame_callback_obj) {
omv_csi_cb_t cb;
omv_csi_t *csi = omv_csi_get(-1);
if (!mp_obj_is_callable(frame_callback_obj)) { if (!mp_obj_is_callable(frame_callback_obj)) {
frame_callback = mp_const_none; frame_callback = mp_const_none;
omv_csi_set_frame_callback(NULL); cb = (omv_csi_cb_t) { NULL, NULL };
} else { } else {
frame_callback = frame_callback_obj; frame_callback = frame_callback_obj;
omv_csi_set_frame_callback(omv_csi_frame_callback); cb = (omv_csi_cb_t) { omv_csi_frame_callback, NULL };
} }
omv_csi_set_frame_callback(csi, cb);
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_frame_callback_obj, py_omv_csi_set_frame_callback); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_frame_callback_obj, py_omv_csi_set_frame_callback);
static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) { static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
omv_csi_t *csi = omv_csi_get(-1);
mp_obj_t ret_obj = mp_const_none; mp_obj_t ret_obj = mp_const_none;
int request = mp_obj_get_int(args[0]); int request = mp_obj_get_int(args[0]);
int error = OMV_CSI_ERROR_INVALID_ARGUMENT; int error = OMV_CSI_ERROR_INVALID_ARGUMENT;
@ -781,14 +846,14 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
MP_ERROR_TEXT("The tuple/list must either be (x, y, w, h) or (w, h)")); MP_ERROR_TEXT("The tuple/list must either be (x, y, w, h) or (w, h)"));
} }
error = omv_csi_ioctl(request, x, y, w, h); error = omv_csi_ioctl(csi, request, x, y, w, h);
} }
break; break;
} }
case OMV_CSI_IOCTL_GET_READOUT_WINDOW: { case OMV_CSI_IOCTL_GET_READOUT_WINDOW: {
int x, y, w, h; int x, y, w, h;
error = omv_csi_ioctl(request, &x, &y, &w, &h); error = omv_csi_ioctl(csi, request, &x, &y, &w, &h);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(x), ret_obj = mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(x),
mp_obj_new_int(y), mp_obj_new_int(y),
@ -802,7 +867,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_SET_FOV_WIDE: case OMV_CSI_IOCTL_SET_FOV_WIDE:
case OMV_CSI_IOCTL_SET_NIGHT_MODE: { case OMV_CSI_IOCTL_SET_NIGHT_MODE: {
if (n_args >= 2) { if (n_args >= 2) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]));
} }
break; break;
} }
@ -811,7 +876,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_GET_FOV_WIDE: case OMV_CSI_IOCTL_GET_FOV_WIDE:
case OMV_CSI_IOCTL_GET_NIGHT_MODE: { case OMV_CSI_IOCTL_GET_NIGHT_MODE: {
int enabled; int enabled;
error = omv_csi_ioctl(request, &enabled); error = omv_csi_ioctl(csi, request, &enabled);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_bool(enabled); ret_obj = mp_obj_new_bool(enabled);
} }
@ -822,18 +887,18 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_TRIGGER_AUTO_FOCUS: case OMV_CSI_IOCTL_TRIGGER_AUTO_FOCUS:
case OMV_CSI_IOCTL_PAUSE_AUTO_FOCUS: case OMV_CSI_IOCTL_PAUSE_AUTO_FOCUS:
case OMV_CSI_IOCTL_RESET_AUTO_FOCUS: { case OMV_CSI_IOCTL_RESET_AUTO_FOCUS: {
error = omv_csi_ioctl(request); error = omv_csi_ioctl(csi, request);
break; break;
} }
case OMV_CSI_IOCTL_WAIT_ON_AUTO_FOCUS: { case OMV_CSI_IOCTL_WAIT_ON_AUTO_FOCUS: {
error = omv_csi_ioctl(request, (n_args < 2) ? 5000 : mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, (n_args < 2) ? 5000 : mp_obj_get_int(args[1]));
break; break;
} }
#endif #endif
case OMV_CSI_IOCTL_LEPTON_GET_WIDTH: { case OMV_CSI_IOCTL_LEPTON_GET_WIDTH: {
int width; int width;
error = omv_csi_ioctl(request, &width); error = omv_csi_ioctl(csi, request, &width);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_int(width); ret_obj = mp_obj_new_int(width);
} }
@ -842,7 +907,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_LEPTON_GET_HEIGHT: { case OMV_CSI_IOCTL_LEPTON_GET_HEIGHT: {
int height; int height;
error = omv_csi_ioctl(request, &height); error = omv_csi_ioctl(csi, request, &height);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_int(height); ret_obj = mp_obj_new_int(height);
} }
@ -851,7 +916,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_LEPTON_GET_RADIOMETRY: { case OMV_CSI_IOCTL_LEPTON_GET_RADIOMETRY: {
int radiometry; int radiometry;
error = omv_csi_ioctl(request, &radiometry); error = omv_csi_ioctl(csi, request, &radiometry);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_int(radiometry); ret_obj = mp_obj_new_int(radiometry);
} }
@ -860,7 +925,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_LEPTON_GET_REFRESH: { case OMV_CSI_IOCTL_LEPTON_GET_REFRESH: {
int refresh; int refresh;
error = omv_csi_ioctl(request, &refresh); error = omv_csi_ioctl(csi, request, &refresh);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_int(refresh); ret_obj = mp_obj_new_int(refresh);
} }
@ -869,7 +934,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_LEPTON_GET_RESOLUTION: { case OMV_CSI_IOCTL_LEPTON_GET_RESOLUTION: {
int resolution; int resolution;
error = omv_csi_ioctl(request, &resolution); error = omv_csi_ioctl(csi, request, &resolution);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_int(resolution); ret_obj = mp_obj_new_int(resolution);
} }
@ -878,7 +943,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_LEPTON_RUN_COMMAND: { case OMV_CSI_IOCTL_LEPTON_RUN_COMMAND: {
if (n_args >= 2) { if (n_args >= 2) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]));
} }
break; break;
} }
@ -889,7 +954,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
int command = mp_obj_get_int(args[1]); int command = mp_obj_get_int(args[1]);
uint16_t *data = (uint16_t *) mp_obj_str_get_data(args[2], &data_len); uint16_t *data = (uint16_t *) mp_obj_str_get_data(args[2], &data_len);
PY_ASSERT_TRUE_MSG(data_len > 0, "0 bytes transferred!"); PY_ASSERT_TRUE_MSG(data_len > 0, "0 bytes transferred!");
error = omv_csi_ioctl(request, command, data, data_len / sizeof(uint16_t)); error = omv_csi_ioctl(csi, request, command, data, data_len / sizeof(uint16_t));
} }
break; break;
} }
@ -900,7 +965,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
size_t data_len = mp_obj_get_int(args[2]); size_t data_len = mp_obj_get_int(args[2]);
PY_ASSERT_TRUE_MSG(data_len > 0, "0 bytes transferred!"); PY_ASSERT_TRUE_MSG(data_len > 0, "0 bytes transferred!");
uint16_t *data = xalloc(data_len * sizeof(uint16_t)); uint16_t *data = xalloc(data_len * sizeof(uint16_t));
error = omv_csi_ioctl(request, command, data, data_len); error = omv_csi_ioctl(csi, request, command, data, data_len);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_bytearray_by_ref(data_len * sizeof(uint16_t), data); ret_obj = mp_obj_new_bytearray_by_ref(data_len * sizeof(uint16_t), data);
} }
@ -911,7 +976,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_LEPTON_GET_FPA_TEMP: case OMV_CSI_IOCTL_LEPTON_GET_FPA_TEMP:
case OMV_CSI_IOCTL_LEPTON_GET_AUX_TEMP: { case OMV_CSI_IOCTL_LEPTON_GET_AUX_TEMP: {
int temp; int temp;
error = omv_csi_ioctl(request, &temp); error = omv_csi_ioctl(csi, request, &temp);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_float((((float) temp) / 100) - 273.15f); ret_obj = mp_obj_new_float((((float) temp) / 100) - 273.15f);
} }
@ -921,13 +986,13 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_LEPTON_SET_MODE: case OMV_CSI_IOCTL_LEPTON_SET_MODE:
if (n_args >= 2) { if (n_args >= 2) {
int high_temp = (n_args == 2) ? false : mp_obj_get_int(args[2]); int high_temp = (n_args == 2) ? false : mp_obj_get_int(args[2]);
error = omv_csi_ioctl(request, mp_obj_get_int(args[1]), high_temp); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]), high_temp);
} }
break; break;
case OMV_CSI_IOCTL_LEPTON_GET_MODE: { case OMV_CSI_IOCTL_LEPTON_GET_MODE: {
int enabled, high_temp; int enabled, high_temp;
error = omv_csi_ioctl(request, &enabled, &high_temp); error = omv_csi_ioctl(csi, request, &enabled, &high_temp);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_bool(enabled), mp_obj_new_bool(high_temp)}); ret_obj = mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_bool(enabled), mp_obj_new_bool(high_temp)});
} }
@ -939,13 +1004,13 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
// GCC will not let us pass floats to ... so we have to pass float pointers instead. // GCC will not let us pass floats to ... so we have to pass float pointers instead.
float min = mp_obj_get_float(args[1]); float min = mp_obj_get_float(args[1]);
float max = mp_obj_get_float(args[2]); float max = mp_obj_get_float(args[2]);
error = omv_csi_ioctl(request, &min, &max); error = omv_csi_ioctl(csi, request, &min, &max);
} }
break; break;
case OMV_CSI_IOCTL_LEPTON_GET_RANGE: { case OMV_CSI_IOCTL_LEPTON_GET_RANGE: {
float min, max; float min, max;
error = omv_csi_ioctl(request, &min, &max); error = omv_csi_ioctl(csi, request, &min, &max);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_float(min), mp_obj_new_float(max)}); ret_obj = mp_obj_new_tuple(2, (mp_obj_t []) {mp_obj_new_float(min), mp_obj_new_float(max)});
} }
@ -955,7 +1020,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
#if (OMV_HM01B0_ENABLE == 1) #if (OMV_HM01B0_ENABLE == 1)
case OMV_CSI_IOCTL_HIMAX_MD_ENABLE: { case OMV_CSI_IOCTL_HIMAX_MD_ENABLE: {
if (n_args >= 2) { if (n_args >= 2) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]));
} }
break; break;
} }
@ -982,26 +1047,26 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
MP_ERROR_TEXT("The tuple/list must either be (x, y, w, h) or (w, h)")); MP_ERROR_TEXT("The tuple/list must either be (x, y, w, h) or (w, h)"));
} }
error = omv_csi_ioctl(request, x, y, w, h); error = omv_csi_ioctl(csi, request, x, y, w, h);
} }
break; break;
} }
case OMV_CSI_IOCTL_HIMAX_MD_THRESHOLD: { case OMV_CSI_IOCTL_HIMAX_MD_THRESHOLD: {
if (n_args >= 2) { if (n_args >= 2) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]));
} }
break; break;
} }
case OMV_CSI_IOCTL_HIMAX_MD_CLEAR: { case OMV_CSI_IOCTL_HIMAX_MD_CLEAR: {
error = omv_csi_ioctl(request); error = omv_csi_ioctl(csi, request);
break; break;
} }
case OMV_CSI_IOCTL_HIMAX_OSC_ENABLE: { case OMV_CSI_IOCTL_HIMAX_OSC_ENABLE: {
if (n_args >= 2) { if (n_args >= 2) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]));
} }
break; break;
} }
@ -1009,7 +1074,7 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
case OMV_CSI_IOCTL_GET_RGB_STATS: { case OMV_CSI_IOCTL_GET_RGB_STATS: {
uint32_t r, gb, gr, b; uint32_t r, gb, gr, b;
error = omv_csi_ioctl(request, &r, &gb, &gr, &b); error = omv_csi_ioctl(csi, request, &r, &gb, &gr, &b);
if (error == 0) { if (error == 0) {
ret_obj = mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(r), ret_obj = mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(r),
mp_obj_new_int(gb), mp_obj_new_int(gb),
@ -1022,21 +1087,21 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
#if (OMV_GENX320_ENABLE == 1) #if (OMV_GENX320_ENABLE == 1)
case OMV_CSI_IOCTL_GENX320_SET_BIASES: { case OMV_CSI_IOCTL_GENX320_SET_BIASES: {
if (n_args == 2) { if (n_args == 2) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]));
} }
break; break;
} }
case OMV_CSI_IOCTL_GENX320_SET_BIAS: { case OMV_CSI_IOCTL_GENX320_SET_BIAS: {
if (n_args == 3) { if (n_args == 3) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1]), mp_obj_get_int(args[2])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]), mp_obj_get_int(args[2]));
} }
break; break;
} }
case OMV_CSI_IOCTL_GENX320_SET_AFK: { case OMV_CSI_IOCTL_GENX320_SET_AFK: {
if (n_args == 2) { if (n_args == 2) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1])); error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]));
} else if (n_args == 4) { } else if (n_args == 4) {
error = omv_csi_ioctl(request, mp_obj_get_int(args[1]), mp_obj_get_int(args[2]), error = omv_csi_ioctl(csi, request, mp_obj_get_int(args[1]), mp_obj_get_int(args[2]),
mp_obj_get_int(args[3])); mp_obj_get_int(args[3]));
} }
break; break;
@ -1058,25 +1123,26 @@ static mp_obj_t py_omv_csi_ioctl(size_t n_args, const mp_obj_t *args) {
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_ioctl_obj, 1, 5, py_omv_csi_ioctl); static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_omv_csi_ioctl_obj, 1, 5, py_omv_csi_ioctl);
static mp_obj_t py_omv_csi_set_color_palette(mp_obj_t palette_obj) { static mp_obj_t py_omv_csi_set_color_palette(mp_obj_t palette_obj) {
omv_csi_t *csi = omv_csi_get(-1);
int palette = mp_obj_get_int(palette_obj); int palette = mp_obj_get_int(palette_obj);
switch (palette) { switch (palette) {
case COLOR_PALETTE_RAINBOW: case COLOR_PALETTE_RAINBOW:
omv_csi_set_color_palette(rainbow_table); omv_csi_set_color_palette(csi, rainbow_table);
break; break;
case COLOR_PALETTE_IRONBOW: case COLOR_PALETTE_IRONBOW:
omv_csi_set_color_palette(ironbow_table); omv_csi_set_color_palette(csi, ironbow_table);
break; break;
#if (MICROPY_PY_TOF == 1) #if (MICROPY_PY_TOF == 1)
case COLOR_PALETTE_DEPTH: case COLOR_PALETTE_DEPTH:
omv_csi_set_color_palette(depth_table); omv_csi_set_color_palette(csi, depth_table);
break; break;
#endif // MICROPY_PY_TOF == 1 #endif // MICROPY_PY_TOF == 1
#if (OMV_GENX320_ENABLE == 1) #if (OMV_GENX320_ENABLE == 1)
case COLOR_PALETTE_EVT_DARK: case COLOR_PALETTE_EVT_DARK:
omv_csi_set_color_palette(evt_dark_table); omv_csi_set_color_palette(csi, evt_dark_table);
break; break;
case COLOR_PALETTE_EVT_LIGHT: case COLOR_PALETTE_EVT_LIGHT:
omv_csi_set_color_palette(evt_light_table); omv_csi_set_color_palette(csi, evt_light_table);
break; break;
#endif // OMV_GENX320_ENABLE == 1 #endif // OMV_GENX320_ENABLE == 1
default: default:
@ -1088,7 +1154,8 @@ static mp_obj_t py_omv_csi_set_color_palette(mp_obj_t palette_obj) {
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_color_palette_obj, py_omv_csi_set_color_palette); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_set_color_palette_obj, py_omv_csi_set_color_palette);
static mp_obj_t py_omv_csi_get_color_palette() { static mp_obj_t py_omv_csi_get_color_palette() {
const uint16_t *palette = omv_csi_get_color_palette(); omv_csi_t *csi = omv_csi_get(-1);
const uint16_t *palette = omv_csi_get_color_palette(csi);
if (palette == rainbow_table) { if (palette == rainbow_table) {
return mp_obj_new_int(COLOR_PALETTE_RAINBOW); return mp_obj_new_int(COLOR_PALETTE_RAINBOW);
} else if (palette == ironbow_table) { } else if (palette == ironbow_table) {
@ -1109,13 +1176,15 @@ static mp_obj_t py_omv_csi_get_color_palette() {
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_color_palette_obj, py_omv_csi_get_color_palette); static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_color_palette_obj, py_omv_csi_get_color_palette);
static mp_obj_t py_omv_csi_write_reg(mp_obj_t addr, mp_obj_t val) { static mp_obj_t py_omv_csi_write_reg(mp_obj_t addr, mp_obj_t val) {
omv_csi_write_reg(mp_obj_get_int(addr), mp_obj_get_int(val)); omv_csi_t *csi = omv_csi_get(-1);
omv_csi_write_reg(csi, mp_obj_get_int(addr), mp_obj_get_int(val));
return mp_const_none; return mp_const_none;
} }
static MP_DEFINE_CONST_FUN_OBJ_2(py_omv_csi_write_reg_obj, py_omv_csi_write_reg); static MP_DEFINE_CONST_FUN_OBJ_2(py_omv_csi_write_reg_obj, py_omv_csi_write_reg);
static mp_obj_t py_omv_csi_read_reg(mp_obj_t addr) { static mp_obj_t py_omv_csi_read_reg(mp_obj_t addr) {
return mp_obj_new_int(omv_csi_read_reg(mp_obj_get_int(addr))); omv_csi_t *csi = omv_csi_get(-1);
return mp_obj_new_int(omv_csi_read_reg(csi, mp_obj_get_int(addr)));
} }
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_read_reg_obj, py_omv_csi_read_reg); static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_read_reg_obj, py_omv_csi_read_reg);
@ -1323,6 +1392,5 @@ const mp_obj_module_t omv_csi_module = {
.globals = (mp_obj_t) &globals_dict, .globals = (mp_obj_t) &globals_dict,
}; };
MP_REGISTER_MODULE(MP_QSTR_csi, omv_csi_module);
MP_REGISTER_MODULE(MP_QSTR_sensor, omv_csi_module); MP_REGISTER_MODULE(MP_QSTR_sensor, omv_csi_module);
#endif // MICROPY_PY_CSI #endif // MICROPY_PY_CSI

1511
modules/py_csi_ng.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -558,11 +558,14 @@ void py_helper_update_framebuffer(image_t *img) {
} }
void py_helper_set_to_framebuffer(image_t *img) { void py_helper_set_to_framebuffer(image_t *img) {
#if MICROPY_PY_CSI
omv_csi_t *csi = omv_csi_get(-1);
framebuffer_t *fb = csi->fb;
omv_csi_set_framebuffers(csi, 1);
#else
framebuffer_t *fb = framebuffer_get(0); framebuffer_t *fb = framebuffer_get(0);
#if MICROPY_PY_CSI
omv_csi_set_framebuffers(1);
#else
framebuffer_set_buffers(fb, 1); framebuffer_set_buffers(fb, 1);
#endif #endif

View File

@ -112,6 +112,7 @@ MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/alif/tinyusb_port
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/alif/lwip_inc MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/alif/lwip_inc
MPY_CFLAGS += -DMICROPY_PY_CSI=$(MICROPY_PY_CSI) MPY_CFLAGS += -DMICROPY_PY_CSI=$(MICROPY_PY_CSI)
MPY_CFLAGS += -DMICROPY_PY_CSI_NG=$(MICROPY_PY_CSI_NG)
MPY_CFLAGS += -DMICROPY_PY_LWIP=$(MICROPY_PY_LWIP) MPY_CFLAGS += -DMICROPY_PY_LWIP=$(MICROPY_PY_LWIP)
MPY_CFLAGS += -DMICROPY_PY_SSL=$(MICROPY_PY_SSL) MPY_CFLAGS += -DMICROPY_PY_SSL=$(MICROPY_PY_SSL)
MPY_CFLAGS += -DMICROPY_PY_SSL_ECDSA_SIGN_ALT=$(MICROPY_PY_SSL_ECDSA_SIGN_ALT) MPY_CFLAGS += -DMICROPY_PY_SSL_ECDSA_SIGN_ALT=$(MICROPY_PY_SSL_ECDSA_SIGN_ALT)
@ -126,6 +127,7 @@ MPY_MKARGS += MICROPY_FLOAT_IMPL=float
MPY_MKARGS += ALIF_DFP_REL_HERE=$(TOP_DIR)/$(HAL_DIR) MPY_MKARGS += ALIF_DFP_REL_HERE=$(TOP_DIR)/$(HAL_DIR)
MPY_MKARGS += CMSIS_DIR=$(TOP_DIR)/$(HAL_DIR)/cmsis/inc MPY_MKARGS += CMSIS_DIR=$(TOP_DIR)/$(HAL_DIR)/cmsis/inc
MPY_MKARGS += MICROPY_PY_CSI=$(MICROPY_PY_CSI) MPY_MKARGS += MICROPY_PY_CSI=$(MICROPY_PY_CSI)
MPY_MKARGS += MICROPY_PY_CSI_NG=$(MICROPY_PY_CSI_NG)
MPY_MKARGS += MICROPY_PY_LWIP=$(MICROPY_PY_LWIP) MPY_MKARGS += MICROPY_PY_LWIP=$(MICROPY_PY_LWIP)
MPY_MKARGS += MICROPY_PY_SSL=$(MICROPY_PY_SSL) MPY_MKARGS += MICROPY_PY_SSL=$(MICROPY_PY_SSL)
MPY_MKARGS += MICROPY_PY_SSL_ECDSA_SIGN_ALT=$(MICROPY_PY_SSL_ECDSA_SIGN_ALT) MPY_MKARGS += MICROPY_PY_SSL_ECDSA_SIGN_ALT=$(MICROPY_PY_SSL_ECDSA_SIGN_ALT)

View File

@ -71,33 +71,9 @@
CAM_INTR_OUTFIFO_OVERRUN | \ CAM_INTR_OUTFIFO_OVERRUN | \
CAM_INTR_BRESP_ERR) CAM_INTR_BRESP_ERR)
static CPI_Type *cpi_get_base_addr(omv_csi_t *csi) { int alif_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
return ((CPI_Type *) CPI_BASE);
}
void omv_csi_init0() {
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.
if (csi.i2c->initialized) {
// Reinitialize the bus using the last used id and speed.
// TODO: Causes Alif's I3C to lock up.
//omv_i2c_init(csi.i2c, csi.i2c->id, csi.i2c->speed);
}
csi.disable_delays = false;
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
// Disable Frame callback.
omv_csi_set_frame_callback(NULL);
}
int omv_csi_config(omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) { if (config == OMV_CSI_CONFIG_INIT) {
CPI_Type *cpi = cpi_get_base_addr(&csi); CPI_Type *cpi = csi->base;
// Configure the FIFO. // Configure the FIFO.
cpi->CAM_FIFO_CTRL &= ~CAM_FIFO_CTRL_RD_WMARK_Msk; cpi->CAM_FIFO_CTRL &= ~CAM_FIFO_CTRL_RD_WMARK_Msk;
@ -113,9 +89,9 @@ int omv_csi_config(omv_csi_config_t config) {
cpi->CAM_CFG |= (CPI_VSYNC_WAIT_DISABLE << CAM_CFG_VSYNC_WAIT_Pos); cpi->CAM_CFG |= (CPI_VSYNC_WAIT_DISABLE << CAM_CFG_VSYNC_WAIT_Pos);
// Set VSYNC, HSYNC and PIXCLK polarities. // Set VSYNC, HSYNC and PIXCLK polarities.
cpi->CAM_CFG |= (csi.vsync_pol << CAM_CFG_VSYNC_POL_Pos); cpi->CAM_CFG |= (csi->vsync_pol << CAM_CFG_VSYNC_POL_Pos);
cpi->CAM_CFG |= (csi.hsync_pol << CAM_CFG_HSYNC_POL_Pos); cpi->CAM_CFG |= (csi->hsync_pol << CAM_CFG_HSYNC_POL_Pos);
cpi->CAM_CFG |= (!csi.pixck_pol << CAM_CFG_PXCLK_POL_Pos); cpi->CAM_CFG |= (!csi->pixck_pol << CAM_CFG_PXCLK_POL_Pos);
// Configure the data bus width, mode, endianness. // Configure the data bus width, mode, endianness.
cpi->CAM_CFG |= (CPI_ROW_ROUNDUP_DISABLE << CAM_CFG_RW_ROUNDUP_Pos); cpi->CAM_CFG |= (CPI_ROW_ROUNDUP_DISABLE << CAM_CFG_RW_ROUNDUP_Pos);
@ -133,25 +109,16 @@ int omv_csi_config(omv_csi_config_t config) {
return 0; return 0;
} }
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { int alif_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
CPI_Type *cpi = cpi_get_base_addr(csi); CPI_Type *cpi = csi->base;
// Stop CPI
cpi->CAM_CTRL = 0; cpi->CAM_CTRL = 0;
// Disable IRQs.
NVIC_DisableIRQ(CAM_IRQ_IRQn); NVIC_DisableIRQ(CAM_IRQ_IRQn);
cpi_disable_interrupt(cpi, CPI_IRQ_FLAGS); cpi_disable_interrupt(cpi, CPI_IRQ_FLAGS);
cpi_irq_handler_clear_intr_status(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;
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; return 0;
} }
@ -179,26 +146,27 @@ uint32_t omv_csi_get_clk_frequency() {
} }
} }
uint32_t omv_csi_get_fb_offset(omv_csi_t *csi) { static uint32_t omv_csi_get_fb_offset(omv_csi_t *csi) {
uint32_t offset = 0; uint32_t offset = 0;
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi);
uint32_t line_size_bytes = resolution[csi->framesize][0] * bytes_per_pixel; uint32_t line_size_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// Offset the pixels buffer for debayering. // Offset the pixels buffer for debayering.
if (csi->raw_output && csi->pixformat == PIXFORMAT_RGB565) { if (csi->raw_output && csi->pixformat == PIXFORMAT_RGB565) {
offset += line_size_bytes * resolution[csi->framesize][1]; offset += line_size_bytes * resolution[csi->framesize][1];
} }
return offset; return offset;
} }
// This is the default snapshot function, which can be replaced in omv_csi_init functions. // 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) { int alif_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
CPI_Type *cpi = csi->base;
framebuffer_t *fb = csi->fb; framebuffer_t *fb = csi->fb;
static uint32_t frames = 0; static uint32_t frames = 0;
static uint32_t r_stat, gb_stat, gr_stat, b_stat; static uint32_t r_stat, gb_stat, gr_stat, b_stat;
CPI_Type *cpi = cpi_get_base_addr(csi);
if (csi->pixformat == PIXFORMAT_INVALID) { if (csi->pixformat == PIXFORMAT_INVALID) {
return OMV_CSI_ERROR_INVALID_PIXFORMAT; return OMV_CSI_ERROR_INVALID_PIXFORMAT;
} }
@ -207,7 +175,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE; return OMV_CSI_ERROR_INVALID_FRAMESIZE;
} }
if (omv_csi_check_framebuffer_size() != 0) { if (omv_csi_check_framebuffer_size(csi) != 0) {
return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW;
} }
@ -220,7 +188,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
// Reconfigure and restart the CSI transfer if it's not running. // Reconfigure and restart the CSI transfer if it's not running.
if (!(cpi->CAM_CTRL & CAM_CTRL_BUSY)) { if (!(cpi->CAM_CTRL & CAM_CTRL_BUSY)) {
framebuffer_setup_buffers(fb); framebuffer_setup_buffers(fb);
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi);
uint32_t line_size_bytes = resolution[csi->framesize][0] * bytes_per_pixel; uint32_t line_size_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// Error out if the transfer size is not compatible with DMA transfer restrictions. // Error out if the transfer size is not compatible with DMA transfer restrictions.
@ -353,7 +321,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
} }
// Crop first to reduce the frame size before debayering. // Crop first to reduce the frame size before debayering.
if (omv_csi_get_cropped()) { if (omv_csi_get_cropped(csi)) {
image_t src_cimage = *dst_image; image_t src_cimage = *dst_image;
image_t dst_cimage = *dst_image; image_t dst_cimage = *dst_image;
@ -387,7 +355,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
// Update AWB stats every n frames. // Update AWB stats every n frames.
if ((frames++ % 100) == 0) { if ((frames++ % 100) == 0) {
omv_csi_ioctl(OMV_CSI_IOCTL_GET_RGB_STATS, &r_stat, &gb_stat, &gr_stat, &b_stat); omv_csi_ioctl(csi, OMV_CSI_IOCTL_GET_RGB_STATS, &r_stat, &gb_stat, &gr_stat, &b_stat);
} }
// Debayer frame. // Debayer frame.
@ -398,9 +366,10 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
int omv_csi_init() { int omv_csi_init() {
int init_ret = 0; int init_ret = 0;
CPI_Type *cpi = cpi_get_base_addr(&csi); static omv_i2c_t i2c;
CPI_Type *base = ((CPI_Type *) CPI_BASE);
alif_hal_csi_init(cpi, 0); alif_hal_csi_init(base, 0);
#if defined(OMV_CSI_POWER_PIN) #if defined(OMV_CSI_POWER_PIN)
omv_gpio_write(OMV_CSI_POWER_PIN, 0); omv_gpio_write(OMV_CSI_POWER_PIN, 0);
@ -410,17 +379,20 @@ int omv_csi_init() {
omv_gpio_write(OMV_CSI_RESET_PIN, 0); omv_gpio_write(OMV_CSI_RESET_PIN, 0);
#endif #endif
// Reset the csi state // Initialize the CSIs using this driver's ops as defaults,
memset(&csi, 0, sizeof(omv_csi_t)); // which can be overridden by sensor drivers during probe.
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
omv_csi_t *csi = &csi_all[i];
// Set default framebuffer memset(csi, 0, sizeof(omv_csi_t));
csi.fb = framebuffer_get(0); csi->i2c = &i2c;
csi->fb = framebuffer_get(-1);
// Set I2C bus csi->base = base;
csi.i2c = &csi_i2c; csi->abort = alif_csi_abort;
csi->config = alif_csi_config;
// Set default snapshot function. csi->snapshot = alif_csi_snapshot;
csi.snapshot = omv_csi_snapshot; csi->color_palette = rainbow_table;
}
// Configure the CSI external clock. // Configure the CSI external clock.
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) { if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
@ -428,36 +400,37 @@ int omv_csi_init() {
return OMV_CSI_ERROR_TIM_INIT_FAILED; return OMV_CSI_ERROR_TIM_INIT_FAILED;
} }
// Initialize the camera bus.
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
// Detect and initialize the image csi. // Detect and initialize the image csi.
if ((init_ret = omv_csi_probe_init(OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED)) != 0) { if ((init_ret = omv_csi_probe(&i2c)) != 0) {
// csi probe/init failed. // csi probe/init failed.
return init_ret; return init_ret;
} }
// Configure the CSI interface. // Configure the DCMI interface.
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) { for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
// CSI config failed omv_csi_t *csi = &csi_all[i];
return OMV_CSI_ERROR_CSI_INIT_FAILED;
if (omv_csi_config(csi, OMV_CSI_CONFIG_INIT) != 0) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
csi->detected = true;
} }
// Set default color palette. // Clear fb_enabled flag.
csi.color_palette = rainbow_table; JPEG_FB()->enabled = 0;
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
// Disable Frame callback.
omv_csi_set_frame_callback(NULL);
// All good!
csi.detected = true;
return 0; return 0;
} }
void CAM_IRQHandler(void) { void CAM_IRQHandler(void) {
uint32_t mask = 0; uint32_t mask = 0;
CPI_Type *cpi = cpi_get_base_addr(&csi); omv_csi_t *csi = omv_csi_get(-1);
CPI_Type *cpi = csi->base;
uint32_t status = cpi_get_interrupt_status(cpi); uint32_t status = cpi_get_interrupt_status(cpi);
if (status & CAM_INTR_VSYNC) { if (status & CAM_INTR_VSYNC) {
@ -470,19 +443,19 @@ void CAM_IRQHandler(void) {
if (status & CAM_INTR_INFIFO_OVERRUN) { if (status & CAM_INTR_INFIFO_OVERRUN) {
mask |= CAM_INTR_INFIFO_OVERRUN; mask |= CAM_INTR_INFIFO_OVERRUN;
omv_csi_abort(&csi, true, true); omv_csi_abort(csi, true, true);
printf("INFIFO_OVERRUN\n"); printf("INFIFO_OVERRUN\n");
} }
if (status & CAM_INTR_OUTFIFO_OVERRUN) { if (status & CAM_INTR_OUTFIFO_OVERRUN) {
mask |= CAM_INTR_OUTFIFO_OVERRUN; mask |= CAM_INTR_OUTFIFO_OVERRUN;
omv_csi_abort(&csi, true, true); omv_csi_abort(csi, true, true);
printf("OUTFIFO_OVERRUN\n"); printf("OUTFIFO_OVERRUN\n");
} }
if (status & CAM_INTR_BRESP_ERR) { if (status & CAM_INTR_BRESP_ERR) {
mask |= CAM_INTR_BRESP_ERR; mask |= CAM_INTR_BRESP_ERR;
omv_csi_abort(&csi, true, true); omv_csi_abort(csi, true, true);
printf("BRESP_ERR %lu\n", cpi->CAM_AXI_ERR_STAT); printf("BRESP_ERR %lu\n", cpi->CAM_AXI_ERR_STAT);
} }
@ -491,24 +464,22 @@ void CAM_IRQHandler(void) {
cpi->CAM_CTRL = 0; cpi->CAM_CTRL = 0;
if (!(status & CPI_ERROR_FLAGS)) { if (!(status & CPI_ERROR_FLAGS)) {
// Release the current framebuffer. // Release the current framebuffer.
framebuffer_get_tail(csi.fb, FB_NO_FLAGS); framebuffer_get_tail(csi->fb, FB_NO_FLAGS);
} }
// Get the current framebuffer (or new tail). // Get the current framebuffer (or new tail).
vbuffer_t *buffer = framebuffer_get_tail(csi.fb, FB_PEEK); vbuffer_t *buffer = framebuffer_get_tail(csi->fb, FB_PEEK);
if (buffer != NULL) { if (buffer != NULL) {
cpi->CAM_CTRL = 0; cpi->CAM_CTRL = 0;
cpi->CAM_CTRL |= CAM_CTRL_SW_RESET; cpi->CAM_CTRL |= CAM_CTRL_SW_RESET;
cpi->CAM_FRAME_ADDR = LocalToGlobal(buffer->data + omv_csi_get_fb_offset(&csi)); cpi->CAM_FRAME_ADDR = LocalToGlobal(buffer->data + omv_csi_get_fb_offset(csi));
cpi_irq_handler_clear_intr_status(cpi, mask); cpi_irq_handler_clear_intr_status(cpi, mask);
cpi->CAM_CTRL = (CAM_CTRL_SNAPSHOT | CAM_CTRL_START | CAM_CTRL_FIFO_CLK_SEL); cpi->CAM_CTRL = (CAM_CTRL_SNAPSHOT | CAM_CTRL_START | CAM_CTRL_FIFO_CLK_SEL);
} }
if (!(status & CPI_ERROR_FLAGS)) { if (csi->frame_cb.fun && !(status & CPI_ERROR_FLAGS)) {
if (csi.frame_callback) { csi->frame_cb.fun(csi->frame_cb.arg);
csi.frame_callback();
}
} }
} }

View File

@ -38,6 +38,7 @@
#include "i2c.h" #include "i2c.h"
#include "i3c.h" #include "i3c.h"
#include "spi.h" #include "spi.h"
#include "cpi.h"
#include "gpio.h" #include "gpio.h"
#include "pinconf.h" #include "pinconf.h"
@ -127,12 +128,18 @@ struct { \
#define OMV_SPI_MAX_TIMEOUT (0xFFFFFFFF) #define OMV_SPI_MAX_TIMEOUT (0xFFFFFFFF)
#define OMV_SPI_NO_DMA (1) #define OMV_SPI_NO_DMA (1)
#define OMV_SPI_PORT_BITS \ #define OMV_SPI_PORT_BITS \
struct { \ struct { \
SPI_Type *inst; \ SPI_Type *inst; \
bool is_lp; \ bool is_lp; \
uint32_t spi_mode; \ uint32_t spi_mode; \
uint32_t bus_mode; \ uint32_t bus_mode; \
uint8_t datasize; \ uint8_t datasize; \
}; };
#define OMV_CSI_PORT_BITS \
struct { \
CPI_Type *base; \
};
#endif // __OMV_PORTCONFIG_H__ #endif // __OMV_PORTCONFIG_H__

View File

@ -40,6 +40,7 @@
#include "omv_boardconfig.h" #include "omv_boardconfig.h"
// Define pin objects in this file. // Define pin objects in this file.
#define OMV_GPIO_DEFINE_PINS (1) #define OMV_GPIO_DEFINE_PINS (1)
#include "omv_csi.h"
#include "omv_gpio.h" #include "omv_gpio.h"
#include "mimxrt_hal.h" #include "mimxrt_hal.h"
@ -320,9 +321,11 @@ int mimxrt_hal_spi_deinit(uint32_t bus_id, uint32_t bus_mode) {
} }
void CSI_IRQHandler(void) { void CSI_IRQHandler(void) {
omv_csi_t *csi = omv_csi_get(-1);
uint32_t csisr = CSI_REG_SR(CSI); uint32_t csisr = CSI_REG_SR(CSI);
extern void omv_csi_sof_callback();
extern void omv_csi_line_callback(uint32_t); extern void omv_csi_sof_callback(omv_csi_t *csi);
extern void omv_csi_line_callback(omv_csi_t *csi, uint32_t);
// Clear interrupt flags. // Clear interrupt flags.
CSI_REG_SR(CSI) = csisr; CSI_REG_SR(CSI) = csisr;
@ -330,11 +333,11 @@ void CSI_IRQHandler(void) {
if (csisr & CSI_SR_SOF_INT_MASK) { if (csisr & CSI_SR_SOF_INT_MASK) {
// Clear the FIFO and re/enable DMA. // Clear the FIFO and re/enable DMA.
CSI_REG_CR3(CSI) |= (CSI_CR3_DMA_REFLASH_RFF_MASK | CSI_CR3_DMA_REQ_EN_RFF_MASK); CSI_REG_CR3(CSI) |= (CSI_CR3_DMA_REFLASH_RFF_MASK | CSI_CR3_DMA_REQ_EN_RFF_MASK);
omv_csi_sof_callback(); omv_csi_sof_callback(csi);
} else if (csisr & CSI_SR_DMA_TSF_DONE_FB1_MASK) { } else if (csisr & CSI_SR_DMA_TSF_DONE_FB1_MASK) {
omv_csi_line_callback(CSI_REG_DMASA_FB1(CSI)); omv_csi_line_callback(csi, CSI_REG_DMASA_FB1(CSI));
} else if (csisr & CSI_SR_DMA_TSF_DONE_FB2_MASK) { } else if (csisr & CSI_SR_DMA_TSF_DONE_FB2_MASK) {
omv_csi_line_callback(CSI_REG_DMASA_FB2(CSI)); omv_csi_line_callback(csi, CSI_REG_DMASA_FB2(CSI));
} }
// Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate // Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate

View File

@ -54,26 +54,7 @@ extern uint8_t _line_buf[OMV_LINE_BUF_SIZE];
| CSI_CR1_FB2_DMA_DONE_INTEN_MASK \ | CSI_CR1_FB2_DMA_DONE_INTEN_MASK \
| CSI_CR1_FB1_DMA_DONE_INTEN_MASK) | CSI_CR1_FB1_DMA_DONE_INTEN_MASK)
void omv_csi_init0() { int imx_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
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.
if (csi.i2c->initialized) {
// Reinitialize the bus using the last used id and speed.
omv_i2c_init(csi.i2c, csi.i2c->id, csi.i2c->speed);
}
csi.disable_delays = false;
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
// Disable Frame callback.
omv_csi_set_frame_callback(NULL);
}
int omv_csi_config(omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) { if (config == OMV_CSI_CONFIG_INIT) {
CSI_Reset(CSI); CSI_Reset(CSI);
NVIC_DisableIRQ(CSI_IRQn); NVIC_DisableIRQ(CSI_IRQn);
@ -88,9 +69,9 @@ int omv_csi_config(omv_csi_config_t config) {
// Configure VSYNC, HSYNC and PIXCLK signals. // Configure VSYNC, HSYNC and PIXCLK signals.
CSI_REG_CR1(CSI) |= CSI_CR1_EXT_VSYNC_MASK; CSI_REG_CR1(CSI) |= CSI_CR1_EXT_VSYNC_MASK;
CSI_REG_CR1(CSI) |= !csi.vsync_pol ? CSI_CR1_SOF_POL_MASK : 0; CSI_REG_CR1(CSI) |= !csi->vsync_pol ? CSI_CR1_SOF_POL_MASK : 0;
CSI_REG_CR1(CSI) |= !csi.hsync_pol ? CSI_CR1_HSYNC_POL_MASK : 0; CSI_REG_CR1(CSI) |= !csi->hsync_pol ? CSI_CR1_HSYNC_POL_MASK : 0;
CSI_REG_CR1(CSI) |= csi.pixck_pol ? CSI_CR1_REDGE_MASK : 0; CSI_REG_CR1(CSI) |= csi->pixck_pol ? CSI_CR1_REDGE_MASK : 0;
// Stride config: No stride. // Stride config: No stride.
CSI_REG_FBUF_PARA(CSI) = 0; CSI_REG_FBUF_PARA(CSI) = 0;
@ -112,28 +93,19 @@ int omv_csi_config(omv_csi_config_t config) {
return 0; return 0;
} }
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { static int imx_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
NVIC_DisableIRQ(CSI_IRQn); NVIC_DisableIRQ(CSI_IRQn);
CSI_DisableInterrupts(CSI, CSI_IRQ_FLAGS); CSI_DisableInterrupts(CSI, CSI_IRQ_FLAGS);
CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK; CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK;
CSI_REG_CR18(CSI) &= ~CSI_CR18_CSI_ENABLE_MASK; CSI_REG_CR18(CSI) &= ~CSI_CR18_CSI_ENABLE_MASK;
csi->dest_inc = 0; 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; return 0;
} }
uint32_t omv_csi_get_clk_frequency() {
return 24000000 / (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1);
}
int omv_csi_set_clk_frequency(uint32_t frequency) { int omv_csi_set_clk_frequency(uint32_t frequency) {
if (frequency >= 24000000) { if (frequency >= 24000000) {
CLOCK_SetDiv(kCLOCK_CsiDiv, 0); CLOCK_SetDiv(kCLOCK_CsiDiv, 0);
@ -151,46 +123,42 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
return 0; return 0;
} }
uint32_t omv_csi_get_clk_frequency() { void omv_csi_sof_callback(omv_csi_t *csi) {
return 24000000 / (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1); csi->first_line = false;
} csi->drop_frame = false;
void omv_csi_sof_callback() {
csi.first_line = false;
csi.drop_frame = false;
// Get current framebuffer. // Get current framebuffer.
vbuffer_t *buffer = framebuffer_get_tail(csi.fb, FB_PEEK); vbuffer_t *buffer = framebuffer_get_tail(csi->fb, FB_PEEK);
if (buffer == NULL) { if (buffer == NULL) {
omv_csi_abort(&csi, false, true); omv_csi_abort(csi, false, true);
} else if (buffer->offset < resolution[csi.framesize][1]) { } else if (buffer->offset < resolution[csi->framesize][1]) {
// Missed a few lines, reset buffer state and continue. // Missed a few lines, reset buffer state and continue.
buffer->reset_state = true; buffer->reset_state = true;
} }
} }
#if defined(OMV_CSI_DMA) #if defined(OMV_CSI_DMA)
int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed) { int omv_csi_dma_memcpy(omv_csi_t *csi, 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;
// EMDA will not perform burst transfers for anything less than 32-byte chunks of four 64-bit // EMDA will not perform burst transfers for anything less than 32-byte chunks of four 64-bit
// beats. Additionally, the CSI hardware lacks cropping so we cannot align the source address. // beats. Additionally, the CSI hardware lacks cropping so we cannot align the source address.
// Given this, performance will be lacking on cropped images. So much so that we do not use // Given this, performance will be lacking on cropped images. So much so that we do not use
// the EDMA for anything less than 4-byte transfers otherwise you get sensor timeout errors. // the EDMA for anything less than 4-byte transfers otherwise you get sensor timeout errors.
if (csi.dest_inc < MIN_EDMA_DST_INC) { if (csi->dest_inc < MIN_EDMA_DST_INC) {
return -1; return -1;
} }
edma_handle_t *handle = dma;
edma_transfer_config_t config;
framebuffer_t *fb = csi.fb;
EDMA_PrepareTransferConfig(&config, EDMA_PrepareTransferConfig(&config,
src, // srcAddr src, // srcAddr
csi.src_size, // srcWidth csi->src_size, // srcWidth
csi.src_inc, // srcOffset csi->src_inc, // srcOffset
dst, // destAddr dst, // destAddr
transposed ? bpp : csi.dest_inc, // destWidth transposed ? bpp : csi->dest_inc, // destWidth
transposed ? (fb->v * bpp) : csi.dest_inc, // destOffset transposed ? (fb->v * bpp) : csi->dest_inc, // destOffset
fb->u * bpp, // bytesEachRequest fb->u * bpp, // bytesEachRequest
fb->u * bpp); // transferBytes fb->u * bpp); // transferBytes
@ -203,7 +171,7 @@ int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed
} }
if (--retry == 0) { if (--retry == 0) {
// Drop the frame if EDMA is not keeping up as the image will be corrupt. // Drop the frame if EDMA is not keeping up as the image will be corrupt.
csi.drop_frame = true; csi->drop_frame = true;
return 0; return 0;
} }
} }
@ -213,21 +181,21 @@ int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed
} }
#endif #endif
void omv_csi_line_callback(uint32_t addr) { void omv_csi_line_callback(omv_csi_t *csi, uint32_t addr) {
framebuffer_t *fb = csi.fb; framebuffer_t *fb = csi->fb;
// Throttle frames to match the current frame rate. // Throttle frames to match the current frame rate.
omv_csi_throttle_framerate(); omv_csi_throttle_framerate(csi);
// Get current framebuffer. // Get current framebuffer.
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
if (csi.pixformat == PIXFORMAT_JPEG) { if (csi->pixformat == PIXFORMAT_JPEG) {
if (csi.drop_frame) { if (csi->drop_frame) {
return; return;
} }
bool jpeg_end = false; bool jpeg_end = false;
if (csi.jpg_format == 4) { if (csi->jpg_format == 4) {
// JPEG MODE 4: // JPEG MODE 4:
// //
// The width and height are fixed in each frame. The first two bytes are valid data // The width and height are fixed in each frame. The first two bytes are valid data
@ -257,7 +225,7 @@ void omv_csi_line_callback(uint32_t addr) {
} }
buffer->offset += size; buffer->offset += size;
} }
} else if (csi.jpg_format == 3) { } else if (csi->jpg_format == 3) {
// OV2640 JPEG TODO // OV2640 JPEG TODO
} }
// In JPEG mode the camera sensor will output some number of lines that doesn't match the // In JPEG mode the camera sensor will output some number of lines that doesn't match the
@ -267,16 +235,16 @@ void omv_csi_line_callback(uint32_t addr) {
// Release the current framebuffer. // Release the current framebuffer.
framebuffer_get_tail(fb, FB_NO_FLAGS); framebuffer_get_tail(fb, FB_NO_FLAGS);
CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK; CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK;
if (csi.frame_callback) { if (csi->frame_cb.fun) {
csi.frame_callback(); csi->frame_cb.fun(csi->frame_cb.arg);
} }
csi.drop_frame = true; csi->drop_frame = true;
} }
return; return;
} }
if (csi.drop_frame) { if (csi->drop_frame) {
if (++buffer->offset == resolution[csi.framesize][1]) { if (++buffer->offset == resolution[csi->framesize][1]) {
buffer->offset = 0; buffer->offset = 0;
CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK; CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK;
} }
@ -285,16 +253,16 @@ void omv_csi_line_callback(uint32_t addr) {
if ((fb->y <= buffer->offset) && (buffer->offset < (fb->y + fb->v))) { if ((fb->y <= buffer->offset) && (buffer->offset < (fb->y + fb->v))) {
// Copy from DMA buffer to framebuffer. // Copy from DMA buffer to framebuffer.
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi);
uint8_t *src = ((uint8_t *) addr) + (fb->x * bytes_per_pixel); uint8_t *src = ((uint8_t *) addr) + (fb->x * bytes_per_pixel);
uint8_t *dst = buffer->data; uint8_t *dst = buffer->data;
// Adjust BPP for Grayscale. // Adjust BPP for Grayscale.
if (csi.pixformat == PIXFORMAT_GRAYSCALE) { if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
bytes_per_pixel = 1; bytes_per_pixel = 1;
} }
if (csi.transpose) { if (csi->transpose) {
dst += bytes_per_pixel * (buffer->offset - fb->y); dst += bytes_per_pixel * (buffer->offset - fb->y);
} else { } else {
dst += fb->u * bytes_per_pixel * (buffer->offset - fb->y); dst += fb->u * bytes_per_pixel * (buffer->offset - fb->y);
@ -304,18 +272,18 @@ void omv_csi_line_callback(uint32_t addr) {
// We're using multiple handles to give each channel the maximum amount of time possible to do the line // We're using multiple handles to give each channel the maximum amount of time possible to do the line
// transfer. In most situations only one channel will be running at a time. However, if SDRAM is // transfer. In most situations only one channel will be running at a time. However, if SDRAM is
// backedup we don't have to disable the channel if it is flushing trailing data to SDRAM. // backedup we don't have to disable the channel if it is flushing trailing data to SDRAM.
omv_csi_copy_line(&csi.dma_channels[buffer->offset % OMV_CSI_DMA_CHANNEL_COUNT], src, dst); omv_csi_copy_line(csi, &csi->dma_channels[buffer->offset % OMV_CSI_DMA_CHANNEL_COUNT], src, dst);
#else #else
omv_csi_copy_line(NULL, src, dst); omv_csi_copy_line(csi, NULL, src, dst);
#endif #endif
} }
if (++buffer->offset == resolution[csi.framesize][1]) { if (++buffer->offset == resolution[csi->framesize][1]) {
// Release the current framebuffer. // Release the current framebuffer.
framebuffer_get_tail(fb, FB_NO_FLAGS); framebuffer_get_tail(fb, FB_NO_FLAGS);
CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK; CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK;
if (csi.frame_callback) { if (csi->frame_cb.fun) {
csi.frame_callback(); csi->frame_cb.fun(csi->frame_cb.arg);
} }
} }
} }
@ -361,7 +329,7 @@ static void edma_config(omv_csi_t *csi, uint32_t bytes_per_pixel) {
} }
#endif #endif
int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) { int imx_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
framebuffer_t *fb = csi->fb; framebuffer_t *fb = csi->fb;
// Used to restore the frame buffer width and height. // Used to restore the frame buffer width and height.
@ -376,7 +344,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
return OMV_CSI_ERROR_INVALID_FRAMESIZE; return OMV_CSI_ERROR_INVALID_FRAMESIZE;
} }
if (omv_csi_check_framebuffer_size() != 0) { if (omv_csi_check_framebuffer_size(csi) != 0) {
return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW;
} }
@ -390,7 +358,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
if (!(CSI->CR18 & CSI_CR18_CSI_ENABLE_MASK)) { if (!(CSI->CR18 & CSI_CR18_CSI_ENABLE_MASK)) {
framebuffer_setup_buffers(fb); framebuffer_setup_buffers(fb);
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi);
uint32_t dma_line_bytes = resolution[csi->framesize][0] * bytes_per_pixel; uint32_t dma_line_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
uint32_t length = dma_line_bytes * h; uint32_t length = dma_line_bytes * h;
@ -530,10 +498,10 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
framebuffer_init_image(fb, image); framebuffer_init_image(fb, image);
return 0; return 0;
} }
#endif
int omv_csi_init() { int omv_csi_init() {
int init_ret = 0; int init_ret = 0;
static omv_i2c_t i2c;
mimxrt_hal_csi_init(CSI); mimxrt_hal_csi_init(CSI);
@ -545,18 +513,19 @@ int omv_csi_init() {
omv_gpio_write(OMV_CSI_RESET_PIN, 1); omv_gpio_write(OMV_CSI_RESET_PIN, 1);
#endif #endif
// Reset the csi state // Initialize the CSIs using this driver's ops as defaults,
memset(&csi, 0, sizeof(omv_csi_t)); // which can be overridden by sensor drivers during probe.
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
omv_csi_t *csi = &csi_all[i];
// Set default framebuffer memset(csi, 0, sizeof(omv_csi_t));
csi.fb = framebuffer_get(0); csi->i2c = &i2c;
csi->fb = framebuffer_get(-1);
// Set I2C bus csi->abort = imx_csi_abort;
csi.i2c = &csi_i2c; csi->config = imx_csi_config;
csi->snapshot = imx_csi_snapshot;
// Set default snapshot function. csi->color_palette = rainbow_table;
// Some sensors need to call snapshot from init. }
csi.snapshot = omv_csi_snapshot;
// Configure the csi external clock (XCLK). // Configure the csi external clock (XCLK).
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) { if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
@ -564,29 +533,28 @@ int omv_csi_init() {
return OMV_CSI_ERROR_TIM_INIT_FAILED; return OMV_CSI_ERROR_TIM_INIT_FAILED;
} }
// Initialize the camera bus.
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
// Detect and initialize the image sensor. // Detect and initialize the image sensor.
if ((init_ret = omv_csi_probe_init(OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED)) != 0) { if ((init_ret = omv_csi_probe(&i2c)) != 0) {
// Sensor probe/init failed. // Sensor probe/init failed.
return init_ret; return init_ret;
} }
// Configure the CSI interface. // Configure the CSI interfaces.
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) { for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
// CSI config failed omv_csi_t *csi = &csi_all[i];
return OMV_CSI_ERROR_CSI_INIT_FAILED;
if (omv_csi_config(csi, OMV_CSI_CONFIG_INIT) != 0) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
csi->detected = true;
} }
// Set default color palette. // Clear fb_enabled flag.
csi.color_palette = rainbow_table; JPEG_FB()->enabled = 0;
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
// Disable Frame callback.
omv_csi_set_frame_callback(NULL);
// All good!
csi.detected = true;
return 0; return 0;
} }
#endif // MICROPY_PY_CSI

View File

@ -61,7 +61,7 @@ static const volatile uint32_t *_pclkPort;
extern void __fatal_error(const char *msg); extern void __fatal_error(const char *msg);
int omv_csi_config(omv_csi_config_t config) { static int nrf_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) { if (config == OMV_CSI_CONFIG_INIT) {
uint32_t csi_pins[] = { uint32_t csi_pins[] = {
OMV_CSI_D0_PIN, OMV_CSI_D0_PIN,
@ -114,12 +114,12 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
return 0; return 0;
} }
int omv_csi_set_windowing(int x, int y, int w, int h) { int omv_csi_set_windowing(omv_csi_t *csi, int x, int y, int w, int h) {
return OMV_CSI_ERROR_CTL_UNSUPPORTED; return OMV_CSI_ERROR_CTL_UNSUPPORTED;
} }
// This is the default snapshot function, which can be replaced in omv_csi_init functions. // 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) { static int nrf_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
framebuffer_t *fb = csi->fb; framebuffer_t *fb = csi->fb;
// Compress the framebuffer for the IDE preview, only if it's not the first frame, // Compress the framebuffer for the IDE preview, only if it's not the first frame,
@ -132,7 +132,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
framebuffer_set_buffers(fb, 1); framebuffer_set_buffers(fb, 1);
} }
if (omv_csi_check_framebuffer_size(fb) != 0) { if (omv_csi_check_framebuffer_size(csi) != 0) {
return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW;
} }
@ -188,8 +188,8 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
interrupts(); interrupts();
// Not useful for the NRF but must call to keep API the same. // Not useful for the NRF but must call to keep API the same.
if (csi->frame_callback) { if (csi->frame_cb.fun) {
csi->frame_callback(); csi->frame_cb.fun(csi->frame_cb.arg);
} }
// Set framebuffer pixel format. // Set framebuffer pixel format.
@ -208,6 +208,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
int omv_csi_init() { int omv_csi_init() {
int init_ret = 0; int init_ret = 0;
static omv_i2c_t i2c;
#if defined(OMV_CSI_POWER_PIN) #if defined(OMV_CSI_POWER_PIN)
nrf_gpio_cfg_output(OMV_CSI_POWER_PIN); nrf_gpio_cfg_output(OMV_CSI_POWER_PIN);
@ -219,17 +220,19 @@ int omv_csi_init() {
nrf_gpio_pin_write(OMV_CSI_RESET_PIN, 1); nrf_gpio_pin_write(OMV_CSI_RESET_PIN, 1);
#endif #endif
// Reset the csi state // Initialize the CSIs using this driver's ops as defaults,
memset(&csi, 0, sizeof(omv_csi_t)); // which can be overridden by sensor drivers during probe.
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
omv_csi_t *csi = &csi_all[i];
// Set default framebuffer memset(csi, 0, sizeof(omv_csi_t));
csi.fb = framebuffer_get(0); csi->i2c = &i2c;
csi->fb = framebuffer_get(-1);
// Set I2C bus csi->abort = NULL;
csi.i2c = &csi_i2c; csi->config = nrf_csi_config;
csi->snapshot = nrf_csi_snapshot;
// Set default snapshot function. csi->color_palette = rainbow_table;
csi.snapshot = omv_csi_snapshot; }
// Configure the csi external clock (XCLK). // Configure the csi external clock (XCLK).
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) { if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
@ -237,30 +240,28 @@ int omv_csi_init() {
return OMV_CSI_ERROR_TIM_INIT_FAILED; return OMV_CSI_ERROR_TIM_INIT_FAILED;
} }
// Initialize the camera bus.
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
// Detect and initialize the image sensor. // Detect and initialize the image sensor.
if ((init_ret = omv_csi_probe_init(OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED)) != 0) { if ((init_ret = omv_csi_probe(&i2c)) != 0) {
// Sensor probe/init failed. // Sensor probe/init failed.
return init_ret; return init_ret;
} }
// Configure the CSI interface. // Configure the CSI interfaces.
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) { for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
// CSI config failed omv_csi_t *csi = &csi_all[i];
return OMV_CSI_ERROR_CSI_INIT_FAILED;
if (omv_csi_config(csi, OMV_CSI_CONFIG_INIT) != 0) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
csi->detected = true;
} }
// Clear fb_enabled flag // Clear fb_enabled flag
// This is executed only once to initialize the FB enabled flag. JPEG_FB()->enabled = 0;
//JPEG_FB()->enabled = 0;
// Set default color palette.
csi.color_palette = rainbow_table;
csi.detected = true;
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
/* All good! */
return 0; return 0;
} }

View File

@ -41,31 +41,56 @@
#include "unaligned_memcpy.h" #include "unaligned_memcpy.h"
#include "dcmi.pio.h" #include "dcmi.pio.h"
static void dma_irq_handler();
extern void __fatal_error(const char *msg); extern void __fatal_error(const char *msg);
static void omv_csi_dma_config(int w, int h, int bpp, uint32_t *capture_buf, bool rev_bytes) { static void dma_irq_handler() {
dma_channel_abort(OMV_CSI_DMA_CHANNEL); omv_csi_t *csi = omv_csi_get(-1);
dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, false); framebuffer_t *fb = csi->fb;
dma_channel_config c = dma_channel_get_default_config(OMV_CSI_DMA_CHANNEL); if (dma_irqn_get_channel_status(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL)) {
channel_config_set_read_increment(&c, false); // Clear the interrupt request.
channel_config_set_write_increment(&c, true); dma_irqn_acknowledge_channel(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL);
channel_config_set_dreq(&c, pio_get_dreq(OMV_CSI_PIO, OMV_CSI_SM, false));
channel_config_set_bswap(&c, rev_bytes);
dma_channel_configure(OMV_CSI_DMA_CHANNEL, &c, framebuffer_get_tail(fb, FB_NO_FLAGS);
capture_buf, // Destinatinon pointer. vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
&OMV_CSI_PIO->rxf[OMV_CSI_SM], // Source pointer. if (buffer != NULL) {
(w * h * bpp) >> 2, // Number of transfers in words. // Set next buffer and retrigger the DMA channel.
true // Start immediately, will block on SM. dma_channel_set_write_addr(OMV_CSI_DMA_CHANNEL, buffer->data, true);
);
// Re-enable DMA IRQs. // Unblock the state machine
dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, true); 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, (fb->v - 1));
pio_sm_put_blocking(OMV_CSI_PIO, OMV_CSI_SM, (fb->u * fb->bpp) - 1);
}
}
} }
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { static int rp2_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) {
// Install new DMA IRQ handler.
irq_set_enabled(OMV_CSI_DMA_IRQ, false);
// Clear DMA interrupts.
dma_irqn_acknowledge_channel(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL);
// Remove current handler if any
irq_handler_t irq_handler = irq_get_exclusive_handler(OMV_CSI_DMA_IRQ);
if (irq_handler != NULL) {
irq_remove_handler(OMV_CSI_DMA_IRQ, irq_handler);
}
// Set new exclusive IRQ handler.
irq_set_exclusive_handler(OMV_CSI_DMA_IRQ, dma_irq_handler);
// Or set shared IRQ handler, but this needs to be called once.
// irq_add_shared_handler(OMV_CSI_DMA_IRQ, dma_irq_handler, PICO_DEFAULT_IRQ_PRIORITY);
irq_set_enabled(OMV_CSI_DMA_IRQ, true);
}
return 0;
}
static int rp2_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
// Disable DMA channel // Disable DMA channel
dma_channel_abort(OMV_CSI_DMA_CHANNEL); dma_channel_abort(OMV_CSI_DMA_CHANNEL);
dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, false); dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, false);
@ -107,39 +132,17 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
return 0; return 0;
} }
int omv_csi_set_windowing(int x, int y, int w, int h) { int omv_csi_set_windowing(omv_csi_t *csi, int x, int y, int w, int h) {
return OMV_CSI_ERROR_CTL_UNSUPPORTED; return OMV_CSI_ERROR_CTL_UNSUPPORTED;
} }
static void dma_irq_handler() { static int rp2_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
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, 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);
// 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, (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; framebuffer_t *fb = csi->fb;
// Compress the framebuffer for the IDE preview. // Compress the framebuffer for the IDE preview.
framebuffer_update_jpeg_buffer(fb); framebuffer_update_jpeg_buffer(fb);
if (omv_csi_check_framebuffer_size() != 0) { if (omv_csi_check_framebuffer_size(csi) != 0) {
return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW; return OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW;
} }
@ -164,10 +167,25 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
return OMV_CSI_ERROR_FRAMEBUFFER_ERROR; return OMV_CSI_ERROR_FRAMEBUFFER_ERROR;
} }
// Configure the DMA on the first frame, for later frames only the write is changed. // Configure the DMA on the first frame.
omv_csi_dma_config(fb->u, fb->v, fb->bpp, dma_channel_abort(OMV_CSI_DMA_CHANNEL);
(void *) buffer->data, dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, false);
(csi->rgb_swap && fb->bpp == 2));
dma_channel_config c = dma_channel_get_default_config(OMV_CSI_DMA_CHANNEL);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_dreq(&c, pio_get_dreq(OMV_CSI_PIO, OMV_CSI_SM, false));
channel_config_set_bswap(&c, csi->rgb_swap && (fb->bpp == 2));
dma_channel_configure(OMV_CSI_DMA_CHANNEL, &c,
(uint32_t *) buffer->data, // Destinatinon pointer.
&OMV_CSI_PIO->rxf[OMV_CSI_SM], // Source pointer.
(fb->u * fb->v * fb->bpp) >> 2, // Number of transfers in words.
true // Start immediately, will block on SM.
);
// Re-enable DMA IRQs.
dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, true);
// Re-enable the state machine. // Re-enable the state machine.
pio_sm_clear_fifos(OMV_CSI_PIO, OMV_CSI_SM); pio_sm_clear_fifos(OMV_CSI_PIO, OMV_CSI_SM);
@ -194,10 +212,10 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
framebuffer_init_image(fb, image); framebuffer_init_image(fb, image);
return 0; return 0;
} }
#endif
int omv_csi_init() { int omv_csi_init() {
int init_ret = 0; int init_ret = 0;
static omv_i2c_t i2c;
// PIXCLK // PIXCLK
gpio_init(OMV_CSI_PXCLK_PIN); gpio_init(OMV_CSI_PXCLK_PIN);
@ -225,17 +243,19 @@ int omv_csi_init() {
gpio_put(OMV_CSI_RESET_PIN, 1); gpio_put(OMV_CSI_RESET_PIN, 1);
#endif #endif
// Reset the csi state // Initialize the CSIs using this driver's ops as defaults,
memset(&csi, 0, sizeof(omv_csi_t)); // which can be overridden by sensor drivers during probe.
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
omv_csi_t *csi = &csi_all[i];
// Set default framebuffer memset(csi, 0, sizeof(omv_csi_t));
csi.fb = framebuffer_get(0); csi->i2c = &i2c;
csi->fb = framebuffer_get(-1);
// Set I2C bus csi->abort = rp2_csi_abort;
csi.i2c = &csi_i2c; csi->config = rp2_csi_config;
csi->snapshot = rp2_csi_snapshot;
// Set default snapshot function. csi->color_palette = rainbow_table;
csi.snapshot = omv_csi_snapshot; }
// Configure the csi external clock (XCLK). // Configure the csi external clock (XCLK).
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) { if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
@ -243,43 +263,28 @@ int omv_csi_init() {
return OMV_CSI_ERROR_TIM_INIT_FAILED; return OMV_CSI_ERROR_TIM_INIT_FAILED;
} }
// Initialize the camera bus.
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
// Detect and initialize the image sensor. // Detect and initialize the image sensor.
if ((init_ret = omv_csi_probe_init(OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED)) != 0) { if ((init_ret = omv_csi_probe(&i2c)) != 0) {
// Sensor probe/init failed. // Sensor probe/init failed.
return init_ret; return init_ret;
} }
// Set default color palette. // Configure the DCMI interface.
csi.color_palette = rainbow_table; for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
omv_csi_t *csi = &csi_all[i];
// Set new DMA IRQ handler. if (omv_csi_config(csi, OMV_CSI_CONFIG_INIT) != 0) {
// Disable IRQs. return OMV_CSI_ERROR_CSI_INIT_FAILED;
irq_set_enabled(OMV_CSI_DMA_IRQ, false); }
// Clear DMA interrupts. csi->detected = true;
dma_irqn_acknowledge_channel(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL);
// Remove current handler if any
irq_handler_t irq_handler = irq_get_exclusive_handler(OMV_CSI_DMA_IRQ);
if (irq_handler != NULL) {
irq_remove_handler(OMV_CSI_DMA_IRQ, irq_handler);
} }
// Set new exclusive IRQ handler. // Clear fb_enabled flag.
irq_set_exclusive_handler(OMV_CSI_DMA_IRQ, dma_irq_handler); JPEG_FB()->enabled = 0;
// Or set shared IRQ handler, but this needs to be called once.
// irq_add_shared_handler(OMV_CSI_DMA_IRQ, dma_irq_handler, PICO_DEFAULT_IRQ_PRIORITY);
irq_set_enabled(OMV_CSI_DMA_IRQ, true);
// Disable VSYNC IRQ and callback
omv_csi_set_vsync_callback(NULL);
// Disable Frame callback.
omv_csi_set_frame_callback(NULL);
/* All good! */
csi.detected = true;
return 0; return 0;
} }
#endif // MICROPY_PY_CSI

View File

@ -70,94 +70,82 @@ extern uint32_t hal_get_exti_gpio(uint32_t line);
#if USE_DCMI #if USE_DCMI
void DCMI_IRQHandler(void) { void DCMI_IRQHandler(void) {
HAL_DCMI_IRQHandler(&csi.dcmi); omv_csi_t *csi = omv_csi_get(-1);
HAL_DCMI_IRQHandler(&csi->dcmi);
} }
#endif #endif
#if USE_DCMIPP #if USE_DCMIPP
void CSI_IRQHandler(void) { void CSI_IRQHandler(void) {
HAL_DCMIPP_CSI_IRQHandler(&csi.dcmi); omv_csi_t *csi = omv_csi_get(-1);
HAL_DCMIPP_CSI_IRQHandler(&csi->dcmi);
} }
void DCMIPP_IRQHandler(void) { void DCMIPP_IRQHandler(void) {
HAL_DCMIPP_IRQHandler(&csi.dcmi); omv_csi_t *csi = omv_csi_get(-1);
HAL_DCMIPP_IRQHandler(&csi->dcmi);
} }
#endif #endif
#if USE_MDMA #if USE_MDMA
void omv_csi_mdma_irq_handler(void) { void omv_csi_mdma_irq_handler(void) {
omv_csi_t *csi = omv_csi_get(-1);
if (MDMA->GISR0 & (1 << OMV_MDMA_CHANNEL_DCMI_0)) { if (MDMA->GISR0 & (1 << OMV_MDMA_CHANNEL_DCMI_0)) {
HAL_MDMA_IRQHandler(&csi.mdma0); HAL_MDMA_IRQHandler(&csi->mdma0);
} }
if (MDMA->GISR0 & (1 << OMV_MDMA_CHANNEL_DCMI_1)) { if (MDMA->GISR0 & (1 << OMV_MDMA_CHANNEL_DCMI_1)) {
HAL_MDMA_IRQHandler(&csi.mdma1); HAL_MDMA_IRQHandler(&csi->mdma1);
} }
} }
#endif #endif
void omv_csi_init0() { static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
omv_csi_abort(&csi, true, false);
// Disable callbacks
omv_csi_set_vsync_callback(NULL);
omv_csi_set_frame_callback(NULL);
csi.disable_delays = 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.
if (csi.i2c->initialized) {
// Reinitialize the bus using the last used id and speed.
omv_i2c_init(csi.i2c, csi.i2c->id, csi.i2c->speed);
}
}
int omv_csi_config(omv_csi_config_t config) {
if (config == OMV_CSI_CONFIG_INIT) { if (config == OMV_CSI_CONFIG_INIT) {
#if USE_DMA #if USE_DMA
// DMA Stream configuration // DMA Stream configuration
csi.dma.Instance = DMA2_Stream1; csi->dma.Instance = DMA2_Stream1;
#if defined(STM32H7) #if defined(STM32H7)
csi.dma.Init.Request = DMA_REQUEST_DCMI; csi->dma.Init.Request = DMA_REQUEST_DCMI;
#else #else
csi.dma.Init.Channel = DMA_CHANNEL_1; csi->dma.Init.Channel = DMA_CHANNEL_1;
#endif #endif
csi.dma.Init.Direction = DMA_PERIPH_TO_MEMORY; csi->dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
csi.dma.Init.MemInc = DMA_MINC_ENABLE; csi->dma.Init.MemInc = DMA_MINC_ENABLE;
csi.dma.Init.PeriphInc = DMA_PINC_DISABLE; csi->dma.Init.PeriphInc = DMA_PINC_DISABLE;
csi.dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; csi->dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
csi.dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; csi->dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
csi.dma.Init.Mode = DMA_NORMAL; csi->dma.Init.Mode = DMA_NORMAL;
csi.dma.Init.Priority = DMA_PRIORITY_HIGH; csi->dma.Init.Priority = DMA_PRIORITY_HIGH;
csi.dma.Init.FIFOMode = DMA_FIFOMODE_ENABLE; csi->dma.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
csi.dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; csi->dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
csi.dma.Init.MemBurst = DMA_MBURST_INC4; csi->dma.Init.MemBurst = DMA_MBURST_INC4;
csi.dma.Init.PeriphBurst = DMA_PBURST_SINGLE; csi->dma.Init.PeriphBurst = DMA_PBURST_SINGLE;
// Initialize the DMA stream // Initialize the DMA stream
HAL_DMA_DeInit(&csi.dma); HAL_DMA_DeInit(&csi->dma);
if (HAL_DMA_Init(&csi.dma) != HAL_OK) { if (HAL_DMA_Init(&csi->dma) != HAL_OK) {
return -1; return OMV_CSI_ERROR_DMA_INIT_FAILED;
} }
// Set DMA IRQ handle // Set DMA IRQ handle
dma_utils_set_irq_descr(DMA2_Stream1, &csi.dma); dma_utils_set_irq_descr(DMA2_Stream1, &csi->dma);
// Configure the DMA IRQ Channel // Configure the DMA IRQ Channel
NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21); NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21);
#if USE_MDMA #if USE_MDMA
csi.mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0); csi->mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0);
csi.mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1); csi->mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1);
#endif #endif
#endif // USE_DMA #endif // USE_DMA
// Configure DCMI/PP. // Configure DCMI/PP.
#if USE_DCMIPP #if USE_DCMIPP
// Initialize the DCMIPP // Initialize the DCMIPP
csi.dcmi.Instance = DCMIPP; csi->dcmi.Instance = DCMIPP;
if (HAL_DCMIPP_Init(&csi.dcmi) != HAL_OK) { if (HAL_DCMIPP_Init(&csi->dcmi) != HAL_OK) {
return -1; return -1;
} }
@ -169,21 +157,21 @@ int omv_csi_config(omv_csi_config_t config) {
NVIC_SetPriority(CSI_IRQn, IRQ_PRI_DCMI); NVIC_SetPriority(CSI_IRQn, IRQ_PRI_DCMI);
HAL_NVIC_EnableIRQ(CSI_IRQn); HAL_NVIC_EnableIRQ(CSI_IRQn);
#else #else
csi.dcmi.Instance = DCMI; csi->dcmi.Instance = DCMI;
csi.dcmi.Init.VSPolarity = csi.vsync_pol ? DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW; csi->dcmi.Init.VSPolarity = csi->vsync_pol ? DCMI_VSPOLARITY_HIGH : DCMI_VSPOLARITY_LOW;
csi.dcmi.Init.HSPolarity = csi.hsync_pol ? DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW; csi->dcmi.Init.HSPolarity = csi->hsync_pol ? DCMI_HSPOLARITY_HIGH : DCMI_HSPOLARITY_LOW;
csi.dcmi.Init.PCKPolarity = csi.pixck_pol ? DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING; csi->dcmi.Init.PCKPolarity = csi->pixck_pol ? DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING;
csi.dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE; csi->dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
csi.dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME; csi->dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
csi.dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B; csi->dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
csi.dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE; csi->dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
// Link the DMA handle to the DCMI handle // Link the DMA handle to the DCMI handle
__HAL_LINKDMA(&csi.dcmi, DMA_Handle, csi.dma); __HAL_LINKDMA(&csi->dcmi, DMA_Handle, csi->dma);
// Initialize the DCMI // Initialize the DCMI
HAL_DCMI_DeInit(&csi.dcmi); HAL_DCMI_DeInit(&csi->dcmi);
if (HAL_DCMI_Init(&csi.dcmi) != HAL_OK) { if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) {
return -1; return -1;
} }
@ -194,16 +182,16 @@ int omv_csi_config(omv_csi_config_t config) {
} else if (config == OMV_CSI_CONFIG_PIXFORMAT) { } else if (config == OMV_CSI_CONFIG_PIXFORMAT) {
#if USE_DCMI #if USE_DCMI
DCMI->CR &= ~(DCMI_CR_JPEG_Msk << DCMI_CR_JPEG_Pos); DCMI->CR &= ~(DCMI_CR_JPEG_Msk << DCMI_CR_JPEG_Pos);
DCMI->CR |= (csi.pixformat == PIXFORMAT_JPEG) ? DCMI_JPEG_ENABLE : DCMI_JPEG_DISABLE; DCMI->CR |= (csi->pixformat == PIXFORMAT_JPEG) ? DCMI_JPEG_ENABLE : DCMI_JPEG_DISABLE;
#else #else
// Select and configure the DCMIPP source. // Select and configure the DCMIPP source.
if (csi.mipi_if) { if (csi->mipi_if) {
DCMIPP_CSI_ConfTypeDef scfg = { DCMIPP_CSI_ConfTypeDef scfg = {
.NumberOfLanes = DCMIPP_CSI_TWO_DATA_LANES, .NumberOfLanes = DCMIPP_CSI_TWO_DATA_LANES,
.DataLaneMapping = DCMIPP_CSI_PHYSICAL_DATA_LANES, .DataLaneMapping = DCMIPP_CSI_PHYSICAL_DATA_LANES,
.PHYBitrate = (csi.mipi_brate == 850) ? DCMIPP_CSI_PHY_BT_850 : DCMIPP_CSI_PHY_BT_1200, .PHYBitrate = (csi->mipi_brate == 850) ? DCMIPP_CSI_PHY_BT_850 : DCMIPP_CSI_PHY_BT_1200,
}; };
if (HAL_DCMIPP_CSI_SetConfig(&csi.dcmi, &scfg) != HAL_OK) { if (HAL_DCMIPP_CSI_SetConfig(&csi->dcmi, &scfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
// Configure CSI virtual channel and pipe. // Configure CSI virtual channel and pipe.
@ -212,61 +200,60 @@ int omv_csi_config(omv_csi_config_t config) {
.DataTypeIDA = DCMIPP_DT_RAW10, .DataTypeIDA = DCMIPP_DT_RAW10,
.DataTypeIDB = DCMIPP_DT_RAW10, .DataTypeIDB = DCMIPP_DT_RAW10,
}; };
if (HAL_DCMIPP_CSI_SetVCConfig(&csi.dcmi, DCMIPP_VIRTUAL_CHANNEL0, if (HAL_DCMIPP_CSI_SetVCConfig(&csi->dcmi, DCMIPP_VIRTUAL_CHANNEL0,
DCMIPP_CSI_DT_BPP10) != HAL_OK) { DCMIPP_CSI_DT_BPP10) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
if (HAL_DCMIPP_CSI_PIPE_SetConfig(&csi.dcmi, DCMIPP_PIPE, &pcfg) != HAL_OK) { if (HAL_DCMIPP_CSI_PIPE_SetConfig(&csi->dcmi, DCMIPP_PIPE, &pcfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
} else { } else {
DCMIPP_ParallelConfTypeDef scfg = { DCMIPP_ParallelConfTypeDef scfg = {
.SynchroMode = DCMIPP_SYNCHRO_HARDWARE, .SynchroMode = DCMIPP_SYNCHRO_HARDWARE,
.ExtendedDataMode = DCMIPP_INTERFACE_8BITS, .ExtendedDataMode = DCMIPP_INTERFACE_8BITS,
.VSPolarity = csi.vsync_pol ? DCMIPP_VSPOLARITY_HIGH : DCMIPP_VSPOLARITY_LOW, .VSPolarity = csi->vsync_pol ? DCMIPP_VSPOLARITY_HIGH : DCMIPP_VSPOLARITY_LOW,
.HSPolarity = csi.hsync_pol ? DCMIPP_HSPOLARITY_HIGH : DCMIPP_HSPOLARITY_LOW, .HSPolarity = csi->hsync_pol ? DCMIPP_HSPOLARITY_HIGH : DCMIPP_HSPOLARITY_LOW,
.PCKPolarity = csi.pixck_pol ? DCMIPP_PCKPOLARITY_RISING : DCMIPP_PCKPOLARITY_FALLING, .PCKPolarity = csi->pixck_pol ? DCMIPP_PCKPOLARITY_RISING : DCMIPP_PCKPOLARITY_FALLING,
}; };
if (csi.raw_output) { if (csi->raw_output) {
scfg.Format = DCMIPP_FORMAT_RAW8; scfg.Format = DCMIPP_FORMAT_RAW8;
} else if (csi.pixformat == PIXFORMAT_RGB565) { } else if (csi->pixformat == PIXFORMAT_RGB565) {
scfg.Format = DCMIPP_FORMAT_RGB565; scfg.Format = DCMIPP_FORMAT_RGB565;
scfg.SwapCycles = DCMIPP_SWAPCYCLES_ENABLE; scfg.SwapCycles = DCMIPP_SWAPCYCLES_ENABLE;
} else if (csi.pixformat == PIXFORMAT_GRAYSCALE) { } else if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
scfg.Format = (csi.mono_bpp == 1) ? DCMIPP_FORMAT_MONOCHROME_8B : DCMIPP_FORMAT_YUV422; scfg.Format = (csi->mono_bpp == 1) ? DCMIPP_FORMAT_MONOCHROME_8B : DCMIPP_FORMAT_YUV422;
} else { } else {
return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED; return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED;
} }
if (HAL_DCMIPP_PARALLEL_SetConfig(&csi.dcmi, &scfg) != HAL_OK) { if (HAL_DCMIPP_PARALLEL_SetConfig(&csi->dcmi, &scfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
} }
// Configure the pixel processing pipeline. // Configure the pixel processing pipeline.
DCMIPP_PipeConfTypeDef pcfg = { .FrameRate = DCMIPP_FRAME_RATE_ALL }; DCMIPP_PipeConfTypeDef pcfg = { .FrameRate = DCMIPP_FRAME_RATE_ALL };
if (csi.pixformat == PIXFORMAT_RGB565) { if (csi->pixformat == PIXFORMAT_RGB565) {
pcfg.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_RGB565_1; pcfg.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_RGB565_1;
} else if (csi.pixformat == PIXFORMAT_GRAYSCALE) { } else if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
pcfg.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_MONO_Y8_G8_1; pcfg.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_MONO_Y8_G8_1;
} else { } else {
return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED; return OMV_CSI_ERROR_PIXFORMAT_UNSUPPORTED;
} }
if (HAL_DCMIPP_PIPE_SetConfig(&csi.dcmi, DCMIPP_PIPE, &pcfg) != HAL_OK) { if (HAL_DCMIPP_PIPE_SetConfig(&csi->dcmi, DCMIPP_PIPE, &pcfg) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
// Swap RGB enabled. // Swap RGB enabled.
if (csi.yuv_swap) { if (csi->yuv_swap) {
HAL_DCMIPP_PIPE_EnableYUVSwap(&csi.dcmi, DCMIPP_PIPE); HAL_DCMIPP_PIPE_EnableYUVSwap(&csi->dcmi, DCMIPP_PIPE);
} }
// Swap YUV if enabled. // Swap YUV if enabled.
if (csi.rgb_swap) { if (csi->rgb_swap) {
HAL_DCMIPP_PIPE_EnableRedBlueSwap(&csi.dcmi, DCMIPP_PIPE); HAL_DCMIPP_PIPE_EnableRedBlueSwap(&csi->dcmi, DCMIPP_PIPE);
} }
// Configure debayer. // Configure debayer.
if (csi.raw_output && csi.pixformat != PIXFORMAT_BAYER) { if (csi->raw_output && csi->pixformat != PIXFORMAT_BAYER) {
DCMIPP_RawBayer2RGBConfTypeDef rawcfg = { DCMIPP_RawBayer2RGBConfTypeDef rawcfg = {
.RawBayerType = DCMIPP_RAWBAYER_BGGR, .RawBayerType = DCMIPP_RAWBAYER_BGGR,
.VLineStrength = DCMIPP_RAWBAYER_ALGO_NONE, .VLineStrength = DCMIPP_RAWBAYER_ALGO_NONE,
@ -274,8 +261,9 @@ int omv_csi_config(omv_csi_config_t config) {
.PeakStrength = DCMIPP_RAWBAYER_ALGO_NONE, .PeakStrength = DCMIPP_RAWBAYER_ALGO_NONE,
.EdgeStrength = DCMIPP_RAWBAYER_ALGO_NONE, .EdgeStrength = DCMIPP_RAWBAYER_ALGO_NONE,
}; };
if (HAL_DCMIPP_PIPE_SetISPRawBayer2RGBConfig(&csi.dcmi, DCMIPP_PIPE, &rawcfg) != HAL_OK ||
HAL_DCMIPP_PIPE_EnableISPRawBayer2RGB(&csi.dcmi, DCMIPP_PIPE) != HAL_OK) { if (HAL_DCMIPP_PIPE_SetISPRawBayer2RGBConfig(&csi->dcmi, DCMIPP_PIPE, &rawcfg) != HAL_OK ||
HAL_DCMIPP_PIPE_EnableISPRawBayer2RGB(&csi->dcmi, DCMIPP_PIPE) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
@ -288,8 +276,8 @@ int omv_csi_config(omv_csi_config_t config) {
.MultiplierBlue = 128, .MultiplierBlue = 128,
}; };
if (HAL_DCMIPP_PIPE_SetISPExposureConfig(&csi.dcmi, DCMIPP_PIPE, &expcfg) != HAL_OK || if (HAL_DCMIPP_PIPE_SetISPExposureConfig(&csi->dcmi, DCMIPP_PIPE, &expcfg) != HAL_OK ||
HAL_DCMIPP_PIPE_EnableISPExposure(&csi.dcmi, DCMIPP_PIPE) != HAL_OK) { HAL_DCMIPP_PIPE_EnableISPExposure(&csi->dcmi, DCMIPP_PIPE) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
@ -307,13 +295,13 @@ int omv_csi_config(omv_csi_config_t config) {
} }
for (size_t i = DCMIPP_STATEXT_MODULE1; i <= DCMIPP_STATEXT_MODULE3; i++) { for (size_t i = DCMIPP_STATEXT_MODULE1; i <= DCMIPP_STATEXT_MODULE3; i++) {
if (HAL_DCMIPP_PIPE_SetISPStatisticExtractionConfig(&csi.dcmi, if (HAL_DCMIPP_PIPE_SetISPStatisticExtractionConfig(&csi->dcmi,
DCMIPP_PIPE, i, DCMIPP_PIPE, i,
&statcfg[i - DCMIPP_STATEXT_MODULE1]) != HAL_OK) { &statcfg[i - DCMIPP_STATEXT_MODULE1]) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
if (HAL_DCMIPP_PIPE_EnableISPStatisticExtraction(&csi.dcmi, DCMIPP_PIPE, i) != HAL_OK) { if (HAL_DCMIPP_PIPE_EnableISPStatisticExtraction(&csi->dcmi, DCMIPP_PIPE, i) != HAL_OK) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; return OMV_CSI_ERROR_CSI_INIT_FAILED;
} }
} }
@ -324,73 +312,74 @@ int omv_csi_config(omv_csi_config_t config) {
} }
// Stop the DCMI from generating more DMA requests, and disable the DMA. // Stop the DCMI from generating more DMA requests, and disable the DMA.
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) { static int stm_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
if (DCMI_IS_ACTIVE()) { if (!DCMI_IS_ACTIVE()) {
#if USE_DCMI return 0;
DCMI->CR &= ~DCMI_CR_ENABLE;
#endif
#if USE_DMA
if (in_irq) {
HAL_DMA_Abort_IT(&csi->dma);
} else {
HAL_DMA_Abort(&csi->dma);
}
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
#endif
#if USE_MDMA
if (!in_irq) {
HAL_MDMA_Abort(&csi->mdma0);
HAL_MDMA_Abort(&csi->mdma1);
}
HAL_MDMA_DeInit(&csi->mdma0);
HAL_MDMA_DeInit(&csi->mdma1);
#endif
#if USE_DCMI
__HAL_DCMI_DISABLE_IT(&csi->dcmi, DCMI_IT_FRAME);
__HAL_DCMI_CLEAR_FLAG(&csi->dcmi, DCMI_FLAG_FRAMERI);
#else
if (!csi->mipi_if) {
HAL_DCMIPP_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE);
} else {
HAL_DCMIPP_CSI_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0);
}
for (size_t i=0; i<DCMIPP_NUM_OF_PIPES; i++) {
csi->dcmi.PipeState[i] = HAL_DCMIPP_PIPE_STATE_RESET;
}
#endif
csi->first_line = false;
csi->drop_frame = false;
csi->last_frame_ms = 0;
csi->last_frame_ms_valid = false;
} }
if (csi->fb) { #if USE_DCMI
if (fifo_flush) { DCMI->CR &= ~DCMI_CR_ENABLE;
framebuffer_flush_buffers(csi->fb, true); #endif
} else if (!csi->disable_full_flush) {
framebuffer_flush_buffers(csi->fb, false); #if USE_DMA
} if (in_irq) {
HAL_DMA_Abort_IT(&csi->dma);
} else {
HAL_DMA_Abort(&csi->dma);
} }
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
#endif
#if USE_MDMA
if (!in_irq) {
HAL_MDMA_Abort(&csi->mdma0);
HAL_MDMA_Abort(&csi->mdma1);
}
HAL_MDMA_DeInit(&csi->mdma0);
HAL_MDMA_DeInit(&csi->mdma1);
#endif
#if USE_DCMI
__HAL_DCMI_DISABLE_IT(&csi->dcmi, DCMI_IT_FRAME);
__HAL_DCMI_CLEAR_FLAG(&csi->dcmi, DCMI_FLAG_FRAMERI);
#else
if (!csi->mipi_if) {
HAL_DCMIPP_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE);
} else {
HAL_DCMIPP_CSI_PIPE_Stop(&csi->dcmi, DCMIPP_PIPE, DCMIPP_VIRTUAL_CHANNEL0);
}
for (size_t i=0; i<DCMIPP_NUM_OF_PIPES; i++) {
csi->dcmi.PipeState[i] = HAL_DCMIPP_PIPE_STATE_RESET;
}
#endif
return 0; return 0;
} }
uint32_t omv_csi_get_clk_frequency() { uint32_t omv_csi_get_clk_frequency() {
return (OMV_CSI_TIM_PCLK_FREQ() * 2) / (csi.tim.Init.Period + 1); omv_csi_t *csi = omv_csi_get(-1);
if (!csi->tim.Instance) {
return 0;
}
return (OMV_CSI_TIM_PCLK_FREQ() * 2) / (csi->tim.Init.Period + 1);
} }
// TODO save frequency.
int omv_csi_set_clk_frequency(uint32_t frequency) { int omv_csi_set_clk_frequency(uint32_t frequency) {
#if (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM) #if (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM)
omv_csi_t *csi = omv_csi_get(-1);
if (frequency == 0) { if (frequency == 0) {
if (csi.tim.Init.Period) { if (csi->tim.Init.Period) {
HAL_TIM_PWM_Stop(&csi.tim, OMV_CSI_TIM_CHANNEL); HAL_TIM_PWM_Stop(&csi->tim, OMV_CSI_TIM_CHANNEL);
HAL_TIM_PWM_DeInit(&csi.tim); HAL_TIM_PWM_DeInit(&csi->tim);
memset(&csi.tim, 0, sizeof(csi.tim)); memset(&csi->tim, 0, sizeof(csi->tim));
} }
return 0; return 0;
} }
csi.tim.Instance = OMV_CSI_TIM; csi->tim.Instance = OMV_CSI_TIM;
// TCLK (PCLK * 2) // TCLK (PCLK * 2)
int tclk = OMV_CSI_TIM_PCLK_FREQ() * 2; int tclk = OMV_CSI_TIM_PCLK_FREQ() * 2;
@ -399,20 +388,20 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
int period = fast_ceilf(tclk / ((float) frequency)) - 1; int period = fast_ceilf(tclk / ((float) frequency)) - 1;
int pulse = (period + 1) / 2; int pulse = (period + 1) / 2;
if (csi.tim.Init.Period && (csi.tim.Init.Period != period)) { if (csi->tim.Init.Period && (csi->tim.Init.Period != period)) {
// __HAL_TIM_SET_AUTORELOAD sets csi.tim.Init.Period... // __HAL_TIM_SET_AUTORELOAD sets csi->tim.Init.Period...
__HAL_TIM_SET_AUTORELOAD(&csi.tim, period); __HAL_TIM_SET_AUTORELOAD(&csi->tim, period);
__HAL_TIM_SET_COMPARE(&csi.tim, OMV_CSI_TIM_CHANNEL, pulse); __HAL_TIM_SET_COMPARE(&csi->tim, OMV_CSI_TIM_CHANNEL, pulse);
return 0; return 0;
} }
/* Timer base configuration */ /* Timer base configuration */
csi.tim.Init.Period = period; csi->tim.Init.Period = period;
csi.tim.Init.Prescaler = 0; csi->tim.Init.Prescaler = 0;
csi.tim.Init.CounterMode = TIM_COUNTERMODE_UP; csi->tim.Init.CounterMode = TIM_COUNTERMODE_UP;
csi.tim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; csi->tim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
csi.tim.Init.RepetitionCounter = 0; csi->tim.Init.RepetitionCounter = 0;
csi.tim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; csi->tim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
/* Timer channel configuration */ /* Timer channel configuration */
TIM_OC_InitTypeDef TIMOCHandle; TIM_OC_InitTypeDef TIMOCHandle;
@ -424,9 +413,9 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
TIMOCHandle.OCIdleState = TIM_OCIDLESTATE_RESET; TIMOCHandle.OCIdleState = TIM_OCIDLESTATE_RESET;
TIMOCHandle.OCNIdleState = TIM_OCNIDLESTATE_RESET; TIMOCHandle.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if ((HAL_TIM_PWM_Init(&csi.tim) != HAL_OK) if ((HAL_TIM_PWM_Init(&csi->tim) != HAL_OK)
|| (HAL_TIM_PWM_ConfigChannel(&csi.tim, &TIMOCHandle, OMV_CSI_TIM_CHANNEL) != HAL_OK) || (HAL_TIM_PWM_ConfigChannel(&csi->tim, &TIMOCHandle, OMV_CSI_TIM_CHANNEL) != HAL_OK)
|| (HAL_TIM_PWM_Start(&csi.tim, OMV_CSI_TIM_CHANNEL) != HAL_OK)) { || (HAL_TIM_PWM_Start(&csi->tim, OMV_CSI_TIM_CHANNEL) != HAL_OK)) {
return -1; return -1;
} }
#elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_MCO) #elif (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_MCO)
@ -442,13 +431,13 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
return 0; return 0;
} }
int omv_csi_shutdown(int enable) { int omv_csi_shutdown(omv_csi_t *csi, int enable) {
int ret = 0; int ret = 0;
omv_csi_abort(&csi, true, false); omv_csi_abort(csi, true, false);
if (enable) { if (enable) {
#if defined(OMV_CSI_POWER_PIN) #if defined(OMV_CSI_POWER_PIN)
if (csi.power_pol == OMV_CSI_ACTIVE_HIGH) { if (csi->power_pol == OMV_CSI_ACTIVE_HIGH) {
omv_gpio_write(OMV_CSI_POWER_PIN, 1); omv_gpio_write(OMV_CSI_POWER_PIN, 1);
} else { } else {
omv_gpio_write(OMV_CSI_POWER_PIN, 0); omv_gpio_write(OMV_CSI_POWER_PIN, 0);
@ -456,39 +445,32 @@ int omv_csi_shutdown(int enable) {
#endif #endif
#if USE_DCMI #if USE_DCMI
HAL_NVIC_DisableIRQ(DCMI_IRQn); HAL_NVIC_DisableIRQ(DCMI_IRQn);
HAL_DCMI_DeInit(&csi.dcmi); HAL_DCMI_DeInit(&csi->dcmi);
#endif #endif
} else { } else {
#if defined(OMV_CSI_POWER_PIN) #if defined(OMV_CSI_POWER_PIN)
if (csi.power_pol == OMV_CSI_ACTIVE_HIGH) { if (csi->power_pol == OMV_CSI_ACTIVE_HIGH) {
omv_gpio_write(OMV_CSI_POWER_PIN, 0); omv_gpio_write(OMV_CSI_POWER_PIN, 0);
} else { } else {
omv_gpio_write(OMV_CSI_POWER_PIN, 1); omv_gpio_write(OMV_CSI_POWER_PIN, 1);
} }
#endif #endif
ret = omv_csi_config(OMV_CSI_CONFIG_INIT); ret = omv_csi_config(csi, OMV_CSI_CONFIG_INIT);
} }
mp_hal_delay_ms(10); mp_hal_delay_ms(10);
return ret; return ret;
} }
static void omv_csi_vsync_callback(void *data) { int omv_csi_set_vsync_callback(omv_csi_t *csi, omv_csi_cb_t cb) {
if (csi.vsync_callback != NULL) { if (cb.fun == NULL) {
csi.vsync_callback(omv_gpio_read(OMV_CSI_VSYNC_PIN));
}
}
int omv_csi_set_vsync_callback(vsync_cb_t vsync_cb) {
csi.vsync_callback = vsync_cb;
if (csi.vsync_callback == NULL) {
#if (DCMI_VSYNC_EXTI_SHARED == 0) #if (DCMI_VSYNC_EXTI_SHARED == 0)
// Disable VSYNC EXTI IRQ // Disable VSYNC EXTI IRQ
omv_gpio_irq_enable(OMV_CSI_VSYNC_PIN, false); omv_gpio_irq_enable(OMV_CSI_VSYNC_PIN, false);
#endif #endif
} else { } else {
// Enable VSYNC EXTI IRQ // Enable VSYNC EXTI IRQ
omv_gpio_irq_register(OMV_CSI_VSYNC_PIN, omv_csi_vsync_callback, NULL); omv_gpio_irq_register(OMV_CSI_VSYNC_PIN, cb.fun, cb.arg);
omv_gpio_irq_enable(OMV_CSI_VSYNC_PIN, true); omv_gpio_irq_enable(OMV_CSI_VSYNC_PIN, true);
} }
return 0; return 0;
@ -498,17 +480,16 @@ int omv_csi_set_vsync_callback(vsync_cb_t vsync_cb) {
// If the image is cropped by more than 1 word in width, align the line start to a word // If the image is cropped by more than 1 word in width, align the line start to a word
// address to improve copy performance. Do not crop by more than 1 word as this will // 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. // 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) { static uint32_t get_dcmi_hw_crop(omv_csi_t *csi, uint32_t bytes_per_pixel) {
framebuffer_t *fb = csi.fb; framebuffer_t *fb = csi->fb;
uint32_t byte_x_offset = (fb->x * bytes_per_pixel) % sizeof(uint32_t); 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 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))) { if (byte_x_offset && (width_remainder >= (sizeof(uint32_t) - byte_x_offset))) {
x_crop = byte_x_offset; return byte_x_offset;
} }
return x_crop; return 0;
} }
#endif #endif
@ -517,7 +498,9 @@ void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) {
#else #else
void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe) { void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe) {
#endif #endif
framebuffer_t *fb = csi.fb; omv_csi_t *csi = omv_csi_get(-1);
framebuffer_t *fb = csi->fb;
#if USE_MDMA #if USE_MDMA
// Clear out any stale flags. // Clear out any stale flags.
DMA2->LIFCR = DMA_FLAG_TCIF1_5 | DMA_FLAG_HTIF1_5; DMA2->LIFCR = DMA_FLAG_TCIF1_5 | DMA_FLAG_HTIF1_5;
@ -526,9 +509,9 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p
#endif #endif
// Reset DCMI_DMAConvCpltUser frame drop state. // Reset DCMI_DMAConvCpltUser frame drop state.
csi.first_line = false; csi->first_line = false;
if (csi.drop_frame) { if (csi->drop_frame) {
csi.drop_frame = false; csi->drop_frame = false;
// Reset the buffer's state if the frame was dropped. // Reset the buffer's state if the frame was dropped.
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
if (buffer) { if (buffer) {
@ -539,15 +522,15 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p
framebuffer_get_tail(fb, FB_NO_FLAGS); framebuffer_get_tail(fb, FB_NO_FLAGS);
if (csi.frame_callback) { if (csi->frame_cb.fun) {
csi.frame_callback(); csi->frame_cb.fun(csi->frame_cb.arg);
} }
#if USE_DCMIPP #if USE_DCMIPP
// Get the destination buffer address. // Get the destination buffer address.
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
if (buffer == NULL) { if (buffer == NULL) {
omv_csi_abort(&csi, false, false); omv_csi_abort(csi, false, false);
} else { } else {
HAL_DCMIPP_PIPE_SetMemoryAddress(dcmipp, pipe, DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data); HAL_DCMIPP_PIPE_SetMemoryAddress(dcmipp, pipe, DCMIPP_MEMORY_ADDRESS_0, (uint32_t) buffer->data);
} }
@ -560,14 +543,15 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p
// Using line buffers allows performing post-processing before writing the frame to the // Using line buffers allows performing post-processing before writing the frame to the
// framebuffer, and help hide external RAM latency. // framebuffer, and help hide external RAM latency.
void DCMI_DMAConvCpltUser(uint32_t addr) { void DCMI_DMAConvCpltUser(uint32_t addr) {
framebuffer_t *fb = csi.fb; omv_csi_t *csi = omv_csi_get(-1);
framebuffer_t *fb = csi->fb;
// Throttle frames to match the current frame rate. // Throttle frames to match the current frame rate.
omv_csi_throttle_framerate(); omv_csi_throttle_framerate(csi);
if (csi.drop_frame) { if (csi->drop_frame) {
#if USE_MDMA #if USE_MDMA
if (!csi.transpose) { if (!csi->transpose) {
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
} }
#endif #endif
@ -576,17 +560,17 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK); vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
if (buffer == NULL) { if (buffer == NULL) {
omv_csi_abort(&csi, false, true); omv_csi_abort(csi, false, true);
return; return;
} }
if (csi.pixformat == PIXFORMAT_JPEG) { if (csi->pixformat == PIXFORMAT_JPEG) {
if (csi.jpg_format == 3) { if (csi->jpg_format == 3) {
// JPEG MODE 3: Variable line width per frame, with the last line potentially shorter and // JPEG MODE 3: Variable line width per frame, with the last line potentially shorter and
// no padding. `offset` is incremented once every max transfer, and the DMA counter holds // no padding. `offset` is incremented once every max transfer, and the DMA counter holds
// the total size. // the total size.
buffer->offset += 1; buffer->offset += 1;
} else if (csi.jpg_format == 4) { } else if (csi->jpg_format == 4) {
// JPEG MODE 4: Fixed width and height per frame. Each line starts with two bytes indicating // JPEG MODE 4: Fixed width and height per frame. Each line starts with two bytes indicating
// valid data length, followed by image data and optional padding (0xFF). `offset` holds the // valid data length, followed by image data and optional padding (0xFF). `offset` holds the
// total size. // total size.
@ -604,41 +588,41 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
#if USE_MDMA #if USE_MDMA
// DCMI_DMAConvCpltUser is called with the other MAR register. // DCMI_DMAConvCpltUser is called with the other MAR register.
// So, we have to fix the address in full MDMA offload mode. // So, we have to fix the address in full MDMA offload mode.
if (!csi.transpose) { if (!csi->transpose) {
addr = (uint32_t) &_line_buf; addr = (uint32_t) &_line_buf;
} }
#endif #endif
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi);
uint8_t *src = ((uint8_t *) addr) + (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(csi, bytes_per_pixel);
uint8_t *dst = buffer->data; uint8_t *dst = buffer->data;
if (csi.pixformat == PIXFORMAT_GRAYSCALE) { if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
bytes_per_pixel = sizeof(uint8_t); bytes_per_pixel = sizeof(uint8_t);
} }
// For all non-JPEG and non-transposed modes image capture can be completely offload to MDMA. // For all non-JPEG and non-transposed modes image capture can be completely offload to MDMA.
#if USE_MDMA #if USE_MDMA
if (!csi.transpose) { if (!csi->transpose) {
// NOTE: MDMA is started here, not in FRAME/VSYNC callbacks, to maximize the time before // NOTE: MDMA is started here, not in FRAME/VSYNC callbacks, to maximize the time before
// the frame has to be dropped. // the frame has to be dropped.
uint32_t line_width_bytes = 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. // mdma0 will copy this line of the image to the final destination.
__HAL_UNLOCK(&csi.mdma0); __HAL_UNLOCK(&csi->mdma0);
csi.mdma0.State = HAL_MDMA_STATE_READY; csi->mdma0.State = HAL_MDMA_STATE_READY;
HAL_MDMA_Start(&csi.mdma0, (uint32_t) src, (uint32_t) dst, HAL_MDMA_Start(&csi->mdma0, (uint32_t) src, (uint32_t) dst,
line_width_bytes, 1); line_width_bytes, 1);
// mdma1 will copy all remaining lines of the image to the final destination. // mdma1 will copy all remaining lines of the image to the final destination.
__HAL_UNLOCK(&csi.mdma1); __HAL_UNLOCK(&csi->mdma1);
csi.mdma1.State = HAL_MDMA_STATE_READY; csi->mdma1.State = HAL_MDMA_STATE_READY;
HAL_MDMA_Start(&csi.mdma1, (uint32_t) src, (uint32_t) (dst + line_width_bytes), HAL_MDMA_Start(&csi->mdma1, (uint32_t) src, (uint32_t) (dst + line_width_bytes),
line_width_bytes, fb->v - 1); line_width_bytes, fb->v - 1);
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn); HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
return; return;
} }
#endif #endif
if (!csi.transpose) { if (!csi->transpose) {
dst += fb->u * bytes_per_pixel * buffer->offset++; dst += fb->u * bytes_per_pixel * buffer->offset++;
} else { } else {
dst += bytes_per_pixel * buffer->offset++; dst += bytes_per_pixel * buffer->offset++;
@ -646,9 +630,9 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
#if USE_MDMA #if USE_MDMA
// Two MDMA channels are used to maximize the time available for each channel to finish the transfer. // Two MDMA channels are used to maximize the time available for each channel to finish the transfer.
omv_csi_copy_line((buffer->offset % 2) ? &csi.mdma1 : &csi.mdma0, src, dst); omv_csi_copy_line(csi, (buffer->offset % 2) ? &csi->mdma1 : &csi->mdma0, src, dst);
#else #else
omv_csi_copy_line(NULL, src, dst); omv_csi_copy_line(csi, NULL, src, dst);
#endif #endif
} }
#endif #endif
@ -676,7 +660,7 @@ static void omv_csi_mdma_config(omv_csi_t *csi, MDMA_InitTypeDef *init, uint32_t
init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; init->Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
} }
uint32_t line_offset_bytes = (fb->x * bytes_per_pixel) - get_dcmi_hw_crop(bytes_per_pixel); uint32_t line_offset_bytes = (fb->x * bytes_per_pixel) - get_dcmi_hw_crop(csi, bytes_per_pixel);
uint32_t line_width_bytes = fb->u * bytes_per_pixel; uint32_t line_width_bytes = fb->u * bytes_per_pixel;
if (csi->transpose) { if (csi->transpose) {
@ -750,13 +734,13 @@ static void omv_csi_mdma_enable(omv_csi_t *csi, uint32_t bytes_per_pixel) {
} }
} }
int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed) { int omv_csi_dma_memcpy(omv_csi_t *csi, void *dma, void *dst, void *src, int bpp, bool transposed) {
framebuffer_t *fb = csi.fb; framebuffer_t *fb = csi->fb;
MDMA_HandleTypeDef *handle = dma; MDMA_HandleTypeDef *handle = dma;
// Drop the frame if MDMA is not keeping up as the image will be corrupted. // Drop the frame if MDMA is not keeping up as the image will be corrupted.
if (handle->Instance->CCR & MDMA_CCR_EN) { if (handle->Instance->CCR & MDMA_CCR_EN) {
csi.drop_frame = true; csi->drop_frame = true;
return 0; return 0;
} }
@ -793,7 +777,7 @@ void omv_csi_update_awb(omv_csi_t *csi, uint32_t n_pixels) {
//printf("Luminance: %f AVG_R: %lu, AVG_G: %lu, AVG_B: %lu\n", (double) luminance, avg[0], avg[1], avg[2]); //printf("Luminance: %f AVG_R: %lu, AVG_G: %lu, AVG_B: %lu\n", (double) luminance, avg[0], avg[1], avg[2]);
if (csi->ioctl) { if (csi->ioctl) {
omv_csi_ioctl(OMV_CSI_IOCTL_UPDATE_AGC_AEC, fast_floorf(luminance)); omv_csi_ioctl(csi, OMV_CSI_IOCTL_UPDATE_AGC_AEC, fast_floorf(luminance));
} }
// Calculate average and exposure factors for each channel (R, G, B) // Calculate average and exposure factors for each channel (R, G, B)
@ -820,8 +804,7 @@ void omv_csi_update_awb(omv_csi_t *csi, uint32_t n_pixels) {
} }
#endif #endif
// This is the default snapshot function, which can be replaced in omv_csi_init functions. static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
uint32_t length = 0; uint32_t length = 0;
framebuffer_t *fb = csi->fb; framebuffer_t *fb = csi->fb;
@ -835,11 +818,13 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
// Compress the framebuffer for the IDE preview, if not the first frame, the // 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 is enabled, and the image sensor doesn't support JPEG encoding.
framebuffer_update_jpeg_buffer(fb); if (flags & OMV_CSI_CAPTURE_FLAGS_UPDATE) {
framebuffer_update_jpeg_buffer(fb);
}
// Ensure that the raw frame fits into the FB. It will be switched from RGB565 to BAYER // 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. // first to save space before being cropped until it fits.
omv_csi_auto_crop_framebuffer(); omv_csi_auto_crop_framebuffer(csi);
// Restore frame buffer 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. // Note that JPEG compression is done first on the framebuffer with the user settings.
@ -864,8 +849,8 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
#if USE_DCMI #if USE_DCMI
// Setup the size and address of the transfer // Setup the size and address of the transfer
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(); uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi);
uint32_t x_crop = get_dcmi_hw_crop(bytes_per_pixel); uint32_t x_crop = get_dcmi_hw_crop(csi, bytes_per_pixel);
uint32_t line_width_bytes = resolution[csi->framesize][0] * bytes_per_pixel; uint32_t line_width_bytes = resolution[csi->framesize][0] * bytes_per_pixel;
// Shrink the captured pixel count by one word to allow cropping to fix alignment. // Shrink the captured pixel count by one word to allow cropping to fix alignment.
@ -928,7 +913,7 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
(uint32_t) &_line_buf, length / sizeof(uint32_t), h); (uint32_t) &_line_buf, length / sizeof(uint32_t), h);
} }
#else #else
uint32_t bytes_per_pixel = omv_csi_get_dst_bpp(); uint32_t bytes_per_pixel = omv_csi_get_dst_bpp(csi);
uint32_t line_width_bytes = fb->u * bytes_per_pixel; uint32_t line_width_bytes = fb->u * bytes_per_pixel;
if (!line_width_bytes || if (!line_width_bytes ||
@ -1089,7 +1074,8 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
} }
int omv_csi_init() { int omv_csi_init() {
int init_ret = 0; int ret = 0;
static omv_i2c_t i2c;
// List of I2C buses to scan. // List of I2C buses to scan.
uint32_t buses[][2] = { uint32_t buses[][2] = {
@ -1099,47 +1085,54 @@ int omv_csi_init() {
#endif #endif
}; };
// Reset the csi state // Initialize the CSIs using this driver's ops as defaults,
memset(&csi, 0, sizeof(omv_csi_t)); // which can be overridden by sensor drivers during probe.
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
omv_csi_t *csi = &csi_all[i];
// Set default framebuffer memset(csi, 0, sizeof(omv_csi_t));
csi.fb = framebuffer_get(0); csi->i2c = &i2c;
csi->fb = framebuffer_get(-1);
// Set I2C bus csi->abort = stm_csi_abort;
csi.i2c = &csi_i2c; csi->config = stm_csi_config;
csi->snapshot = stm_csi_snapshot;
// Set default snapshot function. csi->color_palette = rainbow_table;
csi.snapshot = omv_csi_snapshot; }
// Configure the csi external clock (XCLK). // Configure the csi external clock (XCLK).
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) { if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
return OMV_CSI_ERROR_TIM_INIT_FAILED; return OMV_CSI_ERROR_TIM_INIT_FAILED;
} }
// Detect and initialize the image sensor. // Detect and initialize sensor(s).
for (uint32_t i = 0, n_buses = OMV_ARRAY_SIZE(buses); i < n_buses; i++) { for (uint32_t i = 0, n_buses = OMV_ARRAY_SIZE(buses); i < n_buses; i++) {
uint32_t id = buses[i][0], speed = buses[i][1]; // Initialize the camera bus.
if ((init_ret = omv_csi_probe_init(id, speed)) == 0) { omv_i2c_init(&i2c, buses[i][0], buses[i][1]);
if (!(ret = omv_csi_probe(&i2c))) {
break; break;
} }
omv_i2c_deinit(csi.i2c);
omv_i2c_deinit(&i2c);
// Scan the next bus or fail if this is the last one. // Scan the next bus or fail if this is the last one.
if ((i + 1) == n_buses) { if ((i + 1) == n_buses) {
return init_ret; return ret;
} }
} }
// Configure the DCMI interface. // Configure the DCMI interface.
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) { for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
return OMV_CSI_ERROR_CSI_INIT_FAILED; omv_csi_t *csi = &csi_all[i];
if (omv_csi_config(csi, OMV_CSI_CONFIG_INIT) != 0) {
return OMV_CSI_ERROR_CSI_INIT_FAILED;
}
csi->detected = true;
} }
// Clear fb_enabled flag. // Clear fb_enabled flag.
JPEG_FB()->enabled = 0; JPEG_FB()->enabled = 0;
// Set default color palette.
csi.color_palette = rainbow_table;
csi.detected = true;
return 0; return 0;
} }