10#include "platforms/shared/spi_hw_1.h"
21 const char* adapterName
24 if (hwControllers.empty()) {
25 FL_WARN(
"SpiChannelEngineAdapter::create: No controllers provided");
29 if (hwControllers.size() != priorities.size() || hwControllers.size() != names.size()) {
30 FL_WARN(
"SpiChannelEngineAdapter::create: Size mismatch in arguments");
34 if (!adapterName || adapterName[0] ==
'\0') {
35 FL_WARN(
"SpiChannelEngineAdapter::create: Empty adapter name");
43 for (
size_t i = 0; i < hwControllers.size(); i++) {
44 if (!hwControllers[i]) {
45 FL_WARN(
"SpiChannelEngineAdapter: Null controller at index " << i);
49 adapter->mControllers.emplace_back(hwControllers[i], priorities[i], names[i]);
51 FL_DBG(
"SpiChannelEngineAdapter: Registered controller '" << names[i] <<
"' (priority " << priorities[i] <<
", lanes: " << (
static_cast<int>(hwControllers[i]->getLaneCount())) <<
")");
54 if (adapter->mControllers.empty()) {
55 FL_WARN(
"SpiChannelEngineAdapter: No valid controllers registered");
69 FL_DBG(
"SpiChannelEngineAdapter: Destructor for '" <<
mName <<
"'");
76 while (
poll() != DriverState::READY) {
83 if (ctrl.controller && ctrl.controller->isInitialized()) {
84 ctrl.controller->end();
92 if (ctrl.priority > maxPriority) {
93 maxPriority = ctrl.priority;
102 if (assignment.clockPin == clockPin && assignment.requestedBus == requestedBus) {
103 return assignment.controllerIndex;
109 int bestPriority = -1;
127 if (bestIndex >= 0) {
138 if (!ctrl.isInitialized) {
143 for (
int assignedPin : ctrl.assignedClockPins) {
144 if (assignedPin == clockPin) {
156 if (ctrl.isInitialized) {
158 for (
int pin : ctrl.assignedClockPins) {
159 if (pin == clockPin) {
163 FL_WARN(
"SpiChannelEngineAdapter: Controller " << ctrl.name <<
" already initialized with different clock pin");
168 u8 lanes = ctrl.controller->getLaneCount();
171 SpiHw1::Config config;
172 config.bus_num =
static_cast<fl::u8>(ctrl.controller->getBusId());
173 config.clock_pin = clockPin;
174 config.data_pin = dataPin;
175 config.clock_speed_hz = 20000000;
176 config.max_transfer_sz = 65536;
178 if (!ctrl.controller->begin(&config)) {
179 FL_WARN(
"SpiChannelEngineAdapter: Failed to initialize " << ctrl.name);
184 FL_WARN(
"SpiChannelEngineAdapter: Multi-lane init not yet implemented");
188 ctrl.isInitialized =
true;
189 ctrl.assignedClockPins.push_back(clockPin);
191 FL_DBG(
"SpiChannelEngineAdapter: Initialized " << ctrl.name <<
" with clock pin " << clockPin);
210 return data->isSpi();
215 FL_WARN(
"SpiChannelEngineAdapter: Null channel data passed to enqueue()");
221 FL_WARN(
"SpiChannelEngineAdapter: Cannot handle non-SPI channel data (chipset mismatch)");
244 FL_DBG(
"SpiChannelEngineAdapter: Grouped into " << groups.size() <<
" clock pin groups");
247 for (
size_t i = 0; i < groups.size(); i++) {
250 FL_DBG(
"SpiChannelEngineAdapter: Transmitting group with clock pin " << group.
clockPin <<
" (" << group.
channels.
size() <<
" channels)");
253 FL_WARN(
"SpiChannelEngineAdapter: Failed to transmit batch for clock pin " << group.
clockPin);
258 FL_DBG(
"SpiChannelEngineAdapter: show() complete");
263 bool anyBusy =
false;
265 if (ctrl.controller && ctrl.controller->isBusy()) {
272 return DriverState::BUSY;
281 channel->setInUse(
false);
289 return DriverState::DRAINING;
292 return DriverState::READY;
300 for (
const auto& channel :
channels) {
306 const auto& chipset = channel->getChipset();
308 FL_WARN(
"SpiChannelEngineAdapter: Non-SPI chipset in groupByClockPin");
318 if (group.clockPin == clockPin && group.requestedBus == spiConfig.
spiBus) {
319 existingGroup = &group;
332 groups.push_back(newGroup);
345 const auto& chipset =
channels[0]->getChipset();
347 FL_WARN(
"SpiChannelEngineAdapter: Non-SPI chipset in transmitBatch");
353 int dataPin =
channels[0]->getPin();
357 if (controllerIndex < 0) {
358 FL_WARN(
"SpiChannelEngineAdapter: No available controller for clock pin " << clockPin);
370 for (
const auto& channel :
channels) {
376 channel->setInUse(
true);
379 const auto& data = channel->getData();
381 FL_WARN(
"SpiChannelEngineAdapter: Empty channel data");
382 channel->setInUse(
false);
386 FL_DBG(
"SpiChannelEngineAdapter: Transmitting channel via " << ctrl.
name <<
" (pin " << channel->getPin() <<
", " << data.
size() <<
" bytes)");
389 DMABuffer dmaBuffer = ctrl.
controller->acquireDMABuffer(data.size());
390 if (!dmaBuffer.ok()) {
391 FL_WARN(
"SpiChannelEngineAdapter: Failed to acquire DMA buffer (error " << (
static_cast<int>(dmaBuffer.error())) <<
")");
392 channel->setInUse(
false);
398 if (buffer.
size() < data.size()) {
399 FL_WARN(
"SpiChannelEngineAdapter: DMA buffer too small (" << buffer.
size() <<
" < " << data.size() <<
")");
400 channel->setInUse(
false);
407 if (!ctrl.
controller->transmit(TransmitMode::ASYNC)) {
408 FL_WARN(
"SpiChannelEngineAdapter: Transmission failed");
409 channel->setInUse(
false);
413 FL_DBG(
"SpiChannelEngineAdapter: Transmission queued successfully");
419 FL_WARN(
"SpiChannelEngineAdapter: Transmission timeout");
423 FL_DBG(
"SpiChannelEngineAdapter: Batch transmission complete");
CLEDController * controller
bool transmitBatch(fl::span< const ChannelDataPtr > channels) FL_NO_EXCEPT
Transmit a batch of channels (all same clock pin)
fl::vector< ClockPinGroup > groupByClockPin(fl::span< const ChannelDataPtr > channels) FL_NO_EXCEPT
Group channels by clock pin for efficient transmission.
void enqueue(ChannelDataPtr channelData) FL_NO_EXCEPT override
Enqueue channel data for transmission.
fl::string mName
Engine name for debugging.
int getPriority() const FL_NO_EXCEPT
Get maximum priority among all controllers.
void show() FL_NO_EXCEPT override
Trigger transmission of enqueued data.
bool canHandle(const ChannelDataPtr &data) const FL_NO_EXCEPT override
Check if adapter can handle channel data.
fl::vector< ControllerInfo > mControllers
All managed controllers.
static fl::shared_ptr< SpiChannelEngineAdapter > create(fl::vector< fl::shared_ptr< SpiHwBase > > hwControllers, fl::vector< int > priorities, fl::vector< const char * > names, const char *adapterName) FL_NO_EXCEPT
Create unified adapter managing multiple controllers.
int selectControllerForClockPin(int clockPin, Esp32SpiBus requestedBus) FL_NO_EXCEPT
Select best controller for a given clock pin.
SpiChannelEngineAdapter(const char *name) FL_NO_EXCEPT
Private constructor - use create() factory method.
bool initializeControllerIfNeeded(ControllerInfo &ctrl, int clockPin, int dataPin) FL_NO_EXCEPT
Initialize controller if needed for this clock pin.
bool canControllerHandleClockPin(const ControllerInfo &ctrl, int clockPin) const FL_NO_EXCEPT
Check if controller can handle this clock pin.
DriverState poll() FL_NO_EXCEPT override
Query driver state and perform maintenance.
~SpiChannelEngineAdapter() FL_NO_EXCEPT override
Destructor.
fl::vector< ChannelDataPtr > mEnqueuedChannels
Channels waiting for show()
fl::vector< ClockPinAssignment > mClockPinAssignments
Clock pin → controller mapping.
fl::vector< ChannelDataPtr > mTransmittingChannels
Channels currently transmitting.
fl::vector< ChannelDataPtr > channels
Group data structure for channels with same clock pin.
fl::size size() const FL_NO_EXCEPT
const T * data() const FL_NO_EXCEPT
constexpr fl::size size() const FL_NO_EXCEPT
fl::size size() const FL_NO_EXCEPT
void push_back(const T &value) FL_NO_EXCEPT
Centralized logging categories for FastLED hardware interfaces and subsystems.
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
Esp32SpiBus
ESP-IDF SPI host selection for an individual clocked LED controller.
u8 u8 fract8 blur_amount FL_NO_EXCEPT
FASTLED_FORCE_INLINE void * memcpy(void *dest, const void *src, size_t n) FL_NO_EXCEPT
Base definition for an LED controller.
Adapter that wraps HW SPI controllers for ChannelManager.
Driver state with optional error message.
fl::shared_ptr< SpiHwBase > controller
Hardware instance.
fl::string name
Name (e.g., "SPI2", "SPI3", "I2S0")
Information about a registered SPI hardware controller.
Esp32SpiBus spiBus
ESP-IDF host override, or AUTO.
int clockPin
GPIO clock pin (SCK)
SPI chipset configuration (data + clock pins)