espressif / idf-extra-components

Additional components for ESP-IDF, maintained by Espressif
149 stars 89 forks source link

Issue with WS2812B GPIO Reinitialization on ESP32-S3 (IEC-134) #351

Closed SaberTec-Code closed 2 months ago

SaberTec-Code commented 3 months ago

Answers checklist.

General issue report

After initializing the GPIO to drive a WS2812B LED strip, the LED strip works fine. However, in our setup, we need to change the GPIO mode to input to make a voltage measurement before restoring the GPIO functionality for the LED strip again. This reinitialization fails, as attempting to restore the GPIO to output mode yields incorrect behavior of the LED strip.

Code for Changing GPIO Mode to Input:

#include "driver/gpio.h"

void set_gpio_to_input(gpio_num_t gpio_num) {
    // Enable pull-up on the specified GPIO pin
    gpio_pullup_en(gpio_num);

    // Set the specified GPIO pin to input mode
    gpio_set_direction(gpio_num, GPIO_MODE_INPUT);
}

Code for Restoring GPIO to LED Strip Functionality:

#include "driver/gpio.h"
#include "soc/rmt_reg.h"
#include "esp32s3/rom/gpio.h"
#include "hal/gpio_hal.h"
#include "soc/gpio_sig_map.h"
#include "soc/rmt_periph.h"

void restore_neopixel_pin_mode(gpio_num_t gpio_num, int channel, bool invert_out) {
    // Disable pull-down and enable pull-up
    gpio_pulldown_dis(gpio_num);
    gpio_pullup_en(gpio_num);

    // Disable GPIO interrupt
    gpio_intr_disable(gpio_num);

    // Set GPIO to output mode
    gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);

    // Set the drive capability of the pin
    gpio_set_drive_capability(gpio_num, GPIO_DRIVE_CAP_3);

    // Connect GPIO to RMT signal
    int group_id = channel / SOC_RMT_CHANNELS_PER_GROUP;
    int channel_id = channel % SOC_RMT_CHANNELS_PER_GROUP;
    int sig_idx = rmt_periph_signals.groups[group_id].channels[channel_id].tx_sig;
    esp_rom_gpio_connect_out_signal(gpio_num, sig_idx, invert_out, false);

    // Select the GPIO function
    gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
}

Troubleshooting Steps Taken:

Despite these efforts, the issue persists, suggesting that some state or configuration might still be missing or incorrect during the reinitialization.

Request: We would appreciate any insights or suggestions to resolve this issue. Specifically:

Thank you in advance, Frederic.

SaberTec-Code commented 3 months ago

@suda-morris Do you have any idea maybe? Any help would be greatly appreciated since we're not able to figure it out.

Kainarx commented 2 months ago

@SaberTec-Code You mentioned voltage measurement , are you using the ADC function or just reading the levels of the GPIO pin. And which version of idf are you using? Can you provide more information?

SaberTec-Code commented 2 months ago

@Kainarx Thanks for getting back to me. To clarify, I only need to read the level of the GPIO pin. The process is the following:

  1. Send data to the LED strip.
  2. After the data transfer is completed, read the GPIO level.
  3. Reconfigure the GPIO for the LED strip functionality.
  4. Repeat this process.

We're using the ESP-IDF v5.1. Do you need any additional information?

Kainarx commented 2 months ago

I simply verified that it works fine. The test code is below(use led_strip_rmt_ws2812 example), you can try it with a jumper to connect your led_strip GPIO to the test GPIO. Please let me know if it doesn't work or if you have a special configuration or operation. @SaberTec-Code


#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "led_strip.h"
#include "esp_log.h"
#include "esp_err.h"
#include "driver/gpio.h"
#include "soc/rmt_reg.h"
#include "esp32s3/rom/gpio.h"
#include "hal/gpio_hal.h"
#include "soc/gpio_sig_map.h"
#include "soc/rmt_periph.h"

// GPIO assignment
#define LED_STRIP_BLINK_GPIO  38
// Numbers of the LED in the strip
#define LED_STRIP_LED_NUMBERS 1
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
#define LED_STRIP_RMT_RES_HZ  (10 * 1000 * 1000)

#define DETECT_GPIO  39

static const char *TAG = "example";

