FastLED 3.9.15
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()
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 : 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) {
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
137 : pImpl(fl::make_unique<Impl>(config)) {
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
143 << "), must be 1-32");
144 }
145
146 FL_DBG("ParallelDevice: Created with " << num_pins << " GPIO pins");
147}
148
150 if (pImpl && pImpl->initialized) {
151 end();
152 }
153}
154
156 if (!pImpl) {
157 return fl::task::Error("Device not initialized");
158 }
159
160 if (pImpl->initialized) {
161 // Already initialized - idempotent
162 return fl::nullopt;
163 }
164
165 size_t num_pins = pImpl->config.gpio_pins.size();
166
167 // Validate pin count
168 if (num_pins == 0 || num_pins > 32) {
169 return fl::task::Error("Invalid number of GPIO pins (must be 1-32)");
170 }
171
172 // Determine backend width (round up to next supported width: 1,2,4,8,16,32)
173 u8 backend_width = 1;
174 if (num_pins > 16) backend_width = 32;
175 else if (num_pins > 8) backend_width = 16;
176 else if (num_pins > 4) backend_width = 8;
177 else if (num_pins > 2) backend_width = 4;
178 else if (num_pins > 1) backend_width = 2;
179
180 pImpl->backend_width = backend_width;
181
182 // Determine execution mode
183 bool use_isr = (pImpl->config.mode == SpiParallelMode::ISR_ASYNC) ||
184 (pImpl->config.mode == SpiParallelMode::AUTO); // Default to ISR if AUTO
185 pImpl->is_isr_mode = use_isr;
186
187 // Build default LUT
188 u32 set_masks[256];
189 u32 clear_masks[256];
190 buildDefaultLUT(pImpl->config.gpio_pins, set_masks, clear_masks);
191
192 // Create and initialize backend
193 // Note: We can't use polymorphism here due to different backend types
194 // Instead, we use type-erasure with void* and manual dispatch
195
196 if (use_isr) {
197 // ISR-based backend
198 FL_DBG("ParallelDevice: Initializing ISR mode (width=" << (int)backend_width << ")");
199
200 // Note: These need to be heap-allocated for type-erasure
201 // For now, return error indicating ISR mode not yet implemented
202 return fl::task::Error("ISR mode not yet implemented for ParallelDevice");
203
204 } else {
205 // Bit-bang (blocking) backend
206 FL_DBG("ParallelDevice: Initializing bit-bang mode (width=" << (int)backend_width << ")");
207
208 // Note: These need to be heap-allocated for type-erasure
209 // For now, return error indicating bit-bang mode not yet implemented
210 return fl::task::Error("Bit-bang mode not yet implemented for ParallelDevice");
211 }
212
213 pImpl->initialized = true;
214 return fl::nullopt;
215}
216
218 if (!pImpl || !pImpl->initialized) {
219 return;
220 }
221
222 // Wait for pending operations
223 waitComplete();
224
225 // Release backend
226 pImpl->releaseBackend();
227
228 FL_DBG("ParallelDevice: Shutdown complete");
229}
230
232 return pImpl && pImpl->initialized && pImpl->backend != nullptr;
233}
234
236 if (!isReady()) {
237 return Result<Transaction>::failure(SPIError::NOT_INITIALIZED,
238 "Device not initialized");
239 }
240
241 if (!data || size == 0) {
242 return Result<Transaction>::failure(SPIError::ALLOCATION_FAILED,
243 "Invalid data or size");
244 }
245
246 // Write implementation depends on backend type
247 // For now, return not implemented
248 return Result<Transaction>::failure(SPIError::NOT_SUPPORTED,
249 "ParallelDevice::write() not yet implemented");
250}
251
252bool ParallelDevice::waitComplete(u32 timeout_ms) {
253 (void)timeout_ms; // Unused parameter - reserved for future implementation
254 if (!isReady()) {
255 return false;
256 }
257
258 // Wait implementation depends on backend type
259 // For ISR mode, check status flags
260 // For bit-bang mode, transmission is already complete (blocking)
261
262 return true; // Placeholder
263}
264
266 if (!isReady()) {
267 return false;
268 }
269
270 // Busy check depends on backend type
271 return false; // Placeholder
272}
273
274void ParallelDevice::configureLUT(const u32* set_masks, const u32* clear_masks) {
275 if (!set_masks || !clear_masks) {
276 FL_WARN("ParallelDevice: Invalid LUT pointers");
277 return;
278 }
279
280 // LUT configuration depends on backend type
281 // For now, this is a no-op
282 FL_DBG("ParallelDevice: LUT configuration not yet implemented");
283}
284
286 static const Config empty_config;
287 return pImpl ? pImpl->config : empty_config;
288}
289
290} // namespace spi
291} // namespace fl
static expected failure(E err, const char *msg=nullptr) FL_NOEXCEPT
Create error result.
Definition expected.h:115
bool isReady() const
Check if device is initialized.
~ParallelDevice() FL_NOEXCEPT
Destructor - releases hardware resources.
const Config & getConfig() const
Get current configuration.
Result< Transaction > write(const u8 *data, size_t size)
Write data (single stream drives all pins via LUT)
bool waitComplete(u32 timeout_ms=(fl::numeric_limits< u32 >::max)())
Wait for pending transmission to complete.
void configureLUT(const u32 *set_masks, const u32 *clear_masks)
Configure custom LUT (advanced)
bool isBusy() const
Check if transmission is in progress.
ParallelDevice(const Config &config)
Construct parallel device.
void end()
Shutdown hardware and release resources.
fl::optional< fl::task::Error > begin()
Initialize hardware and setup LUT.
fl::unique_ptr< Impl > pImpl
fl::size size() const FL_NOEXCEPT
Shared implementation helpers for SPI device classes.
#define FL_WARN(X)
Definition log.h:276
#define FL_DBG
Definition log.h:388
Centralized logging categories for FastLED hardware interfaces and subsystems.
void buildDefaultLUT(const fl::vector< u8 > &gpio_pins, u32 *set_masks, u32 *clear_masks)
Build default LUT for parallel GPIO mapping.
fl::result< T, SPIError > Result
Definition transaction.h:29
unsigned char u8
Definition stdint.h:131
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
Definition unique_ptr.h:261
Optional< T > optional
Definition optional.h:16
@ AUTO
Sentinel: defer to DefaultBus<Chipset>::value.
Definition bus.h:61
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.
#define FL_NOEXCEPT
u32 timer_hz
Timer frequency for ISR mode.
fl::vector< u8 > gpio_pins
GPIO pins (1-32 pins)
SpiParallelMode mode
Execution mode (ISR vs bit-bang)
Configuration for parallel GPIO SPI.
Error type for promises.
Definition promise.h:39