A 2.8-inch Arduino-style TFT shield can place an LCD and a microSD socket on the same PCB while giving them completely different electrical interfaces. The pin labels are the quickest way to tell them apart.

On the shield shown here, the LCD side exposes DB0 through DB7 together with LCD_WR, LCD_RD, LCD_RS, LCD_CS, and LCD_RST. Those are the signals of an 8-bit 8080-style parallel display interface. The labels SD_MOSI, SD_MISO, SD_SCK, and SD_SS belong to the microSD socket.

The presence of those SD pins does not turn the LCD into an SPI display. Connecting only MOSI, MISO, SCK, and a chip-select pin can make the SD card accessible while leaving the TFT controller disconnected from the bus it expects.

Read the PCB as two buses

The shield reduces to two independent paths:

ESP32-C6
   |
   +-- LCD: DB0..DB7, WR, RS/DC, CS, RST, RD
   |
   +-- microSD: MOSI, MISO, SCK, SD_CS

The LCD path transfers one byte across eight data lines at a time. WR strobes writes into the display controller. RS, often called DC on other modules, distinguishes command bytes from data bytes. CS selects the LCD controller.

RD is used when the host reads from the LCD bus. A write-only design may not need LCD reads, but the signal still has to remain at the inactive electrical level required by the shield and controller rather than floating.

The microSD path is conventional SPI. Its serial signals have no relationship to LCD DB0..DB7 even though both devices occupy the same board.

ESP32-C6 uses PARLIO rather than native LCD I80

ESP32-C6 does not expose the native LCD I80 peripheral found on some other ESP32 variants. It does contain the Parallel IO peripheral, or PARLIO.

Current ESP-IDF provides an esp_lcd PARLIO panel-I/O driver that can generate an 8-bit I80-style interface. Espressif specifically lists ESP32-C6 among chips that can drive an I80 LCD by simulating its timing through PARLIO.

That creates a hardware-assisted path:

framebuffer / LVGL
       |
       v
LCD controller driver
       |
       v
esp_lcd panel IO
       |
       v
PARLIO, data_width = 8
       |
       +--> DB0..DB7
       +--> WR
       +--> RS/DC
       +--> CS

This is different from esp_lcd_new_i80_bus(). Code for a target with a native I80 LCD peripheral cannot be moved to ESP32-C6 merely by changing GPIO numbers. The transport layer has to use the PARLIO API supported by the target.

PARLIO maps directly to the shield signals

For an 8-bit display, the relevant esp_lcd_panel_io_parl_config_t fields have this shape:

#include "esp_lcd_io_parl.h"

esp_lcd_panel_io_parl_config_t io_config = {
    .dc_gpio_num = PIN_LCD_RS,
    .clk_gpio_num = PIN_LCD_WR,
    .cs_gpio_num = PIN_LCD_CS,
    .data_gpio_nums = {
        PIN_LCD_D0, PIN_LCD_D1, PIN_LCD_D2, PIN_LCD_D3,
        PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7,
    },
    .data_width = 8,
    .pclk_hz = LCD_WRITE_CLOCK_HZ,
    .lcd_cmd_bits = 8,
    .lcd_param_bits = 8,
};

Here, clk_gpio_num drives the write strobe called WR on an I80 display. The eight entries in data_gpio_nums map to DB0..DB7. LCD reset is normally handled by the panel configuration rather than the PARLIO bus itself.

The write clock must follow the display controller and board timing constraints. Copying a frequency from an unrelated SPI module is not valid: an SPI clock and an I80 write strobe are different interfaces.

The controller still has to be identified

A photograph of this shield layout is not enough to prove that its controller is ILI9341. Similar 2.8-inch shields have shipped with different LCD controller ICs, and some Arduino libraries identify the controller by reading registers at runtime.

The bus and controller are separate decisions:

8-bit I80-style electrical bus
            |
            v
controller-specific command set
            |
            v
LCD glass

PARLIO solves the first part. The panel driver solves the second.

If the board really contains an ILI9341, an ILI9341-compatible esp_lcd panel driver can sit above the PARLIO handle. Another controller may require different initialization commands, register handling, orientation settings, and pixel-format behavior despite identical-looking DB0..DB7, WR, and RS wiring.

This explains a common white-screen failure: the parallel bus toggles correctly, but the initialization sequence targets the wrong controller.

The microSD card remains an SPI device

The SD socket can use an ESP32-C6 SPI host independently of PARLIO:

ESP32-C6 SPI      TFT shield SD
-------------     -------------
MOSI          --> SD_MOSI
MISO          <-- SD_MISO
SCK           --> SD_SCK
GPIO          --> SD_SS / SD_CS

Display transfers can therefore run through esp_lcd and PARLIO while filesystem access uses the SD SPI stack. The LCD and SD card do not share those signal wires.

GPIO budgeting is a board-level constraint

An 8-bit parallel shield consumes many signals. A write-oriented LCD connection needs eight data GPIOs plus WR, RS/DC, CS, and usually RST. Routing RD adds another signal. The SD card then needs its SPI signals and a separate chip select.

The exact ESP32-C6 development board matters because some GPIOs may already be used or constrained by flash, USB, boot strapping, LEDs, or other peripherals. Build the pin map from the schematic for the actual board, not from a generic ESP32-C6 GPIO list.

If there are not enough suitable pins, replacing the shield with a TFT module that genuinely exposes the LCD controller’s SPI interface can be simpler than adapting an Arduino parallel shield. That is a hardware-interface change, not a library setting.

Check voltage at the shield boundary

Arduino-format pin names do not establish the voltage seen by every IC pin. Some shields contain regulators or level-shifting networks; others expose signals more directly.

ESP32-C6 uses 3.3 V power domains. Before connecting an Arduino-oriented shield, inspect its schematic or input circuitry to determine the permitted shield supply voltage, whether LCD inputs are level shifted, whether any shield output can exceed the ESP32-C6 GPIO limits, and what voltage the microSD circuitry uses.

A blanket instruction such as “connect shield VCC to 3.3 V” is therefore unsafe without identifying the exact shield revision. Supply voltage and GPIO logic voltage are separate properties.

Start the port at the bus boundary

A reliable port follows the hardware that is actually exposed:

  1. Identify the LCD signals: DB0..DB7, WR, RD, RS, CS, and RST.
  2. Treat SD_MOSI, SD_MISO, SD_SCK, and SD_SS as a separate SPI bus.
  3. Identify the LCD controller instead of assuming it from screen size.
  4. Verify the exact shield’s supply and logic-level circuitry.
  5. Allocate GPIOs from the actual ESP32-C6 development-board schematic.
  6. Use the ESP32-C6 PARLIO-backed esp_lcd path for the 8-bit I80-style display.
  7. Configure the SD card separately through SPI.

The key constraint is visible on the shield: eight LCD data lines mean the display is presenting a parallel bus. The four pins carrying SPI names belong to the SD card. Keeping those boundaries intact prevents plausible-looking SPI wiring from producing a permanently blank TFT.