7#include "platforms/shared/spi_manager.h"
8#include "platforms/shared/spi_hw_1.h"
9#include "platforms/shared/spi_hw_2.h"
10#include "platforms/shared/spi_hw_4.h"
30 FL_LOG_SPI(
"SPI Device: Calling end() from destructor");
34 FL_LOG_SPI(
"SPI Device: Checking owned hardware backend");
38 FL_LOG_SPI(
"SPI Device: Cleaning up owned hardware backend");
39 pImpl->hw_backend->end();
40 pImpl->hw_backend =
nullptr;
50 if (
pImpl->initialized) {
56 if (
pImpl->config.spi_mode > 3) {
57 FL_WARN(
"SPI Device: Invalid SPI mode " <<
pImpl->config.spi_mode <<
" (must be 0-3)");
63 if (
pImpl->config.spi_mode != 0) {
64 FL_WARN(
"SPI Device: SPI mode " <<
pImpl->config.spi_mode
65 <<
" requested but hardware layer only supports mode 0 - ignoring");
69 SPIBusManager& mgr = getSPIBusManager();
70 int data_pin =
pImpl->config.data_pins.empty() ? -1 :
pImpl->config.data_pins[0];
71 pImpl->bus_handle = mgr.registerDevice(
72 pImpl->config.clock_pin,
74 pImpl->config.clock_speed_hz,
78 if (!
pImpl->bus_handle.is_valid) {
79 FL_WARN(
"SPI Device: Failed to register with bus manager");
84 if (!mgr.initialize()) {
85 FL_WARN(
"SPI Device: Bus initialization failed");
90 const SPIBusInfo* bus_info = mgr.getBusInfo(
pImpl->bus_handle.bus_id);
91 if (bus_info && bus_info->bus_type == SPIBusType::SINGLE_SPI && !bus_info->hw_controller) {
94 if (controllers.
empty()) {
95 FL_WARN(
"SPI Device: No SpiHw1 controllers available on this platform");
102 SpiHw1::Config hw_config;
103 hw_config.clock_pin =
pImpl->config.clock_pin;
104 hw_config.data_pin =
pImpl->config.data_pins.empty() ? -1 :
pImpl->config.data_pins[0];
105 hw_config.clock_speed_hz =
pImpl->config.clock_speed_hz;
106 hw_config.bus_num = 0;
108 if (!hw->begin(hw_config)) {
109 FL_WARN(
"SPI Device: Failed to initialize SpiHw1 controller");
113 pImpl->hw_backend = hw;
114 pImpl->owns_backend =
false;
115 FL_LOG_SPI(
"SPI Device: Created SpiHw1 controller for SINGLE_SPI mode");
118 pImpl->hw_backend = bus_info ? bus_info->hw_controller :
nullptr;
119 pImpl->owns_backend =
false;
122 pImpl->initialized =
true;
123 FL_LOG_SPI(
"SPI Device: Initialized successfully");
139 pImpl->hw_backend =
nullptr;
142 if (
pImpl->bus_handle.is_valid) {
143 SPIBusManager& mgr = getSPIBusManager();
144 mgr.unregisterDevice(
pImpl->bus_handle);
145 pImpl->bus_handle = SPIBusHandle();
148 pImpl->initialized =
false;
189 if (!data || size == 0) {
194 if (
pImpl->async_state.active) {
201 FL_WARN(
"SPI Device: Failed to acquire DMA buffer for async write");
207 if (buf_span.
size() < size) {
208 FL_WARN(
"SPI Device: Buffer size mismatch");
212 for (
size_t i = 0; i < size; i++) {
213 buf_span[i] = data[i];
219 FL_WARN(
"SPI Device: Failed to start async transmission");
224 pImpl->async_state.active =
true;
225 pImpl->async_state.tx_buffer = data;
226 pImpl->async_state.rx_buffer =
nullptr;
227 pImpl->async_state.size = size;
233 txn.
pImpl->completed =
false;
235 FL_LOG_SPI(
"SPI Device: Async write started (" << size <<
" bytes)");
261 return DMABuffer(SPIError::NOT_INITIALIZED);
265 if (!
pImpl->hw_backend) {
266 FL_WARN(
"SPI Device: No hardware controller available");
267 return DMABuffer(SPIError::NOT_INITIALIZED);
271 SpiHwBase* hw =
pImpl->hw_backend.get();
275 DMABuffer buffer = hw->acquireDMABuffer(size);
278 FL_WARN(
"SPI Device: Failed to acquire DMA buffer from hardware");
280 FL_LOG_SPI(
"SPI Device: Acquired DMA buffer (" << size <<
" bytes)");
296 if (!
pImpl->hw_backend) {
297 FL_WARN(
"SPI Device: No hardware controller available");
302 SpiHwBase* hw =
pImpl->hw_backend.get();
305 TransmitMode mode = async ? TransmitMode::ASYNC : TransmitMode::SYNC;
306 bool success = hw->transmit(mode);
309 FL_WARN(
"SPI Device: Transmission failed");
315 if (!hw->waitComplete()) {
316 FL_WARN(
"SPI Device: Wait for completion failed");
321 FL_LOG_SPI(
"SPI Device: Transmission started (" << (async ?
"async" :
"blocking") <<
")");
331 if (!
pImpl->hw_backend) {
332 FL_WARN(
"SPI Device: No hardware controller available");
337 SpiHwBase* hw =
pImpl->hw_backend.get();
339 return hw->waitComplete(timeout_ms);
348 if (!
pImpl->hw_backend) {
353 SpiHwBase* hw =
pImpl->hw_backend.get();
364 pImpl->config.clock_speed_hz = speed_hz;
370 if (
pImpl->initialized) {
371 FL_LOG_SPI(
"SPI Device: Clock speed updated to " << speed_hz
372 <<
" Hz (will take effect on next begin())");
374 FL_LOG_SPI(
"SPI Device: Clock speed set to " << speed_hz <<
" Hz");
382 return pImpl->config;
396 if (
this != &other) {
405 if (!
pImpl->completed) {
416 if (
pImpl->completed) {
420 if (
pImpl->cancelled) {
421 pImpl->completed =
true;
426 if (!
pImpl->device) {
427 pImpl->completed =
true;
432 if (!
pImpl->device->isReady()) {
433 pImpl->completed =
true;
440 bool success =
pImpl->device->waitComplete(timeout_ms);
444 if (
pImpl->device->pImpl) {
445 pImpl->device->pImpl->async_state.active =
false;
447 pImpl->completed =
true;
451 FL_LOG_SPI(
"Transaction: Completed successfully (waited " << elapsed <<
"ms)");
455 pImpl->completed =
true;
457 FL_WARN(
"Transaction: Timeout after " << timeout_ms <<
"ms");
479 pImpl->cancelled =
true;
480 pImpl->completed =
true;
485 pImpl->device->pImpl->async_state.active =
false;
496 return pImpl->result;
Multi-lane SPI interface for LED output Hardware (SPI_HW): 1-8 parallel data lanes Software (SPI_BITB...
FastLED chrono implementation - duration types for time measurements.
static expected failure(E err, const char *msg=nullptr) FL_NOEXCEPT
Create error result.
static expected success(T value) FL_NOEXCEPT
Create successful result.
const T * data() const FL_NOEXCEPT
constexpr fl::size size() const FL_NOEXCEPT
bool isReady() const
Check if device is initialized and ready for use.
fl::optional< fl::task::Error > begin()
Initialize the SPI hardware.
void end()
Shutdown the SPI hardware and release resources.
Result< Transaction > writeAsync(const u8 *data, size_t size)
Begin asynchronous write operation (returns immediately)
fl::optional< fl::task::Error > transmit(DMABuffer &buffer, bool async=true)
Transmit from previously acquired DMA buffer.
bool waitComplete(u32 timeout_ms=(fl::numeric_limits< u32 >::max)())
Wait for pending async operation to complete.
Device(const Config &config)
Construct SPI device with configuration.
fl::optional< fl::task::Error > setClockSpeed(u32 speed_hz)
Update clock speed.
fl::unique_ptr< Impl > pImpl
~Device() FL_NOEXCEPT
Destructor - releases hardware resources.
DMABuffer acquireBuffer(size_t size)
Acquire DMA-capable buffer for zero-copy transmission.
const Config & getConfig() const
Get current configuration.
bool isBusy() const
Check if async operation is in progress.
Transaction() FL_NOEXCEPT
fl::unique_ptr< Impl > pImpl
bool cancel()
Cancel pending transaction (if supported by platform)
bool isPending() const
Check if transaction is still in progress.
Transaction(Transaction &&other) FL_NOEXCEPT
~Transaction()
Destructor - automatically waits for completion.
Transaction & operator=(Transaction &&other) FL_NOEXCEPT
bool isDone() const
Check if transaction is complete.
fl::optional< fl::task::Error > getResult() const
Get result of completed transaction.
bool wait(u32 timeout_ms=(fl::numeric_limits< u32 >::max)())
Wait for transaction to complete.
fl::size size() const FL_NOEXCEPT
bool empty() const FL_NOEXCEPT
#define FL_LOG_SPI(X)
Serial Peripheral Interface (SPI) logging Logs SPI configuration, initialization, and transfers.
Private implementation details for fl::spi::Device.
Centralized logging categories for FastLED hardware interfaces and subsystems.
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
fl::result< T, SPIError > Result
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
constexpr nullopt_t nullopt
Base definition for an LED controller.
int clock_pin
SCK pin number.
fl::vector< int > data_pins
Data pins (1 = single-lane, 2-8 = multi-lane)
Private implementation data for Device class.