From 2cbdd18a20558c2a0ea3fc11617fed50b99aa1d0 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Jun 2021 20:15:43 -0500 Subject: [PATCH] esp32s2: Implement parallel image capture --- locale/circuitpython.pot | 8 +- .../esp32s2/common-hal/audiobusio/__init__.c | 9 ++ .../esp32s2/common-hal/audiobusio/__init__.h | 3 + .../imagecapture/ParallelImageCapture.c | 92 ++++++++++++++++++- .../imagecapture/ParallelImageCapture.h | 6 ++ ports/esp32s2/supervisor/port.c | 8 ++ 6 files changed, 120 insertions(+), 6 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 8bff0caaa3..7341d0c2e2 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -115,6 +115,10 @@ msgstr "" msgid "%q must be a tuple of length 2" msgstr "" +#: ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c +msgid "%q must be between %d and %d" +msgstr "" + #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" @@ -3825,6 +3829,7 @@ msgstr "" #: ports/esp32s2/boards/artisense_rd00/mpconfigboard.h #: ports/esp32s2/boards/atmegazero_esp32s2/mpconfigboard.h #: ports/esp32s2/boards/electroniccats_bastwifi/mpconfigboard.h +#: ports/esp32s2/boards/espressif_kaluga_1.3/mpconfigboard.h #: ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.h #: ports/esp32s2/boards/espressif_saola_1_wroom/mpconfigboard.h #: ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h @@ -3832,7 +3837,8 @@ msgstr "" #: ports/esp32s2/boards/franzininho_wifi_wrover/mpconfigboard.h #: ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.h #: ports/esp32s2/boards/microdev_micro_s2/mpconfigboard.h -#: ports/esp32s2/boards/muselab_nanoesp32_s2/mpconfigboard.h +#: ports/esp32s2/boards/muselab_nanoesp32_s2_wroom/mpconfigboard.h +#: ports/esp32s2/boards/muselab_nanoesp32_s2_wrover/mpconfigboard.h #: ports/esp32s2/boards/targett_module_clip_wroom/mpconfigboard.h #: ports/esp32s2/boards/targett_module_clip_wrover/mpconfigboard.h #: ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.h diff --git a/ports/esp32s2/common-hal/audiobusio/__init__.c b/ports/esp32s2/common-hal/audiobusio/__init__.c index 60d00aaa61..d6c3676d6a 100644 --- a/ports/esp32s2/common-hal/audiobusio/__init__.c +++ b/ports/esp32s2/common-hal/audiobusio/__init__.c @@ -41,6 +41,15 @@ static i2s_t *i2s_instance[I2S_NUM_MAX]; static QueueHandle_t i2s_queues[I2S_NUM_MAX]; static TaskHandle_t i2s_tasks[I2S_NUM_MAX]; +void port_i2s_allocate_i2s0(void) { + if (!i2s_instance[0]) { + i2s_instance[0] = (void *)~(intptr_t)0; + return; + } + + mp_raise_RuntimeError(translate("Peripheral in use")); +} + static int8_t port_i2s_allocate(void) { #if defined(I2S_NUM_1) if (!i2s_instance[1]) { diff --git a/ports/esp32s2/common-hal/audiobusio/__init__.h b/ports/esp32s2/common-hal/audiobusio/__init__.h index 7709735dae..00dc99d30a 100644 --- a/ports/esp32s2/common-hal/audiobusio/__init__.h +++ b/ports/esp32s2/common-hal/audiobusio/__init__.h @@ -59,3 +59,6 @@ bool port_i2s_playing(i2s_t *self); bool port_i2s_paused(i2s_t *self); void port_i2s_pause(i2s_t *self); void port_i2s_resume(i2s_t *self); + +// some uses (imagecapture) can only operate on i2s0 and need their own init code +void port_i2s_allocate_i2s0(void); diff --git a/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c index 4a209b238e..67932804f7 100644 --- a/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c +++ b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c @@ -24,26 +24,108 @@ * THE SOFTWARE. */ +#include + #include "py/obj.h" #include "py/runtime.h" +#include "common-hal/audiobusio/__init__.h" #include "common-hal/imagecapture/ParallelImageCapture.h" +#include "cam.h" void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_parallelimagecapture_obj_t *self, - const mcu_pin_obj_t *data0, + const uint8_t data_pins[], + uint8_t data_count, const mcu_pin_obj_t *data_clock, const mcu_pin_obj_t *vertical_sync, - const mcu_pin_obj_t *horizontal_reference, - int data_count) { - mp_raise_NotImplementedError(NULL); + const mcu_pin_obj_t *horizontal_reference) { + + // only 8 bits is supported at present + if (data_count < 8 || data_count > 16) { + mp_raise_ValueError_varg(translate("%q must be between %d and %d"), MP_QSTR_data_count, 8, 16); + } + + // This will throw if unsuccessful. Everything following is guaranteed to succeed. + port_i2s_allocate_i2s0(); + + claim_pin(data_clock); + claim_pin(vertical_sync); + claim_pin(horizontal_reference); + + self->data_count = data_count; + self->data_clock = data_clock->number; + self->vertical_sync = vertical_sync->number; + self->horizontal_reference = horizontal_reference->number; + + self->config = (cam_config_t) { + .bit_width = data_count, + .pin = { + .pclk = data_clock->number, + .vsync = vertical_sync->number, + .hsync = horizontal_reference->number, + }, + .vsync_invert = true, + .hsync_invert = false, + .size = 0, + .max_buffer_size = 8 * 1024, + .task_stack = 1024, + .task_pri = configMAX_PRIORITIES + }; + + for (int i = 0; i < data_count; i++) { + claim_pin_number(data_pins[i]); + self->config.pin_data[i] = data_pins[i]; + } } void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self) { + reset_pin_number(self->data_clock); + self->data_clock = NO_PIN; + + reset_pin_number(self->vertical_sync); + self->vertical_sync = NO_PIN; + + reset_pin_number(self->horizontal_reference); + self->horizontal_reference = NO_PIN; + + for (int i = 0; i < self->data_count; i++) { + if (self->config.pin_data[i] != NO_PIN) { + reset_pin_number(self->config.pin_data[i]); + self->config.pin_data[i] = NO_PIN; + } + } + + port_i2s_reset_instance(0); } bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self) { - return true; + return self->data_clock == NO_PIN; } void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize) { + size_t size = bufsize / 2; // count is in pixels + if (size != self->config.size || buffer != self->config.frame1_buffer) { + cam_deinit(); + self->config.size = bufsize / 2; // count is in pixels(?) + self->config.frame1_buffer = buffer; + + cam_init(&self->config); + } else { + cam_give(buffer); + } + cam_start(); + + while (!cam_ready()) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + self->config.size = 0; // force re-init next time + cam_stop(); + return; + } + } + + uint8_t *unused; + cam_take(&unused); // this just "returns" buffer + + cam_stop(); } diff --git a/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h index ef778bfeff..4dd65bc904 100644 --- a/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h +++ b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h @@ -27,7 +27,13 @@ #pragma once #include "shared-bindings/imagecapture/ParallelImageCapture.h" +#include "cam.h" struct imagecapture_parallelimagecapture_obj { mp_obj_base_t base; + cam_config_t config; + gpio_num_t data_clock; + gpio_num_t vertical_sync; + gpio_num_t horizontal_reference; + uint8_t data_count; }; diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c index d48b7ef892..1cbe61f16b 100644 --- a/ports/esp32s2/supervisor/port.c +++ b/ports/esp32s2/supervisor/port.c @@ -66,6 +66,10 @@ #include "common-hal/audiobusio/__init__.h" #endif +#if CIRCUITPY_IMAGECAPTURE +#include "cam.h" +#endif + #define HEAP_SIZE (48 * 1024) uint32_t *heap; @@ -155,6 +159,10 @@ safe_mode_t port_init(void) { } void reset_port(void) { + #if CIRCUITPY_IMAGECAPTURE + cam_deinit(); + #endif + reset_all_pins(); // A larger delay so the idle task can run and do any IDF cleanup needed.