Merge pull request #3232 from hierophect/esp32-neopixel

ESP32-S2: Add Neopixel support
This commit is contained in:
Scott Shawcroft 2020-08-11 11:04:39 -07:00 committed by GitHub
commit bbac68e77c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 258 additions and 34 deletions

2
main.c
View File

@ -282,7 +282,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"));
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -4,7 +4,6 @@ USB_PRODUCT = "FeatherS2"
USB_MANUFACTURER = "UnexpectedMaker"
USB_DEVICES = "CDC,MSC,HID"
INTERNAL_FLASH_FILESYSTEM = 1
LONGINT_IMPL = MPZ
@ -12,8 +11,6 @@ LONGINT_IMPL = MPZ
# 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

View File

@ -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,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;

View File

@ -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.

View File

@ -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,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);
}

View File

@ -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
@ -33,6 +32,7 @@
#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];
@ -40,17 +40,22 @@ 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,
@ -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++) {
@ -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);

View File

@ -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

View File

@ -0,0 +1,46 @@
/*
* 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;
rmt_driver_uninstall(chan);
}

View File

@ -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 <stdint.h>
rmt_channel_t esp32s2_peripherals_find_and_reserve_rmt(void);
void esp32s2_peripherals_free_rmt(rmt_channel_t chan);
#endif

View File

@ -28,6 +28,7 @@
#include "shared-bindings/microcontroller/Pin.h"
#include "rgb_led_status.h"
#include "supervisor/shared/tick.h"
#include "py/obj.h"
#ifdef MICROPY_HW_NEOPIXEL
uint8_t rgb_status_brightness = 63;
@ -365,6 +366,11 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
status->safe_mode = safe_mode;
status->found_main = found_main;
status->total_exception_cycle = 0;
status->ok = result->return_code != PYEXEC_EXCEPTION;
if (status->ok) {
// If this isn't an exception, skip exception sorting and handling
return;
}
status->ones = result->exception_line % 10;
status->ones += status->ones > 0 ? 1 : 0;
status->tens = (result->exception_line / 10) % 10;
@ -382,11 +388,12 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
}
line /= 10;
}
status->ok = result->return_code != PYEXEC_EXCEPTION;
if (!status->ok) {
status->total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * status->digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places;
}
if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_IndentationError)) {
if (!result->exception_type) {
status->exception_color = OTHER_ERROR;
} else if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_IndentationError)) {
status->exception_color = INDENTATION_ERROR;
} else if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_SyntaxError)) {
status->exception_color = SYNTAX_ERROR;