Skip to content

Commit bbac68e

Browse files
authored
Merge pull request #3232 from hierophect/esp32-neopixel
ESP32-S2: Add Neopixel support
2 parents bccfb8b + 4613b58 commit bbac68e

File tree

16 files changed

+258
-34
lines changed

16 files changed

+258
-34
lines changed

main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ bool run_code_py(safe_mode_t safe_mode) {
282282
}
283283
}
284284

285-
// Wait for connection or character.
285+
// Display a different completion message if the user has no USB attached (cannot save files)
286286
if (!serial_connected_at_start) {
287287
serial_write_compressed(translate("\nCode done running. Waiting for reload.\n"));
288288
}

ports/esp32s2/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ SRC_C += \
171171
lib/utils/stdout_helpers.c \
172172
lib/utils/sys_stdio_mphal.c \
173173
peripherals/pins.c \
174+
peripherals/rmt.c \
174175
supervisor/shared/memory.c
175176

176177
ifneq ($(USB),FALSE)

ports/esp32s2/boards/espressif_saola_1_wroom/mpconfigboard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@
2929
#define MICROPY_HW_BOARD_NAME "Saola 1 w/Wroom"
3030
#define MICROPY_HW_MCU_NAME "ESP32S2"
3131

32+
#define MICROPY_HW_NEOPIXEL (&pin_GPIO18)
33+
3234
#define AUTORESET_DELAY_MS 500

ports/esp32s2/boards/espressif_saola_1_wroom/mpconfigboard.mk

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ LONGINT_IMPL = MPZ
1010
# so increase it to 32.
1111
CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32
1212

13-
CIRCUITPY_NEOPIXEL_WRITE = 0
14-
1513
CIRCUITPY_ESP_FLASH_MODE=dio
1614
CIRCUITPY_ESP_FLASH_FREQ=40m
1715
CIRCUITPY_ESP_FLASH_SIZE=4MB

ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@
2929
#define MICROPY_HW_BOARD_NAME "Saola 1 w/Wrover"
3030
#define MICROPY_HW_MCU_NAME "ESP32S2"
3131

32+
#define MICROPY_HW_NEOPIXEL (&pin_GPIO18)
33+
3234
#define AUTORESET_DELAY_MS 500

ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.mk

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ LONGINT_IMPL = MPZ
1010
# so increase it to 32.
1111
CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32
1212

13-
CIRCUITPY_NEOPIXEL_WRITE = 0
14-
1513
CIRCUITPY_ESP_FLASH_MODE=dio
1614
CIRCUITPY_ESP_FLASH_FREQ=40m
1715
CIRCUITPY_ESP_FLASH_SIZE=4MB

ports/esp32s2/boards/espressif_saola_1_wrover/pins.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,7 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
4242
{ MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) },
4343
{ MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) },
4444
{ MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) },
45+
46+
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO18) },
4547
};
4648
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);

ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@ USB_PRODUCT = "FeatherS2"
44
USB_MANUFACTURER = "UnexpectedMaker"
55
USB_DEVICES = "CDC,MSC,HID"
66

7-
87
INTERNAL_FLASH_FILESYSTEM = 1
98
LONGINT_IMPL = MPZ
109

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

15-
CIRCUITPY_NEOPIXEL_WRITE = 0
16-
1714
CIRCUITPY_ESP_FLASH_MODE=qio
1815
CIRCUITPY_ESP_FLASH_FREQ=40m
1916
CIRCUITPY_ESP_FLASH_SIZE=16MB

ports/esp32s2/common-hal/microcontroller/Pin.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@
2626
*/
2727

2828
#include "shared-bindings/microcontroller/Pin.h"
29+
#include "shared-bindings/digitalio/DigitalInOut.h"
30+
#include "supervisor/shared/rgb_led_status.h"
2931

3032
#include "py/mphal.h"
3133

3234
#include "esp-idf/components/driver/include/driver/gpio.h"
3335
#include "esp-idf/components/soc/include/hal/gpio_hal.h"
3436

37+
#ifdef MICROPY_HW_NEOPIXEL
38+
bool neopixel_in_use;
39+
#endif
40+
3541
STATIC uint32_t never_reset_pins[2];
3642
STATIC uint32_t in_use[2];
3743

