FastLED 3.10.9
Loading...
Searching...
No Matches
slim_spi_bridge_controller.h
Go to the documentation of this file.
1
40
41#pragma once
42
43#include "fl/stl/noexcept.h"
45
47#include "fl/channels/data.h"
48#include "fl/channels/driver.h"
49#include "fl/channels/manager.h"
50#include "fl/channels/bus.h"
51#include "fl/channels/bus_traits.h" // IWYU pragma: keep (DriverTraits<Bus::X> satisfies the DriverTraits contract)
52#include "fl/channels/channel_typed.h" // IWYU pragma: keep (detail::resolve_bus, BusSupports)
53#include "fl/channels/config.h"
55#include "fl/log/log.h"
56
57namespace fl {
58#if defined(FL_IS_ESP32)
59namespace platforms {
60void registerNativeSpiDriver() FL_NO_EXCEPT;
61}
62#endif
63
78template <fl::SpiChipset CHIPSET, EOrder RGB_ORDER, fl::Bus B, fl::u8 B_WHICH>
80public:
84
85 // Compile-time contract, mirrors TypedChannel<B, SpiChipsetConfig, Which>.
88 "SlimSpiBridgeController: Bus does not support SpiChipsetConfig");
89
91#if defined(FL_IS_ESP32)
92 : mExplicitSpiHost(cfg.spiBus != Esp32SpiBus::AUTO)
93#endif
94 {
96#if defined(FL_IS_ESP32)
97 // The explicit-host addLeds() overload registers the native SPI
98 // driver itself; referencing it here would link the ESP-IDF SPI
99 // master stack (~20 KB) into every AUTO-host sketch.
100 if (mExplicitSpiHost) {
101 return;
102 }
103#endif
104 // Idempotent by contract -- safe to call from every instantiation's
105 // constructor (mirrors TypedChannel::create()'s registerWithManager() call).
107 }
108
109 void init() FL_NO_EXCEPT override {}
110
112 const ChannelDataPtr& channelData() const FL_NO_EXCEPT { return mData; }
113
114protected:
117 if (!driver) {
118 return;
119 }
120
121 // (A1) Respect runtime driver enable/disable: ChannelManager is the
122 // single source of truth. A disabled driver means "drop this frame",
123 // not "enqueue and hope" -- silently enqueuing to a disabled driver
124 // is the #2517 silent-drop failure mode.
125 if (!ChannelManager::registry().isDriverEnabled(*driver)) {
126 FL_WARN_ONCE("SlimSpiBridgeController: driver '" << driver->getName() << "' is disabled - dropping frame");
127 return;
128 }
129
130 // (A8) If the previous frame's buffer is still in flight, block
131 // (bounded) until the driver reports it can accept more work rather
132 // than racing the driver's in-progress transfer.
133 if (mData->isInUse()) {
134 if (!driver->waitForReady()) {
135 FL_WARN_ONCE("SlimSpiBridgeController: driver '" << driver->getName() << "' did not become ready in time - dropping frame");
136 return;
137 }
138 }
139
140 // (A5) Publish the pixel byte layout so the driver (and any
141 // downstream padding/diagnostic code) knows the encoded stride.
142 if (this->getRgbww().active()) {
143 mData->setPixelFormat(ChannelPixelFormat::RGBWW);
144 } else if (this->getRgbw().active()) {
145 mData->setPixelFormat(ChannelPixelFormat::RGBW);
146 } else {
147 mData->setPixelFormat(ChannelPixelFormat::RGB);
148 }
149
150 // Dither reseed mirrors Channel::encodeFrame(): only reseed when a
151 // non-zero dither mode is set.
152 if (pixels.ditherActive()) {
153 pixels.reseed_binary_dithering(mDitherPhase);
154 }
155
156 // (A3) Re-encode into the same buffer every frame -- clear() keeps
157 // capacity, so steady-state operation is allocation-free.
158 mData->getData().clear();
159 fl::PixelIterator it(&pixels, this->getRgbw(), this->getRgbww());
160 encode(it, mData->getData());
161
162 driver->enqueue(mData);
163 // Presented, per the driver contract: an accepted enqueue is the
164 // frame -- mirrors Channel::submitFrame()'s ++mDitherPhase placement
165 // (dither only advances when a frame is actually handed to the
166 // driver, not on every showPixels() attempt).
167 ++mDitherPhase;
168 }
169
170private:
178#if defined(FASTLED_DISABLE_DYNAMIC_DRIVER) && FASTLED_DISABLE_DYNAMIC_DRIVER
179 return {};
180#else
181#if defined(FL_IS_ESP32)
182 if (mExplicitSpiHost) {
183 // A requested SPI host cannot be honored by the LCD_CAM driver.
184 // Do not fall through to priority selection if the native adapter
185 // is unavailable.
186 return ChannelManager::registry().findDriverByName(
187 fl::string::from_literal("SPI_UNIFIED"));
188 }
189#endif
190 // Build busKey only when we actually need it (kBus != AUTO).
191 fl::string busKey;
192 if (kBus != Bus::AUTO) {
193 busKey = fl::string::from_literal(busDriverName(kBus, B_WHICH, /*spi=*/true));
194 }
195
197 ChannelManager::registry().selectDriverForChannel(mData, busKey);
198
199#if FASTLED_LOG_RUNTIME_ENABLED
200 if (!driver && !mBusWarned) {
201 FL_ERROR("SlimSpiBridgeController: no compatible driver found for bus '" << busKey << "' - cannot transmit");
202 mBusWarned = true;
203 }
204#endif // FASTLED_LOG_RUNTIME_ENABLED
205 return driver;
206#endif // !FASTLED_DISABLE_DYNAMIC_DRIVER
207 }
208
214 switch (CHIPSET) {
218 it.writeAPA102(&out, false);
219 break;
223 it.writeAPA102(&out, true);
224 break;
226 it.writeSK9822(&out, false);
227 break;
229 it.writeSK9822(&out, true);
230 break;
232 it.writeWS2801(&out);
233 break;
235 it.writeWS2803(&out);
236 break;
238 it.writeP9813(&out);
239 break;
241 it.writeLPD8806(&out);
242 break;
244 it.writeLPD6803(&out);
245 break;
247 it.writeSM16716(&out);
248 break;
250 it.writeHD108(&out, false);
251 break;
253 // Never routed here -- FastLED.h sends MY9221 to the bit-bang
254 // MY9221Controller (see file header comment). Defensive
255 // no-op if this controller is ever instantiated directly
256 // with CHIPSET == MY9221.
257 break;
258 }
259 }
260
261 ChannelDataPtr mData;
262#if defined(FL_IS_ESP32)
263 bool mExplicitSpiHost;
264#endif
266#if FASTLED_LOG_RUNTIME_ENABLED
267 bool mBusWarned = false;
268#endif
269};
270
271} // namespace fl
#define CHIPSET
Portable compile-time identifiers for LED channel buses.
Per-driver traits keyed on fl::Bus.
Phase 3b templated channel facade – Channel<Bus, Chipset> with compile-time bus/chipset enforcement.
CPixelLEDController(RegistrationMode mode) FL_NO_EXCEPT
Protected constructor with registration mode.
Rgbww getRgbww() const FL_NO_EXCEPT
Rgbw getRgbw() const FL_NO_EXCEPT
static ChannelDataPtr create(const ChipsetVariant &chipset, fl::vector_psram< u8 > &&encodedData=fl::vector_psram< u8 >(), ChannelPixelFormat pixelFormat=ChannelPixelFormat::RGB) FL_NO_EXCEPT
Create channel transmission data (modern variant-based API)
Definition data.cpp.hpp:12
static ChannelManager & registry() FL_NO_EXCEPT
Get the singleton WITHOUT registering platform default drivers.
SlimSpiBridgeController(const SpiChipsetConfig &cfg) FL_NO_EXCEPT
static void encode(fl::PixelIterator &it, fl::vector_psram< u8 > &out) FL_NO_EXCEPT
Encode it into out using the writer selected by CHIPSET.
void init() FL_NO_EXCEPT override
Initialize the LED controller.
static constexpr Bus kBus
The bus actually used after resolving Bus::AUTO – mirrors TypedChannel<B, SpiChipsetConfig,...
const ChannelDataPtr & channelData() const FL_NO_EXCEPT
Expose the underlying ChannelDataPtr for conformance tests.
FL_STATIC_ASSERT(BusSupports< kBus, SpiChipsetConfig, B_WHICH >::value, "SlimSpiBridgeController: Bus does not support SpiChipsetConfig")
void showPixels(PixelController< RGB_ORDER > &pixels) FL_NO_EXCEPT override
fl::shared_ptr< IChannelDriver > resolveDriver() FL_NO_EXCEPT
Resolve the driver for this frame.
static string from_literal(const char *literal) FL_NO_EXCEPT
defines the templated version of the CLEDController class
Channel transmission data - lightweight DTO for driver transmission.
Non-templated low level pixel data writing class.
#define FL_WARN_ONCE(...)
Definition log.h:439
#define FL_ERROR(...)
Definition log.h:373
Centralized logging categories for FastLED hardware interfaces and subsystems.
Unified manager for channel drivers with priority-based fallback.
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
const char * busDriverName(Bus b, fl::u8 which=0, bool spi=false) FL_NO_EXCEPT
Concrete ChannelManager driver name for a portable bus.
Definition bus.h:186
fl::variant< ClocklessChipset, SpiChipsetConfig > ChipsetVariant
Definition config.h:157
Bus
Portable public bus selector.
Definition bus.h:27
@ AUTO
Definition bus.h:28
@ SM16716
SM16716 LED chipset.
@ LPD6803
LPD6803 LED chipset.
@ APA102HD
APA102 LED chipset with 5-bit gamma correction.
@ HD107
Same as APA102, but in turbo 40-mhz mode.
@ DOTSTARHD
APA102HD LED chipset alias.
@ SK9822HD
SK9822 LED chipset with 5-bit gamma correction.
@ SK9822
SK9822 LED chipset.
@ DOTSTAR
APA102 LED chipset alias.
@ P9813
P9813 LED chipset.
@ APA102
APA102 LED chipset.
@ HD108
16-bit variant of HD107, always gamma corrected. No SD (standard definition) option available - all H...
@ LPD8806
LPD8806 LED chipset.
@ WS2803
WS2803 LED chipset.
@ MY9221
MY9221 12-channel LED driver (dual-edge clocked; 4 RGB LEDs per chip)
@ WS2801
WS2801 LED chipset.
@ HD107HD
Same as APA102HD, but in turbo 40-mhz mode.
Base definition for an LED controller.
Primary template — intentionally undefined.
Definition bus_traits.h:31
Portable compile-time assertion wrapper.
Pixel controller class.
SPI chipset configuration (data + clock pins)
Definition config.h:103
static constexpr Bus value