FastLED 3.10.8
Loading...
Searching...
No Matches
pins.cpp.hpp
Go to the documentation of this file.
1
3
4#include "fl/system/pins.h"
5#include "fl/system/fastpin.h"
6#include "fl/log/log.h"
8#include "fl/stl/noexcept.h"
9
10namespace fl {
11
12// ============================================================================
13// Pin-to-port mapping using FastPin<N>::port()
14// ============================================================================
15// Strategy:
16// - On platforms with FASTLED_ALL_PINS_VALID (stub, WASM): build a static
17// table of FastPin<0..31>::port() pointers and compare them. Since stub
18// returns nullptr for all, we fall back to pin / 32 grouping.
19// - On platforms with FASTLED_NO_PINMAP (Apollo3, Renesas, Giga, NRF51,
20// STM32, RP2040): digitalPinToPort() is unavailable, so fall back to
21// grouping pins by blocks of 32.
22// - On real platforms with pin maps (AVR, ESP32, SAM): use the platform's
23// digitalPinToPort() macro which is already available.
24
25#ifdef FASTLED_ALL_PINS_VALID
26
27namespace pin_port_table {
28
29constexpr int TABLE_SIZE = 64;
30
31using port_ptr_t = typename FastPin<0>::port_ptr_t;
32
33template <int N> struct PortEntry {
34 static port_ptr_t get() FL_NO_EXCEPT { return FastPin<static_cast<u8>(N)>::port(); }
35};
36
37inline const port_ptr_t* portTable() FL_NO_EXCEPT {
38 static port_ptr_t table[TABLE_SIZE] = {
39 PortEntry<0>::get(), PortEntry<1>::get(), PortEntry<2>::get(), PortEntry<3>::get(),
40 PortEntry<4>::get(), PortEntry<5>::get(), PortEntry<6>::get(), PortEntry<7>::get(),
41 PortEntry<8>::get(), PortEntry<9>::get(), PortEntry<10>::get(), PortEntry<11>::get(),
42 PortEntry<12>::get(), PortEntry<13>::get(), PortEntry<14>::get(), PortEntry<15>::get(),
43 PortEntry<16>::get(), PortEntry<17>::get(), PortEntry<18>::get(), PortEntry<19>::get(),
44 PortEntry<20>::get(), PortEntry<21>::get(), PortEntry<22>::get(), PortEntry<23>::get(),
45 PortEntry<24>::get(), PortEntry<25>::get(), PortEntry<26>::get(), PortEntry<27>::get(),
46 PortEntry<28>::get(), PortEntry<29>::get(), PortEntry<30>::get(), PortEntry<31>::get(),
47 PortEntry<32>::get(), PortEntry<33>::get(), PortEntry<34>::get(), PortEntry<35>::get(),
48 PortEntry<36>::get(), PortEntry<37>::get(), PortEntry<38>::get(), PortEntry<39>::get(),
49 PortEntry<40>::get(), PortEntry<41>::get(), PortEntry<42>::get(), PortEntry<43>::get(),
50 PortEntry<44>::get(), PortEntry<45>::get(), PortEntry<46>::get(), PortEntry<47>::get(),
51 PortEntry<48>::get(), PortEntry<49>::get(), PortEntry<50>::get(), PortEntry<51>::get(),
52 PortEntry<52>::get(), PortEntry<53>::get(), PortEntry<54>::get(), PortEntry<55>::get(),
53 PortEntry<56>::get(), PortEntry<57>::get(), PortEntry<58>::get(), PortEntry<59>::get(),
54 PortEntry<60>::get(), PortEntry<61>::get(), PortEntry<62>::get(), PortEntry<63>::get(),
55 };
56 return table;
57}
58
59inline port_ptr_t getPortPtr(int pin) FL_NO_EXCEPT {
60 if (pin < 0 || pin >= TABLE_SIZE) return nullptr;
61 return portTable()[pin];
62}
63
64inline bool allPortsNull() FL_NO_EXCEPT {
65 const port_ptr_t* table = portTable();
66 for (int i = 0; i < TABLE_SIZE; ++i) {
67 if (table[i] != nullptr) return false;
68 }
69 return true;
70}
71
72} // namespace pin_port_table
73
74int pinToPort(int pin) FL_NO_EXCEPT {
75 if (pin < 0) return -1;
76
77 if (!pin_port_table::allPortsNull()) {
78 auto ptr = pin_port_table::getPortPtr(pin);
79 if (ptr == nullptr) return -1;
80
81 // Assign sequential port IDs by counting unique pointers before this one
82 const auto* table = pin_port_table::portTable();
83 for (int i = 0; i < pin_port_table::TABLE_SIZE; ++i) {
84 if (table[i] == ptr) {
85 int id = 0;
86 for (int j = 0; j < i; ++j) {
87 bool unique = true;
88 for (int k = 0; k < j; ++k) {
89 if (table[k] == table[j]) { unique = false; break; }
90 }
91 if (unique && table[j] != nullptr) ++id;
92 }
93 return id;
94 }
95 }
96 return -1;
97 }
98
99 // Stub/WASM fallback: all FastPin ports are null, group by pin / 32
100 return pin / 32;
101}
102
103#elif defined(FASTLED_NO_PINMAP)
104
105// Platforms that define FASTLED_NO_PINMAP (Apollo3, Renesas, Giga, NRF51,
106// STM32, RP2040) don't provide digitalPinToPort(). Fall back to grouping
107// pins by blocks of 32.
108int pinToPort(int pin) FL_NO_EXCEPT {
109 if (pin < 0) return -1;
110 return pin / 32;
111}
112
113#else // Real platforms with pin maps (AVR, ESP32, SAM, etc.)
114
115namespace {
116// Convert digitalPinToPort() result to int. Most platforms return integers;
117// SAM3X8E returns Pio* which needs mapping to sequential port IDs.
118inline int portValueToId(unsigned char v) FL_NO_EXCEPT { return v; }
119inline int portValueToId(unsigned short v) FL_NO_EXCEPT { return static_cast<int>(v); }
120inline int portValueToId(int v) FL_NO_EXCEPT { return v; }
121inline int portValueToId(long v) FL_NO_EXCEPT { return static_cast<int>(v); }
122
123// Template overload for unsigned multi-byte integers (handles both u32 and unsigned long)
124// Uses enable_if to avoid duplicate overloads on platforms where they're the same type
125template<typename T>
128 int>::type
130 return static_cast<int>(v);
131}
132
133// Pointer overload for SAM and similar platforms where digitalPinToPort
134// returns a peripheral struct pointer (e.g. Pio*).
135template <typename T>
137 if (!ptr) return -1;
138 static T* seen[8] = {};
139 static int n = 0;
140 for (int i = 0; i < n; ++i) {
141 if (seen[i] == ptr) return i;
142 }
143 if (n < 8) {
144 seen[n] = ptr;
145 return n++;
146 }
147 return -1;
148}
149} // namespace
150
152 if (pin < 0) return -1;
153 return portValueToId(digitalPinToPort(pin));
154}
155
156#endif // FASTLED_ALL_PINS_VALID
157
159 for (u8 i = 0; i < 8; ++i) {
160 mPins[i] = pins.pins[i];
161 }
162
163 // Find the majority port and disable pins on other ports.
164 int port_counts[32] = {};
165 int port_ids[8] = {};
166 int num_active = 0;
167 for (u8 i = 0; i < 8; ++i) {
168 if (mPins[i] < 0) {
169 port_ids[i] = -1;
170 continue;
171 }
172 int p = fl::pinToPort(mPins[i]);
173 port_ids[i] = p;
174 if (p >= 0 && p < 32) {
175 port_counts[p]++;
176 }
177 num_active++;
178 }
179
180 // Find the port with the highest pin count.
181 int best_port = -1;
182 int best_count = 0;
183 for (int p = 0; p < 32; ++p) {
184 if (port_counts[p] > best_count) {
185 best_count = port_counts[p];
186 best_port = p;
187 }
188 }
189
190 // Disable pins that aren't on the majority port and warn.
191 if (best_port >= 0 && best_count < num_active) {
192 for (u8 i = 0; i < 8; ++i) {
193 if (mPins[i] < 0) {
194 continue;
195 }
196 if (port_ids[i] != best_port) {
197 FL_WARN("digitalMultiWrite8: pin " << mPins[i] << " (port " << port_ids[i] << ") disabled — not on majority port " << best_port);
198 mPins[i] = -1;
199 }
200 }
201 }
202
203 buildNibbleLut(0, mSetLo, mClrLo); // bits 0-3
204 buildNibbleLut(4, mSetHi, mClrHi); // bits 4-7
205}
206
208 for (fl::size i = 0; i < pin_data.size(); ++i) {
209 const u8 byte = pin_data[i];
210 const u8 lo_nib = byte & 0x0F;
211 const u8 hi_nib = (byte >> 4) & 0x0F;
212 applyNibble(mSetLo[lo_nib], mClrLo[lo_nib]);
213 applyNibble(mSetHi[hi_nib], mClrHi[hi_nib]);
214 }
215}
216
218 int first_port = -1;
219 for (u8 i = 0; i < 8; ++i) {
220 if (mPins[i] < 0) {
221 continue;
222 }
223 int port = fl::pinToPort(mPins[i]);
224 if (first_port < 0) {
225 first_port = port;
226 } else if (port != first_port) {
227 return false;
228 }
229 }
230 return true;
231}
232
233void DigitalMultiWrite8::buildNibbleLut(u8 bit_offset, PinList (&set_lut)[16],
234 PinList (&clr_lut)[16]) FL_NO_EXCEPT {
235 for (u16 nib = 0; nib < 16; ++nib) {
236 PinList &s = set_lut[nib];
237 PinList &c = clr_lut[nib];
238 s.count = 0;
239 c.count = 0;
240 for (u8 b = 0; b < 4; ++b) {
241 int pin = mPins[bit_offset + b];
242 if (pin < 0) {
243 continue;
244 }
245 if (nib & (1 << b)) {
246 s.pins[s.count++] = pin;
247 } else {
248 c.pins[c.count++] = pin;
249 }
250 }
251 }
252}
253
255 for (u8 i = 0; i < set.count; ++i) {
257 }
258 for (u8 i = 0; i < clr.count; ++i) {
259 fl::digitalWrite(clr.pins[i], PinValue::Low);
260 }
261}
262
264 DigitalMultiWrite8 writer;
265 writer.init(pins);
266 writer.write(pin_data);
267}
268
269// ============================================================================
270// DigitalMultiWrite16
271// ============================================================================
272
274 for (u8 i = 0; i < 16; ++i) {
275 mPins[i] = pins.pins[i];
276 }
277
278 // Find the majority port and disable pins on other ports.
279 int port_counts[64] = {};
280 int port_ids[16] = {};
281 int num_active = 0;
282 for (u8 i = 0; i < 16; ++i) {
283 if (mPins[i] < 0) {
284 port_ids[i] = -1;
285 continue;
286 }
287 int p = fl::pinToPort(mPins[i]);
288 port_ids[i] = p;
289 if (p >= 0 && p < 64) {
290 port_counts[p]++;
291 }
292 num_active++;
293 }
294
295 // Find the port with the highest pin count.
296 int best_port = -1;
297 int best_count = 0;
298 for (int p = 0; p < 64; ++p) {
299 if (port_counts[p] > best_count) {
300 best_count = port_counts[p];
301 best_port = p;
302 }
303 }
304
305 // Disable pins that aren't on the majority port and warn.
306 if (best_port >= 0 && best_count < num_active) {
307 for (u8 i = 0; i < 16; ++i) {
308 if (mPins[i] < 0) {
309 continue;
310 }
311 if (port_ids[i] != best_port) {
312 FL_WARN("digitalMultiWrite16: pin " << mPins[i] << " (port " << port_ids[i] << ") disabled — not on majority port " << best_port);
313 mPins[i] = -1;
314 }
315 }
316 }
317
318 for (u8 n = 0; n < 4; ++n) {
319 buildNibbleLut(n * 4, mSetNib[n], mClrNib[n]);
320 }
321}
322
324 for (fl::size i = 0; i < pin_data.size(); ++i) {
325 const u16 word = pin_data[i];
326 for (u8 n = 0; n < 4; ++n) {
327 const u8 nib = (word >> (n * 4)) & 0x0F;
328 applyNibble(mSetNib[n][nib], mClrNib[n][nib]);
329 }
330 }
331}
332
334 int first_port = -1;
335 for (u8 i = 0; i < 16; ++i) {
336 if (mPins[i] < 0) {
337 continue;
338 }
339 int port = fl::pinToPort(mPins[i]);
340 if (first_port < 0) {
341 first_port = port;
342 } else if (port != first_port) {
343 return false;
344 }
345 }
346 return true;
347}
348
350 PinList (&set_lut)[16],
351 PinList (&clr_lut)[16]) FL_NO_EXCEPT {
352 for (u16 nib = 0; nib < 16; ++nib) {
353 PinList &s = set_lut[nib];
354 PinList &c = clr_lut[nib];
355 s.count = 0;
356 c.count = 0;
357 for (u8 b = 0; b < 4; ++b) {
358 int pin = mPins[bit_offset + b];
359 if (pin < 0) {
360 continue;
361 }
362 if (nib & (1 << b)) {
363 s.pins[s.count++] = pin;
364 } else {
365 c.pins[c.count++] = pin;
366 }
367 }
368 }
369}
370
372 for (u8 i = 0; i < set.count; ++i) {
374 }
375 for (u8 i = 0; i < clr.count; ++i) {
376 fl::digitalWrite(clr.pins[i], PinValue::Low);
377 }
378}
379
381 DigitalMultiWrite16 writer;
382 writer.init(pins);
383 writer.write(pin_data);
384}
385
386// ============================================================================
387// pinMap
388// ============================================================================
389
391 for (fl::size i = 0; i < pins.size(); ++i) {
392 if (pins[i].pin >= 0) {
393 pins[i].port = pinToPort(pins[i].pin);
394 }
395 }
396}
397
398} // namespace fl
int pins[]
Definition Spi.ino:11
void init(const Pins16 &pins) FL_NO_EXCEPT
Initialize from 16 pin numbers. -1 means "skip this bit position".
Definition pins.cpp.hpp:273
static void applyNibble(const PinList &set, const PinList &clr) FL_NO_EXCEPT
Definition pins.cpp.hpp:371
PinList mSetNib[4][16]
Definition pins.h:123
void buildNibbleLut(u8 bit_offset, PinList(&set_lut)[16], PinList(&clr_lut)[16]) FL_NO_EXCEPT
Definition pins.cpp.hpp:349
void write(fl::span< const u16 > pin_data) const FL_NO_EXCEPT
Write pre-transposed 16-bit data to the 16 pins.
Definition pins.cpp.hpp:323
bool allSamePort() const FL_NO_EXCEPT
Check whether all active pins (non -1) share the same GPIO port.
Definition pins.cpp.hpp:333
PinList mClrNib[4][16]
Definition pins.h:124
Pre-computed nibble LUT for fast 16-pin digital writes.
Definition pins.h:93
void buildNibbleLut(u8 bit_offset, PinList(&set_lut)[16], PinList(&clr_lut)[16]) FL_NO_EXCEPT
Definition pins.cpp.hpp:233
PinList mClrLo[16]
Definition pins.h:71
void write(fl::span< const u8 > pin_data) const FL_NO_EXCEPT
Write pre-transposed byte data to the 8 pins.
Definition pins.cpp.hpp:207
bool allSamePort() const FL_NO_EXCEPT
Check whether all active pins (non -1) share the same GPIO port.
Definition pins.cpp.hpp:217
PinList mSetHi[16]
Definition pins.h:72
static void applyNibble(const PinList &set, const PinList &clr) FL_NO_EXCEPT
Definition pins.cpp.hpp:254
void init(const Pins8 &pins) FL_NO_EXCEPT
Initialize from 8 pin numbers. -1 means "skip this bit position".
Definition pins.cpp.hpp:158
PinList mSetLo[16]
Definition pins.h:70
PinList mClrHi[16]
Definition pins.h:73
Pre-computed nibble LUT for fast 8-pin digital writes.
Definition pins.h:28
size_type count(const Key &key) const
Definition set.h:459
Definition set.h:367
fl::FastPin< PIN > FastPin
Definition fastpin.h:19
#define FL_WARN(...)
Definition log.h:437
Centralized logging categories for FastLED hardware interfaces and subsystems.
int portValueToId(unsigned char v) FL_NO_EXCEPT
Definition pins.cpp.hpp:118
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
@ Low
Logic low (0V / GND)
Definition pin.h:51
@ High
Logic high (3.3V / 5V, platform-dependent)
Definition pin.h:52
void pinMap(fl::span< PinInfo > pins) FL_NO_EXCEPT
Resolve port IDs for an array of PinInfo in-place.
Definition pins.cpp.hpp:390
void digitalMultiWrite8(const Pins8 &pins, fl::span< const u8 > pin_data) FL_NO_EXCEPT
Convenience free function — creates a temporary DigitalMultiWrite8, initializes it,...
Definition pins.cpp.hpp:263
int pinToPort(int pin) FL_NO_EXCEPT
Map a runtime pin number to an integer port ID using FastPin<N>::port().
Definition pins.cpp.hpp:151
pair_element< I, T1, T2 >::type & get(pair< T1, T2 > &p) FL_NO_EXCEPT
Definition pair.h:119
void digitalMultiWrite16(const Pins16 &pins, fl::span< const u16 > pin_data) FL_NO_EXCEPT
Convenience free function — creates a temporary DigitalMultiWrite16, initializes it,...
Definition pins.cpp.hpp:380
void digitalWrite(int pin, PinValue val) FL_NO_EXCEPT
Write digital value to pin.
Definition pin.cpp.hpp:51
Base definition for an LED controller.
Definition crgb.hpp:179
POD struct holding 16 pin numbers for bulk pin writes.
Definition pins.h:83
POD struct holding 8 pin numbers for bulk pin writes.
Definition pins.h:18
#define FL_NO_EXCEPT