@@ -50,6 +56,14 @@ void common_hal_never_reset_pin(const mcu_pin_obj_t* pin) {
5056
void reset_pin_number(gpio_num_t pin_number) {
5157
never_reset_pins[pin_number / 32] &= ~(1 << pin_number % 32);
5258
in_use[pin_number / 32] &= ~(1 << pin_number % 32);
59+
60+
#ifdef MICROPY_HW_NEOPIXEL
61+
if (pin_number == MICROPY_HW_NEOPIXEL->number) {
62+
neopixel_in_use = false;
63+
rgb_led_status_init();
64+
return;
65+
}
66+
#endif
5367
}
5468

5569
void common_hal_reset_pin(const mcu_pin_obj_t* pin) {
@@ -69,13 +83,32 @@ void reset_all_pins(void) {
6983
}
7084
in_use[0] = 0;
7185
in_use[1] = 0;
86+
87+
#ifdef MICROPY_HW_NEOPIXEL
88+
neopixel_in_use = false;
89+
#endif
7290
}
7391

7492
void claim_pin(const mcu_pin_obj_t* pin) {
7593
in_use[pin->number / 32] |= (1 << pin->number % 32);
94+
#ifdef MICROPY_HW_NEOPIXEL
95+
if (pin == MICROPY_HW_NEOPIXEL) {
96+
neopixel_in_use = true;
97+
}
98+
#endif
99+
}
100+
101+
void common_hal_mcu_pin_claim(const mcu_pin_obj_t* pin) {
102+
claim_pin(pin);
76103
}
77104

78105
bool pin_number_is_free(gpio_num_t pin_number) {
106+
#ifdef MICROPY_HW_NEOPIXEL
107+
if (pin_number == MICROPY_HW_NEOPIXEL->number) {
108+
return !neopixel_in_use;
109+
}
110+
#endif
111+
79112
uint8_t offset = pin_number / 32;
80113
uint8_t mask = 1 << pin_number % 32;
81114
return (never_reset_pins[offset] & mask) == 0 && (in_use[offset] & mask) == 0;

ports/esp32s2/common-hal/microcontroller/Pin.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
extern bool apa102_mosi_in_use;
3535
extern bool apa102_sck_in_use;
3636

37+
#ifdef MICROPY_HW_NEOPIXEL
38+
extern bool neopixel_in_use;
39+
#endif
40+
3741
void reset_all_pins(void);
3842
// reset_pin_number takes the pin number instead of the pointer so that objects don't
3943
// need to store a full pointer.

ports/esp32s2/common-hal/neopixel_write/__init__.c

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* The MIT License (MIT)
55
*
6-
* Copyright (c) 2018 hathach for Adafruit Industries
6+
* Copyright (c) 2020 Lucian Copeland for Adafruit Industries
77
*
88
* Permission is hereby granted, free of charge, to any person obtaining a copy
99
* of this software and associated documentation files (the "Software"), to deal
@@ -24,10 +24,102 @@
2424
* THE SOFTWARE.
2525
*/
2626

27+
/* Uses code from Espressif RGB LED Strip demo and drivers
28+
* Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
29+
*
30+
* Licensed under the Apache License, Version 2.0 (the "License");
31+
* you may not use this file except in compliance with the License.
32+
* You may obtain a copy of the License at
33+
*
34+
* http://www.apache.org/licenses/LICENSE-2.0
35+
*
36+
* Unless required by applicable law or agreed to in writing, software
37+
* distributed under the License is distributed on an "AS IS" BASIS,
38+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39+
* See the License for the specific language governing permissions and
40+
* limitations under the License.
41+
*/
42+
2743
#include "py/mphal.h"
44+
#include "py/runtime.h"
2845
#include "shared-bindings/neopixel_write/__init__.h"
46+
#include "driver/rmt.h"
47+
#include "rmt.h"
48+
49+
#define WS2812_T0H_NS (350)
50+
#define WS2812_T0L_NS (1000)
51+
#define WS2812_T1H_NS (1000)
52+
#define WS2812_T1L_NS (350)
53+
#define WS2812_RESET_US (280)
54+
55+
static uint32_t ws2812_t0h_ticks = 0;
56+
static uint32_t ws2812_t1h_ticks = 0;
57+
static uint32_t ws2812_t0l_ticks = 0;
58+
static uint32_t ws2812_t1l_ticks = 0;
59+
60+
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
61+
size_t wanted_num, size_t *translated_size, size_t *item_num)
62+
{
63+
if (src == NULL || dest == NULL) {
64+
*translated_size = 0;
65+
*item_num = 0;
66+
return;
67+
}
68+
const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
69+
const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
70+
size_t size = 0;
71+
size_t num = 0;
72+
uint8_t *psrc = (uint8_t *)src;
73+
rmt_item32_t *pdest = dest;
74+
while (size < src_size && num < wanted_num) {
75+
for (int i = 0; i < 8; i++) {
76+
// MSB first
77+
if (*psrc & (1 << (7 - i))) {
78+
pdest->val = bit1.val;
79+
} else {
80+
pdest->val = bit0.val;
81+
}
82+
num++;
83+
pdest++;
84+
}
85+
size++;
86+
psrc++;
87+
}
88+
*translated_size = size;
89+
*item_num = num;
90+
}
2991

3092
void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t numBytes) {
31-
(void)digitalinout;
32-
(void)numBytes;
93+
// Reserve channel
94+
uint8_t number = digitalinout->pin->number;
95+
rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt();
96+
97+
// Configure Channel
98+
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(number, channel);
99+
config.clk_div = 2; // set counter clock to 40MHz
100+
rmt_config(&config);
101+
rmt_driver_install(config.channel, 0, 0);
102+
103+
// Convert NS timings to ticks
104+
uint32_t counter_clk_hz = 0;
105+
if (rmt_get_counter_clock(config.channel, &counter_clk_hz) != ESP_OK) {
106+
mp_raise_RuntimeError(translate("Could not retrieve clock"));
107+
}
108+
float ratio = (float)counter_clk_hz / 1e9;
109+
ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
110+
ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
111+
ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
112+
ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
113+
114+
// Initialize automatic timing translator
115+
rmt_translator_init(config.channel, ws2812_rmt_adapter);
116+
117+
// Write and wait to finish
118+
if(rmt_write_sample(config.channel, pixels, (size_t)numBytes, true) != ESP_OK) {
119+
mp_raise_RuntimeError(translate("Input/output error"));
120+
}
121+
rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(100));
122+
123+
// Free channel again
124+
esp32s2_peripherals_free_rmt(config.channel);
33125
}

