espShow() время от времени дает сбой в Adafruit NeoPixel/Adafruit_NeoPixel 2.cpp на ESP32.

Я столкнулся со сбоем в файле Adafruit NeoPixel/Adafruit_NeoPixel 2.cpp библиотеки.

Согласно стеку, проблема возникает в строке 129 файла Adafruit NeoPixel/esp 2.c в espShow(), что соответствует настройке члена tx_config в rmt_config` (см. исходный код ниже).

[ОБНОВЛЕНИЕ] Иногда `abort() происходит со строки 165, иначе:

164      // Инициализировать автоматический транслятор времени
165      rmt_translator_init(config.channel, ws2812_rmt_adapter);

Как мне решить эту проблему?

     1  // Реализует периферийное устройство RMT на SoC Espressif
     2  // Copyright (c) 2020 Lucian Copeland for Adafruit Industries
     3
     4  /* Uses code from Espressif RGB LED Strip demo and drivers
     5   * Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   */
    19
    20  #if defined(ESP32)
    21
    22  #include <Arduino.h>
    23  #include "driver/rmt.h"
    24
    25  #if defined(ESP_IDF_VERSION)
    26  #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
    27  #define HAS_ESP_IDF_4
    28  #endif
    29  #endif
    30
    31  // Этот код адаптирован из ESP-IDF v3.4 RMT "led_strip" пример, измененный
    32  // для работы с версией ESP-IDF для Arduino (3.2)
    33
    34  #define WS2812_T0H_NS (400)
    35  #define WS2812_T0L_NS (850)
    36  #define WS2812_T1H_NS (800)
    37  #define WS2812_T1L_NS (450)
    38
    39  #define WS2811_T0H_NS (500)
    40  #define WS2811_T0L_NS (2000)
    41  #define WS2811_T1H_NS (1200)
    42  #define WS2811_T1L_NS (1300)
    43
    44  static uint32_t t0h_ticks = 0;
    45  static uint32_t t1h_ticks = 0;
    46  static uint32_t t0l_ticks = 0;
    47  static uint32_t t1l_ticks = 0;
    48
    49  // Ограничить количество каналов RMT, доступных для Neopixels. По умолчанию для всех
    50  // каналов (8 на ESP32, 4 на ESP32-S2 и S3). Переопределение этого значения освободит
    51  // любые каналы с более высоким номером для других целей, таких как отправка и получение ИК
    52  // библиотеки. Переопределите как 1, чтобы ограничить Neopixels только одним каналом.
    53  #define ADAFRUIT_RMT_CHANNEL_MAX RMT_CHANNEL_MAX
    54
    55  #define RMT_LL_HW_BASE  (&RMT)
    56
    57  bool rmt_reserved_channels[ADAFRUIT_RMT_CHANNEL_MAX];
    58
    59  static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
    60          size_t wanted_num, size_t *translated_size, size_t *item_num)
    61  {
    62      if (src == NULL || dest == NULL) {
    63          *translated_size = 0;
    64          *item_num = 0;
    65          return;
    66      }
    67      const rmt_item32_t bit0 = {{{ t0h_ticks, 1, t0l_ticks, 0 }}}; // Логический 0
    68      const rmt_item32_t bit1 = {{{ t1h_ticks, 1, t1l_ticks, 0 }}}; // Логический 1
    69      size_t size = 0;
    70      size_t num = 0;
    71      uint8_t *psrc = (uint8_t *)src;
    72      rmt_item32_t *pdest = dest;
    73      while (size < src_size && num < wanted_num) {
    74          for (int i = 0; i < 8; i++) {
    75              // сначала старший бит
    76              if (*psrc & (1 << (7 - i))) {
    77                  pdest->val =  bit1.val;
    78              } else {
    79                  pdest->val =  bit0.val;
    80              }
    81              num++;
    82              pdest++;
    83          }
    84          size++;
    85          psrc++;
    86      }
    87      *translated_size = size;
    88      *item_num = num;
    89  }
    90
    91  void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz) {
    92      // Резервный канал
    93      rmt_channel_t channel = ADAFRUIT_RMT_CHANNEL_MAX;
    94      for (size_t i = 0; i < ADAFRUIT_RMT_CHANNEL_MAX; i++) {
    95          if (!rmt_reserved_channels[i]) {
    96              rmt_reserved_channels[i] = true;
    97              channel = i;
    98              break;
    99          }
   100      }
   101      if (channel == ADAFRUIT_RMT_CHANNEL_MAX) {
   102          // Закончились каналы!
   103          return;
   104      }
   105
   106  #if defined(HAS_ESP_IDF_4)
   107      rmt_config_t config = RMT_DEFAULT_CONFIG_TX(pin, channel);
   108      config.clk_div = 2;
   109  #else
   110      // Соответствует конфигурации TX по умолчанию из ESP-IDF версии 3.4
   111      rmt_config_t config = {
   112          .rmt_mode = RMT_MODE_TX,
   113          .channel = channel,
   114          .gpio_num = pin,
   115          .clk_div = 2,
   116          .mem_block_num = 1,
   117          .tx_config = {
   118              .carrier_freq_hz = 38000,
   119              .carrier_level = RMT_CARRIER_LEVEL_HIGH,
   120              .idle_level = RMT_IDLE_LEVEL_LOW,
   121              .carrier_duty_percent = 33,
   122              .carrier_en = false,
   123              .loop_en = false, 
   124              .idle_output_en = true,
   125          }
   126      };
   127  #endif
   128      rmt_config(&config);
   129      rmt_driver_install(config.channel, 0, 0);
   130
   131      // Преобразование таймингов NS в тики
   132      uint32_t counter_clk_hz = 0;
   133
   134  #if defined(HAS_ESP_IDF_4)
   135      rmt_get_counter_clock(channel, &counter_clk_hz);
   136  #else
   137      // это эмулирует функцию rmt_get_counter_clock() из ESP-IDF 3.4
   138      if (RMT_LL_HW_BASE->conf_ch[config.channel].conf1.ref_always_on == RMT_BASECLK_REF) {
   139          uint32_t div_cnt = RMT_LL_HW_BASE->conf_ch[config.channel].conf0.div_cnt;
   140          uint32_t div = div_cnt == 0 ? 256 : div_cnt;
   141          counter_clk_hz = REF_CLK_FREQ / (div);
   142      } else {
   143          uint32_t div_cnt = RMT_LL_HW_BASE->conf_ch[config.channel].conf0.div_cnt;
   144          uint32_t div = div_cnt == 0 ? 256 : div_cnt;
   145          counter_clk_hz = APB_CLK_FREQ / (div);
   146      }
   147  #endif
   148
   149      // Конвертер NS в тики
   150      float ratio = (float)counter_clk_hz / 1e9;
   151
   152      if (is800KHz) {
   153          t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
   154          t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
   155          t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
   156          t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
   157      } else {
   158          t0h_ticks = (uint32_t)(ratio * WS2811_T0H_NS);
   159          t0l_ticks = (uint32_t)(ratio * WS2811_T0L_NS);
   160          t1h_ticks = (uint32_t)(ratio * WS2811_T1H_NS);
   161          t1l_ticks = (uint32_t)(ratio * WS2811_T1L_NS);
   162      }
   163
   164      // Инициализировать автоматический преобразователь времени
   165      rmt_translator_init(config.channel, ws2812_rmt_adapter);
   166
   167      // Запись и ожидание завершения
   168      rmt_write_sample(config.channel, pixels, (size_t)numBytes, true);
   169      rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(100));
   170
   171      // Снова свободный канал
   172      rmt_driver_uninstall(config.channel);
   173      rmt_reserved_channels[channel] = false;
   174
   175      gpio_set_direction(pin, GPIO_MODE_OUTPUT);
   176  }
   177
   178  #endif

