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