ports/esp32s2/common-hal/pulseio/PWMOut.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
*
44
* The MIT License (MIT)
55
*
6-
* Copyright (c) 2019 Lucian Copeland for Adafruit Industries
7-
* Uses code from Micropython, Copyright (c) 2013-2016 Damien P. George
6+
* Copyright (c) 2020 Lucian Copeland for Adafruit Industries
87
*
98
* Permission is hereby granted, free of charge, to any person obtaining a copy
109
* of this software and associated documentation files (the "Software"), to deal
@@ -33,24 +32,30 @@
3332

3433
#define INDEX_EMPTY 0xFF
3534

35+
STATIC bool not_first_reset = false;
3636
STATIC uint32_t reserved_timer_freq[LEDC_TIMER_MAX];
3737
STATIC uint8_t reserved_channels[LEDC_CHANNEL_MAX];
3838
STATIC bool never_reset_tim[LEDC_TIMER_MAX];
3939
STATIC bool never_reset_chan[LEDC_CHANNEL_MAX];
4040

4141
void pwmout_reset(void) {
4242
for (size_t i = 0; i < LEDC_CHANNEL_MAX; i++ ) {
43-
ledc_stop(LEDC_LOW_SPEED_MODE, i, 0);
43+
if (reserved_channels[i] != INDEX_EMPTY && not_first_reset) {
44+
ledc_stop(LEDC_LOW_SPEED_MODE, i, 0);
45+
}
4446
if (!never_reset_chan[i]) {
4547
reserved_channels[i] = INDEX_EMPTY;
4648
}
4749
}
4850
for (size_t i = 0; i < LEDC_TIMER_MAX; i++ ) {
49-
ledc_timer_rst(LEDC_LOW_SPEED_MODE, i);
51+
if (reserved_timer_freq[i]) {
52+
ledc_timer_rst(LEDC_LOW_SPEED_MODE, i);
53+
}
5054
if (!never_reset_tim[i]) {
5155
reserved_timer_freq[i] = 0;
5256
}
5357
}
58+
not_first_reset = true;
5459
}
5560

5661
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) {
158163
if (common_hal_pulseio_pwmout_deinited(self)) {
159164
return;
160165
}
161-
ledc_stop(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, 0);
166+
167+
if (reserved_channels[self->chan_handle.channel] != INDEX_EMPTY) {
168+
ledc_stop(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, 0);
169+
}
162170
// Search if any other channel is using the timer
163171
bool taken = false;
164172
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) {
168176
}
169177
// Variable frequency means there's only one channel on the timer
170178
if (!taken || self->variable_frequency) {
171-
ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num);
179+
if (reserved_timer_freq[self->tim_handle.timer_num] != 0) {
180+
ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num);
181+
}
172182
reserved_timer_freq[self->tim_handle.timer_num] = 0;
173183
}
174184
reset_pin_number(self->pin_number);

ports/esp32s2/mpconfigport.mk

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,21 @@ USB_SERIAL_NUMBER_LENGTH = 12
1212
# Longints can be implemented as mpz, as longlong, or not
1313
LONGINT_IMPL = MPZ
1414

15-
CIRCUITPY_FULL_BUILD = 0
15+
# These modules are implemented in ports/<port>/common-hal:
1616
CIRCUITPY_ANALOGIO = 0
17+
CIRCUITPY_NVM = 0
1718
CIRCUITPY_AUDIOBUSIO = 0
1819
CIRCUITPY_AUDIOIO = 0
19-
CIRCUITPY_BITBANGIO = 1
20-
CIRCUITPY_BOARD = 1
21-
CIRCUITPY_DIGITALIO = 1
22-
CIRCUITPY_BUSIO = 1
23-
CIRCUITPY_DISPLAYIO = 1
24-
CIRCUITPY_FREQUENCYIO = 0
25-
CIRCUITPY_I2CPERIPHERAL = 0
26-
CIRCUITPY_MICROCONTROLLER = 1
27-
CIRCUITPY_NVM = 0
28-
CIRCUITPY_PULSEIO = 1
2920
CIRCUITPY_ROTARYIO = 0
3021
CIRCUITPY_RTC = 0
31-
CIRCUITPY_TOUCHIO = 0
22+
CIRCUITPY_FREQUENCYIO = 0
23+
CIRCUITPY_I2CPERIPHERAL = 0
24+
CIRCUITPY_COUNTIO = 0
3225

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

3732
CIRCUITPY_MODULE ?= none

0 commit comments

Comments
 (0)