Merge pull request #5892 from tannewt/esp_neopixel

ESP NeoPixel fixes
This commit is contained in:
Dan Halbert 2022-01-19 23:57:39 -05:00 committed by GitHub
commit 1cd3faa062
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 30 deletions

View File

@ -100,7 +100,7 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
"");
}
uint64_t next_start_raw_ticks = 0;
STATIC uint64_t next_start_raw_ticks = 0;
void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, uint8_t *pixels, uint32_t numBytes) {
// This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code:

View File

@ -36,20 +36,6 @@
STATIC uint32_t never_reset_pins[2];
STATIC uint32_t in_use[2];
STATIC void floating_gpio_reset(gpio_num_t pin_number) {
// This is the same as gpio_reset_pin(), but without the pullup.
// Note that gpio_config resets the iomatrix to GPIO_FUNC as well.
gpio_config_t cfg = {
.pin_bit_mask = BIT64(pin_number),
.mode = GPIO_MODE_DISABLE,
.pull_up_en = false,
.pull_down_en = false,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&cfg);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pin_number], 0);
}
void never_reset_pin_number(gpio_num_t pin_number) {
if (pin_number == NO_PIN) {
return;
@ -72,7 +58,7 @@ 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);
floating_gpio_reset(pin_number);
gpio_reset_pin(pin_number);
}
void common_hal_mcu_pin_reset_number(uint8_t i) {
@ -93,7 +79,7 @@ void reset_all_pins(void) {
(never_reset_pins[i / 32] & (1 << i % 32)) != 0) {
continue;
}
floating_gpio_reset(i);
gpio_reset_pin(i);
}
in_use[0] = never_reset_pins[0];
in_use[1] = never_reset_pins[1];

View File

@ -43,20 +43,23 @@
#include "py/mphal.h"
#include "py/runtime.h"
#include "shared-bindings/neopixel_write/__init__.h"
#include "supervisor/port.h"
#include "components/driver/include/driver/rmt.h"
#include "peripherals/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)
// 416 ns is 1/3 of the 1250ns period of a 800khz signal.
#define WS2812_T0H_NS (416)
#define WS2812_T0L_NS (416 * 2)
#define WS2812_T1H_NS (416 * 2)
#define WS2812_T1L_NS (416)
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 uint64_t next_start_raw_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) {
@ -107,21 +110,29 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
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);
size_t ns_per_tick = 1e9 / counter_clk_hz;
ws2812_t0h_ticks = WS2812_T0H_NS / ns_per_tick;
ws2812_t0l_ticks = WS2812_T0L_NS / ns_per_tick;
ws2812_t1h_ticks = WS2812_T1H_NS / ns_per_tick;
ws2812_t1l_ticks = WS2812_T1L_NS / ns_per_tick;
// Initialize automatic timing translator
rmt_translator_init(config.channel, ws2812_rmt_adapter);
// Wait to make sure we don't append onto the last transmission. This should only be a tick or
// two.
while (port_get_raw_ticks(NULL) < next_start_raw_ticks) {
}
// 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));
// Update the next start to +2 ticks. It ensures that we've gone 300+ us.
next_start_raw_ticks = port_get_raw_ticks(NULL) + 2;
// Free channel again
peripherals_free_rmt(config.channel);
// Swap pin back to GPIO mode

View File

@ -98,6 +98,6 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
gpio_init(digitalinout->pin->number);
common_hal_digitalio_digitalinout_switch_to_output((digitalio_digitalinout_obj_t *)digitalinout, false, DRIVE_MODE_PUSH_PULL);
// Update the next start.
next_start_raw_ticks = port_get_raw_ticks(NULL) + 1;
// Update the next start to +2 ticks. This ensures we give it at least 300us.
next_start_raw_ticks = port_get_raw_ticks(NULL) + 2;
}

View File

@ -48,6 +48,7 @@ uint8_t rgb_status_brightness = 63;
#define MICROPY_HW_NEOPIXEL_COUNT (1)
#endif
static uint64_t next_start_raw_ticks;
static uint8_t status_neopixel_color[3 * MICROPY_HW_NEOPIXEL_COUNT];
static digitalio_digitalinout_obj_t status_neopixel;
@ -218,6 +219,10 @@ void status_led_init() {
void status_led_deinit() {
#ifdef MICROPY_HW_NEOPIXEL
// Make sure the pin stays low for the reset period. The pin reset may pull
// it up and stop the reset period.
while (port_get_raw_ticks(NULL) < next_start_raw_ticks) {
}
common_hal_reset_pin(MICROPY_HW_NEOPIXEL);
#elif defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
@ -265,7 +270,7 @@ void new_status_color(uint32_t rgb) {
status_neopixel_color[3 * i + 2] = rgb_adjusted & 0xff;
}
common_hal_neopixel_write(&status_neopixel, status_neopixel_color, 3 * MICROPY_HW_NEOPIXEL_COUNT);
next_start_raw_ticks = port_get_raw_ticks(NULL) + 2;
#elif defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
for (size_t i = 0; i < MICROPY_HW_APA102_COUNT; i++) {
// Skip 4 + offset to skip the header bytes too.