FastLED 3.7.8
Loading...
Searching...
No Matches
fastspi_bitbang.h
Go to the documentation of this file.
1
3
4#ifndef __INC_FASTSPI_BITBANG_H
5#define __INC_FASTSPI_BITBANG_H
6
7#include "FastLED.h"
8
9#include "fastled_delay.h"
10#include "force_inline.h"
11
12FASTLED_NAMESPACE_BEGIN
13
15
23template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED>
25 // The data types for pointers to the pin port - typedef'd here from the ::Pin definition because on AVR these
26 // are pointers to 8 bit values, while on ARM they are 32 bit
27 typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
28 typedef typename FastPin<CLOCK_PIN>::port_ptr_t clock_ptr_t;
29
30 // The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports
31 // are 8 bits wide while on arm they are 32.
32 typedef typename FastPin<DATA_PIN>::port_t data_t;
33 typedef typename FastPin<CLOCK_PIN>::port_t clock_t;
34 Selectable *m_pSelect;
35
36public:
38 AVRSoftwareSPIOutput() { m_pSelect = NULL; }
40 AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
41
44 void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
45
47 void init() {
48 // set the pins to output and make sure the select is released (which apparently means hi? This is a bit
49 // confusing to me)
52 release();
53 }
54
57 static void stop() { }
58
61 static void wait() __attribute__((always_inline)) { }
63 static void waitFully() __attribute__((always_inline)) { wait(); }
64
66 static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
68 static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
69
71 static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
72
75 static void writeByte(uint8_t b) {
76 writeBit<7>(b);
77 writeBit<6>(b);
78 writeBit<5>(b);
79 writeBit<4>(b);
80 writeBit<3>(b);
81 writeBit<2>(b);
82 writeBit<1>(b);
83 writeBit<0>(b);
84 }
85
86private:
88 static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
89 writeBit<7>(b, clockpin, datapin);
90 writeBit<6>(b, clockpin, datapin);
91 writeBit<5>(b, clockpin, datapin);
92 writeBit<4>(b, clockpin, datapin);
93 writeBit<3>(b, clockpin, datapin);
94 writeBit<2>(b, clockpin, datapin);
95 writeBit<1>(b, clockpin, datapin);
96 writeBit<0>(b, clockpin, datapin);
97 }
98
102 static void writeByte(uint8_t b, data_ptr_t datapin,
103 data_t hival, data_t loval,
104 clock_t hiclock, clock_t loclock) {
105 writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
106 writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
107 writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
108 writeBit<4>(b, datapin, hival, loval, hiclock, loclock);
109 writeBit<3>(b, datapin, hival, loval, hiclock, loclock);
110 writeBit<2>(b, datapin, hival, loval, hiclock, loclock);
111 writeBit<1>(b, datapin, hival, loval, hiclock, loclock);
112 writeBit<0>(b, datapin, hival, loval, hiclock, loclock);
113 }
114
119 static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
120 data_t hival, data_t loval,
121 clock_t hiclock, clock_t loclock) {
122 writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock);
123 writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock);
124 writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock);
125 writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock);
126 writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock);
127 writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock);
128 writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock);
129 writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock);
130 }
131
132public:
133
134#if defined(FASTLED_TEENSY4)
135 #define DELAY_NS (1000 / (SPI_SPEED/1000000))
136 #define CLOCK_HI_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
137 #define CLOCK_LO_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
138#else
140 #define MIN_DELAY ((NS(35)>3) ? (NS(35) - 3) : 1)
141
143 #define CLOCK_HI_DELAY do { delaycycles<MIN_DELAY>(); delaycycles<((SPI_SPEED > 10) ? (((SPI_SPEED-6) / 2) - MIN_DELAY) : (SPI_SPEED))>(); } while(0);
145 #define CLOCK_LO_DELAY do { delaycycles<((SPI_SPEED > 10) ? ((SPI_SPEED-6) / 2) : (SPI_SPEED))>(); } while(0);
146#endif
147
151 template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
152 //cli();
153 if(b & (1 << BIT)) {
155#ifdef ESP32
156 // try to ensure we never have adjacent write opcodes to the same register
160#else
163#endif
164 } else {
167#ifdef ESP32
168 // try to ensure we never have adjacent write opcodes to the same register
170#else
172#endif
173 }
174 //sei();
175 }
176
177private:
179 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
180 if(b & (1 << BIT)) {
181 FastPin<DATA_PIN>::hi(datapin);
184 } else {
185 FastPin<DATA_PIN>::lo(datapin);
188 }
189
190 }
191
194 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
195 data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
196 // // only need to explicitly set clock hi if clock and data are on different ports
197 if(b & (1 << BIT)) {
198 FastPin<DATA_PIN>::fastset(datapin, hival);
201 } else {
202 // FL_NOP;
203 FastPin<DATA_PIN>::fastset(datapin, loval);
206 }
207 }
208
211 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, data_ptr_t clockdatapin,
212 data_t datahiclockhi, data_t dataloclockhi,
213 data_t datahiclocklo, data_t dataloclocklo) {
214#if 0
215 writeBit<BIT>(b);
216#else
217 if(b & (1 << BIT)) {
218 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo);
219 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY;
220 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY;
221 } else {
222 // FL_NOP;
223 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo);
224 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY;
225 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY;
226 }
227#endif
228 }
229
230public:
231
237 void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<SELECT_PIN>::hi(); }
238
240 void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::lo(); }
241
246 void writeBytesValue(uint8_t value, int len) {
247 select();
248 writeBytesValueRaw(value, len);
249 release();
250 }
251
254 static void writeBytesValueRaw(uint8_t value, int len) {
255#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
256 // TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled. Need
257 // to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall
258 // back to the degenerative code below
259 while(len--) {
260 writeByte(value);
261 }
262#else
263 FASTLED_REGISTER data_ptr_t datapin = FastPin<DATA_PIN>::port();
264
266 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
267 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
268 FASTLED_REGISTER clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
269 FASTLED_REGISTER data_t datahi = FastPin<DATA_PIN>::hival();
270 FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
271 FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
272 FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::loval();
273 while(len--) {
274 writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo);
275 }
276
277 } else {
278 // If data and clock are on the same port then we can combine setting the data and clock pins
279 FASTLED_REGISTER data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
280 FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
281 FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
282 FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
283
284 while(len--) {
285 writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
286 }
287 }
288#endif
289 }
290
296 template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
297 select();
298#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
299 uint8_t *end = data + len;
300 while(data != end) {
301 writeByte(D::adjust(*data++));
302 }
303#else
304 FASTLED_REGISTER clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
305 FASTLED_REGISTER data_ptr_t datapin = FastPin<DATA_PIN>::port();
306
308 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
309 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
310 FASTLED_REGISTER data_t datahi = FastPin<DATA_PIN>::hival();
311 FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
312 FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
313 FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::loval();
314 uint8_t *end = data + len;
315
316 while(data != end) {
317 writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
318 }
319
320 } else {
321 // FastPin<CLOCK_PIN>::hi();
322 // If data and clock are on the same port then we can combine setting the data and clock pins
323 FASTLED_REGISTER data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
324 FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
325 FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
326 FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
327
328 uint8_t *end = data + len;
329
330 while(data != end) {
331 writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
332 }
333 // FastPin<CLOCK_PIN>::lo();
334 }
335#endif
336 D::postBlock(len);
337 release();
338 }
339
343 void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
344
345
352 template <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
353 select();
354 int len = pixels.mLen;
355
356#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
357 // If interrupts or other things may be generating output while we're working on things, then we need
358 // to use this block
359 while(pixels.has(1)) {
360 if(FLAGS & FLAG_START_BIT) {
361 writeBit<0>(1);
362 }
363 writeByte(D::adjust(pixels.loadAndScale0()));
364 writeByte(D::adjust(pixels.loadAndScale1()));
365 writeByte(D::adjust(pixels.loadAndScale2()));
366 pixels.advanceData();
367 pixels.stepDithering();
368 }
369#else
370 // If we can guaruntee that no one else will be writing data while we are running (namely, changing the values of the PORT/PDOR pins)
371 // then we can use a bunch of optimizations in here
372 FASTLED_REGISTER data_ptr_t datapin = FastPin<DATA_PIN>::port();
373
375 FASTLED_REGISTER clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
376 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
377 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
378 FASTLED_REGISTER data_t datahi = FastPin<DATA_PIN>::hival();
379 FASTLED_REGISTER data_t datalo = FastPin<DATA_PIN>::loval();
380 FASTLED_REGISTER clock_t clockhi = FastPin<CLOCK_PIN>::hival();
381 FASTLED_REGISTER clock_t clocklo = FastPin<CLOCK_PIN>::loval();
382
383 while(pixels.has(1)) {
384 if(FLAGS & FLAG_START_BIT) {
385 writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
386 }
387 writeByte(D::adjust(pixels.loadAndScale0()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
388 writeByte(D::adjust(pixels.loadAndScale1()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
389 writeByte(D::adjust(pixels.loadAndScale2()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
390 pixels.advanceData();
391 pixels.stepDithering();
392 }
393
394 } else {
395 // If data and clock are on the same port then we can combine setting the data and clock pins
396 FASTLED_REGISTER data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
397 FASTLED_REGISTER data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
398 FASTLED_REGISTER data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
399 FASTLED_REGISTER data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
400
401 while(pixels.has(1)) {
402 if(FLAGS & FLAG_START_BIT) {
403 writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
404 }
405 writeByte(D::adjust(pixels.loadAndScale0()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
406 writeByte(D::adjust(pixels.loadAndScale1()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
407 writeByte(D::adjust(pixels.loadAndScale2()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
408 pixels.advanceData();
409 pixels.stepDithering();
410 }
411 }
412#endif
413 D::postBlock(len);
414 release();
415 }
416};
417
418FASTLED_NAMESPACE_END
419
420#endif
central include file for FastLED, defines the CFastLED class/object
Software SPI (aka bit-banging) support Includes aggressive optimizations for when the clock and data ...
void select()
Select the SPI output (chip select)
static void wait()
Wait until the SPI subsystem is ready for more data to write.
void writePixels(PixelController< RGB_ORDER > pixels, void *context=NULL)
Write LED pixel data to the SPI interface.
void setSelect(Selectable *pSelect)
Set the pointer for the SPI chip select.
static void writeWord(uint16_t w)
Write a word (two bytes) over SPI.
AVRSoftwareSPIOutput(Selectable *pSelect)
Constructor with selectable for SPI chip select.
static void writeByte(uint8_t b)
Write a single byte over SPI.
void release()
Release the SPI chip select line.
static void writeBytePostWait(uint8_t b)
Write a single byte over SPI and wait afterwards.
void writeBytes(FASTLED_REGISTER uint8_t *data, int len)
Write an array of data to the SPI interface.
static void stop()
Stop the SPI output.
static void writeBytesValueRaw(uint8_t value, int len)
Write multiple bytes of the given value over SPI, without selecting the interface.
static void waitFully()
Wait until the SPI subsystem is ready for more data to write.
void writeBytesValue(uint8_t value, int len)
Write multiple bytes of the given value over SPI.
static void writeBit(uint8_t b)
Write the BIT'th bit out via SPI, setting the data pin then strobing the clock.
static void writeByteNoWait(uint8_t b)
Write a single byte over SPI without waiting.
void init()
Set the clock/data pins to output and make sure the chip select is released.
void writeBytes(FASTLED_REGISTER uint8_t *data, int len)
Write an array of data to the SPI interface.
AVRSoftwareSPIOutput()
Default constructor.
The simplest level of Pin class.
Definition fastpin.h:205
static port_t hival()
Gets the state of the port with this pin HIGH
Definition fastpin.h:248
RwReg port_t
type for a pin read/write register, non-volatile
Definition fastpin.h:219
static void toggle()
Toggle the pin.
Definition fastpin.h:235
static port_t loval()
Gets the state of the port with this pin LOW
Definition fastpin.h:250
static port_t mask()
Get the pin mask.
Definition fastpin.h:254
volatile RwReg * port_ptr_t
type for a pin read/write register, volatile
Definition fastpin.h:218
static void lo()
Set the pin state to LOW
Definition fastpin.h:229
static port_ptr_t port()
Get the output state of the port.
Definition fastpin.h:252
static void setOutput()
Set the pin mode as OUTPUT
Definition fastpin.h:222
static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val)
Set the state of a port.
Definition fastpin.h:245
static void hi()
Set the pin state to HIGH
Definition fastpin.h:227
Abstract class for "selectable" things.
Definition fastpin.h:33
virtual void release()=0
Release this object.
virtual void select()=0
Select this object.
Utility functions and classes for managing delay cycles.
#define CLOCK_HI_DELAY
Delay for the clock signal 'high' period.
#define CLOCK_LO_DELAY
Delay for the clock signal 'low' period.
#define FLAG_START_BIT
Flag for the start of an SPI transaction.
Pixel controller class.
FASTLED_FORCE_INLINE uint8_t loadAndScale1(int lane, uint8_t scale)
non-template alias of loadAndScale<1>()
FASTLED_FORCE_INLINE uint8_t loadAndScale2(int lane, uint8_t scale)
non-template alias of loadAndScale<2>()
FASTLED_FORCE_INLINE uint8_t loadAndScale0(int lane, uint8_t scale)
non-template alias of loadAndScale<0>()
int mLen
number of LEDs in the data for one lane
FASTLED_FORCE_INLINE void advanceData()
Advance the data pointer forward, adjust position counter.
FASTLED_FORCE_INLINE bool has(int n)
Do we have n pixels left to process?
FASTLED_FORCE_INLINE void stepDithering()
Step the dithering forward.