Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LCD SPI with shared DC / MISO does not return MISO back to initial INPUT state (IDFGH-14740) #15478

Open
3 tasks done
jlmurdoch opened this issue Feb 26, 2025 · 3 comments
Open
3 tasks done
Labels
Status: Opened Issue is new Type: Bug bugs in IDF

Comments

@jlmurdoch
Copy link

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

IDF version.

v5.4

Espressif SoC revision.

ESP32S3

Operating System used.

Linux

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

M5Stack CoreS3 SE

Power Supply used.

USB

What is the expected behavior?

An SPI LCD operations using a shared DC/MISO line should allow other SPI devices sharing the same SPI host bus to regain control by converting the DC OUTPUT line back to an MISO INPUT when the bus is released.

What is the actual behavior?

The SPI LCD init initializes the D/C line as an OUTPUT and never restores the MISO line to an INPUT, causing other devices to receive no data from the SPI bus, resulting in timeouts or no data.

Steps to reproduce.

  1. Setup the SPI Bus:
    spi_bus_config_t bus_cfg = {
        .miso_io_num = PIN_NUM_MISO,
        .mosi_io_num = PIN_NUM_MOSI,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = LCD_IMG_SIZE,
    };
    ret = spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
    ESP_ERROR_CHECK(ret);
  1. Configure the SPI-based LCD:
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = PIN_NUM_MISO,
        .cs_gpio_num = PIN_NUM_LCD_CS,
        .pclk_hz = 10 * 1000 * 1000,
        .spi_mode = 0,
        .trans_queue_depth = 7,
        .lcd_cmd_bits = 8,
        .lcd_param_bits = 8,
    };
    
    ret = esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &io_handle);
    ESP_ERROR_CHECK(ret);

    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = -1, 
        .rgb_endian = LCD_RGB_ENDIAN_RGB,
        .bits_per_pixel = 16,
    };
    ret = esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle);
    ESP_ERROR_CHECK(ret);

    ret = esp_lcd_panel_reset(panel_handle);
    ESP_ERROR_CHECK(ret);
    vTaskDelay(150 / portTICK_PERIOD_MS);

    ret = esp_lcd_panel_init(panel_handle);
    ESP_ERROR_CHECK(ret);
  1. Initialize another SPI device:
    spi_device_interface_config_t spi_cfg = {
        .clock_speed_hz = 10000000,
        .mode = 0, 
        .spics_io_num = PIN_NUM_433_CS,
        .queue_size = 1,
        .command_bits = 8,
    };

    ret = spi_bus_add_device(SPI2_HOST, &spi_cfg, &spi_handle);
    ESP_ERROR_CHECK(ret);
  1. Run an SPI-LCD action:
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, 1));
  1. Attempt to run a basic SPI transaction on the other device (SX127x radio):
     spi_transaction_t t = {
        .flags = SPI_TRANS_USE_RXDATA,
        .cmd = 0x42,
        .length = 8,
        .rxlength = 8,
    };

    ret = spi_device_acquire_bus(spi_handle, portMAX_DELAY);
    assert(ret == ESP_OK);
    ret = spi_device_polling_transmit(spi_handle, &t);
    assert(ret == ESP_OK);
    spi_device_release_bus(spi_handle);
    
    printf("Result: %lx\n", *(uint32_t *)t.rx_data);
  1. See results such as:
Result: 0x0
  1. Remove Steps 2 & 4, disabling the SPI LCD, then we get:
Result: 0x12

Debug Logs.


More Information.

  • This is an M5Stack CoreS3 SE, so there an understanding there will be unavoidable GPIO / SPI contention and the DC / MISO sharing is an edge-case

  • I have tested the following modules which stop working when the LCD is initialized / used:

    • M5Stack W5500 POE Module - warnings events for SPI timeouts
    • M5Stack LoRa868 v1.1 - no results polling register 0x42
    • M5Stack LoRa433 v1.0 - no results polling register 0x42
  • Initialization of the DC / MISO line as an OUTPUT is done here:

    .mode = GPIO_MODE_OUTPUT,

  • No other signs of restoring the MISO to an INPUT is visible in esp_lcd_panel_io_spi.c, other than a gpio-based reset upon esp_lcd failure.

  • Adding a gpio_set_direction(PIN_NUM_MISO, GPIO_MODE_INPUT) after an LCD action makes the SPI devices work, but causes the LCD to stop functioning.

  • I have attempted to modify the SPI LCD callbacks, with mixed results (i.e. commands work, but longer color transfers fail). I probably need to perform a reset on the MISO line.

