8000 ESP32-S2: Add Neopixel support by hierophect · Pull Request #3232 · adafruit/circuitpython · GitHub
[go: up one dir, main page]

Skip to content

ESP32-S2: Add Neopixel support #3232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion main.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ bool run_code_py(safe_mode_t safe_mode) {
}
}

// Wait for connection or character.
// Display a different completion message if the user has no USB attached (cannot save files)
if (!serial_connected_at_start) {
serial_write_compressed(translate("\nCode done running. Waiting for reload.\n"));
}
Expand Down
1 change: 1 addition & 0 deletions ports/esp32s2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions ports/esp32s2/boards/espressif_saola_1_wroom/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@
#define MICROPY_HW_BOARD_NAME "Saola 1 w/Wroom"
#define MICROPY_HW_MCU_NAME "ESP32S2"

#define MICROPY_HW_NEOPIXEL (&pin_GPIO18)

#define AUTORESET_DELAY_MS 500
2 changes: 0 additions & 2 deletions ports/esp32s2/boards/espressif_saola_1_wroom/mpconfigboard.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ LONGINT_IMPL = MPZ
# so increase it to 32.
CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32

CIRCUITPY_NEOPIXEL_WRITE = 0

CIRCUITPY_ESP_FLASH_MODE=dio
CIRCUITPY_ESP_FLASH_FREQ=40m
CIRCUITPY_ESP_FLASH_SIZE=4MB
Expand Down
2 changes: 2 additions & 0 deletions ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ LONGINT_IMPL = MPZ
# so increase it to 32.
CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32

CIRCUITPY_NEOPIXEL_WRITE = 0

CIRCUITPY_ESP_FLASH_MODE=dio
CIRCUITPY_ESP_FLASH_FREQ=40m
CIRCUITPY_ESP_FLASH_SIZE=4MB
Expand Down
2 changes: 2 additions & 0 deletions ports/esp32s2/boards/espressif_saola_1_wrover/pins.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ USB_PRODUCT = "FeatherS2"
USB_MANUFACTURER = "UnexpectedMaker"
USB_DEVICES = "CDC,MSC,HID"


INTERNAL_FLASH_FILESYSTEM = 1
LONGINT_IMPL = MPZ

# The default queue depth of 16 overflows on release builds,
# so increase it to 32.
CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32

CIRCUITPY_NEOPIXEL_WRITE = 0

CIRCUITPY_ESP_FLASH_MODE=qio
CIRCUITPY_ESP_FLASH_FREQ=40m
CIRCUITPY_ESP_FLASH_SIZE=16MB
Expand Down
33 changes: 33 additions & 0 deletions ports/esp32s2/common-hal/microcontroller/Pin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand All @@ -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) {
Expand All @@ -69,13 +83,32 @@ 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
}

void common_hal_mcu_pin_claim(const mcu_pin_obj_t* pin) {
claim_pin(pin);
}

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;
Expand Down
4 changes: 4 additions & 0 deletions ports/esp32s2/common-hal/microcontroller/Pin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
98 changes: 95 additions & 3 deletions ports/esp32s2/common-hal/neopixel_write/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,10 +24,102 @@
* 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 "py/runtime.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_RuntimeError(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_RuntimeError(translate("Input/output error"));
}
rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(100));

// Free channel again
esp32s2_peripherals_free_rmt(config.channel);
}
22 changes: 16 additions & 6 deletions ports/esp32s2/common-hal/pulseio/PWMOut.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -33,24 +32,30 @@

#define INDEX_EMPTY 0xFF

STATIC bool not_first_reset = false;
STATIC uint32_t reserved_timer_freq[LEDC_TIMER_MAX];
STATIC uint8_t reserved_channels[LEDC_CHANNEL_MAX];
STATIC bool never_reset_tim[LEDC_TIMER_MAX];
STATIC bool never_reset_chan[LEDC_CHANNEL_MAX];

void pwmout_reset(void) {
for (size_t i = 0; i < LEDC_CHANNEL_MAX; i++ ) {
ledc_stop(LEDC_LOW_SPEED_MODE, i, 0);
if (reserved_channels[i] != INDEX_EMPTY && not_first_reset) {
ledc_stop(LEDC_LOW_SPEED_MODE, i, 0);
}
if (!never_reset_chan[i]) {
reserved_channels[i] = INDEX_EMPTY;
}
}
for (size_t i = 0; i < LEDC_TIMER_MAX; i++ ) {
ledc_timer_rst(LEDC_LOW_SPEED_MODE, i);
if (reserved_timer_freq[i]) {
ledc_timer_rst(LEDC_LOW_SPEED_MODE, i);
}
if (!never_reset_tim[i]) {
reserved_timer_freq[i] = 0;
}
}
not_first_reset = true;
}

pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self,
Expand Down Expand Up @@ -158,7 +163,10 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) {
if (common_hal_pulseio_pwmout_deinited(self)) {
return;
}
ledc_stop(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, 0);

if (reserved_channels[self->chan_handle.channel] != INDEX_EMPTY) {
ledc_stop(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, 0);
}
// Search if any other channel is using the timer
bool taken = false;
for (size_t i =0; i < LEDC_CHANNEL_MAX; i++) {
Expand All @@ -168,7 +176,9 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) {
}
// Variable frequency means there's only one channel on the timer
if (!taken || self->variable_frequency) {
ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num);
if (reserved_timer_freq[self->tim_handle.timer_num] != 0) {
ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num);
}
reserved_timer_freq[self->tim_handle.timer_num] = 0;
}
reset_pin_number(self->pin_number);
Expand Down
25 changes: 10 additions & 15 deletions ports/esp32s2/mpconfigport.mk
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,21 @@ USB_SERIAL_NUMBER_LENGTH = 12
# Longints can be implemented as mpz, as longlong, or not
LONGINT_IMPL = MPZ

CIRCUITPY_FULL_BUILD = 0
# These modules are implemented in ports/<port>/common-hal:
CIRCUITPY_ANALOGIO = 0
CIRCUITPY_NVM = 0
CIRCUITPY_AUDIOBUSIO = 0
CIRCUITPY_AUDIOIO = 0
CIRCUITPY_BITBANGIO = 1
CIRCUITPY_BOARD = 1
CIRCUITPY_DIGITALIO = 1
CIRCUITPY_BUSIO = 1
CIRCUITPY_DISPLAYIO = 1
CIRCUITPY_FREQUENCYIO = 0
CIRCUITPY_I2CPERIPHERAL = 0
CIRCUITPY_MICROCONTROLLER = 1
CIRCUITPY_NVM = 0
CIRCUITPY_PULSEIO = 1
CIRCUITPY_ROTARYIO = 0
CIRCUITPY_RTC = 0
CIRCUITPY_TOUCHIO = 0
CIRCUITPY_FREQUENCYIO = 0
CIRCUITPY_I2CPERIPHERAL = 0
CIRCUITPY_COUNTIO = 0

# Enable USB HID support
CIRCUITPY_USB_HID = 1
CIRCUITPY_USB_MIDI = 0
# These modules are implemented in shared-module/ - they can be included in
# any port once their prerequisites in common-hal are complete.
CIRCUITPY_RANDOM = 0 # Requires OS
CIRCUITPY_USB_MIDI = 0 # Requires USB
CIRCUITPY_ULAB = 0 # No requirements, but takes extra flash

CIRCUITPY_MODULE ?= none
Loading
0