diff --git a/ports/esp32s2/Makefile b/ports/esp32s2/Makefile index 55db73604c..19b89a13a1 100644 --- a/ports/esp32s2/Makefile +++ b/ports/esp32s2/Makefile @@ -171,6 +171,7 @@ SRC_C += \ lib/utils/stdout_helpers.c \ lib/utils/sys_stdio_mphal.c \ peripherals/pins.c \ + peripherals/rmt.c \ supervisor/shared/memory.c ifneq ($(USB),FALSE) diff --git a/ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h b/ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h index 7d08ccf7f1..338186dc52 100644 --- a/ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h +++ b/ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h @@ -29,4 +29,6 @@ #define MICROPY_HW_BOARD_NAME "Saola 1 w/Wrover" #define MICROPY_HW_MCU_NAME "ESP32S2" +#define MICROPY_HW_NEOPIXEL (&pin_GPIO18) + #define AUTORESET_DELAY_MS 500 diff --git a/ports/esp32s2/boards/espressif_saola_1_wrover/pins.c b/ports/esp32s2/boards/espressif_saola_1_wrover/pins.c index 1d4e2c4eba..0562d9331f 100644 --- a/ports/esp32s2/boards/espressif_saola_1_wrover/pins.c +++ b/ports/esp32s2/boards/espressif_saola_1_wrover/pins.c @@ -42,5 +42,7 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO18) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table); diff --git a/ports/esp32s2/common-hal/microcontroller/Pin.c b/ports/esp32s2/common-hal/microcontroller/Pin.c index 5a059f0bbc..2b2b70a45a 100644 --- a/ports/esp32s2/common-hal/microcontroller/Pin.c +++ b/ports/esp32s2/common-hal/microcontroller/Pin.c @@ -26,12 +26,18 @@ */ #include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "supervisor/shared/rgb_led_status.h" #include "py/mphal.h" #include "esp-idf/components/driver/include/driver/gpio.h" #include "esp-idf/components/soc/include/hal/gpio_hal.h" +#ifdef MICROPY_HW_NEOPIXEL +bool neopixel_in_use; +#endif + STATIC uint32_t never_reset_pins[2]; STATIC uint32_t in_use[2]; @@ -50,6 +56,14 @@ void common_hal_never_reset_pin(const mcu_pin_obj_t* pin) { void reset_pin_number(gpio_num_t pin_number) { never_reset_pins[pin_number / 32] &= ~(1 << pin_number % 32); in_use[pin_number / 32] &= ~(1 << pin_number % 32); + + #ifdef MICROPY_HW_NEOPIXEL + if (pin_number == MICROPY_HW_NEOPIXEL->number) { + neopixel_in_use = false; + rgb_led_status_init(); + return; + } + #endif } void common_hal_reset_pin(const mcu_pin_obj_t* pin) { @@ -69,13 +83,28 @@ void reset_all_pins(void) { } in_use[0] = 0; in_use[1] = 0; + + #ifdef MICROPY_HW_NEOPIXEL + neopixel_in_use = false; + #endif } void claim_pin(const mcu_pin_obj_t* pin) { in_use[pin->number / 32] |= (1 << pin->number % 32); + #ifdef MICROPY_HW_NEOPIXEL + if (pin == MICROPY_HW_NEOPIXEL) { + neopixel_in_use = true; + } + #endif } bool pin_number_is_free(gpio_num_t pin_number) { + #ifdef MICROPY_HW_NEOPIXEL + if (pin_number == MICROPY_HW_NEOPIXEL->number) { + return !neopixel_in_use; + } + #endif + uint8_t offset = pin_number / 32; uint8_t mask = 1 << pin_number % 32; return (never_reset_pins[offset] & mask) == 0 && (in_use[offset] & mask) == 0; diff --git a/ports/esp32s2/common-hal/microcontroller/Pin.h b/ports/esp32s2/common-hal/microcontroller/Pin.h index 19985bda6f..f6c0087031 100644 --- a/ports/esp32s2/common-hal/microcontroller/Pin.h +++ b/ports/esp32s2/common-hal/microcontroller/Pin.h @@ -34,6 +34,10 @@ extern bool apa102_mosi_in_use; extern bool apa102_sck_in_use; +#ifdef MICROPY_HW_NEOPIXEL +extern bool neopixel_in_use; +#endif + void reset_all_pins(void); // reset_pin_number takes the pin number instead of the pointer so that objects don't // need to store a full pointer. diff --git a/ports/esp32s2/common-hal/neopixel_write/__init__.c b/ports/esp32s2/common-hal/neopixel_write/__init__.c index a9b42fc96a..beff528566 100644 --- a/ports/esp32s2/common-hal/neopixel_write/__init__.c +++ b/ports/esp32s2/common-hal/neopixel_write/__init__.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2018 hathach for Adafruit Industries + * Copyright (c) 2020 Lucian Copeland 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 @@ -24,10 +24,101 @@ * THE SOFTWARE. */ +/* Uses code from Espressif RGB LED Strip demo and drivers + * Copyright 2015-2020 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 "py/mphal.h" #include "shared-bindings/neopixel_write/__init__.h" +#include "driver/rmt.h" +#include "rmt.h" + +#define WS2812_T0H_NS (350) +#define WS2812_T0L_NS (1000) +#define WS2812_T1H_NS (1000) +#define WS2812_T1L_NS (350) +#define WS2812_RESET_US (280) + +static uint32_t ws2812_t0h_ticks = 0; +static uint32_t ws2812_t1h_ticks = 0; +static uint32_t ws2812_t0l_ticks = 0; +static uint32_t ws2812_t1l_ticks = 0; + +static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size, + size_t wanted_num, size_t *translated_size, size_t *item_num) +{ + if (src == NULL || dest == NULL) { + *translated_size = 0; + *item_num = 0; + return; + } + const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0 + const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1 + size_t size = 0; + size_t num = 0; + uint8_t *psrc = (uint8_t *)src; + rmt_item32_t *pdest = dest; + while (size < src_size && num < wanted_num) { + for (int i = 0; i < 8; i++) { + // MSB first + if (*psrc & (1 << (7 - i))) { + pdest->val = bit1.val; + } else { + pdest->val = bit0.val; + } + num++; + pdest++; + } + size++; + psrc++; + } + *translated_size = size; + *item_num = num; +} void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t numBytes) { - (void)digitalinout; - (void)numBytes; + // Reserve channel + uint8_t number = digitalinout->pin->number; + rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt(); + + // Configure Channel + rmt_config_t config = RMT_DEFAULT_CONFIG_TX(number, channel); + config.clk_div = 2; // set counter clock to 40MHz + rmt_config(&config); + rmt_driver_install(config.channel, 0, 0); + + // Convert NS timings to ticks + uint32_t counter_clk_hz = 0; + if (rmt_get_counter_clock(config.channel, &counter_clk_hz) != ESP_OK) { + mp_raise_ValueError(translate("Could not retrieve clock")); + } + float ratio = (float)counter_clk_hz / 1e9; + ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS); + ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS); + ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS); + ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS); + + // Initialize automatic timing translator + rmt_translator_init(config.channel, ws2812_rmt_adapter); + + // Write and wait to finish + if(rmt_write_sample(config.channel, pixels, (size_t)numBytes, true) != ESP_OK) { + mp_raise_ValueError(translate("Input/output error")); + } + rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(100)); + + // Free channel again + esp32s2_peripherals_free_rmt(config.channel); } diff --git a/ports/esp32s2/common-hal/pulseio/PWMOut.c b/ports/esp32s2/common-hal/pulseio/PWMOut.c index c4f5e18dd4..f38bd44340 100644 --- a/ports/esp32s2/common-hal/pulseio/PWMOut.c +++ b/ports/esp32s2/common-hal/pulseio/PWMOut.c @@ -3,8 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Lucian Copeland for Adafruit Industries - * Uses code from Micropython, Copyright (c) 2013-2016 Damien P. George + * Copyright (c) 2020 Lucian Copeland 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 diff --git a/ports/esp32s2/mpconfigport.mk b/ports/esp32s2/mpconfigport.mk index c2d21e0841..21d6e7cffd 100644 --- a/ports/esp32s2/mpconfigport.mk +++ b/ports/esp32s2/mpconfigport.mk @@ -12,6 +12,8 @@ USB_SERIAL_NUMBER_LENGTH = 12 # Longints can be implemented as mpz, as longlong, or not LONGINT_IMPL = MPZ +CIRCUITPY_NEOPIXEL_WRITE = 1 + CIRCUITPY_FULL_BUILD = 0 CIRCUITPY_ANALOGIO = 0 CIRCUITPY_AUDIOBUSIO = 0 diff --git a/ports/esp32s2/peripherals/rmt.c b/ports/esp32s2/peripherals/rmt.c new file mode 100644 index 0000000000..e9ad2c32ed --- /dev/null +++ b/ports/esp32s2/peripherals/rmt.c @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Lucian Copeland 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 "rmt.h" +#include "py/runtime.h" + +bool rmt_reserved_channels[RMT_CHANNEL_MAX]; + +rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void) { + for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) { + if (!rmt_reserved_channels[i]) { + rmt_reserved_channels[i] = true; + return i; + } + } + mp_raise_RuntimeError(translate("All timers in use")); + return false; +} + +void esp32s2_peripherals_free_rmt(rmt_channel_t chan) { + rmt_reserved_channels[chan] = false; +} diff --git a/ports/esp32s2/peripherals/rmt.h b/ports/esp32s2/peripherals/rmt.h new file mode 100644 index 0000000000..4741a5bc59 --- /dev/null +++ b/ports/esp32s2/peripherals/rmt.h @@ -0,0 +1,37 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Lucian Copeland 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. + */ + +#ifndef MICROPY_INCLUDED_ESP32S2_PERIPHERALS_RMT_H +#define MICROPY_INCLUDED_ESP32S2_PERIPHERALS_RMT_H + +#include "py/mphal.h" +#include "driver/rmt.h" +#include + +rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void); +void esp32s2_peripherals_free_rmt(rmt_channel_t chan); + +#endif