@jlmurdoch jlmurdoch added the Type: Bug bugs in IDF label Feb 26, 2025
@github-actions github-actions bot changed the title LCD SPI with shared DC / MISO does not return MISO back to initial INPUT state LCD SPI with shared DC / MISO does not return MISO back to initial INPUT state (IDFGH-14740) Feb 26, 2025
@espressif-bot espressif-bot added the Status: Opened Issue is new label Feb 26, 2025
@suda-morris
Copy link
Collaborator

Hi @jlmurdoch

Can you try if the following fix help?

diff --git a/components/esp_lcd/spi/esp_lcd_panel_io_spi.c b/components/esp_lcd/spi/esp_lcd_panel_io_spi.c
index 57948340294..70c299c5182 100644
--- a/components/esp_lcd/spi/esp_lcd_panel_io_spi.c
+++ b/components/esp_lcd/spi/esp_lcd_panel_io_spi.c
@@ -93,11 +93,9 @@ esp_err_t esp_lcd_new_panel_io_spi(esp_lcd_spi_bus_handle_t bus, const esp_lcd_p

     // if the DC line is not encoded into any spi transaction phase or it's not controlled by SPI peripheral
     if (io_config->dc_gpio_num >= 0) {
-        gpio_config_t io_conf = {
-            .mode = GPIO_MODE_OUTPUT,
-            .pin_bit_mask = 1ULL << io_config->dc_gpio_num,
-        };
-        ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for D/C line failed");
+        gpio_set_level(io_config->dc_gpio_num, 0);
+        gpio_func_sel(io_config->dc_gpio_num, PIN_FUNC_GPIO);
+        gpio_output_enable(io_config->dc_gpio_num);
     }

@jlmurdoch
Copy link
Author

Thank you @suda-morris ! - this is working for me (with minor include additions).

I tested with esp_fill_random() and esp_lcd_panel_draw_bitmap() to test drawing, not just commands.

I'll go off and test the W5500...

Before change:

LCD Panel: Change
SX1276 Data: 0x0

After change:

LCD Panel: Change
SX1276 Data: 0x12

Changes made to patch:

  • add include for esp_private/gpio.h for gpio_func_sel()
  • add include for hal/gpio_hal.h for #define PIN_FUNC_GPIO
--- components/esp_lcd/spi/esp_lcd_panel_io_spi.c	2025-02-28 18:08:10.258623844 +0000
+++ components/esp_lcd/spi/esp_lcd_panel_io_spi.c	2025-02-28 20:37:21.501463321 +0000
@@ -20,6 +20,8 @@
 #include "esp_log.h"
 #include "esp_check.h"
 #include "esp_lcd_common.h"
+#include "esp_private/gpio.h"
+#include "hal/gpio_hal.h"
 
 static const char *TAG = "lcd_panel.io.spi";
 
@@ -92,11 +94,9 @@
 
     // if the DC line is not encoded into any spi transaction phase or it's not controlled by SPI peripheral
     if (io_config->dc_gpio_num >= 0) {
-        gpio_config_t io_conf = {
-            .mode = GPIO_MODE_OUTPUT,
-            .pin_bit_mask = 1ULL << io_config->dc_gpio_num,
-        };
-        ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for D/C line failed");
+        gpio_set_level(io_config->dc_gpio_num, 0);
+        gpio_func_sel(io_config->dc_gpio_num, PIN_FUNC_GPIO);
+        gpio_output_enable(io_config->dc_gpio_num);
     }
 
     spi_panel_io->flags.dc_cmd_level = io_config->flags.dc_high_on_cmd;

@jlmurdoch
Copy link
Author

The W5500 Ethernet (esp_eth) over SPI is continuing to fail when the esp_eth timer tries to check the link. If I remove esp_lcd, it works. It may be that the half duplex SPI that the esp_lcd uses causing issues. Let me know if a separate GitHub issue should be raised.

Repeating error below:

LCD Panel: Change
SX1276 Data: 0x12
E (14711) w5500.mac: w5500_send_command(208): send command timeout
E (14711) w5500.mac: emac_w5500_start(376): issue OPEN command failed
E (14711) w5500.mac: emac_w5500_set_link(467): w5500 start failed
E (14711) esp_eth: eth_on_state_changed(131): ethernet mac set link failed
E (14721) w5500.phy: w5500_update_link_duplex_speed(88): change link failed
E (14731) w5500.phy: w5500_get_link(112): update link duplex speed failed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Opened Issue is new Type: Bug bugs in IDF
Projects
None yet
Development

No branches or pull requests

3 participants