Back to Fastled

ESP32 SPI Driver for FastLED

src/platforms/esp/32/drivers/spi/README.md

3.10.36.6 KB
Original Source

ESP32 SPI Driver for FastLED

This directory contains the ESP32-specific implementation of FastLED's SPI channel driver for LED control.

Quick Reference

Main Documentation: See src/platforms/README_SPI_ADVANCED.md for comprehensive SPI system architecture.

ESP32 Platform SPI Peripheral Availability

PlatformSPI0SPI1SPI2SPI3Available for LEDsFastLED Status
ESP32 (classic)Flash cacheFlash✅ General✅ General2 hosts (SPI2+SPI3)Enabled
ESP32-S2Flash cacheFlash✅ General✅ General2 hosts (SPI2+SPI3)Enabled
ESP32-S3Flash cacheFlash✅ General✅ General2 hosts (SPI2+SPI3)Enabled
ESP32-C3Flash cacheFlash✅ General❌ N/A1 host (SPI2)Enabled (limited)
ESP32-C6Flash cacheFlash✅ General❌ N/A1 host (SPI2)Disabled (RMT5 used)
ESP32-H2Flash cacheFlash✅ General❌ N/A1 host (SPI2)Disabled (RMT5 used)
ESP32-P4Flash cacheFlash✅ General✅ General2 hosts (SPI2+SPI3)Enabled + Octal

Key Facts

  1. SPI0/SPI1 are ALWAYS reserved for flash/PSRAM operations on all ESP32 variants
  2. SPI2/SPI3 are general-purpose SPI hosts available for peripherals like LEDs
  3. ESP-IDF explicitly prohibits using SPI0/SPI1 via spi_bus_initialize() API

ESP32-C6 and ESP32-H2 - Why No SPI Engine?

Answer: These chips DO have SPI2 available, but FastLED intentionally doesn't use it:

Reasons:

  • Limited scalability: Only 1 SPI host (vs 2-3 on other ESP32 chips)
  • Better alternative: RMT5 provides superior performance with nanosecond-precise timing
  • Preserves resources: Leaves SPI2 available for other user peripherals
  • Simplified architecture: RMT5-only chips use a single, optimized code path

Misconception: Earlier comments stated "ESP32-C6 does not have available SPI hosts (max 0 hosts)" - this was imprecise. The accurate statement is: "ESP32-C6 has 1 SPI host (SPI2), but RMT5 is the preferred driver for LED control."

File Overview

FilePurpose
channel_engine_spi.cppMain SPI channel driver implementation (900+ lines)
channel_engine_spi.hGeneric SPI channel interface (480 lines)
spi_hw_1_esp32.cppSingle-lane SPI hardware driver
spi_esp32_init.cppESP32 SPI initialization and configuration
spi_platform_esp32.cppPlatform-specific SPI utilities
spi_device_proxy.hTemplate proxy for routing SPI calls
idf5_clockless_spi_esp32.hESP-IDF 5.x SPI configuration

Architecture

User API (FastLED.addLeds<APA102, DATA, CLK>)
  ↓
ChannelEngineSpi (this directory)
  ↓
SPI Bus Manager (acquires SPI2/SPI3 hosts)
  ↓
ESP32 SPI Peripheral (hardware DMA)

Supported LED Chipsets

SPI-based LED chipsets that work with this driver:

  • APA102 (4-wire: Clock + Data)
  • SK9822 (APA102 compatible)
  • LPD8806 (3-wire SPI)
  • WS2801 (3-wire SPI)
  • P9813 (4-wire SPI)

Multi-Lane Support

Single-Lane SPI (Standard)

  • Uses MOSI (data) + CLK (clock)
  • 1 LED strip per SPI host
  • Platforms: All ESP32 variants

Dual-Lane SPI (2 parallel strips)

  • Uses MOSI + MISO (2 data lines) + CLK
  • 2 LED strips transmit simultaneously
  • Platforms: All ESP32 variants with SPI driver enabled

Quad-Lane SPI (4 parallel strips)

  • Uses MOSI + MISO + WP + HD (4 data lines) + CLK
  • 4 LED strips transmit simultaneously
  • Platforms: ESP32, ESP32-S2, ESP32-S3 only
  • NOT supported: ESP32-C3, ESP32-C6, ESP32-H2 (hardware limitation)

SPI Host Acquisition Priority

Code: channel_engine_spi.cpp:525-538

cpp
spi_host_device_t hosts[] = {
#ifdef SPI2_HOST
    SPI2_HOST,  // Priority 1: HSPI/SPI2
#endif
#ifdef SPI3_HOST
    SPI3_HOST,  // Priority 2: VSPI/SPI3
#endif
#ifdef SPI1_HOST
    SPI1_HOST,  // Priority 3: Flash SPI (avoid, last resort)
#endif
};

Note: SPI1 is included with lowest priority but is typically reserved for flash. In practice, ESP32 variants use SPI2 and SPI3 for LED control.

Configuration

Clock Speed

Dynamically calculated from chipset T1/T2/T3 timing via calculateSpiTiming(). For WS2812: ~6.67 MHz (8 SPI bits per 1250ns LED bit period).

Wave8 Encoding

  • Bit expansion: 8x (each LED bit → 8 SPI bits = 1 SPI byte)
  • Encoding: Wave8 LUT built from chipset timing at channel creation
  • Bit patterns (MSB first, for WS2812):
    • LED bit 011000000 (2 HIGH bits = short pulse ~300ns)
    • LED bit 111111000 (5 HIGH bits = long pulse ~750ns)
  • Timing: SPI clock = 1e9 / quantum_ns where quantum = GCD of T1, T2, T3
  • Implementation: See wave8_encoder_spi.h (LUT builder) and encodeChunk() in channel_engine_spi.cpp.hpp

Performance

ESP32 @ 240 MHz, 40 MHz SPI clock, 100 LEDs

ConfigurationTimeCPU UsageSpeedup
Software bit-bang2.0 ms100%1x baseline
Single-lane SPI2.0 ms0%~1x (DMA)
Dual-lane SPI (2 strips)1.0 ms0%2x
Quad-lane SPI (4 strips)0.5 ms0%4x

Key Advantage: DMA-driven transmission = 0% CPU usage during LED updates

Debugging

Enable debug output:

cpp
#define FASTLED_DEBUG 1
#include <FastLED.h>

Debug messages include:

  • SPI host acquisition
  • Bus initialization
  • DMA buffer allocation
  • Transmission timing

Known Limitations

  1. ESP32-C6/H2: SPI driver disabled (see explanation above)
  2. PARLIO on ESP32-C6: Clock gating bug in ESP-IDF ≤ v5.4.1 (may be fixed in future releases)
  3. SPI1 usage: Generally avoided due to flash conflict
  4. Quad-SPI on RISC-V: ESP32-C3/C6/H2 support dual-lane max (hardware limitation)
  • Main SPI Documentation: src/platforms/README_SPI_ADVANCED.md (comprehensive guide)
  • Channel Bus Manager: src/platforms/esp/32/drivers/channel_bus_manager_esp32.cpp
  • Platform Detection: src/platforms/esp/is_esp.h
  • Feature Flags: src/platforms/esp/32/feature_flags/enabled.h

References