mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
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
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:
commit
190aecc45c
5
Makefile
5
Makefile
@ -138,6 +138,11 @@ MPY_CFLAGS += -DMICROPY_PY_CSI=1
|
||||
MPY_MKARGS += MICROPY_PY_CSI=1
|
||||
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)
|
||||
MPY_CFLAGS += -DMICROPY_PY_FIR=1
|
||||
MPY_MKARGS += MICROPY_PY_FIR=1
|
||||
|
@ -15,6 +15,7 @@ OMV_BOARD_CFLAGS = -DCORE_CM7 \
|
||||
OMV_ENABLE_BL=0
|
||||
OMV_ENABLE_UVC=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_LWIP = 1
|
||||
|
@ -14,6 +14,7 @@ OMV_BOARD_CFLAGS = -DNRF52_SERIES \
|
||||
-DCONFIG_GPIO_AS_PINRESET
|
||||
DFU_DEVICE=0x0483:0xdf11
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB=1
|
||||
MICROPY_PY_AUDIO=1
|
||||
|
@ -73,7 +73,7 @@ PIXEL_LOOP:
|
||||
.wrap
|
||||
|
||||
% 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) {
|
||||
uint offset;
|
||||
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);
|
||||
|
||||
if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
offset = pio_add_program(OMV_CSI_PIO, &dcmi_odd_byte_program);
|
||||
config = dcmi_odd_byte_program_get_default_config(offset);
|
||||
} else {
|
||||
|
@ -1,4 +1,5 @@
|
||||
set(MICROPY_PY_CSI 1)
|
||||
set(MICROPY_PY_CSI_NG 1)
|
||||
set(MICROPY_PY_FIR 1)
|
||||
set(MICROPY_PY_ULAB 1)
|
||||
set(MICROPY_PY_NINAW10 1)
|
||||
|
@ -17,6 +17,7 @@ OMV_ENABLE_BL = 0
|
||||
OMV_ENABLE_UVC = 1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 0
|
||||
|
@ -16,6 +16,7 @@ OMV_ENABLE_BL=0
|
||||
OMV_ENABLE_UVC=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 0
|
||||
|
@ -11,5 +11,6 @@ OMV_ENABLE_BL=1
|
||||
OMV_ENABLE_UVC=1
|
||||
OMV_BOARD_CFLAGS=-DUSE_USB_FS
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_WINC1500 = 1
|
||||
$(error "This camera is no longer supported :O Have fun hacking the code $(BRACKET)")
|
||||
|
@ -11,6 +11,7 @@ OMV_ENABLE_BL=1
|
||||
OMV_ENABLE_UVC=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_WINC1500 = 1
|
||||
MICROPY_PY_DISPLAY = 1
|
||||
|
@ -11,6 +11,7 @@ OMV_ENABLE_BL=1
|
||||
OMV_ENABLE_UVC=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 1
|
||||
|
@ -12,6 +12,7 @@ OMV_ENABLE_BL=1
|
||||
OMV_ENABLE_UVC=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 1
|
||||
|
@ -13,6 +13,7 @@ OMV_ENABLE_UVC=1
|
||||
OMV_GENX320_ENABLE=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 1
|
||||
|
@ -12,6 +12,7 @@ OMV_ENABLE_BL=1
|
||||
OMV_ENABLE_UVC=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 1
|
||||
|
@ -249,6 +249,7 @@ extern unsigned char OMV_BOARD_UID_ADDR[12]; // Unique address.
|
||||
// CSI I2C bus
|
||||
#define OMV_CSI_I2C_ID (0)
|
||||
#define OMV_CSI_I2C_SPEED (OMV_I2C_SPEED_FULL)
|
||||
#define OMV_CSI_I2C_REINIT (0)
|
||||
|
||||
// FIR I2C bus
|
||||
#define OMV_FIR_I2C_ID (1)
|
||||
|
@ -14,6 +14,7 @@ OMV_HSE_VALUE=12000000
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
OMV_TOF_VL53L8CX_ENABLE=1
|
||||
MICROPY_PY_CSI = $(CORE_M55_HP)
|
||||
MICROPY_PY_CSI_NG = $(CORE_M55_HP)
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_TOF = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
|
@ -52,6 +52,7 @@
|
||||
#define OMV_LEPTON_ENABLE (1)
|
||||
#define OMV_PAG7936_ENABLE (1)
|
||||
#define OMV_PAG7936_MIPI_CSI2 (1)
|
||||
#define OMV_SOFTCSI_ENABLE (0)
|
||||
#define OMV_PS5520_ENABLE (1)
|
||||
#define OMV_GENX320_EHC_ENABLE (1)
|
||||
#define OMV_GENX320_HSYNC_VALUE (0x1)
|
||||
@ -279,6 +280,7 @@
|
||||
#define OMV_CSI_DMA_REQUEST (HPDMA1_REQUEST_DCMI_PSSI)
|
||||
#define OMV_CSI_DMA_MEMCPY_ENABLE (0)
|
||||
#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_D1_PIN (&omv_pin_A10_DCMI)
|
||||
|
@ -27,6 +27,7 @@ OMV_BOSON_ENABLE=1
|
||||
OMV_GENX320_ENABLE=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_IMU = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
|
@ -202,6 +202,7 @@
|
||||
#define OMV_CSI_DMA_MEMCPY_ENABLE (1)
|
||||
#define OMV_CSI_HW_SWAP_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_D1_PIN (&omv_pin_DCMI_D1)
|
||||
|
@ -12,6 +12,7 @@ OMV_BOSON_ENABLE=1
|
||||
OMV_GENX320_ENABLE=1
|
||||
OMV_LEPTON_SDK_ENABLE=1
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 1
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_LWIP = 1
|
||||
|
@ -57,7 +57,7 @@ PIXEL_LOOP:
|
||||
.wrap
|
||||
|
||||
% 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) {
|
||||
uint offset;
|
||||
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);
|
||||
|
||||
if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
offset = pio_add_program(OMV_CSI_PIO, &dcmi_odd_byte_program);
|
||||
config = dcmi_odd_byte_program_get_default_config(offset);
|
||||
} else {
|
||||
|
@ -1,4 +1,5 @@
|
||||
set(MICROPY_PY_CSI 1)
|
||||
set(MICROPY_PY_CSI_NG 1)
|
||||
set(MICROPY_PY_FIR 1)
|
||||
set(MICROPY_PY_ULAB 1)
|
||||
set(MICROPY_PY_NINAW10 0)
|
||||
|
1272
common/omv_csi.c
1272
common/omv_csi.c
File diff suppressed because it is too large
Load Diff
151
common/omv_csi.h
151
common/omv_csi.h
@ -50,7 +50,7 @@
|
||||
#define FROGEYE2020_SLV_ADDR (0x6E)
|
||||
#define PAG7920_SLV_ADDR (0x80)
|
||||
#define PAG7936_SLV_ADDR (0x80)
|
||||
#define SOFTCSI_SLV_ADDR (0x7f)
|
||||
#define PAG7936_SLV_ADDR_ALT (0x2A)
|
||||
#define PS5520_SLV_ADDR (0x90)
|
||||
|
||||
// Chip ID Registers
|
||||
@ -104,6 +104,10 @@
|
||||
#define OMV_CSI_CLK_SOURCE_TIM (1U)
|
||||
#define OMV_CSI_CLK_SOURCE_OSC (2U)
|
||||
|
||||
#ifndef OMV_CSI_MAX_DEVICES
|
||||
#define OMV_CSI_MAX_DEVICES (1)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OMV_CSI_ACTIVE_LOW = 0,
|
||||
OMV_CSI_ACTIVE_HIGH = 1
|
||||
@ -189,6 +193,11 @@ typedef enum {
|
||||
OMV_CSI_IOCTL_FLAGS_ABORT = (1 << 8),
|
||||
} 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 {
|
||||
OMV_CSI_IOCTL_SET_READOUT_WINDOW = 0x00 | OMV_CSI_IOCTL_FLAGS_ABORT,
|
||||
OMV_CSI_IOCTL_GET_READOUT_WINDOW = 0x01,
|
||||
@ -250,6 +259,7 @@ typedef enum {
|
||||
OMV_CSI_ERROR_FRAMEBUFFER_ERROR = -18,
|
||||
OMV_CSI_ERROR_FRAMEBUFFER_OVERFLOW = -19,
|
||||
OMV_CSI_ERROR_JPEG_OVERFLOW = -20,
|
||||
OMV_CSI_ERROR_WOULD_BLOCK = -21,
|
||||
} omv_csi_error_t;
|
||||
|
||||
#if (OMV_GENX320_ENABLE == 1)
|
||||
@ -270,10 +280,13 @@ typedef enum {
|
||||
} omv_csi_genx320_bias_t;
|
||||
#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_callback_t {
|
||||
void (*fun) (void *);
|
||||
void *arg;
|
||||
} omv_csi_cb_t;
|
||||
|
||||
typedef struct _omv_csi {
|
||||
uint32_t chip_id; // Sensor ID 32 bits.
|
||||
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 mipi_if : 1; // CSI-2 interface.
|
||||
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.
|
||||
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.
|
||||
|
||||
vsync_cb_t vsync_callback; // VSYNC callback.
|
||||
frame_cb_t frame_callback; // Frame callback.
|
||||
omv_csi_cb_t vsync_cb; // VSYNC callback
|
||||
omv_csi_cb_t frame_cb; // Frame callback
|
||||
|
||||
// Sensor state
|
||||
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.
|
||||
|
||||
omv_i2c_t *i2c; // SCCB/I2C bus.
|
||||
|
||||
framebuffer_t *fb; // Frame buffer pointer
|
||||
|
||||
#ifdef OMV_CSI_PORT_BITS
|
||||
// Additional port-specific members like device base pointer,
|
||||
// dma handles, more I/Os etc... are included directly here,
|
||||
// so that they can be accessible from this struct.
|
||||
// DMA handles, and additional I/Os, are included directly here.
|
||||
OMV_CSI_PORT_BITS
|
||||
#endif
|
||||
|
||||
// Can be used by port or image sensor drivers to store private
|
||||
// data, internal state, or additional operations.
|
||||
void *priv;
|
||||
|
||||
// Sensor function pointers
|
||||
int (*reset) (omv_csi_t *csi);
|
||||
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 (*write_reg) (omv_csi_t *csi, uint16_t reg_addr, uint16_t reg_data);
|
||||
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_lens_correction) (omv_csi_t *csi, int enable, int radi, int coef);
|
||||
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);
|
||||
} omv_csi_t;
|
||||
|
||||
extern omv_csi_t csi;
|
||||
extern omv_i2c_t csi_i2c;
|
||||
// CSI array
|
||||
extern omv_csi_t csi_all[OMV_CSI_MAX_DEVICES];
|
||||
|
||||
// Resolution table
|
||||
extern uint16_t resolution[][2];
|
||||
|
||||
// Initialize the sensor state.
|
||||
// Resets the sensor state on soft-reboots.
|
||||
void omv_csi_init0();
|
||||
|
||||
// Initialize the sensor and probe the image sensor.
|
||||
// Initializes CSI struct with default ops.
|
||||
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.
|
||||
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
|
||||
// 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..
|
||||
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq);
|
||||
|
||||
// Reset the sensor to its default state.
|
||||
int omv_csi_reset();
|
||||
int omv_csi_reset(omv_csi_t *csi, bool hard);
|
||||
|
||||
// Return csi PID.
|
||||
int omv_csi_get_id();
|
||||
int omv_csi_get_id(omv_csi_t *csi);
|
||||
|
||||
// Returns the xclk freq in hz.
|
||||
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);
|
||||
|
||||
// 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.
|
||||
int omv_csi_sleep(int enable);
|
||||
int omv_csi_sleep(omv_csi_t *csi, int enable);
|
||||
|
||||
// Shutdown mode.
|
||||
int omv_csi_shutdown(int enable);
|
||||
int omv_csi_shutdown(omv_csi_t *csi, int enable);
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
bool omv_csi_get_cropped();
|
||||
bool omv_csi_get_cropped(omv_csi_t *csi);
|
||||
|
||||
// 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).
|
||||
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).
|
||||
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).
|
||||
int omv_csi_set_saturation(int level);
|
||||
int omv_csi_set_saturation(omv_csi_t *csi, int level);
|
||||
|
||||
// Set the sensor AGC gain ceiling.
|
||||
// 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).
|
||||
int omv_csi_set_quality(int qs);
|
||||
int omv_csi_set_quality(omv_csi_t *csi, int qs);
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
int omv_csi_set_hmirror(int enable);
|
||||
int omv_csi_set_hmirror(omv_csi_t *csi, int enable);
|
||||
|
||||
// Get hmirror status.
|
||||
bool omv_csi_get_hmirror();
|
||||
bool omv_csi_get_hmirror(omv_csi_t *csi);
|
||||
|
||||
// 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.
|
||||
bool omv_csi_get_vflip();
|
||||
bool omv_csi_get_vflip(omv_csi_t *csi);
|
||||
|
||||
// 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.
|
||||
bool omv_csi_get_transpose();
|
||||
bool omv_csi_get_transpose(omv_csi_t *csi);
|
||||
|
||||
// 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.
|
||||
bool omv_csi_get_auto_rotation();
|
||||
bool omv_csi_get_auto_rotation(omv_csi_t *csi);
|
||||
|
||||
// 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.
|
||||
void omv_csi_throttle_framerate();
|
||||
void omv_csi_throttle_framerate(omv_csi_t *csi);
|
||||
|
||||
// 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
|
||||
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
|
||||
int omv_csi_ioctl(int request, ...);
|
||||
int omv_csi_ioctl(omv_csi_t *csi, int request, ...);
|
||||
|
||||
// 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.
|
||||
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
|
||||
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
|
||||
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.
|
||||
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).
|
||||
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
|
||||
// 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.
|
||||
int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags);
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "imlib.h"
|
||||
#if MICROPY_PY_CSI
|
||||
#if MICROPY_PY_CSI || MICROPY_PY_CSI_NG
|
||||
#include "omv_i2c.h"
|
||||
#include "omv_csi.h"
|
||||
#endif
|
||||
@ -146,8 +146,9 @@ void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
|
||||
case USBDBG_SENSOR_ID: {
|
||||
uint32_t buffer = 0xFF;
|
||||
#if MICROPY_PY_CSI
|
||||
if (omv_csi_is_detected() == true) {
|
||||
buffer = omv_csi_get_id();
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
if (omv_csi_is_detected(csi) == true) {
|
||||
buffer = omv_csi_get_id(csi);
|
||||
}
|
||||
#endif
|
||||
cmd = USBDBG_NONE;
|
||||
@ -363,21 +364,24 @@ void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback) {
|
||||
struct {
|
||||
int32_t name;
|
||||
int32_t value;
|
||||
}
|
||||
attr;
|
||||
} attr;
|
||||
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
|
||||
read_callback(&attr, sizeof(attr));
|
||||
|
||||
switch (attr.name) {
|
||||
case OMV_CSI_ATTR_CONTRAST:
|
||||
omv_csi_set_contrast(attr.value);
|
||||
omv_csi_set_contrast(csi, attr.value);
|
||||
break;
|
||||
case OMV_CSI_ATTR_BRIGHTNESS:
|
||||
omv_csi_set_brightness(attr.value);
|
||||
omv_csi_set_brightness(csi, attr.value);
|
||||
break;
|
||||
case OMV_CSI_ATTR_SATURATION:
|
||||
omv_csi_set_saturation(attr.value);
|
||||
omv_csi_set_saturation(csi, attr.value);
|
||||
break;
|
||||
case OMV_CSI_ATTR_GAINCEILING:
|
||||
omv_csi_set_gainceiling(attr.value);
|
||||
omv_csi_set_gainceiling(csi, attr.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
#define LEPTON_BOOT_TIMEOUT (1000)
|
||||
#define LEPTON_SNAPSHOT_RETRY (3)
|
||||
#define LEPTON_SNAPSHOT_TIMEOUT (10000)
|
||||
#define LEPTON_SNAPSHOT_TIMEOUT (5000)
|
||||
|
||||
// Min/Max temperatures in Celsius.
|
||||
#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 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) {
|
||||
uint16_t reg_data;
|
||||
if (omv_i2c_readw2(csi->i2c, csi->slv_addr, reg_addr, ®_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) {
|
||||
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_AGC_ROI_T roi;
|
||||
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)) {
|
||||
if (LEP_OpenPort(csi->i2c, LEP_CCI_TWI, 0, &lepton.port) == LEP_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) {
|
||||
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)) {
|
||||
LEP_SDK_BOOT_STATUS_E status;
|
||||
if (LEP_GetCameraBootStatus(&lepton.port, &status) != LEP_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status == LEP_BOOT_STATUS_BOOTED) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) {
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(status & LEP_I2C_STATUS_BUSY_BIT_MASK)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mp_hal_ticks_ms() - start) >= LEPTON_BOOT_TIMEOUT) {
|
||||
return -1;
|
||||
}
|
||||
@ -357,6 +357,14 @@ static int lepton_reset(omv_csi_t *csi, bool measurement_mode, bool high_temp_mo
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
csi->reset = reset;
|
||||
csi->sleep = sleep;
|
||||
csi->config = NULL;
|
||||
csi->abort = NULL;
|
||||
csi->match = match;
|
||||
csi->snapshot = snapshot;
|
||||
csi->read_reg = read_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->ioctl = ioctl;
|
||||
|
||||
csi->auxiliary = 1;
|
||||
csi->vsync_pol = 1;
|
||||
csi->hsync_pol = 0;
|
||||
csi->pixck_pol = 0;
|
||||
csi->frame_sync = 0;
|
||||
csi->mono_bpp = 1;
|
||||
|
||||
// Extra delay after power-on
|
||||
mp_hal_delay_ms(1000);
|
||||
|
||||
if (reset(csi) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -68,7 +68,9 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
framebuffer_update_jpeg_buffer(fb);
|
||||
if (flags & OMV_CSI_CAPTURE_FLAGS_UPDATE) {
|
||||
framebuffer_update_jpeg_buffer(fb);
|
||||
}
|
||||
|
||||
if (fb->n_buffers != 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) {
|
||||
csi->reset = reset;
|
||||
csi->abort = NULL;
|
||||
csi->config = NULL;
|
||||
csi->set_pixformat = set_pixformat;
|
||||
csi->set_framesize = set_framesize;
|
||||
csi->set_hmirror = set_hmirror;
|
||||
csi->set_vflip = set_vflip;
|
||||
csi->snapshot = snapshot;
|
||||
|
||||
csi->auxiliary = 1;
|
||||
csi->vsync_pol = 1;
|
||||
csi->hsync_pol = 0;
|
||||
csi->pixck_pol = 0;
|
||||
|
@ -42,26 +42,28 @@ extern char _jpeg_memory_end;
|
||||
jpegbuffer_t *jpegbuffer = (jpegbuffer_t *) &_jpeg_memory_start;
|
||||
|
||||
void framebuffer_init0() {
|
||||
// Save fb_enabled flag state
|
||||
// Save enable flag.
|
||||
int fb_enabled = jpegbuffer->enabled;
|
||||
uint32_t fb_size = (char *) &_fb_memory_end - (char *) framebuffer->data;
|
||||
|
||||
// Clear framebuffers
|
||||
memset(framebuffer, 0, sizeof(*framebuffer));
|
||||
// Initialize frame buffer.
|
||||
framebuffer_init_fb(framebuffer, fb_size, false);
|
||||
|
||||
// Initialize jpeg buffer.
|
||||
memset(jpegbuffer, 0, sizeof(*jpegbuffer));
|
||||
|
||||
mutex_init0(&jpegbuffer->lock);
|
||||
|
||||
// Enable streaming.
|
||||
framebuffer->streaming_enabled = true; // controlled by the OpenMV Cam.
|
||||
|
||||
// Set default quality
|
||||
jpegbuffer->enabled = fb_enabled;
|
||||
jpegbuffer->quality = ((OMV_JPEG_QUALITY_HIGH - OMV_JPEG_QUALITY_LOW) / 2) + OMV_JPEG_QUALITY_LOW;
|
||||
}
|
||||
|
||||
// Set fb_enabled
|
||||
jpegbuffer->enabled = fb_enabled; // controlled by the IDE.
|
||||
void framebuffer_init_fb(framebuffer_t *fb, size_t size, bool dynamic) {
|
||||
// Clear framebuffers
|
||||
memset(fb, 0, sizeof(*fb));
|
||||
|
||||
// Setup buffering.
|
||||
framebuffer_set_buffers(framebuffer, 1);
|
||||
fb->raw_size = size;
|
||||
fb->streaming_enabled = true;
|
||||
fb->dynamic = dynamic;
|
||||
framebuffer_set_buffers(fb, 1);
|
||||
}
|
||||
|
||||
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.
|
||||
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);
|
||||
|
||||
// No fb_alloc on dynamic FBs.
|
||||
if (fb->dynamic) {
|
||||
fb_avail_size = fb_total_size;
|
||||
}
|
||||
|
||||
return IM_MIN(fb_total_size, fb_avail_size);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ typedef struct framebuffer {
|
||||
int32_t u, v;
|
||||
PIXFORMAT_STRUCT;
|
||||
int32_t streaming_enabled;
|
||||
uint32_t raw_size;
|
||||
uint32_t buff_size;
|
||||
uint32_t n_buffers;
|
||||
uint32_t frame_size;
|
||||
@ -50,6 +51,7 @@ typedef struct framebuffer {
|
||||
volatile int32_t tail;
|
||||
bool check_head;
|
||||
int32_t sampled_head;
|
||||
bool dynamic;
|
||||
OMV_ATTR_ALIGNED(uint8_t data[], FRAMEBUFFER_ALIGNMENT);
|
||||
} framebuffer_t;
|
||||
|
||||
@ -108,6 +110,9 @@ uint32_t framebuffer_get_buffer_size(framebuffer_t *fb);
|
||||
// Return the state of a buffer.
|
||||
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.
|
||||
void framebuffer_init_image(framebuffer_t *fb, image_t *img);
|
||||
|
||||
|
@ -284,7 +284,7 @@ void image_init(image_t *ptr, int w, int h, pixformat_t pixfmt, uint32_t size, v
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -570,7 +570,7 @@ typedef struct image {
|
||||
void image_xalloc(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_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_size(image_t *ptr);
|
||||
bool image_get_mask_pixel(image_t *ptr, int x, int y);
|
||||
|
330
modules/py_csi.c
330
modules/py_csi.c
@ -38,6 +38,8 @@
|
||||
#if MICROPY_PY_CSI
|
||||
|
||||
#include "omv_csi.h"
|
||||
#include "omv_gpio.h"
|
||||
|
||||
#include "imlib.h"
|
||||
#include "xalloc.h"
|
||||
#include "py_assert.h"
|
||||
@ -50,7 +52,6 @@
|
||||
#include "py_helper.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
extern omv_csi_t csi;
|
||||
static mp_obj_t vsync_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);
|
||||
|
||||
#if MICROPY_PY_IMU
|
||||
static void do_auto_rotation(int pitch_deadzone, int roll_activezone) {
|
||||
if (omv_csi_get_auto_rotation()) {
|
||||
static void do_auto_rotation(omv_csi_t *csi, int pitch_deadzone, int roll_activezone) {
|
||||
if (omv_csi_get_auto_rotation(csi)) {
|
||||
float pitch = py_imu_pitch_rotation();
|
||||
if (((pitch <= (90 - pitch_deadzone)) || ((90 + 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();
|
||||
if (((360 - roll_activezone) <= roll) || (roll < (0 + roll_activezone)) ) {
|
||||
// center is 0/360, upright
|
||||
omv_csi_set_hmirror(false);
|
||||
omv_csi_set_vflip(false);
|
||||
omv_csi_set_transpose(false);
|
||||
omv_csi_set_hmirror(csi, false);
|
||||
omv_csi_set_vflip(csi, false);
|
||||
omv_csi_set_transpose(csi, false);
|
||||
} else if (((270 - roll_activezone) <= roll) && (roll < (270 + roll_activezone))) {
|
||||
// center is 270, rotated right
|
||||
omv_csi_set_hmirror(true);
|
||||
omv_csi_set_vflip(false);
|
||||
omv_csi_set_transpose(true);
|
||||
omv_csi_set_hmirror(csi, true);
|
||||
omv_csi_set_vflip(csi, false);
|
||||
omv_csi_set_transpose(csi, true);
|
||||
} else if (((180 - roll_activezone) <= roll) && (roll < (180 + roll_activezone))) {
|
||||
// center is 180, upside down
|
||||
omv_csi_set_hmirror(true);
|
||||
omv_csi_set_vflip(true);
|
||||
omv_csi_set_transpose(false);
|
||||
omv_csi_set_hmirror(csi, true);
|
||||
omv_csi_set_vflip(csi, true);
|
||||
omv_csi_set_transpose(csi, false);
|
||||
} else if (((90 - roll_activezone) <= roll) && (roll < (90 + roll_activezone))) {
|
||||
// center is 90, rotated left
|
||||
omv_csi_set_hmirror(false);
|
||||
omv_csi_set_vflip(true);
|
||||
omv_csi_set_transpose(true);
|
||||
omv_csi_set_hmirror(csi, false);
|
||||
omv_csi_set_vflip(csi, 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
|
||||
|
||||
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 gets called when the module is imported, so it's a good
|
||||
// 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);
|
||||
}
|
||||
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_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) {
|
||||
omv_csi_raise_error(error);
|
||||
}
|
||||
#if MICROPY_PY_IMU
|
||||
// +-10 degree dead-zone around pitch 90/270.
|
||||
// +-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 full range on roll to set the initial state.
|
||||
#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_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;
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_shutdown_obj, py_omv_csi_shutdown);
|
||||
|
||||
static mp_obj_t py_omv_csi_flush() {
|
||||
framebuffer_update_jpeg_buffer(csi.fb);
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
framebuffer_update_jpeg_buffer(csi->fb);
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_flush_obj, py_omv_csi_flush);
|
||||
|
||||
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
|
||||
// +-10 degree dead-zone around pitch 90/270.
|
||||
// +-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 not setting the full range on roll to prevent oscillation.
|
||||
#endif // MICROPY_PY_IMU
|
||||
|
||||
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) {
|
||||
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_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_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_obj_t py_omv_csi_get_fb() {
|
||||
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;
|
||||
}
|
||||
|
||||
framebuffer_init_image(csi.fb, &image);
|
||||
framebuffer_init_image(csi->fb, &image);
|
||||
return py_image_from_struct(&image);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_fb_obj, py_omv_csi_get_fb);
|
||||
|
||||
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_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);
|
||||
}
|
||||
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_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) {
|
||||
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_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);
|
||||
}
|
||||
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_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) {
|
||||
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_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);
|
||||
}
|
||||
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_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) {
|
||||
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_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);
|
||||
}
|
||||
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_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);
|
||||
}
|
||||
|
||||
rectangle_t temp;
|
||||
temp.x = 0;
|
||||
temp.y = 0;
|
||||
temp.w = resolution[csi.framesize][0];
|
||||
temp.h = resolution[csi.framesize][1];
|
||||
temp.w = resolution[csi->framesize][0];
|
||||
temp.h = resolution[csi->framesize][1];
|
||||
|
||||
mp_obj_t *array = (mp_obj_t *) 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);
|
||||
|
||||
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) {
|
||||
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_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);
|
||||
}
|
||||
|
||||
return mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(framebuffer_get_x(csi.fb)),
|
||||
mp_obj_new_int(framebuffer_get_y(csi.fb)),
|
||||
mp_obj_new_int(framebuffer_get_u(csi.fb)),
|
||||
mp_obj_new_int(framebuffer_get_v(csi.fb))});
|
||||
return mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(framebuffer_get_x(csi->fb)),
|
||||
mp_obj_new_int(framebuffer_get_y(csi->fb)),
|
||||
mp_obj_new_int(framebuffer_get_u(csi->fb)),
|
||||
mp_obj_new_int(framebuffer_get_v(csi->fb))});
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_windowing_obj, py_omv_csi_get_windowing);
|
||||
|
||||
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;
|
||||
switch (mp_obj_get_int(gainceiling)) {
|
||||
case 2:
|
||||
@ -389,7 +414,7 @@ static mp_obj_t py_omv_csi_set_gainceiling(mp_obj_t gainceiling) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (omv_csi_set_gainceiling(gain) != 0) {
|
||||
if (omv_csi_set_gainceiling(csi, gain) != 0) {
|
||||
return mp_const_false;
|
||||
}
|
||||
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_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_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_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_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_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_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_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);
|
||||
PY_ASSERT_TRUE((q >= 0 && q <= 100));
|
||||
|
||||
q = 100 - q; //invert quality
|
||||
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_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_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_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.
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
int enable = mp_obj_get_int(pos_args[0]);
|
||||
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);
|
||||
@ -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_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 != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
|
||||
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_obj_t py_omv_csi_get_gain_db() {
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
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) {
|
||||
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.
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
int enable = mp_obj_get_int(pos_args[0]);
|
||||
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);
|
||||
|
||||
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 != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
|
||||
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() {
|
||||
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) {
|
||||
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.
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
int enable = mp_obj_get_int(pos_args[0]);
|
||||
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);
|
||||
@ -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};
|
||||
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 != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
|
||||
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() {
|
||||
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) {
|
||||
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_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 regs[csi.blc_size];
|
||||
int regs[csi->blc_size];
|
||||
bool regs_present = args[ARG_regs].u_obj != mp_const_none;
|
||||
|
||||
if (regs_present) {
|
||||
mp_obj_t *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++) {
|
||||
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++) {
|
||||
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 != OMV_CSI_ERROR_CTL_UNSUPPORTED) {
|
||||
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_obj_t py_omv_csi_get_blc_regs() {
|
||||
int regs[csi.blc_size];
|
||||
int error = omv_csi_get_blc_regs(regs);
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
int regs[csi->blc_size];
|
||||
|
||||
int error = omv_csi_get_blc_regs(csi, regs);
|
||||
if (error != 0) {
|
||||
omv_csi_raise_error(error);
|
||||
}
|
||||
|
||||
mp_obj_list_t *l = mp_obj_new_list(csi.blc_size, NULL);
|
||||
for (uint32_t i = 0; i < csi.blc_size; i++) {
|
||||
mp_obj_list_t *l = mp_obj_new_list(csi->blc_size, NULL);
|
||||
for (uint32_t i = 0; i < csi->blc_size; i++) {
|
||||
l->items[i] = mp_obj_new_int(regs[i]);
|
||||
}
|
||||
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_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) {
|
||||
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_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_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) {
|
||||
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_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_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) {
|
||||
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_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_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) {
|
||||
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_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_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);
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
|
||||
if (fb->n_buffers == c) {
|
||||
if (c == csi->fb->n_buffers) {
|
||||
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);
|
||||
}
|
||||
|
||||
int error = omv_csi_set_framebuffers(c);
|
||||
int error = omv_csi_set_framebuffers(csi, c);
|
||||
if (error != 0) {
|
||||
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_obj_t py_omv_csi_get_framebuffers() {
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
return mp_obj_new_int(fb->n_buffers);
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
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_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) {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
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_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_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) {
|
||||
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 void omv_csi_vsync_callback(uint32_t vsync) {
|
||||
static void omv_csi_vsync_callback(void *data) {
|
||||
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) {
|
||||
omv_csi_cb_t cb;
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
|
||||
if (!mp_obj_is_callable(vsync_callback_obj)) {
|
||||
vsync_callback = mp_const_none;
|
||||
omv_csi_set_vsync_callback(NULL);
|
||||
cb = (omv_csi_cb_t) { NULL, NULL };
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
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)) {
|
||||
mp_call_function_0(frame_callback);
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
frame_callback = mp_const_none;
|
||||
omv_csi_set_frame_callback(NULL);
|
||||
cb = (omv_csi_cb_t) { NULL, NULL };
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
mp_obj_t ret_obj = mp_const_none;
|
||||
int request = mp_obj_get_int(args[0]);
|
||||
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)"));
|
||||
}
|
||||
|
||||
error = omv_csi_ioctl(request, x, y, w, h);
|
||||
error = omv_csi_ioctl(csi, request, x, y, w, h);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OMV_CSI_IOCTL_GET_READOUT_WINDOW: {
|
||||
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) {
|
||||
ret_obj = mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(x),
|
||||
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_NIGHT_MODE: {
|
||||
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;
|
||||
}
|
||||
@ -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_NIGHT_MODE: {
|
||||
int enabled;
|
||||
error = omv_csi_ioctl(request, &enabled);
|
||||
error = omv_csi_ioctl(csi, request, &enabled);
|
||||
if (error == 0) {
|
||||
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_PAUSE_AUTO_FOCUS:
|
||||
case OMV_CSI_IOCTL_RESET_AUTO_FOCUS: {
|
||||
error = omv_csi_ioctl(request);
|
||||
error = omv_csi_ioctl(csi, request);
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
case OMV_CSI_IOCTL_LEPTON_GET_WIDTH: {
|
||||
int width;
|
||||
error = omv_csi_ioctl(request, &width);
|
||||
error = omv_csi_ioctl(csi, request, &width);
|
||||
if (error == 0) {
|
||||
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: {
|
||||
int height;
|
||||
error = omv_csi_ioctl(request, &height);
|
||||
error = omv_csi_ioctl(csi, request, &height);
|
||||
if (error == 0) {
|
||||
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: {
|
||||
int radiometry;
|
||||
error = omv_csi_ioctl(request, &radiometry);
|
||||
error = omv_csi_ioctl(csi, request, &radiometry);
|
||||
if (error == 0) {
|
||||
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: {
|
||||
int refresh;
|
||||
error = omv_csi_ioctl(request, &refresh);
|
||||
error = omv_csi_ioctl(csi, request, &refresh);
|
||||
if (error == 0) {
|
||||
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: {
|
||||
int resolution;
|
||||
error = omv_csi_ioctl(request, &resolution);
|
||||
error = omv_csi_ioctl(csi, request, &resolution);
|
||||
if (error == 0) {
|
||||
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: {
|
||||
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;
|
||||
}
|
||||
@ -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]);
|
||||
uint16_t *data = (uint16_t *) mp_obj_str_get_data(args[2], &data_len);
|
||||
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;
|
||||
}
|
||||
@ -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]);
|
||||
PY_ASSERT_TRUE_MSG(data_len > 0, "0 bytes transferred!");
|
||||
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) {
|
||||
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_AUX_TEMP: {
|
||||
int temp;
|
||||
error = omv_csi_ioctl(request, &temp);
|
||||
error = omv_csi_ioctl(csi, request, &temp);
|
||||
if (error == 0) {
|
||||
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:
|
||||
if (n_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;
|
||||
|
||||
case OMV_CSI_IOCTL_LEPTON_GET_MODE: {
|
||||
int enabled, high_temp;
|
||||
error = omv_csi_ioctl(request, &enabled, &high_temp);
|
||||
error = omv_csi_ioctl(csi, request, &enabled, &high_temp);
|
||||
if (error == 0) {
|
||||
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.
|
||||
float min = mp_obj_get_float(args[1]);
|
||||
float max = mp_obj_get_float(args[2]);
|
||||
error = omv_csi_ioctl(request, &min, &max);
|
||||
error = omv_csi_ioctl(csi, request, &min, &max);
|
||||
}
|
||||
break;
|
||||
|
||||
case OMV_CSI_IOCTL_LEPTON_GET_RANGE: {
|
||||
float min, max;
|
||||
error = omv_csi_ioctl(request, &min, &max);
|
||||
error = omv_csi_ioctl(csi, request, &min, &max);
|
||||
if (error == 0) {
|
||||
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)
|
||||
case OMV_CSI_IOCTL_HIMAX_MD_ENABLE: {
|
||||
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;
|
||||
}
|
||||
@ -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)"));
|
||||
}
|
||||
|
||||
error = omv_csi_ioctl(request, x, y, w, h);
|
||||
error = omv_csi_ioctl(csi, request, x, y, w, h);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OMV_CSI_IOCTL_HIMAX_MD_THRESHOLD: {
|
||||
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;
|
||||
}
|
||||
|
||||
case OMV_CSI_IOCTL_HIMAX_MD_CLEAR: {
|
||||
error = omv_csi_ioctl(request);
|
||||
error = omv_csi_ioctl(csi, request);
|
||||
break;
|
||||
}
|
||||
|
||||
case OMV_CSI_IOCTL_HIMAX_OSC_ENABLE: {
|
||||
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;
|
||||
}
|
||||
@ -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: {
|
||||
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) {
|
||||
ret_obj = mp_obj_new_tuple(4, (mp_obj_t []) {mp_obj_new_int(r),
|
||||
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)
|
||||
case OMV_CSI_IOCTL_GENX320_SET_BIASES: {
|
||||
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;
|
||||
}
|
||||
case OMV_CSI_IOCTL_GENX320_SET_BIAS: {
|
||||
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;
|
||||
}
|
||||
case OMV_CSI_IOCTL_GENX320_SET_AFK: {
|
||||
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) {
|
||||
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]));
|
||||
}
|
||||
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_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);
|
||||
switch (palette) {
|
||||
case COLOR_PALETTE_RAINBOW:
|
||||
omv_csi_set_color_palette(rainbow_table);
|
||||
omv_csi_set_color_palette(csi, rainbow_table);
|
||||
break;
|
||||
case COLOR_PALETTE_IRONBOW:
|
||||
omv_csi_set_color_palette(ironbow_table);
|
||||
omv_csi_set_color_palette(csi, ironbow_table);
|
||||
break;
|
||||
#if (MICROPY_PY_TOF == 1)
|
||||
case COLOR_PALETTE_DEPTH:
|
||||
omv_csi_set_color_palette(depth_table);
|
||||
omv_csi_set_color_palette(csi, depth_table);
|
||||
break;
|
||||
#endif // MICROPY_PY_TOF == 1
|
||||
#if (OMV_GENX320_ENABLE == 1)
|
||||
case COLOR_PALETTE_EVT_DARK:
|
||||
omv_csi_set_color_palette(evt_dark_table);
|
||||
omv_csi_set_color_palette(csi, evt_dark_table);
|
||||
break;
|
||||
case COLOR_PALETTE_EVT_LIGHT:
|
||||
omv_csi_set_color_palette(evt_light_table);
|
||||
omv_csi_set_color_palette(csi, evt_light_table);
|
||||
break;
|
||||
#endif // OMV_GENX320_ENABLE == 1
|
||||
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_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) {
|
||||
return mp_obj_new_int(COLOR_PALETTE_RAINBOW);
|
||||
} 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_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;
|
||||
}
|
||||
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) {
|
||||
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);
|
||||
|
||||
@ -1323,6 +1392,5 @@ const mp_obj_module_t omv_csi_module = {
|
||||
.globals = (mp_obj_t) &globals_dict,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_csi, omv_csi_module);
|
||||
MP_REGISTER_MODULE(MP_QSTR_sensor, omv_csi_module);
|
||||
#endif // MICROPY_PY_CSI
|
||||
|
1511
modules/py_csi_ng.c
Normal file
1511
modules/py_csi_ng.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -558,11 +558,14 @@ void py_helper_update_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);
|
||||
|
||||
#if MICROPY_PY_CSI
|
||||
omv_csi_set_framebuffers(1);
|
||||
#else
|
||||
framebuffer_set_buffers(fb, 1);
|
||||
#endif
|
||||
|
||||
|
@ -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 += -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_SSL=$(MICROPY_PY_SSL)
|
||||
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 += CMSIS_DIR=$(TOP_DIR)/$(HAL_DIR)/cmsis/inc
|
||||
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_SSL=$(MICROPY_PY_SSL)
|
||||
MPY_MKARGS += MICROPY_PY_SSL_ECDSA_SIGN_ALT=$(MICROPY_PY_SSL_ECDSA_SIGN_ALT)
|
||||
|
@ -71,33 +71,9 @@
|
||||
CAM_INTR_OUTFIFO_OVERRUN | \
|
||||
CAM_INTR_BRESP_ERR)
|
||||
|
||||
static CPI_Type *cpi_get_base_addr(omv_csi_t *csi) {
|
||||
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) {
|
||||
int alif_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
|
||||
if (config == OMV_CSI_CONFIG_INIT) {
|
||||
CPI_Type *cpi = cpi_get_base_addr(&csi);
|
||||
CPI_Type *cpi = csi->base;
|
||||
|
||||
// Configure the FIFO.
|
||||
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);
|
||||
|
||||
// Set VSYNC, HSYNC and PIXCLK polarities.
|
||||
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.pixck_pol << CAM_CFG_PXCLK_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->pixck_pol << CAM_CFG_PXCLK_POL_Pos);
|
||||
|
||||
// Configure the data bus width, mode, endianness.
|
||||
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;
|
||||
}
|
||||
|
||||
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
|
||||
CPI_Type *cpi = cpi_get_base_addr(csi);
|
||||
int alif_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
|
||||
CPI_Type *cpi = csi->base;
|
||||
|
||||
// Stop CPI
|
||||
cpi->CAM_CTRL = 0;
|
||||
|
||||
// Disable IRQs.
|
||||
NVIC_DisableIRQ(CAM_IRQ_IRQn);
|
||||
cpi_disable_interrupt(cpi, CPI_IRQ_FLAGS);
|
||||
cpi_irq_handler_clear_intr_status(cpi, CPI_IRQ_FLAGS);
|
||||
|
||||
csi->first_line = false;
|
||||
csi->drop_frame = false;
|
||||
csi->last_frame_ms = 0;
|
||||
csi->last_frame_ms_valid = false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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 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;
|
||||
|
||||
// Offset the pixels buffer for debayering.
|
||||
if (csi->raw_output && csi->pixformat == PIXFORMAT_RGB565) {
|
||||
offset += line_size_bytes * resolution[csi->framesize][1];
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
static uint32_t frames = 0;
|
||||
static uint32_t r_stat, gb_stat, gr_stat, b_stat;
|
||||
|
||||
CPI_Type *cpi = cpi_get_base_addr(csi);
|
||||
|
||||
if (csi->pixformat == PIXFORMAT_INVALID) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (omv_csi_check_framebuffer_size() != 0) {
|
||||
if (omv_csi_check_framebuffer_size(csi) != 0) {
|
||||
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.
|
||||
if (!(cpi->CAM_CTRL & CAM_CTRL_BUSY)) {
|
||||
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;
|
||||
|
||||
// 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.
|
||||
if (omv_csi_get_cropped()) {
|
||||
if (omv_csi_get_cropped(csi)) {
|
||||
image_t src_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.
|
||||
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.
|
||||
@ -398,9 +366,10 @@ int omv_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
|
||||
|
||||
int omv_csi_init() {
|
||||
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)
|
||||
omv_gpio_write(OMV_CSI_POWER_PIN, 0);
|
||||
@ -410,17 +379,20 @@ int omv_csi_init() {
|
||||
omv_gpio_write(OMV_CSI_RESET_PIN, 0);
|
||||
#endif
|
||||
|
||||
// Reset the csi state
|
||||
memset(&csi, 0, sizeof(omv_csi_t));
|
||||
// Initialize the CSIs using this driver's ops as defaults,
|
||||
// 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
|
||||
csi.fb = framebuffer_get(0);
|
||||
|
||||
// Set I2C bus
|
||||
csi.i2c = &csi_i2c;
|
||||
|
||||
// Set default snapshot function.
|
||||
csi.snapshot = omv_csi_snapshot;
|
||||
memset(csi, 0, sizeof(omv_csi_t));
|
||||
csi->i2c = &i2c;
|
||||
csi->fb = framebuffer_get(-1);
|
||||
csi->base = base;
|
||||
csi->abort = alif_csi_abort;
|
||||
csi->config = alif_csi_config;
|
||||
csi->snapshot = alif_csi_snapshot;
|
||||
csi->color_palette = rainbow_table;
|
||||
}
|
||||
|
||||
// Configure the CSI external clock.
|
||||
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;
|
||||
}
|
||||
|
||||
// Initialize the camera bus.
|
||||
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
|
||||
|
||||
// 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.
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
// Configure the CSI interface.
|
||||
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) {
|
||||
// CSI config failed
|
||||
return OMV_CSI_ERROR_CSI_INIT_FAILED;
|
||||
// Configure the DCMI interface.
|
||||
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Set default color palette.
|
||||
csi.color_palette = rainbow_table;
|
||||
|
||||
// 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;
|
||||
// Clear fb_enabled flag.
|
||||
JPEG_FB()->enabled = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CAM_IRQHandler(void) {
|
||||
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);
|
||||
|
||||
if (status & CAM_INTR_VSYNC) {
|
||||
@ -470,19 +443,19 @@ void CAM_IRQHandler(void) {
|
||||
|
||||
if (status & 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");
|
||||
}
|
||||
|
||||
if (status & 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");
|
||||
}
|
||||
|
||||
if (status & 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);
|
||||
}
|
||||
|
||||
@ -491,24 +464,22 @@ void CAM_IRQHandler(void) {
|
||||
cpi->CAM_CTRL = 0;
|
||||
if (!(status & CPI_ERROR_FLAGS)) {
|
||||
// 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).
|
||||
vbuffer_t *buffer = framebuffer_get_tail(csi.fb, FB_PEEK);
|
||||
vbuffer_t *buffer = framebuffer_get_tail(csi->fb, FB_PEEK);
|
||||
|
||||
if (buffer != NULL) {
|
||||
cpi->CAM_CTRL = 0;
|
||||
cpi->CAM_CTRL |= CAM_CTRL_SW_RESET;
|
||||
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->CAM_CTRL = (CAM_CTRL_SNAPSHOT | CAM_CTRL_START | CAM_CTRL_FIFO_CLK_SEL);
|
||||
}
|
||||
|
||||
if (!(status & CPI_ERROR_FLAGS)) {
|
||||
if (csi.frame_callback) {
|
||||
csi.frame_callback();
|
||||
}
|
||||
if (csi->frame_cb.fun && !(status & CPI_ERROR_FLAGS)) {
|
||||
csi->frame_cb.fun(csi->frame_cb.arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "i2c.h"
|
||||
#include "i3c.h"
|
||||
#include "spi.h"
|
||||
#include "cpi.h"
|
||||
#include "gpio.h"
|
||||
#include "pinconf.h"
|
||||
|
||||
@ -127,12 +128,18 @@ struct { \
|
||||
#define OMV_SPI_MAX_TIMEOUT (0xFFFFFFFF)
|
||||
#define OMV_SPI_NO_DMA (1)
|
||||
|
||||
#define OMV_SPI_PORT_BITS \
|
||||
struct { \
|
||||
SPI_Type *inst; \
|
||||
bool is_lp; \
|
||||
uint32_t spi_mode; \
|
||||
uint32_t bus_mode; \
|
||||
uint8_t datasize; \
|
||||
};
|
||||
#define OMV_SPI_PORT_BITS \
|
||||
struct { \
|
||||
SPI_Type *inst; \
|
||||
bool is_lp; \
|
||||
uint32_t spi_mode; \
|
||||
uint32_t bus_mode; \
|
||||
uint8_t datasize; \
|
||||
};
|
||||
|
||||
#define OMV_CSI_PORT_BITS \
|
||||
struct { \
|
||||
CPI_Type *base; \
|
||||
};
|
||||
|
||||
#endif // __OMV_PORTCONFIG_H__
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "omv_boardconfig.h"
|
||||
// Define pin objects in this file.
|
||||
#define OMV_GPIO_DEFINE_PINS (1)
|
||||
#include "omv_csi.h"
|
||||
#include "omv_gpio.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) {
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
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.
|
||||
CSI_REG_SR(CSI) = csisr;
|
||||
@ -330,11 +333,11 @@ void CSI_IRQHandler(void) {
|
||||
if (csisr & CSI_SR_SOF_INT_MASK) {
|
||||
// Clear the FIFO and re/enable DMA.
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
|
@ -54,26 +54,7 @@ extern uint8_t _line_buf[OMV_LINE_BUF_SIZE];
|
||||
| CSI_CR1_FB2_DMA_DONE_INTEN_MASK \
|
||||
| CSI_CR1_FB1_DMA_DONE_INTEN_MASK)
|
||||
|
||||
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.
|
||||
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) {
|
||||
int imx_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
|
||||
if (config == OMV_CSI_CONFIG_INIT) {
|
||||
CSI_Reset(CSI);
|
||||
NVIC_DisableIRQ(CSI_IRQn);
|
||||
@ -88,9 +69,9 @@ int omv_csi_config(omv_csi_config_t config) {
|
||||
|
||||
// Configure VSYNC, HSYNC and PIXCLK signals.
|
||||
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.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->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->pixck_pol ? CSI_CR1_REDGE_MASK : 0;
|
||||
|
||||
// Stride config: No stride.
|
||||
CSI_REG_FBUF_PARA(CSI) = 0;
|
||||
@ -112,28 +93,19 @@ int omv_csi_config(omv_csi_config_t config) {
|
||||
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);
|
||||
CSI_DisableInterrupts(CSI, CSI_IRQ_FLAGS);
|
||||
CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK;
|
||||
CSI_REG_CR18(CSI) &= ~CSI_CR18_CSI_ENABLE_MASK;
|
||||
|
||||
csi->dest_inc = 0;
|
||||
csi->first_line = false;
|
||||
csi->drop_frame = false;
|
||||
csi->last_frame_ms = 0;
|
||||
csi->last_frame_ms_valid = false;
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
uint32_t omv_csi_get_clk_frequency() {
|
||||
return 24000000 / (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1);
|
||||
}
|
||||
|
||||
int omv_csi_set_clk_frequency(uint32_t frequency) {
|
||||
if (frequency >= 24000000) {
|
||||
CLOCK_SetDiv(kCLOCK_CsiDiv, 0);
|
||||
@ -151,46 +123,42 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t omv_csi_get_clk_frequency() {
|
||||
return 24000000 / (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1);
|
||||
}
|
||||
|
||||
void omv_csi_sof_callback() {
|
||||
csi.first_line = false;
|
||||
csi.drop_frame = false;
|
||||
void omv_csi_sof_callback(omv_csi_t *csi) {
|
||||
csi->first_line = false;
|
||||
csi->drop_frame = false;
|
||||
|
||||
// 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) {
|
||||
omv_csi_abort(&csi, false, true);
|
||||
} else if (buffer->offset < resolution[csi.framesize][1]) {
|
||||
omv_csi_abort(csi, false, true);
|
||||
} else if (buffer->offset < resolution[csi->framesize][1]) {
|
||||
// Missed a few lines, reset buffer state and continue.
|
||||
buffer->reset_state = true;
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
// 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
|
||||
// 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;
|
||||
}
|
||||
|
||||
edma_handle_t *handle = dma;
|
||||
edma_transfer_config_t config;
|
||||
framebuffer_t *fb = csi.fb;
|
||||
|
||||
EDMA_PrepareTransferConfig(&config,
|
||||
src, // srcAddr
|
||||
csi.src_size, // srcWidth
|
||||
csi.src_inc, // srcOffset
|
||||
csi->src_size, // srcWidth
|
||||
csi->src_inc, // srcOffset
|
||||
dst, // destAddr
|
||||
transposed ? bpp : csi.dest_inc, // destWidth
|
||||
transposed ? (fb->v * bpp) : csi.dest_inc, // destOffset
|
||||
transposed ? bpp : csi->dest_inc, // destWidth
|
||||
transposed ? (fb->v * bpp) : csi->dest_inc, // destOffset
|
||||
fb->u * bpp, // bytesEachRequest
|
||||
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) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@ -213,21 +181,21 @@ int omv_csi_dma_memcpy(void *dma, void *dst, void *src, int bpp, bool transposed
|
||||
}
|
||||
#endif
|
||||
|
||||
void omv_csi_line_callback(uint32_t addr) {
|
||||
framebuffer_t *fb = csi.fb;
|
||||
void omv_csi_line_callback(omv_csi_t *csi, uint32_t addr) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
// Throttle frames to match the current frame rate.
|
||||
omv_csi_throttle_framerate();
|
||||
omv_csi_throttle_framerate(csi);
|
||||
|
||||
// Get current framebuffer.
|
||||
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
|
||||
|
||||
if (csi.pixformat == PIXFORMAT_JPEG) {
|
||||
if (csi.drop_frame) {
|
||||
if (csi->pixformat == PIXFORMAT_JPEG) {
|
||||
if (csi->drop_frame) {
|
||||
return;
|
||||
}
|
||||
bool jpeg_end = false;
|
||||
if (csi.jpg_format == 4) {
|
||||
if (csi->jpg_format == 4) {
|
||||
// JPEG MODE 4:
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
} else if (csi.jpg_format == 3) {
|
||||
} else if (csi->jpg_format == 3) {
|
||||
// OV2640 JPEG TODO
|
||||
}
|
||||
// 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.
|
||||
framebuffer_get_tail(fb, FB_NO_FLAGS);
|
||||
CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK;
|
||||
if (csi.frame_callback) {
|
||||
csi.frame_callback();
|
||||
if (csi->frame_cb.fun) {
|
||||
csi->frame_cb.fun(csi->frame_cb.arg);
|
||||
}
|
||||
csi.drop_frame = true;
|
||||
csi->drop_frame = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (csi.drop_frame) {
|
||||
if (++buffer->offset == resolution[csi.framesize][1]) {
|
||||
if (csi->drop_frame) {
|
||||
if (++buffer->offset == resolution[csi->framesize][1]) {
|
||||
buffer->offset = 0;
|
||||
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))) {
|
||||
// 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 *dst = buffer->data;
|
||||
|
||||
// Adjust BPP for Grayscale.
|
||||
if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
bytes_per_pixel = 1;
|
||||
}
|
||||
|
||||
if (csi.transpose) {
|
||||
if (csi->transpose) {
|
||||
dst += bytes_per_pixel * (buffer->offset - fb->y);
|
||||
} else {
|
||||
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
|
||||
// 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.
|
||||
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
|
||||
omv_csi_copy_line(NULL, src, dst);
|
||||
omv_csi_copy_line(csi, NULL, src, dst);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (++buffer->offset == resolution[csi.framesize][1]) {
|
||||
if (++buffer->offset == resolution[csi->framesize][1]) {
|
||||
// Release the current framebuffer.
|
||||
framebuffer_get_tail(fb, FB_NO_FLAGS);
|
||||
CSI_REG_CR3(CSI) &= ~CSI_CR3_DMA_REQ_EN_RFF_MASK;
|
||||
if (csi.frame_callback) {
|
||||
csi.frame_callback();
|
||||
if (csi->frame_cb.fun) {
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (omv_csi_check_framebuffer_size() != 0) {
|
||||
if (omv_csi_check_framebuffer_size(csi) != 0) {
|
||||
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)) {
|
||||
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 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);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int omv_csi_init() {
|
||||
int init_ret = 0;
|
||||
static omv_i2c_t i2c;
|
||||
|
||||
mimxrt_hal_csi_init(CSI);
|
||||
|
||||
@ -545,18 +513,19 @@ int omv_csi_init() {
|
||||
omv_gpio_write(OMV_CSI_RESET_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Reset the csi state
|
||||
memset(&csi, 0, sizeof(omv_csi_t));
|
||||
// Initialize the CSIs using this driver's ops as defaults,
|
||||
// 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
|
||||
csi.fb = framebuffer_get(0);
|
||||
|
||||
// Set I2C bus
|
||||
csi.i2c = &csi_i2c;
|
||||
|
||||
// Set default snapshot function.
|
||||
// Some sensors need to call snapshot from init.
|
||||
csi.snapshot = omv_csi_snapshot;
|
||||
memset(csi, 0, sizeof(omv_csi_t));
|
||||
csi->i2c = &i2c;
|
||||
csi->fb = framebuffer_get(-1);
|
||||
csi->abort = imx_csi_abort;
|
||||
csi->config = imx_csi_config;
|
||||
csi->snapshot = imx_csi_snapshot;
|
||||
csi->color_palette = rainbow_table;
|
||||
}
|
||||
|
||||
// Configure the csi external clock (XCLK).
|
||||
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;
|
||||
}
|
||||
|
||||
// Initialize the camera bus.
|
||||
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
|
||||
|
||||
// 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.
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
// Configure the CSI interface.
|
||||
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) {
|
||||
// CSI config failed
|
||||
return OMV_CSI_ERROR_CSI_INIT_FAILED;
|
||||
// Configure the CSI interfaces.
|
||||
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Set default color palette.
|
||||
csi.color_palette = rainbow_table;
|
||||
|
||||
// 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;
|
||||
|
||||
// Clear fb_enabled flag.
|
||||
JPEG_FB()->enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
#endif // MICROPY_PY_CSI
|
||||
|
@ -61,7 +61,7 @@ static const volatile uint32_t *_pclkPort;
|
||||
|
||||
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) {
|
||||
uint32_t csi_pins[] = {
|
||||
OMV_CSI_D0_PIN,
|
||||
@ -114,12 +114,12 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (omv_csi_check_framebuffer_size(fb) != 0) {
|
||||
if (omv_csi_check_framebuffer_size(csi) != 0) {
|
||||
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();
|
||||
|
||||
// Not useful for the NRF but must call to keep API the same.
|
||||
if (csi->frame_callback) {
|
||||
csi->frame_callback();
|
||||
if (csi->frame_cb.fun) {
|
||||
csi->frame_cb.fun(csi->frame_cb.arg);
|
||||
}
|
||||
|
||||
// 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 init_ret = 0;
|
||||
static omv_i2c_t i2c;
|
||||
|
||||
#if defined(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);
|
||||
#endif
|
||||
|
||||
// Reset the csi state
|
||||
memset(&csi, 0, sizeof(omv_csi_t));
|
||||
// Initialize the CSIs using this driver's ops as defaults,
|
||||
// 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
|
||||
csi.fb = framebuffer_get(0);
|
||||
|
||||
// Set I2C bus
|
||||
csi.i2c = &csi_i2c;
|
||||
|
||||
// Set default snapshot function.
|
||||
csi.snapshot = omv_csi_snapshot;
|
||||
memset(csi, 0, sizeof(omv_csi_t));
|
||||
csi->i2c = &i2c;
|
||||
csi->fb = framebuffer_get(-1);
|
||||
csi->abort = NULL;
|
||||
csi->config = nrf_csi_config;
|
||||
csi->snapshot = nrf_csi_snapshot;
|
||||
csi->color_palette = rainbow_table;
|
||||
}
|
||||
|
||||
// Configure the csi external clock (XCLK).
|
||||
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;
|
||||
}
|
||||
|
||||
// Initialize the camera bus.
|
||||
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
|
||||
|
||||
// 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.
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
// Configure the CSI interface.
|
||||
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) {
|
||||
// CSI config failed
|
||||
return OMV_CSI_ERROR_CSI_INIT_FAILED;
|
||||
// Configure the CSI interfaces.
|
||||
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
|
@ -41,31 +41,56 @@
|
||||
#include "unaligned_memcpy.h"
|
||||
#include "dcmi.pio.h"
|
||||
|
||||
static void dma_irq_handler();
|
||||
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) {
|
||||
dma_channel_abort(OMV_CSI_DMA_CHANNEL);
|
||||
dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, false);
|
||||
static void dma_irq_handler() {
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
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, rev_bytes);
|
||||
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);
|
||||
|
||||
dma_channel_configure(OMV_CSI_DMA_CHANNEL, &c,
|
||||
capture_buf, // Destinatinon pointer.
|
||||
&OMV_CSI_PIO->rxf[OMV_CSI_SM], // Source pointer.
|
||||
(w * h * bpp) >> 2, // Number of transfers in words.
|
||||
true // Start immediately, will block on SM.
|
||||
);
|
||||
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);
|
||||
|
||||
// Re-enable DMA IRQs.
|
||||
dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, 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_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
|
||||
dma_channel_abort(OMV_CSI_DMA_CHANNEL);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void dma_irq_handler() {
|
||||
framebuffer_t *fb = csi.fb;
|
||||
|
||||
if (dma_irqn_get_channel_status(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL)) {
|
||||
// Clear the interrupt request.
|
||||
dma_irqn_acknowledge_channel(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL);
|
||||
|
||||
framebuffer_get_tail(fb, 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) {
|
||||
static int rp2_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
// Compress the framebuffer for the IDE preview.
|
||||
framebuffer_update_jpeg_buffer(fb);
|
||||
|
||||
if (omv_csi_check_framebuffer_size() != 0) {
|
||||
if (omv_csi_check_framebuffer_size(csi) != 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Configure the DMA on the first frame, for later frames only the write is changed.
|
||||
omv_csi_dma_config(fb->u, fb->v, fb->bpp,
|
||||
(void *) buffer->data,
|
||||
(csi->rgb_swap && fb->bpp == 2));
|
||||
// Configure the DMA on the first frame.
|
||||
dma_channel_abort(OMV_CSI_DMA_CHANNEL);
|
||||
dma_irqn_set_channel_enabled(OMV_CSI_DMA, OMV_CSI_DMA_CHANNEL, false);
|
||||
|
||||
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.
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int omv_csi_init() {
|
||||
int init_ret = 0;
|
||||
static omv_i2c_t i2c;
|
||||
|
||||
// PIXCLK
|
||||
gpio_init(OMV_CSI_PXCLK_PIN);
|
||||
@ -225,17 +243,19 @@ int omv_csi_init() {
|
||||
gpio_put(OMV_CSI_RESET_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Reset the csi state
|
||||
memset(&csi, 0, sizeof(omv_csi_t));
|
||||
// Initialize the CSIs using this driver's ops as defaults,
|
||||
// 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
|
||||
csi.fb = framebuffer_get(0);
|
||||
|
||||
// Set I2C bus
|
||||
csi.i2c = &csi_i2c;
|
||||
|
||||
// Set default snapshot function.
|
||||
csi.snapshot = omv_csi_snapshot;
|
||||
memset(csi, 0, sizeof(omv_csi_t));
|
||||
csi->i2c = &i2c;
|
||||
csi->fb = framebuffer_get(-1);
|
||||
csi->abort = rp2_csi_abort;
|
||||
csi->config = rp2_csi_config;
|
||||
csi->snapshot = rp2_csi_snapshot;
|
||||
csi->color_palette = rainbow_table;
|
||||
}
|
||||
|
||||
// Configure the csi external clock (XCLK).
|
||||
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;
|
||||
}
|
||||
|
||||
// Initialize the camera bus.
|
||||
omv_i2c_init(&i2c, OMV_CSI_I2C_ID, OMV_CSI_I2C_SPEED);
|
||||
|
||||
// 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.
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
// Set default color palette.
|
||||
csi.color_palette = rainbow_table;
|
||||
// Configure the DCMI interface.
|
||||
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
|
||||
omv_csi_t *csi = &csi_all[i];
|
||||
|
||||
// Set new DMA IRQ handler.
|
||||
// Disable IRQs.
|
||||
irq_set_enabled(OMV_CSI_DMA_IRQ, false);
|
||||
if (omv_csi_config(csi, OMV_CSI_CONFIG_INIT) != 0) {
|
||||
return OMV_CSI_ERROR_CSI_INIT_FAILED;
|
||||
}
|
||||
|
||||
// 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);
|
||||
csi->detected = true;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
|
||||
// Clear fb_enabled flag.
|
||||
JPEG_FB()->enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
#endif // MICROPY_PY_CSI
|
||||
|
@ -70,94 +70,82 @@ extern uint32_t hal_get_exti_gpio(uint32_t line);
|
||||
|
||||
#if USE_DCMI
|
||||
void DCMI_IRQHandler(void) {
|
||||
HAL_DCMI_IRQHandler(&csi.dcmi);
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
HAL_DCMI_IRQHandler(&csi->dcmi);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_DCMIPP
|
||||
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) {
|
||||
HAL_DCMIPP_IRQHandler(&csi.dcmi);
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
HAL_DCMIPP_IRQHandler(&csi->dcmi);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_MDMA
|
||||
void omv_csi_mdma_irq_handler(void) {
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
|
||||
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)) {
|
||||
HAL_MDMA_IRQHandler(&csi.mdma1);
|
||||
HAL_MDMA_IRQHandler(&csi->mdma1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void omv_csi_init0() {
|
||||
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) {
|
||||
static int stm_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
|
||||
if (config == OMV_CSI_CONFIG_INIT) {
|
||||
#if USE_DMA
|
||||
// DMA Stream configuration
|
||||
csi.dma.Instance = DMA2_Stream1;
|
||||
csi->dma.Instance = DMA2_Stream1;
|
||||
#if defined(STM32H7)
|
||||
csi.dma.Init.Request = DMA_REQUEST_DCMI;
|
||||
csi->dma.Init.Request = DMA_REQUEST_DCMI;
|
||||
#else
|
||||
csi.dma.Init.Channel = DMA_CHANNEL_1;
|
||||
csi->dma.Init.Channel = DMA_CHANNEL_1;
|
||||
#endif
|
||||
csi.dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
csi.dma.Init.MemInc = DMA_MINC_ENABLE;
|
||||
csi.dma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
csi.dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
csi.dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
csi.dma.Init.Mode = DMA_NORMAL;
|
||||
csi.dma.Init.Priority = DMA_PRIORITY_HIGH;
|
||||
csi.dma.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||
csi.dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
csi.dma.Init.MemBurst = DMA_MBURST_INC4;
|
||||
csi.dma.Init.PeriphBurst = DMA_PBURST_SINGLE;
|
||||
|
||||
csi->dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
csi->dma.Init.MemInc = DMA_MINC_ENABLE;
|
||||
csi->dma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
csi->dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
|
||||
csi->dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
|
||||
csi->dma.Init.Mode = DMA_NORMAL;
|
||||
csi->dma.Init.Priority = DMA_PRIORITY_HIGH;
|
||||
csi->dma.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
|
||||
csi->dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
csi->dma.Init.MemBurst = DMA_MBURST_INC4;
|
||||
csi->dma.Init.PeriphBurst = DMA_PBURST_SINGLE;
|
||||
|
||||
// Initialize the DMA stream
|
||||
HAL_DMA_DeInit(&csi.dma);
|
||||
if (HAL_DMA_Init(&csi.dma) != HAL_OK) {
|
||||
return -1;
|
||||
HAL_DMA_DeInit(&csi->dma);
|
||||
if (HAL_DMA_Init(&csi->dma) != HAL_OK) {
|
||||
return OMV_CSI_ERROR_DMA_INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
NVIC_SetPriority(DMA2_Stream1_IRQn, IRQ_PRI_DMA21);
|
||||
|
||||
|
||||
#if USE_MDMA
|
||||
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->mdma0.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_0);
|
||||
csi->mdma1.Instance = MDMA_CHAN_TO_INSTANCE(OMV_MDMA_CHANNEL_DCMI_1);
|
||||
#endif
|
||||
|
||||
#endif // USE_DMA
|
||||
#endif // USE_DMA
|
||||
|
||||
// Configure DCMI/PP.
|
||||
#if USE_DCMIPP
|
||||
// Initialize the DCMIPP
|
||||
csi.dcmi.Instance = DCMIPP;
|
||||
if (HAL_DCMIPP_Init(&csi.dcmi) != HAL_OK) {
|
||||
csi->dcmi.Instance = DCMIPP;
|
||||
if (HAL_DCMIPP_Init(&csi->dcmi) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -169,21 +157,21 @@ int omv_csi_config(omv_csi_config_t config) {
|
||||
NVIC_SetPriority(CSI_IRQn, IRQ_PRI_DCMI);
|
||||
HAL_NVIC_EnableIRQ(CSI_IRQn);
|
||||
#else
|
||||
csi.dcmi.Instance = DCMI;
|
||||
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.PCKPolarity = csi.pixck_pol ? DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING;
|
||||
csi.dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
|
||||
csi.dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
|
||||
csi.dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
|
||||
csi.dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
|
||||
csi->dcmi.Instance = DCMI;
|
||||
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.PCKPolarity = csi->pixck_pol ? DCMI_PCKPOLARITY_RISING : DCMI_PCKPOLARITY_FALLING;
|
||||
csi->dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
|
||||
csi->dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
|
||||
csi->dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
|
||||
csi->dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
|
||||
|
||||
// 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
|
||||
HAL_DCMI_DeInit(&csi.dcmi);
|
||||
if (HAL_DCMI_Init(&csi.dcmi) != HAL_OK) {
|
||||
HAL_DCMI_DeInit(&csi->dcmi);
|
||||
if (HAL_DCMI_Init(&csi->dcmi) != HAL_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -194,16 +182,16 @@ int omv_csi_config(omv_csi_config_t config) {
|
||||
} else if (config == OMV_CSI_CONFIG_PIXFORMAT) {
|
||||
#if USE_DCMI
|
||||
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
|
||||
// Select and configure the DCMIPP source.
|
||||
if (csi.mipi_if) {
|
||||
if (csi->mipi_if) {
|
||||
DCMIPP_CSI_ConfTypeDef scfg = {
|
||||
.NumberOfLanes = DCMIPP_CSI_TWO_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;
|
||||
}
|
||||
// Configure CSI virtual channel and pipe.
|
||||
@ -212,61 +200,60 @@ int omv_csi_config(omv_csi_config_t config) {
|
||||
.DataTypeIDA = 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) {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
DCMIPP_ParallelConfTypeDef scfg = {
|
||||
.SynchroMode = DCMIPP_SYNCHRO_HARDWARE,
|
||||
.ExtendedDataMode = DCMIPP_INTERFACE_8BITS,
|
||||
.VSPolarity = csi.vsync_pol ? DCMIPP_VSPOLARITY_HIGH : DCMIPP_VSPOLARITY_LOW,
|
||||
.HSPolarity = csi.hsync_pol ? DCMIPP_HSPOLARITY_HIGH : DCMIPP_HSPOLARITY_LOW,
|
||||
.PCKPolarity = csi.pixck_pol ? DCMIPP_PCKPOLARITY_RISING : DCMIPP_PCKPOLARITY_FALLING,
|
||||
.VSPolarity = csi->vsync_pol ? DCMIPP_VSPOLARITY_HIGH : DCMIPP_VSPOLARITY_LOW,
|
||||
.HSPolarity = csi->hsync_pol ? DCMIPP_HSPOLARITY_HIGH : DCMIPP_HSPOLARITY_LOW,
|
||||
.PCKPolarity = csi->pixck_pol ? DCMIPP_PCKPOLARITY_RISING : DCMIPP_PCKPOLARITY_FALLING,
|
||||
};
|
||||
if (csi.raw_output) {
|
||||
if (csi->raw_output) {
|
||||
scfg.Format = DCMIPP_FORMAT_RAW8;
|
||||
} else if (csi.pixformat == PIXFORMAT_RGB565) {
|
||||
} else if (csi->pixformat == PIXFORMAT_RGB565) {
|
||||
scfg.Format = DCMIPP_FORMAT_RGB565;
|
||||
scfg.SwapCycles = DCMIPP_SWAPCYCLES_ENABLE;
|
||||
} else if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
scfg.Format = (csi.mono_bpp == 1) ? DCMIPP_FORMAT_MONOCHROME_8B : DCMIPP_FORMAT_YUV422;
|
||||
} else if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
scfg.Format = (csi->mono_bpp == 1) ? DCMIPP_FORMAT_MONOCHROME_8B : DCMIPP_FORMAT_YUV422;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the pixel processing pipeline.
|
||||
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;
|
||||
} else if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
} else if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
pcfg.PixelPackerFormat = DCMIPP_PIXEL_PACKER_FORMAT_MONO_Y8_G8_1;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
// Swap RGB enabled.
|
||||
if (csi.yuv_swap) {
|
||||
HAL_DCMIPP_PIPE_EnableYUVSwap(&csi.dcmi, DCMIPP_PIPE);
|
||||
if (csi->yuv_swap) {
|
||||
HAL_DCMIPP_PIPE_EnableYUVSwap(&csi->dcmi, DCMIPP_PIPE);
|
||||
}
|
||||
// Swap YUV if enabled.
|
||||
if (csi.rgb_swap) {
|
||||
HAL_DCMIPP_PIPE_EnableRedBlueSwap(&csi.dcmi, DCMIPP_PIPE);
|
||||
if (csi->rgb_swap) {
|
||||
HAL_DCMIPP_PIPE_EnableRedBlueSwap(&csi->dcmi, DCMIPP_PIPE);
|
||||
}
|
||||
|
||||
// Configure debayer.
|
||||
if (csi.raw_output && csi.pixformat != PIXFORMAT_BAYER) {
|
||||
|
||||
if (csi->raw_output && csi->pixformat != PIXFORMAT_BAYER) {
|
||||
DCMIPP_RawBayer2RGBConfTypeDef rawcfg = {
|
||||
.RawBayerType = DCMIPP_RAWBAYER_BGGR,
|
||||
.VLineStrength = DCMIPP_RAWBAYER_ALGO_NONE,
|
||||
@ -274,8 +261,9 @@ int omv_csi_config(omv_csi_config_t config) {
|
||||
.PeakStrength = 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;
|
||||
}
|
||||
|
||||
@ -288,8 +276,8 @@ int omv_csi_config(omv_csi_config_t config) {
|
||||
.MultiplierBlue = 128,
|
||||
};
|
||||
|
||||
if (HAL_DCMIPP_PIPE_SetISPExposureConfig(&csi.dcmi, DCMIPP_PIPE, &expcfg) != HAL_OK ||
|
||||
HAL_DCMIPP_PIPE_EnableISPExposure(&csi.dcmi, DCMIPP_PIPE) != HAL_OK) {
|
||||
if (HAL_DCMIPP_PIPE_SetISPExposureConfig(&csi->dcmi, DCMIPP_PIPE, &expcfg) != HAL_OK ||
|
||||
HAL_DCMIPP_PIPE_EnableISPExposure(&csi->dcmi, DCMIPP_PIPE) != HAL_OK) {
|
||||
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++) {
|
||||
if (HAL_DCMIPP_PIPE_SetISPStatisticExtractionConfig(&csi.dcmi,
|
||||
if (HAL_DCMIPP_PIPE_SetISPStatisticExtractionConfig(&csi->dcmi,
|
||||
DCMIPP_PIPE, i,
|
||||
&statcfg[i - DCMIPP_STATEXT_MODULE1]) != HAL_OK) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
int omv_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
|
||||
if (DCMI_IS_ACTIVE()) {
|
||||
#if USE_DCMI
|
||||
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;
|
||||
static int stm_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
|
||||
if (!DCMI_IS_ACTIVE()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (csi->fb) {
|
||||
if (fifo_flush) {
|
||||
framebuffer_flush_buffers(csi->fb, true);
|
||||
} else if (!csi->disable_full_flush) {
|
||||
framebuffer_flush_buffers(csi->fb, false);
|
||||
}
|
||||
#if USE_DCMI
|
||||
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
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
#if (OMV_CSI_CLK_SOURCE == OMV_CSI_CLK_SOURCE_TIM)
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
|
||||
if (frequency == 0) {
|
||||
if (csi.tim.Init.Period) {
|
||||
HAL_TIM_PWM_Stop(&csi.tim, OMV_CSI_TIM_CHANNEL);
|
||||
HAL_TIM_PWM_DeInit(&csi.tim);
|
||||
memset(&csi.tim, 0, sizeof(csi.tim));
|
||||
if (csi->tim.Init.Period) {
|
||||
HAL_TIM_PWM_Stop(&csi->tim, OMV_CSI_TIM_CHANNEL);
|
||||
HAL_TIM_PWM_DeInit(&csi->tim);
|
||||
memset(&csi->tim, 0, sizeof(csi->tim));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
csi.tim.Instance = OMV_CSI_TIM;
|
||||
csi->tim.Instance = OMV_CSI_TIM;
|
||||
|
||||
// TCLK (PCLK * 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 pulse = (period + 1) / 2;
|
||||
|
||||
if (csi.tim.Init.Period && (csi.tim.Init.Period != period)) {
|
||||
// __HAL_TIM_SET_AUTORELOAD sets csi.tim.Init.Period...
|
||||
__HAL_TIM_SET_AUTORELOAD(&csi.tim, period);
|
||||
__HAL_TIM_SET_COMPARE(&csi.tim, OMV_CSI_TIM_CHANNEL, pulse);
|
||||
if (csi->tim.Init.Period && (csi->tim.Init.Period != period)) {
|
||||
// __HAL_TIM_SET_AUTORELOAD sets csi->tim.Init.Period...
|
||||
__HAL_TIM_SET_AUTORELOAD(&csi->tim, period);
|
||||
__HAL_TIM_SET_COMPARE(&csi->tim, OMV_CSI_TIM_CHANNEL, pulse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Timer base configuration */
|
||||
csi.tim.Init.Period = period;
|
||||
csi.tim.Init.Prescaler = 0;
|
||||
csi.tim.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
csi.tim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
csi.tim.Init.RepetitionCounter = 0;
|
||||
csi.tim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
csi->tim.Init.Period = period;
|
||||
csi->tim.Init.Prescaler = 0;
|
||||
csi->tim.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
csi->tim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
csi->tim.Init.RepetitionCounter = 0;
|
||||
csi->tim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
|
||||
/* Timer channel configuration */
|
||||
TIM_OC_InitTypeDef TIMOCHandle;
|
||||
@ -424,9 +413,9 @@ int omv_csi_set_clk_frequency(uint32_t frequency) {
|
||||
TIMOCHandle.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
TIMOCHandle.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
|
||||
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_Start(&csi.tim, OMV_CSI_TIM_CHANNEL) != 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_Start(&csi->tim, OMV_CSI_TIM_CHANNEL) != HAL_OK)) {
|
||||
return -1;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
int omv_csi_shutdown(int enable) {
|
||||
int omv_csi_shutdown(omv_csi_t *csi, int enable) {
|
||||
int ret = 0;
|
||||
omv_csi_abort(&csi, true, false);
|
||||
omv_csi_abort(csi, true, false);
|
||||
|
||||
if (enable) {
|
||||
#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);
|
||||
} else {
|
||||
omv_gpio_write(OMV_CSI_POWER_PIN, 0);
|
||||
@ -456,39 +445,32 @@ int omv_csi_shutdown(int enable) {
|
||||
#endif
|
||||
#if USE_DCMI
|
||||
HAL_NVIC_DisableIRQ(DCMI_IRQn);
|
||||
HAL_DCMI_DeInit(&csi.dcmi);
|
||||
HAL_DCMI_DeInit(&csi->dcmi);
|
||||
#endif
|
||||
} else {
|
||||
#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);
|
||||
} else {
|
||||
omv_gpio_write(OMV_CSI_POWER_PIN, 1);
|
||||
}
|
||||
#endif
|
||||
ret = omv_csi_config(OMV_CSI_CONFIG_INIT);
|
||||
ret = omv_csi_config(csi, OMV_CSI_CONFIG_INIT);
|
||||
}
|
||||
|
||||
mp_hal_delay_ms(10);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void omv_csi_vsync_callback(void *data) {
|
||||
if (csi.vsync_callback != 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) {
|
||||
int omv_csi_set_vsync_callback(omv_csi_t *csi, omv_csi_cb_t cb) {
|
||||
if (cb.fun == NULL) {
|
||||
#if (DCMI_VSYNC_EXTI_SHARED == 0)
|
||||
// Disable VSYNC EXTI IRQ
|
||||
omv_gpio_irq_enable(OMV_CSI_VSYNC_PIN, false);
|
||||
#endif
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
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
|
||||
// address to improve copy performance. Do not crop by more than 1 word as this will
|
||||
// result in less time between DMA transfers complete interrupts on 16-byte boundaries.
|
||||
static uint32_t get_dcmi_hw_crop(uint32_t bytes_per_pixel) {
|
||||
framebuffer_t *fb = csi.fb;
|
||||
static uint32_t get_dcmi_hw_crop(omv_csi_t *csi, uint32_t bytes_per_pixel) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
uint32_t byte_x_offset = (fb->x * bytes_per_pixel) % sizeof(uint32_t);
|
||||
uint32_t width_remainder = (resolution[csi.framesize][0] - (fb->x + fb->u)) * bytes_per_pixel;
|
||||
uint32_t x_crop = 0;
|
||||
uint32_t width_remainder = (resolution[csi->framesize][0] - (fb->x + fb->u)) * bytes_per_pixel;
|
||||
|
||||
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
|
||||
|
||||
@ -517,7 +498,9 @@ void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) {
|
||||
#else
|
||||
void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t pipe) {
|
||||
#endif
|
||||
framebuffer_t *fb = csi.fb;
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
framebuffer_t *fb = csi->fb;
|
||||
|
||||
#if USE_MDMA
|
||||
// Clear out any stale flags.
|
||||
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
|
||||
|
||||
// Reset DCMI_DMAConvCpltUser frame drop state.
|
||||
csi.first_line = false;
|
||||
if (csi.drop_frame) {
|
||||
csi.drop_frame = false;
|
||||
csi->first_line = false;
|
||||
if (csi->drop_frame) {
|
||||
csi->drop_frame = false;
|
||||
// Reset the buffer's state if the frame was dropped.
|
||||
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
|
||||
if (buffer) {
|
||||
@ -539,15 +522,15 @@ void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *dcmipp, uint32_t p
|
||||
|
||||
framebuffer_get_tail(fb, FB_NO_FLAGS);
|
||||
|
||||
if (csi.frame_callback) {
|
||||
csi.frame_callback();
|
||||
if (csi->frame_cb.fun) {
|
||||
csi->frame_cb.fun(csi->frame_cb.arg);
|
||||
}
|
||||
|
||||
#if USE_DCMIPP
|
||||
// Get the destination buffer address.
|
||||
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
|
||||
if (buffer == NULL) {
|
||||
omv_csi_abort(&csi, false, false);
|
||||
omv_csi_abort(csi, false, false);
|
||||
} else {
|
||||
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
|
||||
// framebuffer, and help hide external RAM latency.
|
||||
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.
|
||||
omv_csi_throttle_framerate();
|
||||
omv_csi_throttle_framerate(csi);
|
||||
|
||||
if (csi.drop_frame) {
|
||||
if (csi->drop_frame) {
|
||||
#if USE_MDMA
|
||||
if (!csi.transpose) {
|
||||
if (!csi->transpose) {
|
||||
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
|
||||
}
|
||||
#endif
|
||||
@ -576,17 +560,17 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
|
||||
vbuffer_t *buffer = framebuffer_get_tail(fb, FB_PEEK);
|
||||
if (buffer == NULL) {
|
||||
omv_csi_abort(&csi, false, true);
|
||||
omv_csi_abort(csi, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (csi.pixformat == PIXFORMAT_JPEG) {
|
||||
if (csi.jpg_format == 3) {
|
||||
if (csi->pixformat == PIXFORMAT_JPEG) {
|
||||
if (csi->jpg_format == 3) {
|
||||
// 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
|
||||
// the total size.
|
||||
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
|
||||
// valid data length, followed by image data and optional padding (0xFF). `offset` holds the
|
||||
// total size.
|
||||
@ -604,41 +588,41 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
#if USE_MDMA
|
||||
// DCMI_DMAConvCpltUser is called with the other MAR register.
|
||||
// So, we have to fix the address in full MDMA offload mode.
|
||||
if (!csi.transpose) {
|
||||
if (!csi->transpose) {
|
||||
addr = (uint32_t) &_line_buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t bytes_per_pixel = omv_csi_get_src_bpp();
|
||||
uint8_t *src = ((uint8_t *) addr) + (fb->x * bytes_per_pixel) - get_dcmi_hw_crop(bytes_per_pixel);
|
||||
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(csi, bytes_per_pixel);
|
||||
uint8_t *dst = buffer->data;
|
||||
|
||||
if (csi.pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
if (csi->pixformat == PIXFORMAT_GRAYSCALE) {
|
||||
bytes_per_pixel = sizeof(uint8_t);
|
||||
}
|
||||
|
||||
// For all non-JPEG and non-transposed modes image capture can be completely offload to 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
|
||||
// the frame has to be dropped.
|
||||
uint32_t line_width_bytes = fb->u * bytes_per_pixel;
|
||||
// mdma0 will copy this line of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi.mdma0);
|
||||
csi.mdma0.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi.mdma0, (uint32_t) src, (uint32_t) dst,
|
||||
__HAL_UNLOCK(&csi->mdma0);
|
||||
csi->mdma0.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma0, (uint32_t) src, (uint32_t) dst,
|
||||
line_width_bytes, 1);
|
||||
// mdma1 will copy all remaining lines of the image to the final destination.
|
||||
__HAL_UNLOCK(&csi.mdma1);
|
||||
csi.mdma1.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi.mdma1, (uint32_t) src, (uint32_t) (dst + line_width_bytes),
|
||||
__HAL_UNLOCK(&csi->mdma1);
|
||||
csi->mdma1.State = HAL_MDMA_STATE_READY;
|
||||
HAL_MDMA_Start(&csi->mdma1, (uint32_t) src, (uint32_t) (dst + line_width_bytes),
|
||||
line_width_bytes, fb->v - 1);
|
||||
HAL_NVIC_DisableIRQ(DMA2_Stream1_IRQn);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!csi.transpose) {
|
||||
if (!csi->transpose) {
|
||||
dst += fb->u * bytes_per_pixel * buffer->offset++;
|
||||
} else {
|
||||
dst += bytes_per_pixel * buffer->offset++;
|
||||
@ -646,9 +630,9 @@ void DCMI_DMAConvCpltUser(uint32_t addr) {
|
||||
|
||||
#if USE_MDMA
|
||||
// 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
|
||||
omv_csi_copy_line(NULL, src, dst);
|
||||
omv_csi_copy_line(csi, NULL, src, dst);
|
||||
#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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
framebuffer_t *fb = csi.fb;
|
||||
int omv_csi_dma_memcpy(omv_csi_t *csi, void *dma, void *dst, void *src, int bpp, bool transposed) {
|
||||
framebuffer_t *fb = csi->fb;
|
||||
MDMA_HandleTypeDef *handle = dma;
|
||||
|
||||
// Drop the frame if MDMA is not keeping up as the image will be corrupted.
|
||||
if (handle->Instance->CCR & MDMA_CCR_EN) {
|
||||
csi.drop_frame = true;
|
||||
csi->drop_frame = true;
|
||||
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]);
|
||||
|
||||
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)
|
||||
@ -820,8 +804,7 @@ void omv_csi_update_awb(omv_csi_t *csi, uint32_t n_pixels) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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 stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
uint32_t length = 0;
|
||||
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
|
||||
// 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
|
||||
// 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.
|
||||
// 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
|
||||
// Setup the size and address of the transfer
|
||||
uint32_t bytes_per_pixel = omv_csi_get_src_bpp();
|
||||
uint32_t x_crop = get_dcmi_hw_crop(bytes_per_pixel);
|
||||
uint32_t bytes_per_pixel = omv_csi_get_src_bpp(csi);
|
||||
uint32_t x_crop = get_dcmi_hw_crop(csi, 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.
|
||||
@ -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);
|
||||
}
|
||||
#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;
|
||||
|
||||
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 init_ret = 0;
|
||||
int ret = 0;
|
||||
static omv_i2c_t i2c;
|
||||
|
||||
// List of I2C buses to scan.
|
||||
uint32_t buses[][2] = {
|
||||
@ -1099,47 +1085,54 @@ int omv_csi_init() {
|
||||
#endif
|
||||
};
|
||||
|
||||
// Reset the csi state
|
||||
memset(&csi, 0, sizeof(omv_csi_t));
|
||||
// Initialize the CSIs using this driver's ops as defaults,
|
||||
// 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
|
||||
csi.fb = framebuffer_get(0);
|
||||
|
||||
// Set I2C bus
|
||||
csi.i2c = &csi_i2c;
|
||||
|
||||
// Set default snapshot function.
|
||||
csi.snapshot = omv_csi_snapshot;
|
||||
memset(csi, 0, sizeof(omv_csi_t));
|
||||
csi->i2c = &i2c;
|
||||
csi->fb = framebuffer_get(-1);
|
||||
csi->abort = stm_csi_abort;
|
||||
csi->config = stm_csi_config;
|
||||
csi->snapshot = stm_csi_snapshot;
|
||||
csi->color_palette = rainbow_table;
|
||||
}
|
||||
|
||||
// Configure the csi external clock (XCLK).
|
||||
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) {
|
||||
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++) {
|
||||
uint32_t id = buses[i][0], speed = buses[i][1];
|
||||
if ((init_ret = omv_csi_probe_init(id, speed)) == 0) {
|
||||
// Initialize the camera bus.
|
||||
omv_i2c_init(&i2c, buses[i][0], buses[i][1]);
|
||||
|
||||
if (!(ret = omv_csi_probe(&i2c))) {
|
||||
break;
|
||||
}
|
||||
omv_i2c_deinit(csi.i2c);
|
||||
|
||||
omv_i2c_deinit(&i2c);
|
||||
|
||||
// Scan the next bus or fail if this is the last one.
|
||||
if ((i + 1) == n_buses) {
|
||||
return init_ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the DCMI interface.
|
||||
if (omv_csi_config(OMV_CSI_CONFIG_INIT) != 0) {
|
||||
return OMV_CSI_ERROR_CSI_INIT_FAILED;
|
||||
for (size_t i=0; i<OMV_CSI_MAX_DEVICES; i++) {
|
||||
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.
|
||||
JPEG_FB()->enabled = 0;
|
||||
|
||||
// Set default color palette.
|
||||
csi.color_palette = rainbow_table;
|
||||
|
||||
csi.detected = true;
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user