Трассировка стека выглядит следующим образом:

/Users/stephanedeluca/.espressif/tools/xtensa-esp32-elf/esp-2021r2-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-addr2line -pfiaC -e .pio/build/esp32dev/firmware.elf  0x40095618:0x3fff9ec0 0x40095891:0x3fff9ee0 0x40096cd6:0x3fff9f00 0x401e2d7d:0x3fff9f40 0x400dfa83:0x3fff9f70 0x400df96c:0x3fff9fd0 0x400dbdda:0x3fff9ff0 0x400dc0ce:0x3fffa040 0x400dcc3d:0x3fffa080 0x4009739e:0x3fffa0a0
0x40095618: invoke_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c:156
0x40095891: abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c:171
0x40096cd6: xQueueGenericSend at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:719 (discriminator 1)
0x401e2d7d: rmt_driver_install at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/driver/rmt.c:777
0x400dfa83: espShow at /Users/stephanedeluca/XXX/.pio/libdeps/esp32dev/Adafruit NeoPixel/esp 2.c:129
0x400df96c: Adafruit_NeoPixel::show() at /Users/stephanedeluca/XXX/.pio/libdeps/esp32dev/Adafruit NeoPixel/Adafruit_NeoPixel 2.cpp:2885
0x400dbdda: LedIndicator::showSocAndCurrent() at /Users/stephanedeluca/XXX/src/ledIndicator.cpp:648
0x400dc0ce: LedIndicator::loop() at /Users/stephanedeluca/XXX/src/ledIndicator.cpp:172 (discriminator 3)
0x400dcc3d: Module::_loop()::{lambda(void*)#1}::_FUN(void*) at /Users/stephanedeluca/XXX/src/module.cpp:256
 (inlined by) _FUN at /Users/stephanedeluca/XXX/src/module.cpp:264
0x4009739e: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:143

, 👍0

Обсуждение

Возможно, вам лучше поднять вопрос о github (я полагаю, что это где-то на github) с авторами библиотеки., @Majenko