From 1d03ec12b528f4f29034d427f6bc5e9f0b110d7f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 9 Jun 2021 11:26:22 -0500 Subject: [PATCH 01/17] supervisor: Fix assertion error in supervisor_move_memory It was possible for _only_ a low allocation to be performed. In this case, `high_head` is NULL, and the comparison `MP_STATE_VM(first_embedded_allocation) < high_head` would fail. Closes: #4871 --- supervisor/shared/memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c index a20df618ca..d03b69e937 100644 --- a/supervisor/shared/memory.c +++ b/supervisor/shared/memory.c @@ -236,6 +236,7 @@ size_t get_allocation_length(supervisor_allocation *allocation) { return ALLOCATION_NODE(allocation)->length & ~FLAGS; } + void supervisor_move_memory(void) { // This whole function is not needed when there are no movable allocations, let it be optimized // out. @@ -244,7 +245,9 @@ void supervisor_move_memory(void) { } // This must be called exactly after freeing the heap, so that the embedded allocations, if any, // are now in the free region. - assert(MP_STATE_VM(first_embedded_allocation) == NULL || (low_head < MP_STATE_VM(first_embedded_allocation) && MP_STATE_VM(first_embedded_allocation) < high_head)); + assert(MP_STATE_VM(first_embedded_allocation) == NULL || ( + (low_head == NULL || low_head < MP_STATE_VM(first_embedded_allocation)) && + (high_head == NULL || MP_STATE_VM(first_embedded_allocation) < high_head))); // Save the old pointers for allocation_from_ptr(). supervisor_allocation old_allocations_array[CIRCUITPY_SUPERVISOR_ALLOC_COUNT]; From 3e83834621a37ea65cc3839d5fa288fdfcf6e1a7 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Jun 2021 20:16:30 -0500 Subject: [PATCH 02/17] esp32s2: Add claim_pin_number In ParallelImageCapture we need to grab a group of pins by number --- ports/esp32s2/common-hal/microcontroller/Pin.c | 7 +++++++ ports/esp32s2/common-hal/microcontroller/Pin.h | 1 + 2 files changed, 8 insertions(+) diff --git a/ports/esp32s2/common-hal/microcontroller/Pin.c b/ports/esp32s2/common-hal/microcontroller/Pin.c index d912402fb9..7b69c1d6ee 100644 --- a/ports/esp32s2/common-hal/microcontroller/Pin.c +++ b/ports/esp32s2/common-hal/microcontroller/Pin.c @@ -96,6 +96,13 @@ void reset_all_pins(void) { in_use[1] = 0; } +void claim_pin_number(gpio_num_t pin_number) { + if (pin_number == NO_PIN) { + return; + } + in_use[pin_number / 32] |= (1 << (pin_number % 32)); +} + void claim_pin(const mcu_pin_obj_t *pin) { in_use[pin->number / 32] |= (1 << (pin->number % 32)); } diff --git a/ports/esp32s2/common-hal/microcontroller/Pin.h b/ports/esp32s2/common-hal/microcontroller/Pin.h index e82b28f282..de7ea8e093 100644 --- a/ports/esp32s2/common-hal/microcontroller/Pin.h +++ b/ports/esp32s2/common-hal/microcontroller/Pin.h @@ -37,6 +37,7 @@ void reset_all_pins(void); void reset_pin_number(gpio_num_t pin_number); void common_hal_reset_pin(const mcu_pin_obj_t *pin); void claim_pin(const mcu_pin_obj_t *pin); +void claim_pin_number(gpio_num_t pin_number); bool pin_number_is_free(gpio_num_t pin_number); void never_reset_pin_number(gpio_num_t pin_number); From 80f909001dc882e230825f71553e31f4e46e2849 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 9 Jun 2021 11:28:30 -0500 Subject: [PATCH 03/17] esp32s2: Add support for `make monitor` `idf.py monitor` connects to the debug UART and shows the messages. In contrast to a traditional terminal program, it also has the limited ability to transform hex addresses into file & line number information, especially for debug builds. This requires the elf file be copied to a specific place. --- ports/esp32s2/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index 201989e0c9..7edc2f1625 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -337,6 +337,10 @@ flash: $(BUILD)/firmware.bin flash-circuitpython-only: $(BUILD)/circuitpython-firmware.bin esptool.py --chip esp32s2 -p $(PORT) $(ESPTOOL_FLAGS) write_flash $(FLASH_FLAGS) 0x10000 $^ +monitor: $(BUILD)/firmware.elf + cp $< build/circuitpython.elf + idf.py monitor -p $(PORT) + include $(TOP)/py/mkrules.mk # Print out the value of a make variable. From a0ec95f0922976b836f77bc4b222fca78cf8bbe2 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 7 Jun 2021 08:15:11 -0500 Subject: [PATCH 04/17] kaluga 1.2: add camera_data_tuple --- ports/esp32s2/boards/espressif_kaluga_1/pins.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ports/esp32s2/boards/espressif_kaluga_1/pins.c b/ports/esp32s2/boards/espressif_kaluga_1/pins.c index c1657d6c0e..f28062a164 100644 --- a/ports/esp32s2/boards/espressif_kaluga_1/pins.c +++ b/ports/esp32s2/boards/espressif_kaluga_1/pins.c @@ -1,5 +1,21 @@ +#include "py/objtuple.h" #include "shared-bindings/board/__init__.h" +STATIC const mp_rom_obj_tuple_t camera_data_tuple = { + {&mp_type_tuple}, + 8, + { + MP_ROM_PTR(&pin_GPIO46), + MP_ROM_PTR(&pin_GPIO45), + MP_ROM_PTR(&pin_GPIO41), + MP_ROM_PTR(&pin_GPIO42), + MP_ROM_PTR(&pin_GPIO39), + MP_ROM_PTR(&pin_GPIO40), + MP_ROM_PTR(&pin_GPIO21), + MP_ROM_PTR(&pin_GPIO38), + } +}; + STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, @@ -54,6 +70,7 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOC), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA), MP_ROM_PTR(&camera_data_tuple) }, { MP_ROM_QSTR(MP_QSTR_CAMERA_D2), MP_ROM_PTR(&pin_GPIO46) }, { MP_ROM_QSTR(MP_QSTR_CAMERA_D3), MP_ROM_PTR(&pin_GPIO45) }, { MP_ROM_QSTR(MP_QSTR_CAMERA_D4), MP_ROM_PTR(&pin_GPIO41) }, From dc6472bb5b7b4bb47815f9066c993381dc1d33b3 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 31 May 2021 11:03:25 -0500 Subject: [PATCH 05/17] esp32s2: Add "cam" files from esp-dev-kits@282a356 .. these files have been re-formatted by our code-formatting scripts, so their content does NOT match upstream. --- ports/esp32s2/Makefile | 1 + ports/esp32s2/cam.c | 508 +++++++++++++++++++++++++++++++++++++++++ ports/esp32s2/cam.h | 103 +++++++++ 3 files changed, 612 insertions(+) create mode 100644 ports/esp32s2/cam.c create mode 100644 ports/esp32s2/cam.h diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index 7edc2f1625..4786422128 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -176,6 +176,7 @@ CFLAGS += -DCFG_TUD_VENDOR_RX_BUFSIZE=128 -DCFG_TUD_VENDOR_TX_BUFSIZE=128 SRC_C += \ background.c \ + cam.c \ fatfs_port.c \ mphalport.c \ bindings/espidf/__init__.c \ diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c new file mode 100644 index 0000000000..10b2cf0a59 --- /dev/null +++ b/ports/esp32s2/cam.c @@ -0,0 +1,508 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2s.h" +#include "esp_system.h" +#include "esp_log.h" +#include "soc/i2s_struct.h" +#include "soc/apb_ctrl_reg.h" +#include "esp32s2/rom/lldesc.h" +#include "esp32s2/rom/cache.h" +#include "soc/dport_access.h" +#include "soc/dport_reg.h" +#include "driver/ledc.h" +#include "cam.h" +#include "hal/gpio_ll.h" + +static const char *TAG = "cam"; + +#define CAM_DMA_MAX_SIZE (4095) + +typedef enum { + CAM_IN_SUC_EOF_EVENT = 0, + CAM_VSYNC_EVENT +} cam_event_t; + +typedef struct { + uint8_t *frame_buffer; + size_t len; +} frame_buffer_event_t; + +typedef struct { + uint32_t buffer_size; + uint32_t half_buffer_size; + uint32_t node_cnt; + uint32_t half_node_cnt; + uint32_t dma_size; + uint32_t cnt; + uint32_t total_cnt; + uint16_t width; + uint16_t high; + lldesc_t *dma; + uint8_t *buffer; + uint8_t *frame1_buffer; + uint8_t *frame2_buffer; + uint8_t frame1_buffer_en; + uint8_t frame2_buffer_en; + uint8_t jpeg_mode; + uint8_t vsync_pin; + uint8_t vsync_invert; + uint8_t hsync_invert; + QueueHandle_t event_queue; + QueueHandle_t frame_buffer_queue; + TaskHandle_t task_handle; + intr_handle_t intr_handle; +} cam_obj_t; + +static cam_obj_t *cam_obj = NULL; + +void IRAM_ATTR cam_isr(void *arg) { + cam_event_t cam_event = {0}; + BaseType_t HPTaskAwoken = pdFALSE; + typeof(I2S0.int_st) int_st = I2S0.int_st; + I2S0.int_clr.val = int_st.val; + + if (int_st.in_suc_eof) { + cam_event = CAM_IN_SUC_EOF_EVENT; + xQueueSendFromISR(cam_obj->event_queue, (void *)&cam_event, &HPTaskAwoken); + } + + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +void IRAM_ATTR cam_vsync_isr(void *arg) { + cam_event_t cam_event = {0}; + BaseType_t HPTaskAwoken = pdFALSE; + /*!< filter */ + ets_delay_us(1); + + if (gpio_ll_get_level(&GPIO, cam_obj->vsync_pin) == !cam_obj->vsync_invert) { + cam_event = CAM_VSYNC_EVENT; + xQueueSendFromISR(cam_obj->event_queue, (void *)&cam_event, &HPTaskAwoken); + } + + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +static void cam_config(const cam_config_t *config) { + /*!bit_width; + + /*!< Configuration data format */ + I2S0.conf.val = 0; + I2S0.conf.tx_right_first = 1; + I2S0.conf.tx_msb_right = 1; + I2S0.conf.tx_dma_equal = 1; + I2S0.conf.rx_right_first = 1; + I2S0.conf.rx_msb_right = 1; + I2S0.conf.rx_dma_equal = 1; + + I2S0.conf1.val = 0; + I2S0.conf1.tx_pcm_bypass = 1; + I2S0.conf1.tx_stop_en = 1; + I2S0.conf1.rx_pcm_bypass = 1; + + I2S0.conf2.val = 0; + I2S0.conf2.cam_sync_fifo_reset = 1; + I2S0.conf2.cam_sync_fifo_reset = 0; + I2S0.conf2.lcd_en = 1; + I2S0.conf2.camera_en = 1; + I2S0.conf2.i_v_sync_filter_en = 1; + I2S0.conf2.i_v_sync_filter_thres = 1; + + I2S0.conf_chan.val = 0; + I2S0.conf_chan.tx_chan_mod = 1; + I2S0.conf_chan.rx_chan_mod = 1; + + I2S0.fifo_conf.val = 0; + I2S0.fifo_conf.rx_fifo_mod_force_en = 1; + I2S0.fifo_conf.rx_data_num = 32; + I2S0.fifo_conf.rx_fifo_mod = 2; + I2S0.fifo_conf.tx_fifo_mod_force_en = 1; + I2S0.fifo_conf.tx_data_num = 32; + I2S0.fifo_conf.tx_fifo_mod = 2; + I2S0.fifo_conf.dscr_en = 1; + + I2S0.lc_conf.out_rst = 1; + I2S0.lc_conf.out_rst = 0; + I2S0.lc_conf.in_rst = 1; + I2S0.lc_conf.in_rst = 0; + + I2S0.timing.val = 0; + + I2S0.int_ena.val = 0; + I2S0.int_clr.val = ~0; + + I2S0.lc_conf.check_owner = 0; + I2S0.conf.rx_start = 1; +} + +static void cam_set_pin(const cam_config_t *config) { + gpio_config_t io_conf = {0}; + io_conf.intr_type = config->vsync_invert ? GPIO_PIN_INTR_NEGEDGE : GPIO_PIN_INTR_POSEDGE; + io_conf.pin_bit_mask = 1 << config->pin.vsync; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = 1; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); + gpio_install_isr_service(0); + gpio_isr_handler_add(config->pin.vsync, cam_vsync_isr, NULL); + gpio_intr_disable(config->pin.vsync); + + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin.pclk], PIN_FUNC_GPIO); + gpio_set_direction(config->pin.pclk, GPIO_MODE_INPUT); + gpio_set_pull_mode(config->pin.pclk, GPIO_FLOATING); + gpio_matrix_in(config->pin.pclk, I2S0I_WS_IN_IDX, false); + + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin.vsync], PIN_FUNC_GPIO); + gpio_set_direction(config->pin.vsync, GPIO_MODE_INPUT); + gpio_set_pull_mode(config->pin.vsync, GPIO_FLOATING); + gpio_matrix_in(config->pin.vsync, I2S0I_V_SYNC_IDX, config->vsync_invert); + + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin.hsync], PIN_FUNC_GPIO); + gpio_set_direction(config->pin.hsync, GPIO_MODE_INPUT); + gpio_set_pull_mode(config->pin.hsync, GPIO_FLOATING); + gpio_matrix_in(config->pin.hsync, I2S0I_H_SYNC_IDX, config->hsync_invert); + + for (int i = 0; i < config->bit_width; i++) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_data[i]], PIN_FUNC_GPIO); + gpio_set_direction(config->pin_data[i], GPIO_MODE_INPUT); + gpio_set_pull_mode(config->pin_data[i], GPIO_FLOATING); + /*!< High position alignment, IN16 is always the highest bit */ + /*!< Fifo accesses data by bit, and when rx_bits_mod is 8, the data needs to be aligned by 8 bits */ + gpio_matrix_in(config->pin_data[i], I2S0I_DATA_IN0_IDX + (16 - config->bit_width) + i, false); + } + + ledc_timer_config_t ledc_timer = { + .duty_resolution = LEDC_TIMER_1_BIT, + .freq_hz = config->xclk_fre, + .speed_mode = LEDC_LOW_SPEED_MODE, + .timer_num = LEDC_TIMER_1 + }; + ledc_timer_config(&ledc_timer); + ledc_channel_config_t ledc_channel = { + .channel = LEDC_CHANNEL_2, + .duty = 1, + .gpio_num = config->pin.xclk, + .speed_mode = LEDC_LOW_SPEED_MODE, + .timer_sel = LEDC_TIMER_1, + .hpoint = 0 + }; + ledc_channel_config(&ledc_channel); + + gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false); + ESP_LOGI(TAG, "cam_xclk_pin setup\n"); +} + +static void cam_vsync_intr_enable(uint8_t en) { + if (en) { + gpio_intr_enable(cam_obj->vsync_pin); + } else { + gpio_intr_disable(cam_obj->vsync_pin); + } +} + +static void cam_dma_stop(void) { + if (I2S0.int_ena.in_suc_eof == 1) { + I2S0.conf.rx_start = 0; + I2S0.int_ena.in_suc_eof = 0; + I2S0.int_clr.in_suc_eof = 1; + I2S0.in_link.stop = 1; + } +} + +static void cam_dma_start(void) { + if (I2S0.int_ena.in_suc_eof == 0) { + I2S0.int_clr.in_suc_eof = 1; + I2S0.int_ena.in_suc_eof = 1; + I2S0.conf.rx_reset = 1; + I2S0.conf.rx_reset = 0; + I2S0.conf.rx_fifo_reset = 1; + I2S0.conf.rx_fifo_reset = 0; + I2S0.lc_conf.in_rst = 1; + I2S0.lc_conf.in_rst = 0; + I2S0.lc_conf.ahbm_fifo_rst = 1; + I2S0.lc_conf.ahbm_fifo_rst = 0; + I2S0.lc_conf.ahbm_rst = 1; + I2S0.lc_conf.ahbm_rst = 0; + I2S0.in_link.start = 1; + I2S0.conf.rx_start = 1; + + if (cam_obj->jpeg_mode) { + /*!< Manually give the vsync data for the first frame */ + gpio_matrix_in(cam_obj->vsync_pin, I2S0I_V_SYNC_IDX, !cam_obj->vsync_invert); + gpio_matrix_in(cam_obj->vsync_pin, I2S0I_V_SYNC_IDX, cam_obj->vsync_invert); + } + } +} + +void cam_stop(void) { + cam_vsync_intr_enable(0); + cam_dma_stop(); +} + +void cam_start(void) { + cam_vsync_intr_enable(1); +} + +typedef enum { + CAM_STATE_IDLE = 0, + CAM_STATE_READ_BUF1 = 1, + CAM_STATE_READ_BUF2 = 2, +} cam_state_t; + +/*!event_queue); + + while (1) { + xQueueReceive(cam_obj->event_queue, (void *)&cam_event, portMAX_DELAY); + + switch (state) { + case CAM_STATE_IDLE: { + if (cam_event == CAM_VSYNC_EVENT) { + if (cam_obj->frame1_buffer_en) { + cam_dma_start(); + cam_vsync_intr_enable(0); + state = CAM_STATE_READ_BUF1; + } else if (cam_obj->frame2_buffer_en) { + cam_dma_start(); + cam_vsync_intr_enable(0); + state = CAM_STATE_READ_BUF2; + } + + cam_obj->cnt = 0; + } + } + break; + + case CAM_STATE_READ_BUF1: { + if (cam_event == CAM_IN_SUC_EOF_EVENT) { + if (cam_obj->cnt == 0) { + cam_vsync_intr_enable(1); /*!< CAM real start is required to receive the first buf data and then turn on the vsync interrupt */ + } + + memcpy(&cam_obj->frame1_buffer[cam_obj->cnt * cam_obj->half_buffer_size], &cam_obj->buffer[(cam_obj->cnt % 2) * cam_obj->half_buffer_size], cam_obj->half_buffer_size); + + if (cam_obj->jpeg_mode) { + if (cam_obj->frame1_buffer_en == 0) { + cam_dma_stop(); + } + } else { + if (cam_obj->cnt == cam_obj->total_cnt - 1) { + cam_obj->frame1_buffer_en = 0; + } + } + + if (cam_obj->frame1_buffer_en == 0) { + frame_buffer_event.frame_buffer = cam_obj->frame1_buffer; + frame_buffer_event.len = (cam_obj->cnt + 1) * cam_obj->half_buffer_size; + xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, portMAX_DELAY); + state = CAM_STATE_IDLE; + } else { + cam_obj->cnt++; + } + } else if (cam_event == CAM_VSYNC_EVENT) { + if (cam_obj->jpeg_mode) { + cam_obj->frame1_buffer_en = 0; + } + } + } + break; + + case CAM_STATE_READ_BUF2: { + if (cam_event == CAM_IN_SUC_EOF_EVENT) { + if (cam_obj->cnt == 0) { + cam_vsync_intr_enable(1); /*!< CAM real start is required to receive the first buf data and then turn on the vsync interrupt */ + } + + memcpy(&cam_obj->frame2_buffer[cam_obj->cnt * cam_obj->half_buffer_size], &cam_obj->buffer[(cam_obj->cnt % 2) * cam_obj->half_buffer_size], cam_obj->half_buffer_size); + + if (cam_obj->jpeg_mode) { + if (cam_obj->frame2_buffer_en == 0) { + cam_dma_stop(); + } + } else { + if (cam_obj->cnt == cam_obj->total_cnt - 1) { + cam_obj->frame2_buffer_en = 0; + } + } + + if (cam_obj->frame2_buffer_en == 0) { + frame_buffer_event.frame_buffer = cam_obj->frame2_buffer; + frame_buffer_event.len = (cam_obj->cnt + 1) * cam_obj->half_buffer_size; + xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, portMAX_DELAY); + state = CAM_STATE_IDLE; + } else { + cam_obj->cnt++; + } + } else if (cam_event == CAM_VSYNC_EVENT) { + if (cam_obj->jpeg_mode) { + cam_obj->frame2_buffer_en = 0; + } + } + } + break; + } + } +} + +size_t cam_take(uint8_t **buffer_p) { + frame_buffer_event_t frame_buffer_event; + xQueueReceive(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, portMAX_DELAY); + *buffer_p = frame_buffer_event.frame_buffer; + return frame_buffer_event.len; +} + +void cam_give(uint8_t *buffer) { + if (buffer == cam_obj->frame1_buffer) { + cam_obj->frame1_buffer_en = 1; + } else if (buffer == cam_obj->frame2_buffer) { + cam_obj->frame2_buffer_en = 1; + } +} + +void cam_dma_config(const cam_config_t *config) { + int cnt = 0; + + if (config->mode.jpeg) { + cam_obj->buffer_size = 2048; + cam_obj->half_buffer_size = cam_obj->buffer_size / 2; + cam_obj->dma_size = 1024; + } else { + for (cnt = 0;; cnt++) { /*!< Find the divisible buffer size */ + if ((config->size.width * config->size.high * 2) % (config->max_buffer_size - cnt) == 0) { + break; + } + } + + cam_obj->buffer_size = config->max_buffer_size - cnt; + + cam_obj->half_buffer_size = cam_obj->buffer_size / 2; + + for (cnt = 0;; cnt++) { /*!< Look for divisible dma sizes */ + if ((cam_obj->half_buffer_size) % (CAM_DMA_MAX_SIZE - cnt) == 0) { + break; + } + } + + cam_obj->dma_size = CAM_DMA_MAX_SIZE - cnt; + } + + cam_obj->node_cnt = (cam_obj->buffer_size) / cam_obj->dma_size; /*!< Number of DMA nodes */ + cam_obj->half_node_cnt = cam_obj->node_cnt / 2; + cam_obj->total_cnt = (config->size.width * config->size.high * 2) / cam_obj->half_buffer_size; /*!< Number of interrupt copies produced. Ping pong copies */ + + ESP_LOGI(TAG, "cam_buffer_size: %d, cam_dma_size: %d, cam_dma_node_cnt: %d, cam_total_cnt: %d\n", cam_obj->buffer_size, cam_obj->dma_size, cam_obj->node_cnt, cam_obj->total_cnt); + + cam_obj->dma = (lldesc_t *)heap_caps_malloc(cam_obj->node_cnt * sizeof(lldesc_t), MALLOC_CAP_DMA); + cam_obj->buffer = (uint8_t *)heap_caps_malloc(cam_obj->buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA); + + for (int x = 0; x < cam_obj->node_cnt; x++) { + cam_obj->dma[x].size = cam_obj->dma_size; + cam_obj->dma[x].length = cam_obj->dma_size; + cam_obj->dma[x].eof = 0; + cam_obj->dma[x].owner = 1; + cam_obj->dma[x].buf = (cam_obj->buffer + cam_obj->dma_size * x); + cam_obj->dma[x].empty = &cam_obj->dma[(x + 1) % cam_obj->node_cnt]; + } + + I2S0.in_link.addr = ((uint32_t)&cam_obj->dma[0]) & 0xfffff; + I2S0.rx_eof_num = cam_obj->half_buffer_size; /*!< Ping-pong operation */ +} + +esp_err_t cam_deinit() { + if (!cam_obj) { + return ESP_FAIL; + } + + cam_stop(); + esp_intr_free(cam_obj->intr_handle); + vTaskDelete(cam_obj->task_handle); + vQueueDelete(cam_obj->event_queue); + vQueueDelete(cam_obj->frame_buffer_queue); + free(cam_obj->dma); + free(cam_obj->buffer); + free(cam_obj); + + return ESP_OK; +} + +esp_err_t cam_init(const cam_config_t *config) { + cam_obj = (cam_obj_t *)heap_caps_calloc(1, sizeof(cam_obj_t), MALLOC_CAP_DMA); + + if (!cam_obj) { + ESP_LOGI(TAG, "camera object malloc error\n"); + return ESP_FAIL; + } + + cam_obj->width = config->size.width; + cam_obj->high = config->size.high; + cam_obj->frame1_buffer = config->frame1_buffer; + cam_obj->frame2_buffer = config->frame2_buffer; + cam_obj->jpeg_mode = config->mode.jpeg; + cam_obj->vsync_pin = config->pin.vsync; + cam_obj->vsync_invert = config->vsync_invert; + cam_obj->hsync_invert = config->hsync_invert; + cam_set_pin(config); + cam_config(config); + cam_dma_config(config); + + cam_obj->event_queue = xQueueCreate(2, sizeof(cam_event_t)); + cam_obj->frame_buffer_queue = xQueueCreate(2, sizeof(frame_buffer_event_t)); + + if (cam_obj->frame1_buffer != NULL) { + ESP_LOGI(TAG, "frame1_buffer_en\n"); + cam_obj->frame1_buffer_en = 1; + } else { + cam_obj->frame1_buffer_en = 0; + } + + if (cam_obj->frame2_buffer != NULL) { + ESP_LOGI(TAG, "frame2_buffer_en\n"); + cam_obj->frame2_buffer_en = 1; + } else { + cam_obj->frame2_buffer_en = 0; + } + + esp_intr_alloc(ETS_I2S0_INTR_SOURCE, 0, cam_isr, NULL, &cam_obj->intr_handle); + xTaskCreate(cam_task, "cam_task", config->task_stack, NULL, config->task_pri, &cam_obj->task_handle); + return ESP_OK; +} diff --git a/ports/esp32s2/cam.h b/ports/esp32s2/cam.h new file mode 100644 index 0000000000..49b44ebe96 --- /dev/null +++ b/ports/esp32s2/cam.h @@ -0,0 +1,103 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t bit_width; + uint32_t xclk_fre; + union { + struct { + uint32_t xclk : 8; + uint32_t pclk : 8; + uint32_t vsync : 8; + uint32_t hsync : 8; + }; + uint32_t val; + } pin; /*!< Pin of camera */ + uint8_t pin_data[16]; /*!< Data pin of camera*/ + uint8_t vsync_invert; + uint8_t hsync_invert; + union { + struct { + uint32_t width : 16; + uint32_t high : 16; + }; + uint32_t val; + } size; /*!< size of camera */ + uint32_t max_buffer_size; /*!< DMA used */ + uint32_t task_stack; /*!< The priority of the task at run time */ + uint8_t task_pri; + union { + struct { + uint32_t jpeg : 1; + }; + uint32_t val; + } mode; + uint8_t *frame1_buffer; /*!< PingPang buffers , cache the image*/ + uint8_t *frame2_buffer; /*!< PingPang buffers , cache the image*/ +} cam_config_t; + +/** + * @brief enable camera + */ +void cam_start(void); + +/** + * @brief disable camera + */ +void cam_stop(void); + +/** + * @brief Accepts frame data and returns a pointer to the frame data. + * + * @param buffer_p The address of the frame buffer pointer + * + * @return - len of buffer + */ +size_t cam_take(uint8_t **buffer_p); + +/** + * @brief enable frame buffer to get the next frame data. + * + * @param buffer The address of the frame buffer pointer + * + */ +void cam_give(uint8_t *buffer); + +/** + * @brief Initialize camera + * + * @param config Parameter configuration, including pin, buffer, output image size, and so on. + * + * @return - ESP_OK :Initialize success + * ESP_FAIL: Initialize fails + */ +esp_err_t cam_init(const cam_config_t *config); + +/** + * @brief delete the camera + * + * @return - ESP_OK :Delete success + * ESP_FAIL: Delete fails + */ +esp_err_t cam_deinit(void); + +#ifdef __cplusplus +} +#endif From eaf2def340d059d661230c65530e8463167a8ec6 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 31 May 2021 11:04:39 -0500 Subject: [PATCH 06/17] esp32s2: fix build errors in cam.c --- ports/esp32s2/cam.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index 10b2cf0a59..38d1867f1f 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -435,13 +435,13 @@ void cam_dma_config(const cam_config_t *config) { cam_obj->dma = (lldesc_t *)heap_caps_malloc(cam_obj->node_cnt * sizeof(lldesc_t), MALLOC_CAP_DMA); cam_obj->buffer = (uint8_t *)heap_caps_malloc(cam_obj->buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA); - for (int x = 0; x < cam_obj->node_cnt; x++) { + for (uint32_t x = 0; x < cam_obj->node_cnt; x++) { cam_obj->dma[x].size = cam_obj->dma_size; cam_obj->dma[x].length = cam_obj->dma_size; cam_obj->dma[x].eof = 0; cam_obj->dma[x].owner = 1; cam_obj->dma[x].buf = (cam_obj->buffer + cam_obj->dma_size * x); - cam_obj->dma[x].empty = &cam_obj->dma[(x + 1) % cam_obj->node_cnt]; + cam_obj->dma[x].empty = (intptr_t)&cam_obj->dma[(x + 1) % cam_obj->node_cnt]; } I2S0.in_link.addr = ((uint32_t)&cam_obj->dma[0]) & 0xfffff; From 43509495ea8938962ac23410a7d33eca805a593c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 31 May 2021 11:14:18 -0500 Subject: [PATCH 07/17] cam: Always work in total pixels, not width x height --- ports/esp32s2/cam.c | 8 ++------ ports/esp32s2/cam.h | 8 +------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index 38d1867f1f..b1a9b4079d 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -51,8 +51,6 @@ typedef struct { uint32_t dma_size; uint32_t cnt; uint32_t total_cnt; - uint16_t width; - uint16_t high; lldesc_t *dma; uint8_t *buffer; uint8_t *frame1_buffer; @@ -408,7 +406,7 @@ void cam_dma_config(const cam_config_t *config) { cam_obj->dma_size = 1024; } else { for (cnt = 0;; cnt++) { /*!< Find the divisible buffer size */ - if ((config->size.width * config->size.high * 2) % (config->max_buffer_size - cnt) == 0) { + if ((config->size * 2) % (config->max_buffer_size - cnt) == 0) { break; } } @@ -428,7 +426,7 @@ void cam_dma_config(const cam_config_t *config) { cam_obj->node_cnt = (cam_obj->buffer_size) / cam_obj->dma_size; /*!< Number of DMA nodes */ cam_obj->half_node_cnt = cam_obj->node_cnt / 2; - cam_obj->total_cnt = (config->size.width * config->size.high * 2) / cam_obj->half_buffer_size; /*!< Number of interrupt copies produced. Ping pong copies */ + cam_obj->total_cnt = (config->size * 2) / cam_obj->half_buffer_size; /*!< Number of interrupt copies produced. Ping pong copies */ ESP_LOGI(TAG, "cam_buffer_size: %d, cam_dma_size: %d, cam_dma_node_cnt: %d, cam_total_cnt: %d\n", cam_obj->buffer_size, cam_obj->dma_size, cam_obj->node_cnt, cam_obj->total_cnt); @@ -473,8 +471,6 @@ esp_err_t cam_init(const cam_config_t *config) { return ESP_FAIL; } - cam_obj->width = config->size.width; - cam_obj->high = config->size.high; cam_obj->frame1_buffer = config->frame1_buffer; cam_obj->frame2_buffer = config->frame2_buffer; cam_obj->jpeg_mode = config->mode.jpeg; diff --git a/ports/esp32s2/cam.h b/ports/esp32s2/cam.h index 49b44ebe96..79eaf8043a 100644 --- a/ports/esp32s2/cam.h +++ b/ports/esp32s2/cam.h @@ -33,13 +33,7 @@ typedef struct { uint8_t pin_data[16]; /*!< Data pin of camera*/ uint8_t vsync_invert; uint8_t hsync_invert; - union { - struct { - uint32_t width : 16; - uint32_t high : 16; - }; - uint32_t val; - } size; /*!< size of camera */ + uint32_t size; /*!< size of camera frame (width times height) */ uint32_t max_buffer_size; /*!< DMA used */ uint32_t task_stack; /*!< The priority of the task at run time */ uint8_t task_pri; From 20098dfdd8fca4662d396f07e73400a6bfedec41 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 31 May 2021 11:15:18 -0500 Subject: [PATCH 08/17] cam: Improve memory allocation * realloc the dma descriptors and buffers, so we can change the image size * NULL out the camera pointer after deinit, so deinit is idempotent --- ports/esp32s2/cam.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index b1a9b4079d..daa2597bfc 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -430,8 +430,8 @@ void cam_dma_config(const cam_config_t *config) { ESP_LOGI(TAG, "cam_buffer_size: %d, cam_dma_size: %d, cam_dma_node_cnt: %d, cam_total_cnt: %d\n", cam_obj->buffer_size, cam_obj->dma_size, cam_obj->node_cnt, cam_obj->total_cnt); - cam_obj->dma = (lldesc_t *)heap_caps_malloc(cam_obj->node_cnt * sizeof(lldesc_t), MALLOC_CAP_DMA); - cam_obj->buffer = (uint8_t *)heap_caps_malloc(cam_obj->buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA); + cam_obj->dma = (lldesc_t *)heap_caps_realloc(cam_obj->dma, cam_obj->node_cnt * sizeof(lldesc_t), MALLOC_CAP_DMA); + cam_obj->buffer = (uint8_t *)heap_caps_realloc(cam_obj->buffer, cam_obj->buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA); for (uint32_t x = 0; x < cam_obj->node_cnt; x++) { cam_obj->dma[x].size = cam_obj->dma_size; @@ -460,6 +460,8 @@ esp_err_t cam_deinit() { free(cam_obj->buffer); free(cam_obj); + cam_obj = NULL; + return ESP_OK; } From 5d5098152a7344d5b5ce832b719ceba06e20e3c3 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Jun 2021 11:16:08 -0500 Subject: [PATCH 09/17] cam: Remove support for xclk we do this our own way in CP --- ports/esp32s2/cam.c | 19 +------------------ ports/esp32s2/cam.h | 4 ++-- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index daa2597bfc..7d87b35826 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -205,25 +205,8 @@ static void cam_set_pin(const cam_config_t *config) { gpio_matrix_in(config->pin_data[i], I2S0I_DATA_IN0_IDX + (16 - config->bit_width) + i, false); } - ledc_timer_config_t ledc_timer = { - .duty_resolution = LEDC_TIMER_1_BIT, - .freq_hz = config->xclk_fre, - .speed_mode = LEDC_LOW_SPEED_MODE, - .timer_num = LEDC_TIMER_1 - }; - ledc_timer_config(&ledc_timer); - ledc_channel_config_t ledc_channel = { - .channel = LEDC_CHANNEL_2, - .duty = 1, - .gpio_num = config->pin.xclk, - .speed_mode = LEDC_LOW_SPEED_MODE, - .timer_sel = LEDC_TIMER_1, - .hpoint = 0 - }; - ledc_channel_config(&ledc_channel); - gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false); - ESP_LOGI(TAG, "cam_xclk_pin setup\n"); + ESP_LOGI(TAG, "cam_set_pin\n"); } static void cam_vsync_intr_enable(uint8_t en) { diff --git a/ports/esp32s2/cam.h b/ports/esp32s2/cam.h index 79eaf8043a..b55d23dc04 100644 --- a/ports/esp32s2/cam.h +++ b/ports/esp32s2/cam.h @@ -14,16 +14,16 @@ #pragma once +#include "esp_err.h" + #ifdef __cplusplus extern "C" { #endif typedef struct { uint8_t bit_width; - uint32_t xclk_fre; union { struct { - uint32_t xclk : 8; uint32_t pclk : 8; uint32_t vsync : 8; uint32_t hsync : 8; From 400d1d90c97a5e28a13ca856207ccd6b62bf33d6 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Jun 2021 20:15:58 -0500 Subject: [PATCH 10/17] cam: Add a way to check if a frame is ready without blocking --- ports/esp32s2/cam.c | 7 +++++++ ports/esp32s2/cam.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index 7d87b35826..5cdae71e19 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -372,6 +372,13 @@ size_t cam_take(uint8_t **buffer_p) { return frame_buffer_event.len; } +bool cam_ready(void) { + frame_buffer_event_t frame_buffer_event; + BaseType_t result = xQueuePeek(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0); + return result != pdFALSE; +} + + void cam_give(uint8_t *buffer) { if (buffer == cam_obj->frame1_buffer) { cam_obj->frame1_buffer_en = 1; diff --git a/ports/esp32s2/cam.h b/ports/esp32s2/cam.h index b55d23dc04..779e575750 100644 --- a/ports/esp32s2/cam.h +++ b/ports/esp32s2/cam.h @@ -66,6 +66,13 @@ void cam_stop(void); */ size_t cam_take(uint8_t **buffer_p); +/** + * @brief Check whether a frame is available + * + * @return - true if cam_take() will complete immediately, false if it will block + */ +bool cam_ready(void); + /** * @brief enable frame buffer to get the next frame data. * From 8c88ab8142f8f71d8e19f294dc040b24c07f2957 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 7 Jun 2021 08:21:11 -0500 Subject: [PATCH 11/17] cam: Use a named constant instead of a number --- ports/esp32s2/cam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index 5cdae71e19..9dacbedb95 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -205,7 +205,7 @@ static void cam_set_pin(const cam_config_t *config) { gpio_matrix_in(config->pin_data[i], I2S0I_DATA_IN0_IDX + (16 - config->bit_width) + i, false); } - gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false); + gpio_matrix_in(GPIO_FUNC_IN_HIGH, I2S0I_H_ENABLE_IDX, false); ESP_LOGI(TAG, "cam_set_pin\n"); } From 929d6dab7338cf8da7899a55bbcdcebdf8c35aac Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 10 Jun 2021 10:42:36 -0500 Subject: [PATCH 12/17] esp32s2: cam.c: work around possible compiler bug --- ports/esp32s2/cam.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index 9dacbedb95..74225dc2a3 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -106,13 +106,18 @@ static void cam_config(const cam_config_t *config) { periph_module_enable(PERIPH_I2S0_MODULE); /*!< Configure the clock */ + #if 0 I2S0.clkm_conf.val = 0; - I2S0.clkm_conf.clkm_div_num = 2; + I2S0.clkm_conf.clkm_div_num = 2; // <--- this should set the low 8 bits, but doesn't somehow I2S0.clkm_conf.clkm_div_b = 0; I2S0.clkm_conf.clkm_div_a = 0; I2S0.clkm_conf.clk_sel = 2; I2S0.clkm_conf.clk_en = 1; - + #else + // The above code should set the clock to this value .. however, it sets it to 0x0050_0000 instead, the "clkm_div_num" value missing! + // it's not clear why this behavior arises + I2S0.clkm_conf.val = 0x00500002; + #endif /*!< Configuration sampling rate */ I2S0.sample_rate_conf.val = 0; I2S0.sample_rate_conf.tx_bck_div_num = 2; From 98f812e0c2465aaceb15f744e569b840e05e6215 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 10 Jun 2021 10:42:52 -0500 Subject: [PATCH 13/17] esp32s2: cam.c: Remove GPIO interrupt at deinit --- ports/esp32s2/cam.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/esp32s2/cam.c b/ports/esp32s2/cam.c index 74225dc2a3..e435993c6d 100644 --- a/ports/esp32s2/cam.c +++ b/ports/esp32s2/cam.c @@ -447,6 +447,7 @@ esp_err_t cam_deinit() { } cam_stop(); + gpio_isr_handler_remove(cam_obj->vsync_pin); esp_intr_free(cam_obj->intr_handle); vTaskDelete(cam_obj->task_handle); vQueueDelete(cam_obj->event_queue); From 7782bc2e2eda5f991831f3bcf037f71e618e4d93 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 28 May 2021 11:15:48 -0500 Subject: [PATCH 14/17] Skeleton implementation, does nothing --- .../imagecapture/ParallelImageCapture.c | 49 +++++++++++++++++++ .../imagecapture/ParallelImageCapture.h | 33 +++++++++++++ .../common-hal/imagecapture/__init__.c | 0 .../common-hal/imagecapture/__init__.h | 0 ports/esp32s2/mpconfigport.mk | 1 + 5 files changed, 83 insertions(+) create mode 100644 ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c create mode 100644 ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h create mode 100644 ports/esp32s2/common-hal/imagecapture/__init__.c create mode 100644 ports/esp32s2/common-hal/imagecapture/__init__.h diff --git a/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c new file mode 100644 index 0000000000..4a209b238e --- /dev/null +++ b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/runtime.h" + +#include "common-hal/imagecapture/ParallelImageCapture.h" + +void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_parallelimagecapture_obj_t *self, + const mcu_pin_obj_t *data0, + 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); +} + +void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self) { +} + +bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self) { + return true; +} + +void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize) { +} diff --git a/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h new file mode 100644 index 0000000000..ef778bfeff --- /dev/null +++ b/ports/esp32s2/common-hal/imagecapture/ParallelImageCapture.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "shared-bindings/imagecapture/ParallelImageCapture.h" + +struct imagecapture_parallelimagecapture_obj { + mp_obj_base_t base; +}; diff --git a/ports/esp32s2/common-hal/imagecapture/__init__.c b/ports/esp32s2/common-hal/imagecapture/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/esp32s2/common-hal/imagecapture/__init__.h b/ports/esp32s2/common-hal/imagecapture/__init__.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/esp32s2/mpconfigport.mk b/ports/esp32s2/mpconfigport.mk index 1f5021bc15..c7f903589d 100644 --- a/ports/esp32s2/mpconfigport.mk +++ b/ports/esp32s2/mpconfigport.mk @@ -23,6 +23,7 @@ CIRCUITPY_COUNTIO = 1 CIRCUITPY_DUALBANK = 1 CIRCUITPY_FRAMEBUFFERIO = 1 CIRCUITPY_FREQUENCYIO = 1 +CIRCUITPY_IMAGECAPTURE ?= 1 CIRCUITPY_I2CPERIPHERAL = 0 CIRCUITPY_RGBMATRIX = 1 CIRCUITPY_ROTARYIO = 1 From 268717e42790ff4d298408917b16f7bc3fbc4943 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 9 Jun 2021 12:15:31 -0500 Subject: [PATCH 15/17] ParallelImageCapture: Switch to taking a list of pins .. adopting validate_pins from RGBMatrix into shared-bindings .. updating other platforms for API change --- locale/circuitpython.pot | 6 ++++ .../imagecapture/ParallelImageCapture.c | 15 ++++++---- .../imagecapture/ParallelImageCapture.c | 29 ++++++++++++++++--- .../imagecapture/ParallelImageCapture.c | 16 +++++----- .../imagecapture/ParallelImageCapture.h | 7 +++-- shared-bindings/microcontroller/Pin.c | 8 +++++ shared-bindings/microcontroller/Pin.h | 1 + shared-bindings/rgbmatrix/RGBMatrix.c | 8 ----- 8 files changed, 61 insertions(+), 29 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index fbb6402051..8bff0caaa3 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1280,6 +1280,11 @@ msgstr "" msgid "Invalid data_count %d" msgstr "" +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +#, c-format +msgid "Invalid data_pins[%d]" +msgstr "" + #: shared-bindings/digitalio/DigitalInOut.c msgid "Invalid direction." msgstr "" @@ -1813,6 +1818,7 @@ msgid "" "constructor" msgstr "" +#: ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c #: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c msgid "Pins must be sequential" msgstr "" diff --git a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c index 2a01057b7f..f3284f0c32 100644 --- a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +++ b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c @@ -50,13 +50,16 @@ #define PIN_PCC_CLK (PIN_PA14) 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) { - if (data0->number != PIN_PCC_D0) { - mp_raise_ValueError_varg(translate("Invalid %q pin"), MP_QSTR_data0); + const mcu_pin_obj_t *horizontal_reference) { + + for (int i = 0; i < data_count; i++) { + if (data_pins[i] != PIN_PCC_D0 + i) { + mp_raise_ValueError_varg(translate("Invalid data_pins[%d]"), i); + } } // The peripheral supports 8, 10, 12, or 14 data bits, but the code only supports 8 at present if (data_count != 8) { @@ -73,7 +76,7 @@ void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_paralle } // technically, 0 was validated as free already but check again for (int i = 0; i < data_count; i++) { - if (!pin_number_is_free(data0->number + i)) { + if (!pin_number_is_free(data_pins[i])) { mp_raise_ValueError_varg(translate("data pin #%d in use"), i); } } diff --git a/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c index 2a175470ee..da3ab8772c 100644 --- a/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c +++ b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c @@ -68,12 +68,33 @@ /* .wrap */ \ } +mcu_pin_obj_t *pin_from_number(uint8_t number) { + const mp_map_t *mcu_map = &mcu_pin_globals.map; + for (uint8_t i = 0; i < mcu_map->alloc; i++) { + mp_obj_t val = mcu_map->table[i].value; + if (!mp_obj_is_type(val, &mcu_pin_type)) { + continue; + } + mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(val); + if (pin->number == number) { + return pin; + } + } + return NULL; +} + 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) { + const mcu_pin_obj_t *horizontal_reference) { + + for (int i = 1; i < data_count; i++) { + if (data_pins[i] - data_pins[0] != i) { + mp_raise_RuntimeError(translate("Pins must be sequential")); + } + } uint16_t imagecapture_code[] = IMAGECAPTURE_CODE(data_count, data_clock->number, vertical_sync->number, horizontal_reference->number); @@ -82,7 +103,7 @@ void common_hal_imagecapture_parallelimagecapture_construct(imagecapture_paralle common_hal_mcu_processor_get_frequency(), // full speed (4 instructions per loop -> max pclk 30MHz @ 120MHz) 0, 0, // init NULL, 0, 0, 0, // out pins - data0, data_count, // in pins + pin_from_number(data_pins[0]), data_count, // in pins 0, 0, // in pulls NULL, 0, 0, 0, // set pins #if DEBUG_STATE_MACHINE diff --git a/shared-bindings/imagecapture/ParallelImageCapture.c b/shared-bindings/imagecapture/ParallelImageCapture.c index ee04dcd5ee..98cc93e735 100644 --- a/shared-bindings/imagecapture/ParallelImageCapture.c +++ b/shared-bindings/imagecapture/ParallelImageCapture.c @@ -39,15 +39,13 @@ //| def __init__( //| self, //| *, -//| data0: microcontroller.Pin, -//| data_count: int=8, +//| data_pins: List[microcontroller.Pin], //| clock: microcontroller.Pin, //| vsync: Optional[microcontroller.Pin], //| href: Optional[microcontroller.Pin], //| ): //| """Create a parallel image capture object //| :param microcontroller.Pin data0: The first data pin. Additional data pins are assumed to follow this pin directly in the microcontroller's standard pin ordering. -//| :param int data_count: The number of data pins. //| :param microcontroller.Pin clock: The pixel clock input. //| :param microcontroller.Pin vsync: The vertical sync input, which has a negative-going pulse at the beginning of each frame. //| :param microcontroller.Pin href: The horizontal reference input, which is high whenever the camera is transmitting valid pixel information. @@ -55,11 +53,10 @@ //| ... //| STATIC mp_obj_t imagecapture_parallelimagecapture_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_data0, ARG_data_count, ARG_clock, ARG_vsync, ARG_href, + enum { ARG_data_pins, ARG_clock, ARG_vsync, ARG_href, NUM_ARGS }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_data0, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, - { MP_QSTR_data_count, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 8 } }, + { MP_QSTR_data_pins, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = MP_ROM_NONE } }, { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, { MP_QSTR_vsync, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, { MP_QSTR_href, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, @@ -68,7 +65,10 @@ STATIC mp_obj_t imagecapture_parallelimagecapture_make_new(const mp_obj_type_t * MP_STATIC_ASSERT(MP_ARRAY_SIZE(allowed_args) == NUM_ARGS); mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mcu_pin_obj_t *data0 = validate_obj_is_free_pin(args[ARG_data0].u_obj); + uint8_t pins[32]; + uint8_t pin_count; + validate_pins(MP_QSTR_data, pins, MP_ARRAY_SIZE(pins), args[ARG_data_pins].u_obj, &pin_count); + mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj); mcu_pin_obj_t *vsync = validate_obj_is_free_pin_or_none(args[ARG_vsync].u_obj); mcu_pin_obj_t *href = validate_obj_is_free_pin_or_none(args[ARG_href].u_obj); @@ -76,7 +76,7 @@ STATIC mp_obj_t imagecapture_parallelimagecapture_make_new(const mp_obj_type_t * imagecapture_parallelimagecapture_obj_t *self = m_new_obj(imagecapture_parallelimagecapture_obj_t); self->base.type = &imagecapture_parallelimagecapture_type; - common_hal_imagecapture_parallelimagecapture_construct(self, data0, clock, vsync, href, args[ARG_data_count].u_int); + common_hal_imagecapture_parallelimagecapture_construct(self, pins, pin_count, clock, vsync, href); return self; } diff --git a/shared-bindings/imagecapture/ParallelImageCapture.h b/shared-bindings/imagecapture/ParallelImageCapture.h index 67e05f2bf2..bcd827aa23 100644 --- a/shared-bindings/imagecapture/ParallelImageCapture.h +++ b/shared-bindings/imagecapture/ParallelImageCapture.h @@ -31,12 +31,13 @@ typedef struct imagecapture_parallelimagecapture_obj imagecapture_parallelimagecapture_obj_t; extern const mp_obj_type_t imagecapture_parallelimagecapture_type; +// if only the first element of data_pins is non-NULL, the pins are sequential in microcontroller pin numbering. 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_sync, - int data_count); + const mcu_pin_obj_t *horizontal_sync); void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self); bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self); void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize); diff --git a/shared-bindings/microcontroller/Pin.c b/shared-bindings/microcontroller/Pin.c index deba9bec6b..acb1710f3d 100644 --- a/shared-bindings/microcontroller/Pin.c +++ b/shared-bindings/microcontroller/Pin.c @@ -137,3 +137,11 @@ void assert_pin_free(const mcu_pin_obj_t *pin) { mp_raise_ValueError_varg(translate("%q in use"), name); } } + +void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out) { + mcu_pin_obj_t *pins[max_pins]; + validate_list_is_free_pins(what, pins, max_pins, seq, count_out); + for (mp_int_t i = 0; i < *count_out; i++) { + pin_nos[i] = common_hal_mcu_pin_number(pins[i]); + } +} diff --git a/shared-bindings/microcontroller/Pin.h b/shared-bindings/microcontroller/Pin.h index 60c5400bd3..44019ff267 100644 --- a/shared-bindings/microcontroller/Pin.h +++ b/shared-bindings/microcontroller/Pin.h @@ -38,6 +38,7 @@ mcu_pin_obj_t *validate_obj_is_pin_or_none(mp_obj_t obj); mcu_pin_obj_t *validate_obj_is_free_pin(mp_obj_t obj); mcu_pin_obj_t *validate_obj_is_free_pin_or_none(mp_obj_t obj); void validate_list_is_free_pins(qstr what, mcu_pin_obj_t **pins_out, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); +void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); void assert_pin_free(const mcu_pin_obj_t *pin); diff --git a/shared-bindings/rgbmatrix/RGBMatrix.c b/shared-bindings/rgbmatrix/RGBMatrix.c index 5bf5016e08..0a296c6780 100644 --- a/shared-bindings/rgbmatrix/RGBMatrix.c +++ b/shared-bindings/rgbmatrix/RGBMatrix.c @@ -49,14 +49,6 @@ STATIC uint8_t validate_pin(mp_obj_t obj) { return common_hal_mcu_pin_number(result); } -STATIC void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out) { - mcu_pin_obj_t *pins[max_pins]; - validate_list_is_free_pins(what, pins, max_pins, seq, count_out); - for (mp_int_t i = 0; i < *count_out; i++) { - pin_nos[i] = common_hal_mcu_pin_number(pins[i]); - } -} - STATIC void claim_and_never_reset_pin(mp_obj_t pin) { common_hal_mcu_pin_claim(pin); common_hal_never_reset_pin(pin); From 2cbdd18a20558c2a0ea3fc11617fed50b99aa1d0 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 1 Jun 2021 20:15:43 -0500 Subject: [PATCH 16/17] 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. From 198e3aded72fbb70fb33c0536339fdbb432b7d45 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 18 Jun 2021 12:40:35 -0500 Subject: [PATCH 17/17] imagecapture: Fix doc markup, update docs for data_pins Closes: #4896 --- shared-bindings/imagecapture/ParallelImageCapture.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared-bindings/imagecapture/ParallelImageCapture.c b/shared-bindings/imagecapture/ParallelImageCapture.c index 98cc93e735..8e8991339b 100644 --- a/shared-bindings/imagecapture/ParallelImageCapture.c +++ b/shared-bindings/imagecapture/ParallelImageCapture.c @@ -45,7 +45,8 @@ //| href: Optional[microcontroller.Pin], //| ): //| """Create a parallel image capture object -//| :param microcontroller.Pin data0: The first data pin. Additional data pins are assumed to follow this pin directly in the microcontroller's standard pin ordering. +//| +//| :param List[microcontroller.Pin] data_pins: The data pins. //| :param microcontroller.Pin clock: The pixel clock input. //| :param microcontroller.Pin vsync: The vertical sync input, which has a negative-going pulse at the beginning of each frame. //| :param microcontroller.Pin href: The horizontal reference input, which is high whenever the camera is transmitting valid pixel information.