FastLED 3.10.8
Loading...
Searching...
No Matches
parallel_device.cpp.hpp
Go to the documentation of this file.
3#include "fl/log/log.h"
4#include "fl/log/log.h"
5
6// Include parallel SPI backends
7// IWYU pragma: begin_keep
8#include "platforms/shared/spi_bitbang/spi_isr_1.h" // ok platform headers
9#include "platforms/shared/spi_bitbang/spi_isr_2.h" // ok platform headers
10#include "platforms/shared/spi_bitbang/spi_isr_4.h" // ok platform headers
11#include "platforms/shared/spi_bitbang/spi_isr_8.h" // ok platform headers
12#include "platforms/shared/spi_bitbang/spi_isr_16.h" // ok platform headers
13#include "platforms/shared/spi_bitbang/spi_isr_32.h" // ok platform headers
14#include "platforms/shared/spi_bitbang/spi_block_1.h" // ok platform headers
15#include "platforms/shared/spi_bitbang/spi_block_2.h" // ok platform headers
16#include "platforms/shared/spi_bitbang/spi_block_4.h" // ok platform headers
17#include "platforms/shared/spi_bitbang/spi_block_8.h" // ok platform headers
18#include "platforms/shared/spi_bitbang/spi_block_16.h" // ok platform headers
19#include "platforms/shared/spi_bitbang/spi_block_32.h" // ok platform headers
20#include "fl/stl/noexcept.h"
21// IWYU pragma: end_keep
22
23namespace fl {
24namespace spi {
25
26// ============================================================================
27// Config Implementation (to avoid circular dependency with Spi class)
28// ============================================================================
29
30ParallelDevice::Config::Config() FL_NO_EXCEPT
31 : clock_pin(0xFF)
33 , timer_hz(1600000) {
34}
35
36// ============================================================================
37// Implementation Details (pImpl pattern)
38// ============================================================================
39
43 void* backend; // Points to SpiIsr* or SpiBlock* (not owned by shared_ptr)
45 u8 backend_width; // 1, 2, 4, 8, 16, or 32
46
47 Impl(const Config& cfg)
48 FL_NO_EXCEPT : config(cfg)
49 , initialized(false)
50 , backend(nullptr)
51 , is_isr_mode(false)
52 , backend_width(0) {
53 }
54
56 if (initialized && backend) {
58 }
59 }
60
62 if (!backend) return;
63
64 // Stop ISR if in ISR mode
65 if (is_isr_mode) {
66 // ISR backends have stopISR() method
67 if (backend_width == 1) {
68 static_cast<SpiIsr1*>(backend)->stopISR();
69 } else if (backend_width == 2) {
70 static_cast<SpiIsr2*>(backend)->stopISR();
71 } else if (backend_width == 4) {
72 static_cast<SpiIsr4*>(backend)->stopISR();
73 } else if (backend_width == 8) {
74 static_cast<SpiIsr8*>(backend)->stopISR();
75 } else if (backend_width == 16) {
76 static_cast<SpiIsr16*>(backend)->stopISR();
77 } else if (backend_width == 32) {
78 static_cast<SpiIsr32*>(backend)->stopISR();
79 }
80 }
81
82 // Note: SpiIsr* and SpiBlock* backends don't need delete
83 // (they're typically stack-allocated in this impl)
84 backend = nullptr;
85 initialized = false;
86 }
87};
88
89// ============================================================================
90// Helper Functions
91// ============================================================================
92
93namespace {
94
99void buildDefaultLUT(const fl::vector<u8>& gpio_pins,
100 u32* set_masks,
101 u32* clear_masks) FL_NO_EXCEPT {
102 size_t num_pins = gpio_pins.size();
103
104 // For each possible byte value (0-255)
105 for (int byte_val = 0; byte_val < 256; byte_val++) {
106 u32 set_mask = 0;
107 u32 clear_mask = 0;
108
109 // Map byte bits to GPIO pins
110 for (size_t bit_pos = 0; bit_pos < num_pins && bit_pos < 8; bit_pos++) {
111 u32 pin_mask = 1u << gpio_pins[bit_pos];
112
113 if (byte_val & (1 << bit_pos)) {
114 set_mask |= pin_mask; // Set this pin high
115 } else {
116 clear_mask |= pin_mask; // Clear this pin low
117 }
118 }
119
120 // For pins beyond bit 7, always clear them (if num_pins > 8)
121 for (size_t pin_idx = 8; pin_idx < num_pins; pin_idx++) {
122 clear_mask |= (1u << gpio_pins[pin_idx]);
123 }
124
125 set_masks[byte_val] = set_mask;
126 clear_masks[byte_val] = clear_mask;
127 }
128}
129
130} // anonymous namespace
131
132// ============================================================================
133// ParallelDevice Implementation
134// ============================================================================
135
138
139 // Validate configuration
140 size_t num_pins = config.gpio_pins.size();
141 if (num_pins == 0 || num_pins > 32) {
142 FL_WARN("ParallelDevice: Invalid number of GPIO pins (" << num_pins << "), must be 1-32");
143 }
144
145 FL_DBG("ParallelDevice: Created with " << num_pins << " GPIO pins");
146}
147
149 if (pImpl && pImpl->initialized) {
150 end();
151 }
152}
153
155 if (!pImpl) {
156 return fl::task::Error("Device not initialized");
157 }
158
159 if (pImpl->initialized) {
160 // Already initialized - idempotent
161 return fl::nullopt;
162 }
163
164 size_t num_pins = pImpl->config.gpio_pins.size();
165
166 // Validate pin count
167 if (num_pins == 0 || num_pins > 32) {
168 return fl::task::Error("Invalid number of GPIO pins (must be 1-32)");
169 }
170
171 // Determine backend width (round up to next supported width: 1,2,4,8,16,32)
172 u8 backend_width = 1;
173 if (num_pins > 16) backend_width = 32;
174 else if (num_pins > 8) backend_width = 16;
175 else if (num_pins > 4) backend_width = 8;
176 else if (num_pins > 2) backend_width = 4;
177 else if (num_pins > 1) backend_width = 2;
178
179 pImpl->backend_width = backend_width;
180
181 // Determine execution mode
182 bool use_isr = (pImpl->config.mode == SpiParallelMode::ISR_ASYNC) ||
183 (pImpl->config.mode == SpiParallelMode::AUTO); // Default to ISR if AUTO
184 pImpl->is_isr_mode = use_isr;
185
186 // Build default LUT
187 u32 set_masks[256];
188 u32 clear_masks[256];
189 buildDefaultLUT(pImpl->config.gpio_pins, set_masks, clear_masks);
190
191 // Create and initialize backend
192 // Note: We can't use polymorphism here due to different backend types
193 // Instead, we use type-erasure with void* and manual dispatch
194
195 if (use_isr) {
196 // ISR-based backend
197 FL_DBG("ParallelDevice: Initializing ISR mode (width=" << ((int)backend_width) << ")");
198
199 // Note: These need to be heap-allocated for type-erasure
200 // For now, return error indicating ISR mode not yet implemented
201 return fl::task::Error("ISR mode not yet implemented for ParallelDevice");
202
203 } else {
204 // Bit-bang (blocking) backend
205 FL_DBG("ParallelDevice: Initializing bit-bang mode (width=" << ((int)backend_width) << ")");
206
207 // Note: These need to be heap-allocated for type-erasure
208 // For now, return error indicating bit-bang mode not yet implemented
209 return fl::task::Error("Bit-bang mode not yet implemented for ParallelDevice");
210 }
211
212 pImpl->initialized = true;
213 return fl::nullopt;
214}
215
217 if (!pImpl || !pImpl->initialized) {
218 return;
219 }
220
221 // Wait for pending operations
222 waitComplete();
223
224 // Release backend
225 pImpl->releaseBackend();
226
227 FL_DBG("ParallelDevice: Shutdown complete");
228}
229
231 return pImpl && pImpl->initialized && pImpl->backend != nullptr;
232}
233
235 if (!isReady()) {
236 return Result<Transaction>::failure(SPIError::NOT_INITIALIZED,
237 "Device not initialized");
238 }
239
240 if (!data || size == 0) {
241 return Result<Transaction>::failure(SPIError::ALLOCATION_FAILED,
242 "Invalid data or size");
243 }
244
245 // Write implementation depends on backend type
246 // For now, return not implemented
247 return Result<Transaction>::failure(SPIError::NOT_SUPPORTED,
248 "ParallelDevice::write() not yet implemented");
249}
250
252 (void)timeout_ms; // Unused parameter - reserved for future implementation
253 if (!isReady()) {
254 return false;
255 }
256
257 // Wait implementation depends on backend type
258 // For ISR mode, check status flags
259 // For bit-bang mode, transmission is already complete (blocking)
260
261 return true; // Placeholder
262}
263
265 if (!isReady()) {
266 return false;
267 }
268
269 // Busy check depends on backend type
270 return false; // Placeholder
271}
272
273void ParallelDevice::configureLUT(const u32* set_masks, const u32* clear_masks) FL_NO_EXCEPT {
274 if (!set_masks || !clear_masks) {
275 FL_WARN("ParallelDevice: Invalid LUT pointers");
276 return;
277 }
278
279 // LUT configuration depends on backend type
280 // For now, this is a no-op
281 FL_DBG("ParallelDevice: LUT configuration not yet implemented");
282}
283
285 static const Config empty_config;
286 return pImpl ? pImpl->config : empty_config;
287}
288
289} // namespace spi
290} // namespace fl
static expected failure(E err, const char *msg=nullptr) FL_NO_EXCEPT
Create error result.
Definition expected.h:115
bool isBusy() const FL_NO_EXCEPT
Check if transmission is in progress.
void configureLUT(const u32 *set_masks, const u32 *clear_masks) FL_NO_EXCEPT
Configure custom LUT (advanced)
bool isReady() const FL_NO_EXCEPT
Check if device is initialized.
void end() FL_NO_EXCEPT
Shutdown hardware and release resources.
fl::optional< fl::task::Error > begin() FL_NO_EXCEPT
Initialize hardware and setup LUT.
~ParallelDevice() FL_NO_EXCEPT
Destructor - releases hardware resources.
Result< Transaction > write(const u8 *data, size_t size) FL_NO_EXCEPT
Write data (single stream drives all pins via LUT)
const Config & getConfig() const FL_NO_EXCEPT
Get current configuration.
ParallelDevice(const Config &config) FL_NO_EXCEPT
Construct parallel device.
bool waitComplete(u32 timeout_ms=(fl::numeric_limits< u32 >::max)()) FL_NO_EXCEPT
Wait for pending transmission to complete.
fl::unique_ptr< Impl > pImpl
Shared implementation helpers for SPI device classes.
#define FL_WARN(...)
Definition log.h:437
#define FL_DBG
Definition log.h:553
Centralized logging categories for FastLED hardware interfaces and subsystems.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NO_EXCEPT
Definition unique_ptr.h:263
void buildDefaultLUT(const fl::vector< u8 > &gpio_pins, u32 *set_masks, u32 *clear_masks) FL_NO_EXCEPT
Build default LUT for parallel GPIO mapping.
fl::result< T, SPIError > Result
Definition transaction.h:29
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
Optional< T > optional
Definition optional.h:16
@ AUTO
Definition bus.h:28
constexpr nullopt_t nullopt
Definition optional.h:13
SpiParallelMode
Parallel device execution modes.
Definition config.h:33
@ ISR_ASYNC
ISR-driven async mode.
Definition config.h:35
@ AUTO
Auto-select best mode (default: ISR)
Definition config.h:34
Base definition for an LED controller.
Definition crgb.hpp:179
Parallel GPIO SPI device for 1-32 outputs driven from single data stream.
u32 timer_hz
Timer frequency for ISR mode.
SpiParallelMode mode
Execution mode (ISR vs bit-bang)
Configuration for parallel GPIO SPI.
Impl(const Config &cfg) FL_NO_EXCEPT
Error type for promises.
Definition promise.h:39