led_strip_handle_t configure_led(void)
{
    // LED strip general initialization, according to your led board design
    led_strip_config_t strip_config = {
        .strip_gpio_num = LED_STRIP_BLINK_GPIO,   // The GPIO that connected to the LED strip's data line
        .max_leds = LED_STRIP_LED_NUMBERS,        // The number of LEDs in the strip,
        .led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
        .led_model = LED_MODEL_WS2812,            // LED strip model
        .flags.invert_out = false,                // whether to invert the output signal
    };

    // LED strip backend configuration: RMT
    led_strip_rmt_config_t rmt_config = {
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
        .rmt_channel = 0,
#else
        .clk_src = RMT_CLK_SRC_DEFAULT,        // different clock source can lead to different power consumption
        .resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
        .flags.with_dma = false,               // DMA feature is available on ESP target like ESP32-S3
#endif
    };

    // LED Strip object handle
    led_strip_handle_t led_strip;
    ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
    ESP_LOGI(TAG, "Created LED strip object with RMT backend");
    return led_strip;
}

void set_gpio_to_input(gpio_num_t gpio_num) {
    // Enable pull-up on the specified GPIO pin
    gpio_pullup_en(gpio_num);

    // Set the specified GPIO pin to input mode
    gpio_set_direction(gpio_num, GPIO_MODE_INPUT);
}

void restore_neopixel_pin_mode(gpio_num_t gpio_num, int channel, bool invert_out) {
    // Disable pull-down and enable pull-up
    gpio_pulldown_dis(gpio_num);
    gpio_pullup_en(gpio_num);

    // Disable GPIO interrupt
    gpio_intr_disable(gpio_num);

    // Set GPIO to output mode
    gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);

    // Set the drive capability of the pin
    gpio_set_drive_capability(gpio_num, GPIO_DRIVE_CAP_3);

    // Connect GPIO to RMT signal
    int group_id = channel / SOC_RMT_CHANNELS_PER_GROUP;
    int channel_id = channel % SOC_RMT_CHANNELS_PER_GROUP;
    int sig_idx = rmt_periph_signals.groups[group_id].channels[channel_id].tx_sig;
    esp_rom_gpio_connect_out_signal(gpio_num, sig_idx, invert_out, false);

    // Select the GPIO function
    gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
}

void app_main(void)
{
    led_strip_handle_t led_strip = configure_led();

    gpio_reset_pin(DETECT_GPIO);
    /* Set the GPIO as a push/pull output */
    gpio_set_direction(DETECT_GPIO, GPIO_MODE_OUTPUT);

    ESP_LOGI(TAG, "Start blinking LED strip");

    /* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
    for (int i = 0; i < LED_STRIP_LED_NUMBERS; i++) {
        ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
    }
    /* Refresh the strip to send data */
    ESP_ERROR_CHECK(led_strip_refresh(led_strip));
    ESP_LOGI(TAG, "LED ON!");
    vTaskDelay(pdMS_TO_TICKS(50));
    ESP_LOGI(TAG, "set input!");

    // Connect the two gpios with jumper cable
    vTaskDelay(pdMS_TO_TICKS(500));
    set_gpio_to_input(LED_STRIP_BLINK_GPIO);
    gpio_set_level(DETECT_GPIO, 1);
    ESP_LOGI(TAG, "detect state%d!", gpio_get_level(LED_STRIP_BLINK_GPIO));
    gpio_set_level(DETECT_GPIO, 0);
    ESP_LOGI(TAG, "detect state%d!", gpio_get_level(LED_STRIP_BLINK_GPIO));
    restore_neopixel_pin_mode(LED_STRIP_BLINK_GPIO,0,0);

    ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, 0, 50, 0, 5));
    ESP_ERROR_CHECK(led_strip_refresh(led_strip));

    vTaskDelay(pdMS_TO_TICKS(500));
    set_gpio_to_input(LED_STRIP_BLINK_GPIO);
    gpio_set_level(DETECT_GPIO, 1);
    ESP_LOGI(TAG, "detect state%d!", gpio_get_level(LED_STRIP_BLINK_GPIO));
    gpio_set_level(DETECT_GPIO, 0);
    ESP_LOGI(TAG, "detect state%d!", gpio_get_level(LED_STRIP_BLINK_GPIO));
    restore_neopixel_pin_mode(LED_STRIP_BLINK_GPIO,0,0);

    ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, 0, 5, 0, 50));
    ESP_ERROR_CHECK(led_strip_refresh(led_strip));

    vTaskDelay(pdMS_TO_TICKS(500));

}
SaberTec-Code commented 2 months ago

Thank you very much for your answer! We were able to get it working. It turned out that the value of channel_id was incorrect.