diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 7b019191e3..c1f8074e61 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1185,6 +1185,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Invalid %q pin" msgstr "" @@ -1255,6 +1256,11 @@ msgstr "" msgid "Invalid channel count" msgstr "" +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +#, c-format +msgid "Invalid data_count %d" +msgstr "" + #: shared-bindings/digitalio/DigitalInOut.c msgid "Invalid direction." msgstr "" @@ -2114,6 +2120,14 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +msgid "Timeout waiting for DRDY" +msgstr "" + +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +msgid "Timeout waiting for VSYNC" +msgstr "" + #: supervisor/shared/safe_mode.c msgid "To exit, please reset the board without " msgstr "" @@ -2791,6 +2805,11 @@ msgstr "" msgid "data must be of equal length" msgstr "" +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +#, c-format +msgid "data pin #%d in use" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "data type not understood" msgstr "" diff --git a/ports/atmel-samd/audio_dma.c b/ports/atmel-samd/audio_dma.c index 0fe71e6d5d..7b1ca42c06 100644 --- a/ports/atmel-samd/audio_dma.c +++ b/ports/atmel-samd/audio_dma.c @@ -45,7 +45,15 @@ static volatile bool audio_dma_pending[AUDIO_DMA_CHANNEL_COUNT]; static bool audio_dma_allocated[AUDIO_DMA_CHANNEL_COUNT]; -uint8_t audio_dma_allocate_channel(void) { +uint8_t find_sync_event_channel_raise() { + uint8_t event_channel = find_sync_event_channel(); + if (event_channel >= EVSYS_SYNCH_NUM) { + mp_raise_RuntimeError(translate("All sync event channels in use")); + } + return event_channel; +} + +uint8_t dma_allocate_channel(void) { uint8_t channel; for (channel = 0; channel < AUDIO_DMA_CHANNEL_COUNT; channel++) { if (!audio_dma_allocated[channel]) { @@ -56,7 +64,7 @@ uint8_t audio_dma_allocate_channel(void) { return channel; // i.e., return failure } -void audio_dma_free_channel(uint8_t channel) { +void dma_free_channel(uint8_t channel) { assert(channel < AUDIO_DMA_CHANNEL_COUNT); assert(audio_dma_allocated[channel]); audio_dma_disable_channel(channel); @@ -180,7 +188,7 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, bool output_signed, uint32_t output_register_address, uint8_t dma_trigger_source) { - uint8_t dma_channel = audio_dma_allocate_channel(); + uint8_t dma_channel = dma_allocate_channel(); if (dma_channel >= AUDIO_DMA_CHANNEL_COUNT) { return AUDIO_DMA_DMA_BUSY; } @@ -230,11 +238,7 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, // We're likely double buffering so set up the block interrupts. turn_on_event_system(); - dma->event_channel = find_sync_event_channel(); - - if (dma->event_channel >= EVSYS_SYNCH_NUM) { - mp_raise_RuntimeError(translate("All sync event channels in use")); - } + dma->event_channel = find_sync_event_channel_raise(); init_event_channel_interrupt(dma->event_channel, CORE_GCLK, EVSYS_ID_GEN_DMAC_CH_0 + dma_channel); // We keep the audio_dma_t for internal use and the sample as a root pointer because it @@ -302,7 +306,7 @@ void audio_dma_stop(audio_dma_t *dma) { disable_event_channel(dma->event_channel); MP_STATE_PORT(playing_audio)[channel] = NULL; audio_dma_state[channel] = NULL; - audio_dma_free_channel(dma->dma_channel); + dma_free_channel(dma->dma_channel); } dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT; } diff --git a/ports/atmel-samd/audio_dma.h b/ports/atmel-samd/audio_dma.h index 5506cff628..4e7d193b1d 100644 --- a/ports/atmel-samd/audio_dma.h +++ b/ports/atmel-samd/audio_dma.h @@ -66,8 +66,8 @@ uint8_t audiosample_channel_count(mp_obj_t sample_obj); void audio_dma_init(audio_dma_t *dma); void audio_dma_reset(void); -uint8_t audio_dma_allocate_channel(void); -void audio_dma_free_channel(uint8_t channel); +uint8_t dma_allocate_channel(void); +void dma_free_channel(uint8_t channel); // This sets everything up but doesn't start the timer. // Sample is the python object for the sample to play. @@ -97,4 +97,6 @@ bool audio_dma_get_paused(audio_dma_t *dma); void audio_dma_background(void); +uint8_t find_sync_event_channel_raise(void); + #endif // MICROPY_INCLUDED_ATMEL_SAMD_AUDIO_DMA_H diff --git a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk index 88c6e9bf16..6d9ecff636 100644 --- a/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/grandcentral_m4_express/mpconfigboard.mk @@ -11,3 +11,4 @@ EXTERNAL_FLASH_DEVICES = "W25Q64JVxQ, GD25Q64C" LONGINT_IMPL = MPZ CIRCUITPY_SDIOIO = 1 +CIRCUITPY_IMAGECAPTURE = 1 diff --git a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c index 8911aef2f1..6ea70e39dc 100644 --- a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c +++ b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c @@ -367,11 +367,8 @@ static uint16_t filter_sample(uint32_t pdm_samples[4]) { // output_buffer_length is the number of slots, not the number of bytes. uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* self, uint16_t* output_buffer, uint32_t output_buffer_length) { - uint8_t dma_channel = audio_dma_allocate_channel(); - uint8_t event_channel = find_sync_event_channel(); - if (event_channel >= EVSYS_SYNCH_NUM) { - mp_raise_RuntimeError(translate("All sync event channels in use")); - } + uint8_t dma_channel = dma_allocate_channel(); + uint8_t event_channel = find_sync_event_channel_raise(); // We allocate two buffers on the stack to use for double buffering. const uint8_t samples_per_buffer = SAMPLES_PER_BUFFER; @@ -476,7 +473,7 @@ uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t* se } disable_event_channel(event_channel); - audio_dma_free_channel(dma_channel); + dma_free_channel(dma_channel); // Turn off serializer, but leave clock on, to avoid mic startup delay. i2s_set_serializer_enable(self->serializer, false); diff --git a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c new file mode 100644 index 0000000000..074c2b6d23 --- /dev/null +++ b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c @@ -0,0 +1,198 @@ +/* + * 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 "lib/utils/context_manager_helpers.h" +#include "lib/utils/interrupt_char.h" + +#include "shared-bindings/imagecapture/ParallelImageCapture.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "common-hal/imagecapture/ParallelImageCapture.h" + +#include "hal/include/hal_gpio.h" +#include "atmel_start_pins.h" + +#include "audio_dma.h" +#include "samd/clocks.h" +#include "samd/events.h" + +#define GPIO_PIN_FUNCTION_PCC (GPIO_PIN_FUNCTION_K) + +#define PIN_PCC_D0 (PIN_PA16) +#define PIN_PCC_DEN1 (PIN_PA12) +#define PIN_PCC_DEN2 (PIN_PA13) +#define PIN_PCC_CLK (PIN_PA14) + +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) +{ + if (data0->number != PIN_PCC_D0) { + mp_raise_ValueError_varg(translate("Invalid %q pin"), MP_QSTR_data0); + } + // The peripheral supports 8, 10, 12, or 14 data bits, but the code only supports 8 at present + if (data_count != 8) + { + mp_raise_ValueError_varg(translate("Invalid data_count %d"), data_count); + } + if (vertical_sync && vertical_sync->number != PIN_PCC_DEN1) { + mp_raise_ValueError_varg(translate("Invalid %q pin"), MP_QSTR_vsync); + } + if (horizontal_reference && horizontal_reference->number != PIN_PCC_DEN2) { + mp_raise_ValueError_varg(translate("Invalid %q pin"), MP_QSTR_href); + } + if (data_clock->number != PIN_PCC_CLK) { + mp_raise_ValueError_varg(translate("Invalid %q pin"), MP_QSTR_data_clock); + } + // technically, 0 was validated as free already but check again + for (int i=0; inumber + i)) { + mp_raise_ValueError_varg(translate("data pin #%d in use"), i); + } + } + + PCC->MR.bit.PCEN = 0; // Make sure PCC is disabled before setting MR reg + + PCC->IDR.reg = 0b1111; // Disable all PCC interrupts + MCLK->APBDMASK.bit.PCC_ = 1; // Enable PCC clock + + // Accumulate 4 bytes into RHR register (two 16-bit pixels) + PCC->MR.reg = PCC_MR_CID(0x1) | // Clear on falling DEN1 (VSYNC) + PCC_MR_ISIZE(0x0) | // Input data bus is 8 bits + PCC_MR_DSIZE(0x2); // "4 data" at a time (accumulate in RHR) + + PCC->MR.bit.PCEN = 1; // Enable PCC + + + // Now we know we can allocate all pins + self->data_count = data_count; + self->vertical_sync = vertical_sync ? vertical_sync->number : NO_PIN; + self->horizontal_reference = horizontal_reference ? horizontal_reference->number : NO_PIN; + gpio_set_pin_direction(PIN_PCC_CLK, GPIO_DIRECTION_IN); + gpio_set_pin_pull_mode(PIN_PCC_CLK, GPIO_PULL_OFF); + gpio_set_pin_function(PIN_PCC_CLK, GPIO_PIN_FUNCTION_PCC); + //claim_pin_number(PIN_PCC_CLK); + if (vertical_sync) { + gpio_set_pin_direction(PIN_PCC_DEN1, GPIO_DIRECTION_IN); + gpio_set_pin_pull_mode(PIN_PCC_DEN1, GPIO_PULL_OFF); + gpio_set_pin_function(PIN_PCC_DEN1, GPIO_PIN_FUNCTION_PCC); // VSYNC + //claim_pin_number(PIN_PCC_DEN1); + } + if (horizontal_reference) { + gpio_set_pin_direction(PIN_PCC_DEN2, GPIO_DIRECTION_IN); + gpio_set_pin_pull_mode(PIN_PCC_DEN2, GPIO_PULL_OFF); + gpio_set_pin_function(PIN_PCC_DEN2, GPIO_PIN_FUNCTION_PCC); // HSYNC + //claim_pin_number(PIN_PCC_DEN2); + } + for (int i=0; ivertical_sync); + reset_pin_number(self->horizontal_reference); + reset_pin_number(PIN_PCC_CLK); + for (int i=0; idata_count; i++) { + reset_pin_number(PIN_PCC_D0 + i); + } + self->data_count = 0; +} + +bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self) +{ + return self->data_count == 0; +} + +static void setup_dma(DmacDescriptor* descriptor, size_t count, uint32_t *buffer) { + descriptor->BTCTRL.reg = DMAC_BTCTRL_VALID | + DMAC_BTCTRL_BLOCKACT_NOACT | + DMAC_BTCTRL_EVOSEL_BLOCK | + DMAC_BTCTRL_DSTINC | + DMAC_BTCTRL_BEATSIZE_WORD; + descriptor->BTCNT.reg = count; + descriptor->DSTADDR.reg = (uint32_t)buffer + 4*count; + descriptor->SRCADDR.reg = (uint32_t)&PCC->RHR.reg; + descriptor->DESCADDR.reg = 0; +} + +#include + +void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize) +{ + + uint8_t dma_channel = dma_allocate_channel(); + + uint32_t *dest = buffer; + size_t count = bufsize / 4; // PCC receives 4 bytes (2 pixels) at a time + + turn_on_event_system(); + + setup_dma(dma_descriptor(dma_channel), count, dest); + dma_configure(dma_channel, PCC_DMAC_ID_RX, true); + + if (self->vertical_sync) { + const volatile uint32_t *vsync_reg = &PORT->Group[(self->vertical_sync / 32)].IN.reg; + uint32_t vsync_bit = 1 << (self->vertical_sync % 32); + + while (*vsync_reg & vsync_bit) + { + // Wait for VSYNC low (frame end) + + RUN_BACKGROUND_TASKS; + // Allow user to break out of a timeout with a KeyboardInterrupt. + if (mp_hal_is_interrupted()) { + dma_free_channel(dma_channel); + return; + } + } + } + + dma_enable_channel(dma_channel); + + while (DMAC->Channel[dma_channel].CHCTRLA.bit.ENABLE) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } + } + + dma_disable_channel(dma_channel); + dma_free_channel(dma_channel); +} diff --git a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.h b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.h new file mode 100644 index 0000000000..67311c5b00 --- /dev/null +++ b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.h @@ -0,0 +1,34 @@ +/* + * 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; + uint8_t data_pin, data_clock, vertical_sync, horizontal_reference, data_count; +}; diff --git a/ports/atmel-samd/common-hal/imagecapture/__init__.c b/ports/atmel-samd/common-hal/imagecapture/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/atmel-samd/common-hal/imagecapture/__init__.h b/ports/atmel-samd/common-hal/imagecapture/__init__.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/atmel-samd/common-hal/pwmio/PWMOut.c b/ports/atmel-samd/common-hal/pwmio/PWMOut.c index b5142c21e1..c46145d7d7 100644 --- a/ports/atmel-samd/common-hal/pwmio/PWMOut.c +++ b/ports/atmel-samd/common-hal/pwmio/PWMOut.c @@ -156,7 +156,8 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self, return PWMOUT_INVALID_PIN; } - if (frequency == 0 || frequency > 6000000) { + uint32_t system_clock = common_hal_mcu_processor_get_frequency(); + if (frequency == 0 || frequency > system_clock/2) { return PWMOUT_INVALID_FREQUENCY; } @@ -237,7 +238,6 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self, resolution = tcc_sizes[timer->index]; } // First determine the divisor that gets us the highest resolution. - uint32_t system_clock = common_hal_mcu_processor_get_frequency(); uint32_t top; uint8_t divisor; for (divisor = 0; divisor < 8; divisor++) { @@ -413,7 +413,8 @@ uint16_t common_hal_pwmio_pwmout_get_duty_cycle(pwmio_pwmout_obj_t* self) { void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t* self, uint32_t frequency) { - if (frequency == 0 || frequency > 6000000) { + uint32_t system_clock = common_hal_mcu_processor_get_frequency(); + if (frequency == 0 || frequency > system_clock/2) { mp_raise_ValueError(translate("Invalid PWM frequency")); } const pin_timer_t* t = self->timer; @@ -424,7 +425,6 @@ void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t* self, // TCC resolution varies so look it up. resolution = tcc_sizes[t->index]; } - uint32_t system_clock = common_hal_mcu_processor_get_frequency(); uint32_t new_top; uint8_t new_divisor; for (new_divisor = 0; new_divisor < 8; new_divisor++) { diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.c b/ports/raspberrypi/bindings/rp2pio/StateMachine.c index fe36423256..b85ee5d2d4 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.c +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.c @@ -241,6 +241,7 @@ STATIC mp_obj_t rp2pio_statemachine_make_new(const mp_obj_type_t *type, size_t n first_in_pin, args[ARG_in_pin_count].u_int, args[ARG_pull_in_pin_up].u_int, args[ARG_pull_in_pin_down].u_int, first_set_pin, args[ARG_set_pin_count].u_int, args[ARG_initial_set_pin_state].u_int, args[ARG_initial_set_pin_direction].u_int, first_sideset_pin, args[ARG_sideset_pin_count].u_int, args[ARG_initial_sideset_pin_state].u_int, args[ARG_initial_sideset_pin_direction].u_int, + 0, args[ARG_exclusive_pin_use].u_bool, args[ARG_auto_pull].u_bool, pull_threshold, args[ARG_out_shift_right].u_bool, args[ARG_wait_for_txstall].u_bool, diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.h b/ports/raspberrypi/bindings/rp2pio/StateMachine.h index bb65ade0e3..0ebcca5291 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.h +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.h @@ -44,6 +44,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, const mcu_pin_obj_t *first_in_pin, uint8_t in_pin_count, uint32_t pull_pin_up, uint32_t pull_pin_down, const mcu_pin_obj_t *first_set_pin, uint8_t set_pin_count, uint32_t initial_set_pin_state, uint32_t initial_set_pin_direction, const mcu_pin_obj_t *first_sideset_pin, uint8_t sideset_pin_count, uint32_t initial_sideset_pin_state, uint32_t initial_sideset_pin_direction, + uint32_t wait_gpio_mask, bool exclusive_pin_use, bool auto_pull, uint8_t pull_threshold, bool out_shift_right, bool wait_for_txstall, diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c index 5f77a74a5e..0ad44c2527 100644 --- a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c +++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c @@ -126,6 +126,7 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, 0, 0, // in pulls NULL, 0, 0, 0x1f, // set pins bit_clock, 2, 0, 0x1f, // sideset pins + 0, // wait gpio pins true, // exclusive pin use false, 32, false, // shift out left to start with MSB false, // Wait for txstall diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c index bbcfbb805a..3f2b448eac 100644 --- a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c +++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -71,6 +71,7 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, 0, 0, // in pulls NULL, 0, 0, 0x1f, // set pins clock_pin, 1, 0, 0x1f, // sideset pins + 0, // wait gpio pins true, // exclusive pin use false, 32, false, // out settings false, // Wait for txstall diff --git a/ports/raspberrypi/common-hal/displayio/ParallelBus.c b/ports/raspberrypi/common-hal/displayio/ParallelBus.c index 8a976fb251..80dbb13bb7 100644 --- a/ports/raspberrypi/common-hal/displayio/ParallelBus.c +++ b/ports/raspberrypi/common-hal/displayio/ParallelBus.c @@ -99,6 +99,7 @@ void common_hal_displayio_parallelbus_construct(displayio_parallelbus_obj_t *sel NULL, 0, 0, 0, // first in pin, # in pins NULL, 0, 0, 0, // first set pin write, 1, 0, 1, // first sideset pin + 0, // wait gpio pins true, // exclusive pin usage true, 8, true, // TX, auto pull every 8 bits. shift left to output msb first false, // wait for TX stall diff --git a/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c new file mode 100644 index 0000000000..2c91a2fa16 --- /dev/null +++ b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c @@ -0,0 +1,135 @@ +/* + * 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 "lib/utils/context_manager_helpers.h" +#include "lib/utils/interrupt_char.h" + +#include "bindings/rp2pio/StateMachine.h" +#include "bindings/rp2pio/__init__.h" +#include "common-hal/imagecapture/ParallelImageCapture.h" +#include "shared-bindings/imagecapture/ParallelImageCapture.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/Processor.h" +#include "shared-bindings/microcontroller/__init__.h" + +#include "src/rp2_common/hardware_pio/include/hardware/pio.h" +#include "src/rp2_common/hardware_pio/include/hardware/pio_instructions.h" + +// Define this to (1), and you can scope the instruction-pointer of the state machine on D26..28 (note the weird encoding though!) +#define DEBUG_STATE_MACHINE (0) +#if DEBUG_STATE_MACHINE +#define SIDE(x) ((x)<<8) +#else +#define SIDE(x) (0) +#endif + +#define _0 SIDE(0b11100) +#define _1 SIDE(0b00000) +#define _2 SIDE(0b10000) +#define _3 SIDE(0b10100) +#define _4 SIDE(0b11000) +#define _5 SIDE(0b10100) + +#define IMAGECAPTURE_CODE(width, pclk, vsync, href) \ + { \ +/* 0 */ pio_encode_wait_gpio(0, vsync) | _0, \ +/* 1 */ pio_encode_wait_gpio(1, vsync) | _1, \ + /* .wrap_target */ \ +/* 2 */ pio_encode_wait_gpio(1, href) | _2, \ +/* 3 */ pio_encode_wait_gpio(1, pclk) | _3, \ +/* 4 */ pio_encode_in(pio_pins, width) | _4, \ +/* 5 */ pio_encode_wait_gpio(0, pclk) | _5, \ + /* .wrap */ \ + } + +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) +{ + + uint16_t imagecapture_code[] = IMAGECAPTURE_CODE(data_count, data_clock->number, vertical_sync->number, horizontal_reference->number); + + common_hal_rp2pio_statemachine_construct(&self->state_machine, + imagecapture_code, MP_ARRAY_SIZE(imagecapture_code), + 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 + 0, 0, // in pulls + NULL, 0, 0, 0, // set pins +#if DEBUG_STATE_MACHINE + &pin_GPIO26, 3, 7, 7, // sideset pins +#else + NULL, 0, 0, 0, // sideset pins +#endif + (1<number) | (1<number) | (1<number), // wait gpio pins + true, // exclusive pin use + false, 32, false, // out settings + false, // wait for txstall + true, 32, true); // in settings + + PIO pio = self->state_machine.pio; + uint8_t pio_index = pio_get_index(pio); + uint sm = self->state_machine.state_machine; + rp2pio_statemachine_set_wrap(&self->state_machine, 2, 5); +} + +void common_hal_imagecapture_parallelimagecapture_deinit(imagecapture_parallelimagecapture_obj_t *self) +{ + if (common_hal_imagecapture_parallelimagecapture_deinited(self)) { + return; + } + return common_hal_rp2pio_statemachine_deinit(&self->state_machine); +} + +bool common_hal_imagecapture_parallelimagecapture_deinited(imagecapture_parallelimagecapture_obj_t *self) +{ + return common_hal_rp2pio_statemachine_deinited(&self->state_machine); +} + +void common_hal_imagecapture_parallelimagecapture_capture(imagecapture_parallelimagecapture_obj_t *self, void *buffer, size_t bufsize) +{ + PIO pio = self->state_machine.pio; + uint sm = self->state_machine.state_machine; + uint8_t offset = rp2pio_statemachine_program_offset(&self->state_machine); + + pio_sm_set_enabled(pio, sm, false); + pio_sm_clear_fifos(pio, sm); + + pio_sm_restart(pio, sm); + pio_sm_exec(pio, sm, pio_encode_jmp(offset)); + pio_sm_set_enabled(pio, sm, true); + + common_hal_rp2pio_statemachine_readinto(&self->state_machine, buffer, bufsize, 4); + + pio_sm_set_enabled(pio, sm, false); +} diff --git a/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.h b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.h new file mode 100644 index 0000000000..9f5d1ab32f --- /dev/null +++ b/ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.h @@ -0,0 +1,35 @@ +/* + * 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 "common-hal/rp2pio/StateMachine.h" +#include "shared-bindings/imagecapture/ParallelImageCapture.h" + +struct imagecapture_parallelimagecapture_obj { + mp_obj_base_t base; + rp2pio_statemachine_obj_t state_machine; +}; diff --git a/ports/raspberrypi/common-hal/imagecapture/__init__.c b/ports/raspberrypi/common-hal/imagecapture/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/raspberrypi/common-hal/imagecapture/__init__.h b/ports/raspberrypi/common-hal/imagecapture/__init__.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c index 045f111ca3..c441ea2e84 100644 --- a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c @@ -84,6 +84,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode 3, 0, // in pulls NULL, 0, 0, 0x1f, // set pins NULL, 0, 0, 0x1f, // sideset pins + 0, // wait gpio pins true, // exclusive pin use false, 32, false, // out settings false, // Wait for txstall diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index 716693228f..21ec09c42c 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -329,13 +329,14 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, uint32_t pull_pin_up, uint32_t pull_pin_down, const mcu_pin_obj_t *first_set_pin, uint8_t set_pin_count, uint32_t initial_set_pin_state, uint32_t initial_set_pin_direction, const mcu_pin_obj_t *first_sideset_pin, uint8_t sideset_pin_count, uint32_t initial_sideset_pin_state, uint32_t initial_sideset_pin_direction, + uint32_t wait_gpio_mask, bool exclusive_pin_use, bool auto_pull, uint8_t pull_threshold, bool out_shift_right, bool wait_for_txstall, bool auto_push, uint8_t push_threshold, bool in_shift_right) { // First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false. - uint32_t pins_we_use = 0; + uint32_t pins_we_use = wait_gpio_mask; pins_we_use |= _check_pins_free(first_out_pin, out_pin_count, exclusive_pin_use); pins_we_use |= _check_pins_free(first_in_pin, in_pin_count, exclusive_pin_use); pins_we_use |= _check_pins_free(first_set_pin, set_pin_count, exclusive_pin_use); @@ -801,3 +802,16 @@ STATIC void rp2pio_statemachine_interrupt_handler(void) { } } } + +uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self) { + uint8_t pio_index = pio_get_index(self->pio); + uint8_t sm = self->state_machine; + return _current_program_offset[pio_index][sm]; +} + +void rp2pio_statemachine_set_wrap(rp2pio_statemachine_obj_t *self, uint wrap_target, uint wrap) { + uint8_t sm = self->state_machine; + uint8_t offset = rp2pio_statemachine_program_offset(self); + + pio_sm_set_wrap(self->pio, sm, offset+wrap_target, offset+wrap); +} diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h index 38410a3629..2503aace8d 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.h +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.h @@ -74,6 +74,9 @@ bool rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, bool auto_push, uint8_t push_threshold, bool in_shift_right, bool claim_pins); +uint8_t rp2pio_statemachine_program_offset(rp2pio_statemachine_obj_t *self); +void rp2pio_statemachine_set_wrap(rp2pio_statemachine_obj_t *self, uint wrap_target, uint wrap); + void rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self, bool leave_pins); extern const mp_obj_type_t rp2pio_statemachine_type; diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 635f1a75e7..ed0eeba8f0 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -23,6 +23,7 @@ CIRCUITPY_FRAMEBUFFERIO ?= 1 CIRCUITPY_FULL_BUILD ?= 1 CIRCUITPY_AUDIOMP3 ?= 1 CIRCUITPY_BITOPS ?= 1 +CIRCUITPY_IMAGECAPTURE ?= 1 CIRCUITPY_PWMIO ?= 1 CIRCUITPY_RGBMATRIX ?= 1 CIRCUITPY_ROTARYIO ?= 1 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 5745c4f1d2..93488d3d90 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -319,6 +319,9 @@ endif ifeq ($(CIRCUITPY_PEW),1) SRC_PATTERNS += _pew/% endif +ifeq ($(CIRCUITPY_IMAGECAPTURE),1) +SRC_PATTERNS += imagecapture/% +endif ifeq ($(CIRCUITPY_MSGPACK),1) SRC_PATTERNS += msgpack/% endif @@ -367,8 +370,11 @@ SRC_COMMON_HAL_ALL = \ digitalio/DigitalInOut.c \ digitalio/__init__.c \ displayio/ParallelBus.c \ + dualbank/__init__.c \ frequencyio/FrequencyIn.c \ frequencyio/__init__.c \ + imagecapture/ParallelImageCapture.c \ + imagecapture/__init__.c \ gnss/__init__.c \ gnss/GNSS.c \ gnss/PositionFix.c \ @@ -382,7 +388,6 @@ SRC_COMMON_HAL_ALL = \ nvm/ByteArray.c \ nvm/__init__.c \ os/__init__.c \ - dualbank/__init__.c \ ps2io/Ps2.c \ ps2io/__init__.c \ pulseio/PulseIn.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 801e1867e7..15881f41bd 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -412,6 +412,13 @@ extern const struct _mp_obj_module_t terminalio_module; #define TERMINALIO_MODULE #endif +#if CIRCUITPY_DUALBANK +extern const struct _mp_obj_module_t dualbank_module; +#define DUALBANK_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_dualbank), (mp_obj_t)&dualbank_module }, +#else +#define DUALBANK_MODULE +#endif + #if CIRCUITPY_ERRNO #define MICROPY_PY_UERRNO (1) // Uses about 80 bytes. @@ -486,6 +493,13 @@ extern const struct _mp_obj_module_t i2cperipheral_module; #define I2CPERIPHERAL_MODULE #endif +#if CIRCUITPY_IMAGECAPTURE +extern const struct _mp_obj_module_t imagecapture_module; +#define IMAGECAPTURE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_imagecapture), (mp_obj_t)&imagecapture_module }, +#else +#define IMAGECAPTURE_MODULE +#endif + #if CIRCUITPY_IPADDRESS extern const struct _mp_obj_module_t ipaddress_module; #define IPADDRESS_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_ipaddress), (mp_obj_t)&ipaddress_module }, @@ -574,13 +588,6 @@ extern const struct _mp_obj_module_t os_module; #define OS_MODULE_ALT_NAME #endif -#if CIRCUITPY_DUALBANK -extern const struct _mp_obj_module_t dualbank_module; -#define DUALBANK_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_dualbank), (mp_obj_t)&dualbank_module }, -#else -#define DUALBANK_MODULE -#endif - #if CIRCUITPY_PEW extern const struct _mp_obj_module_t pew_module; #define PEW_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__pew),(mp_obj_t)&pew_module }, @@ -860,6 +867,7 @@ extern const struct _mp_obj_module_t msgpack_module; COUNTIO_MODULE \ DIGITALIO_MODULE \ DISPLAYIO_MODULE \ + DUALBANK_MODULE \ FONTIO_MODULE \ TERMINALIO_MODULE \ VECTORIO_MODULE \ @@ -872,6 +880,7 @@ extern const struct _mp_obj_module_t msgpack_module; GNSS_MODULE \ I2CPERIPHERAL_MODULE \ IPADDRESS_MODULE \ + IMAGECAPTURE_MODULE \ JSON_MODULE \ MATH_MODULE \ _EVE_MODULE \ @@ -882,7 +891,6 @@ extern const struct _mp_obj_module_t msgpack_module; NETWORK_MODULE \ SOCKET_MODULE \ WIZNET_MODULE \ - DUALBANK_MODULE \ PEW_MODULE \ PIXELBUF_MODULE \ PS2IO_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 600e56ee23..965b5dde42 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -218,6 +218,9 @@ CFLAGS += -DCIRCUITPY_NVM=$(CIRCUITPY_NVM) CIRCUITPY_OS ?= 1 CFLAGS += -DCIRCUITPY_OS=$(CIRCUITPY_OS) +CIRCUITPY_IMAGECAPTURE ?= 0 +CFLAGS += -DCIRCUITPY_IMAGECAPTURE=$(CIRCUITPY_IMAGECAPTURE) + CIRCUITPY_PEW ?= 0 CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW) diff --git a/py/misc.h b/py/misc.h index cb7192a56f..acb0d50cec 100644 --- a/py/misc.h +++ b/py/misc.h @@ -55,6 +55,13 @@ typedef unsigned int uint; // Static assertion macro #define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)])) +// Explicit fallthrough delcarations for case statements +#ifdef __GNUC__ +#define FALLTHROUGH __attribute__((fallthrough)) +#else +#define FALLTHROUGH ((void)0) /* FALLTHROUGH */ +#endif + /** memory allocation ******************************************/ // TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element) diff --git a/shared-bindings/displayio/ColorConverter.c b/shared-bindings/displayio/ColorConverter.c index d102d7f9d1..a5b154b341 100644 --- a/shared-bindings/displayio/ColorConverter.c +++ b/shared-bindings/displayio/ColorConverter.c @@ -30,6 +30,7 @@ #include "lib/utils/context_manager_helpers.h" #include "py/binary.h" +#include "py/enum.h" #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/microcontroller/Pin.h" @@ -39,20 +40,20 @@ //| class ColorConverter: //| """Converts one color format to another.""" //| -//| def __init__(self, *, dither: bool = False) -> None: -//| """Create a ColorConverter object to convert color formats. Only supports RGB888 to RGB565 -//| currently. +//| def __init__(self, *, colorspace: Colorspace=Colorspace.RGB888, dither: bool = False) -> None: +//| """Create a ColorConverter object to convert color formats. +//| +//| :param Colorspace colorspace: The source colorspace, one of the Colorspace constants //| :param bool dither: Adds random noise to dither the output image""" //| ... //| -// TODO(tannewt): Add support for other color formats. -//| STATIC mp_obj_t displayio_colorconverter_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_dither}; + enum { ARG_dither, ARG_input_colorspace }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_dither, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} } + { MP_QSTR_dither, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_input_colorspace, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = (void *)&displayio_colorspace_RGB888_obj} }, }; 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); @@ -60,13 +61,13 @@ STATIC mp_obj_t displayio_colorconverter_make_new(const mp_obj_type_t *type, siz displayio_colorconverter_t *self = m_new_obj(displayio_colorconverter_t); self->base.type = &displayio_colorconverter_type; - common_hal_displayio_colorconverter_construct(self, args[ARG_dither].u_bool); + common_hal_displayio_colorconverter_construct(self, args[ARG_dither].u_bool, (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_input_colorspace].u_obj)); return MP_OBJ_FROM_PTR(self); } //| def convert(self, color: int) -> int: -//| """Converts the given RGB888 color to RGB565""" +//| """Converts the given color to RGB565 according to the Colorspace""" //| ... //| STATIC mp_obj_t displayio_colorconverter_obj_convert(mp_obj_t self_in, mp_obj_t color_obj) { diff --git a/shared-bindings/displayio/ColorConverter.h b/shared-bindings/displayio/ColorConverter.h index d5a5d05243..018db92c96 100644 --- a/shared-bindings/displayio/ColorConverter.h +++ b/shared-bindings/displayio/ColorConverter.h @@ -27,13 +27,14 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_COLORCONVERTER_H #define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_COLORCONVERTER_H +#include "shared-bindings/displayio/__init__.h" #include "shared-module/displayio/ColorConverter.h" #include "shared-module/displayio/Palette.h" extern const mp_obj_type_t displayio_colorconverter_type; -void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither); +void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither, displayio_colorspace_t input_colorspace); void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *colorconverter, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color); void common_hal_displayio_colorconverter_set_dither(displayio_colorconverter_t *self, bool dither); diff --git a/shared-bindings/displayio/__init__.c b/shared-bindings/displayio/__init__.c index bca4289b0e..b39e72b946 100644 --- a/shared-bindings/displayio/__init__.c +++ b/shared-bindings/displayio/__init__.c @@ -26,6 +26,7 @@ #include +#include "py/enum.h" #include "py/obj.h" #include "py/runtime.h" @@ -49,7 +50,6 @@ //| including synchronizing with refresh rates and partial updating.""" //| - //| def release_displays() -> None: //| """Releases any actively used displays so their busses and pins can be used again. This will also //| release the builtin display on boards that have one. You will need to reinitialize it yourself @@ -65,10 +65,48 @@ STATIC mp_obj_t displayio_release_displays(void) { } MP_DEFINE_CONST_FUN_OBJ_0(displayio_release_displays_obj, displayio_release_displays); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB888, DISPLAYIO_COLORSPACE_RGB888); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565, DISPLAYIO_COLORSPACE_RGB565); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565_SWAPPED, DISPLAYIO_COLORSPACE_RGB565_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555, DISPLAYIO_COLORSPACE_RGB555); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555_SWAPPED, DISPLAYIO_COLORSPACE_RGB555_SWAPPED); + +//| class Colorspace: +//| """The colorspace for a `ColorConverter` to operate in""" +//| +//| RGB888: Colorspace +//| """The standard 24-bit colorspace. Bits 0-7 are blue, 8-15 are green, and 16-24 are red. (0xRRGGBB)""" +//| +//| RGB565: Colorspace +//| """The standard 16-bit colorspace. Bits 0-4 are blue, bits 5-10 are green, and 11-15 are red (0bRRRRRGGGGGGBBBBB)""" +//| +//| RGB565_SWAPPED: Colorspace +//| """The swapped 16-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB565""" +//| +//| RGB555: Colorspace +//| """The standard 15-bit colorspace. Bits 0-4 are blue, bits 5-9 are green, and 11-14 are red. The top bit is ignored. (0bxRRRRRGGGGGBBBBB)""" +//| +//| RGB555_SWAPPED: Colorspace +//| """The swapped 15-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB555""" +//| +MAKE_ENUM_MAP(displayio_colorspace) { + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB888), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555_SWAPPED), +}; +STATIC MP_DEFINE_CONST_DICT(displayio_colorspace_locals_dict, displayio_colorspace_locals_table); + +MAKE_PRINTER(displayio, displayio_colorspace); +MAKE_ENUM_TYPE(displayio, ColorSpace, displayio_colorspace); + + STATIC const mp_rom_map_elem_t displayio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_displayio) }, { MP_ROM_QSTR(MP_QSTR_Bitmap), MP_ROM_PTR(&displayio_bitmap_type) }, { MP_ROM_QSTR(MP_QSTR_ColorConverter), MP_ROM_PTR(&displayio_colorconverter_type) }, + { MP_ROM_QSTR(MP_QSTR_Colorspace), MP_ROM_PTR(&displayio_colorspace_type) }, { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&displayio_display_type) }, { MP_ROM_QSTR(MP_QSTR_EPaperDisplay), MP_ROM_PTR(&displayio_epaperdisplay_type) }, { MP_ROM_QSTR(MP_QSTR_Group), MP_ROM_PTR(&displayio_group_type) }, diff --git a/shared-bindings/displayio/__init__.h b/shared-bindings/displayio/__init__.h index 4fc666c598..31af2ff2ba 100644 --- a/shared-bindings/displayio/__init__.h +++ b/shared-bindings/displayio/__init__.h @@ -27,6 +27,7 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H +#include "py/enum.h" #include "py/obj.h" typedef enum { @@ -39,6 +40,14 @@ typedef enum { CHIP_SELECT_TOGGLE_EVERY_BYTE } display_chip_select_behavior_t; +typedef enum { + DISPLAYIO_COLORSPACE_RGB888, + DISPLAYIO_COLORSPACE_RGB565, + DISPLAYIO_COLORSPACE_RGB555, + DISPLAYIO_COLORSPACE_RGB565_SWAPPED, + DISPLAYIO_COLORSPACE_RGB555_SWAPPED, +} displayio_colorspace_t; + typedef bool (*display_bus_bus_reset)(mp_obj_t bus); typedef bool (*display_bus_bus_free)(mp_obj_t bus); typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); @@ -48,4 +57,7 @@ typedef void (*display_bus_end_transaction)(mp_obj_t bus); void common_hal_displayio_release_displays(void); +extern const mp_obj_type_t displayio_colorspace_type; +extern const cp_enum_obj_t displayio_colorspace_RGB888_obj; + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H diff --git a/shared-bindings/imagecapture/ParallelImageCapture.c b/shared-bindings/imagecapture/ParallelImageCapture.c new file mode 100644 index 0000000000..ee04dcd5ee --- /dev/null +++ b/shared-bindings/imagecapture/ParallelImageCapture.c @@ -0,0 +1,145 @@ +/* + * 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 "lib/utils/context_manager_helpers.h" + +#include "shared-bindings/imagecapture/ParallelImageCapture.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "common-hal/imagecapture/ParallelImageCapture.h" + +//| class ParallelImageCapture: +//| """Capture image frames from a camera with parallel data interface""" +//| +//| def __init__( +//| self, +//| *, +//| data0: microcontroller.Pin, +//| data_count: int=8, +//| 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. +//| """ +//| ... +//| +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, + 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_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 }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + 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); + 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); + + 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); + + return self; +} + +//| def capture(self, buffer: WriteableBuffer, width: int, height: int, bpp: int=16) -> None: +//| """Capture a single frame into the given buffer""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_capture(mp_obj_t self_in, mp_obj_t buffer) { + imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW); + common_hal_imagecapture_parallelimagecapture_capture(self, bufinfo.buf, bufinfo.len); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(imagecapture_parallelimagecapture_capture_obj, imagecapture_parallelimagecapture_capture); + +//| def deinit(self) -> None: +//| """Deinitialize this instance""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture_deinit(mp_obj_t self_in) { + imagecapture_parallelimagecapture_obj_t *self = (imagecapture_parallelimagecapture_obj_t *)self_in; + common_hal_imagecapture_parallelimagecapture_deinit(self); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(imagecapture_parallelimagecapture_deinit_obj, imagecapture_parallelimagecapture_deinit); + +//| def __enter__(self) -> ParallelImageCapture: +//| """No-op used in Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware on context exit. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +STATIC mp_obj_t imagecapture_parallelimagecapture___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_imagecapture_parallelimagecapture_deinit(args[0]); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(imagecapture_parallelimagecapture___exit___obj, 4, 4, imagecapture_parallelimagecapture___exit__); + + +STATIC const mp_rom_map_elem_t imagecapture_parallelimagecapture_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&imagecapture_parallelimagecapture_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&imagecapture_parallelimagecapture___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_capture), MP_ROM_PTR(&imagecapture_parallelimagecapture_capture_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(imagecapture_parallelimagecapture_locals_dict, imagecapture_parallelimagecapture_locals_dict_table); + +const mp_obj_type_t imagecapture_parallelimagecapture_type = { + { &mp_type_type }, + .name = MP_QSTR_ParallelImageCapture, + .make_new = imagecapture_parallelimagecapture_make_new, + .locals_dict = (mp_obj_dict_t *)&imagecapture_parallelimagecapture_locals_dict, +}; diff --git a/shared-bindings/imagecapture/ParallelImageCapture.h b/shared-bindings/imagecapture/ParallelImageCapture.h new file mode 100644 index 0000000000..67e05f2bf2 --- /dev/null +++ b/shared-bindings/imagecapture/ParallelImageCapture.h @@ -0,0 +1,42 @@ +/* + * 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 "common-hal/microcontroller/Pin.h" + +typedef struct imagecapture_parallelimagecapture_obj imagecapture_parallelimagecapture_obj_t; +extern const mp_obj_type_t imagecapture_parallelimagecapture_type; + +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_sync, + int data_count); +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/imagecapture/__init__.c b/shared-bindings/imagecapture/__init__.c new file mode 100644 index 0000000000..8f7ce231a6 --- /dev/null +++ b/shared-bindings/imagecapture/__init__.c @@ -0,0 +1,47 @@ +/* + * 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 + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/imagecapture/ParallelImageCapture.h" + +//| """Support for "Parallel capture" interfaces""" +//| + +STATIC const mp_rom_map_elem_t imagecapture_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_imagecapture) }, + { MP_ROM_QSTR(MP_QSTR_ParallelImageCapture), MP_ROM_PTR(&imagecapture_parallelimagecapture_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(imagecapture_module_globals, imagecapture_module_globals_table); + +const mp_obj_module_t imagecapture_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&imagecapture_module_globals, +}; diff --git a/shared-bindings/imagecapture/__init__.h b/shared-bindings/imagecapture/__init__.h new file mode 100644 index 0000000000..1e170aa941 --- /dev/null +++ b/shared-bindings/imagecapture/__init__.h @@ -0,0 +1,27 @@ +/* + * 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 diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index be9e9dc1a5..47cb903f3f 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -41,9 +41,10 @@ uint32_t displayio_colorconverter_dither_noise_2(uint32_t x, uint32_t y) { return displayio_colorconverter_dither_noise_1(x + y * 0xFFFF); } -void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither) { +void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither, displayio_colorspace_t input_colorspace) { self->dither = dither; self->transparent_color = NO_TRANSPARENT_COLOR; + self->input_colorspace = input_colorspace; } uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888) { @@ -152,6 +153,33 @@ void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _d return; } + switch (self->input_colorspace) { + case DISPLAYIO_COLORSPACE_RGB565_SWAPPED: + pixel = __builtin_bswap16(pixel); + FALLTHROUGH; + case DISPLAYIO_COLORSPACE_RGB565: { + uint32_t r8 = (pixel >> 11) << 3; + uint32_t g8 = ((pixel >> 5) << 2) & 0xff; + uint32_t b8 = (pixel << 3) & 0xff; + pixel = (r8 << 16) | (g8 << 8) | b8; + } + break; + + case DISPLAYIO_COLORSPACE_RGB555_SWAPPED: + pixel = __builtin_bswap16(pixel); + FALLTHROUGH; + case DISPLAYIO_COLORSPACE_RGB555: { + uint32_t r8 = (pixel >> 10) << 3; + uint32_t g8 = ((pixel >> 5) << 3) & 0xff; + uint32_t b8 = (pixel << 3) & 0xff; + pixel = (r8 << 16) | (g8 << 8) | b8; + } + break; + + case DISPLAYIO_COLORSPACE_RGB888: + break; + } + if (self->dither) { uint8_t randr = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x,input_pixel->tile_y)); uint8_t randg = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x + 33,input_pixel->tile_y)); diff --git a/shared-module/displayio/ColorConverter.h b/shared-module/displayio/ColorConverter.h index ebe4b27194..55f86982dc 100644 --- a/shared-module/displayio/ColorConverter.h +++ b/shared-module/displayio/ColorConverter.h @@ -36,6 +36,7 @@ typedef struct { mp_obj_base_t base; bool dither; + uint8_t input_colorspace; uint32_t transparent_color; } displayio_colorconverter_t;