src/platforms/readme.md
Table of Contents
The src/platforms/ directory contains platform backends and integrations that FastLED targets. Each subfolder provides pin helpers, timing implementations, and controllers tailored to specific microcontrollers or host environments.
If you are targeting a desktop/browser host for demos or tests:
FastLED has evolved its platform directory to contain dispatch headers that follow a coarse-to-fine delegation pattern. These dispatch headers live at the root of src/platforms/ (e.g., int.h, io_arduino.h, quad_spi_platform.h) and route the compiler to the appropriate platform-specific implementations. This keeps platform routing clean and maintainable.
Architecture: Headers at platforms/header.h perform coarse platform detection (ESP32, AVR, ARM families) and delegate to platform-specific subdirectories for fine-grained detection (ESP32-S3, ATmega328P, SAMD51, etc.).
// platforms/header.h - Coarse detection
#if defined(FASTLED_TESTING)
#include "platforms/stub/platform_stub.h"
#elif defined(ESP32)
// Delegate to ESP-specific header for fine-grained variant detection
#include "platforms/esp/platform_esp.h"
#elif defined(__AVR__)
#include "platforms/avr/platform_avr.h"
#else
// Fallback
#endif
// platforms/esp/platform_esp.h - Fine-grained detection
#if defined(ESP32) || defined(CONFIG_IDF_TARGET_ESP32)
// ESP32 classic
#elif defined(ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S2)
// ESP32-S2
#elif defined(ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S3)
// ESP32-S3
#elif defined(ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C3)
// ESP32-C3
#elif defined(ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32P4)
// ESP32-P4
// ... etc
#endif
ESP32, __AVR__, FL_IS_ARM)ESP32S3, CONFIG_IDF_TARGET_ESP32C6) lives in platform subdirectoriesplatforms/int.h, platforms/audio.h for reference implementationsFastLED uses a unified hardware manager pattern for initializing SPI controllers across all platforms. This pattern centralizes hardware initialization, uses feature flags for conditional compilation, and implements priority-based registration for multi-lane controllers.
Single Entry Point: Each platform has one initSpiHardware() function in a platform-specific manager file:
platforms/esp/32/drivers/spi_hw_manager_esp32.cpp.hppplatforms/arm/stm32/spi_hw_manager_stm32.cpp.hppplatforms/arm/teensy/teensy4_common/spi_hw_manager_mxrt1062.cpp.hppplatforms/arm/rp/rpcommon/spi_hw_manager_rp.cpp.hppplatforms/arm/d21/spi_hw_manager_samd21.cpp.hppplatforms/arm/d51/spi_hw_manager_samd51.cpp.hppplatforms/arm/nrf52/spi_hw_manager_nrf52.cpp.hppplatforms/stub/spi_hw_manager_stub.cpp.hppDispatch Headers: Coarse-to-fine platform routing via platforms/init_spi_hw.h:
// Top-level: platforms/init_spi_hw.h
#if defined(FASTLED_TESTING)
#include "platforms/stub/init_spi_hw.h"
#elif defined(FL_IS_ESP)
#include "platforms/esp/init_spi_hw.h"
#elif defined(FL_IS_ARM)
#include "platforms/arm/init_spi_hw.h"
#endif
// Platform-level: platforms/esp/init_spi_hw.h
namespace fl {
namespace platform {
void initSpiHardware();
}
}
Each platform manager follows this structure (example from ESP32):
namespace fl {
namespace detail {
/// Priority constants (higher = preferred for routing)
constexpr int PRIORITY_HW_16 = 9; // Highest (16-lane I2S)
constexpr int PRIORITY_HW_8 = 8;
constexpr int PRIORITY_HW_4 = 7;
constexpr int PRIORITY_HW_2 = 6;
constexpr int PRIORITY_HW_1 = 5; // Lowest (single-lane)
/// Helper function pattern with feature flags
static void addSpiHw16IfPossible() {
#if FASTLED_ESP32_HAS_I2S
// Include concrete implementation
#include "platforms/esp/32/drivers/i2s/spi_hw_i2s_esp32.cpp.hpp"
// Create and register instances
static auto i2s0 = fl::make_shared<SpiHwI2SESP32>(0);
SpiHw16::registerInstance(i2s0, PRIORITY_HW_16);
FL_DBG("ESP32: Added I2S SpiHw16 controller");
#else
// No-op if feature not available
#endif
}
} // namespace detail
namespace platform {
/// Unified initialization entry point
void initSpiHardware() {
FL_DBG("ESP32: Initializing SPI hardware");
// Register in priority order (highest to lowest)
detail::addSpiHw16IfPossible(); // Priority 9
detail::addSpiHw8IfPossible(); // Priority 8
detail::addSpiHw4IfPossible(); // Priority 7
detail::addSpiHw2IfPossible(); // Priority 6
detail::addSpiHw1IfPossible(); // Priority 5
FL_DBG("ESP32: SPI hardware initialized");
}
} // namespace platform
} // namespace fl
SpiHwN::getAll()PLATFORM_HAS_* macrosfl::shared_ptr| Platform | SpiHw1 | SpiHw2 | SpiHw4 | SpiHw8 | SpiHw16 | Hardware |
|---|---|---|---|---|---|---|
| ESP32 classic | ✅ | ✅ | ✅ | ✅ | ✅ | I2S LCD_CAM (16-lane) |
| ESP32-S3 | ✅ | ✅ | ✅ | ✅ | ❌ | SPI peripheral |
| STM32 F2/F4/F7/H7/L4 | ✅ | ✅ | ✅ | ✅ | ❌ | Timer+DMA (stream-based) |
| Teensy 4.x | ✅ | ✅ | ✅ | ❌ | ❌ | LPSPI WIDTH field |
| RP2040/RP2350 | ✅ | ✅ | ✅ | ✅ | ❌ | PIO state machines |
| SAMD21 | ✅ | ✅ | ❌ | ❌ | ❌ | SERCOM + DMA |
| SAMD51 | ✅ | ✅ | ✅ | ❌ | ❌ | SERCOM + DMA |
| nRF52 | ✅ | ✅ | ✅ | ❌ | ❌ | Timer/PPI |
See shared/SPI_MANAGER_PATTERN.md for a detailed implementation guide and template code for adding SPI hardware support to a new platform
FastLED provides two primary controller categories. Choose based on your LED chipset and platform capabilities.
show()Special cases:
show()FastLED.show() or select a hardware‑assisted backend.FASTLED_USE_PROGMEM=0); on AVR it is enabled.Clockless controllers generate the one‑wire NRZ waveforms used by WS281x‑class LEDs by toggling a GPIO with precise timings. FastLED’s clockless base controllers accept three timing parameters per bit: T1, T2, T3.
The timing model is:
Mapping to datasheet values:
Where duration is the max of the two bit periods: max(T0H + T0L, T1H + T1L).
Embedded helper script (from src/chipsets.h) to derive T1/T2/T3 from datasheet T0H/T0L/T1H/T1L:
print("Enter the values of T0H, T0L, T1H, T1L, in nanoseconds: ")
T0H = int(input(" T0H: "))
T0L = int(input(" T0L: "))
T1H = int(input(" T1H: "))
T1L = int(input(" T1L: "))
duration = max(T0H + T0L, T1H + T1L)
print("The max duration of the signal is: ", duration)
T1 = T0H
T2 = T1H
T3 = duration - T0H - T0L
print("T1: ", T1)
print("T2: ", T2)
print("T3: ", T3)
Notes:
ClocklessController implementation for the required units and conversion helpers.“Multilane” or “block clockless” controllers send several strips in parallel by transposing pixel data into per‑bit planes and toggling multiple pins simultaneously.
Supported in this codebase:
Typically single‑lane only in this codebase:
For Teensy, also consider OctoWS2811 for DMA‑driven parallel output.
SPI‑driven LEDs use a clocked data line plus a clock line. Implementation is generally simpler and more ISR‑tolerant than clockless.
FastLED’s SPIOutput abstracts both; platform headers select efficient implementations where available.
When choosing SPI speed, consult chipset and wiring limits (signal integrity on long runs). Long APA102 runs may require reduced clock rates.
Some platforms store constant tables and palettes in flash (program) memory rather than RAM. FastLED abstracts PROGMEM usage via macros; support varies by platform.
FASTLED_USE_PROGMEM=1. Flash is separate from RAM; use pgm_read_ accessors for reads.FL_PROGMEM, FL_PGM_READ_*, and FL_ALIGN_PROGMEM (4‑byte alignment) helpers.FASTLED_USE_PROGMEM=0 on these families to avoid unnecessary indirection.Aligned reads for PROGMEM:
FL_ALIGN_PROGMEM for this purpose.Platforms without PROGMEM (or where it’s a no‑op in this codebase):
FASTLED_USE_PROGMEM=0.Check each platform’s led_sysdefs_* header for the recommended PROGMEM and interrupt policy.
These are commonly available across multiple platforms. Pass them as build defines (e.g., build_flags in PlatformIO), and define them prior to including FastLED.h.
FASTLED_USE_PROGMEM — Control PROGMEM usage (enabled on AVR, typically disabled elsewhere)FASTLED_ALLOW_INTERRUPTS — Allow interrupts during show() (platform defaults vary)FASTLED_INTERRUPT_RETRY_COUNT — Global retry count when timing is disruptedFASTLED_DEBUG_COUNT_FRAME_RETRIES — Enable counters/logging of frame retries due to timing issuesFor platform‑specific feature defines (e.g., ESP32 RMT/I2S knobs, RP2040 PIO selection, Teensy/STM32 options), see the README in that platform’s directory: