FastLED 3.9.15
Loading...
Searching...
No Matches
fastspi_bitbang.h
Go to the documentation of this file.
1#pragma once
2
5
6#ifndef __INC_FASTSPI_BITBANG_H
7#define __INC_FASTSPI_BITBANG_H
8
9#include "FastLED.h"
10
11#include "fastled_delay.h"
12#include "fl/force_inline.h"
13
15
17
25template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint32_t SPI_SPEED>
27 // The data types for pointers to the pin port - typedef'd here from the ::Pin definition because on AVR these
28 // are pointers to 8 bit values, while on ARM they are 32 bit
31
32 // The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports
33 // are 8 bits wide while on arm they are 32.
37
38public:
42 AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
43
46 void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
47
49 void init() {
50 // set the pins to output and make sure the select is released (which apparently means hi? This is a bit
51 // confusing to me)
54 release();
55 }
56
59 static void stop() { }
60
63 static void wait() __attribute__((always_inline)) { }
65 static void waitFully() __attribute__((always_inline)) { wait(); }
66
68 static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
70 static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
71
73 static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
74
77 static void writeByte(uint8_t b) {
78 writeBit<7>(b);
79 writeBit<6>(b);
80 writeBit<5>(b);
81 writeBit<4>(b);
82 writeBit<3>(b);
83 writeBit<2>(b);
84 writeBit<1>(b);
85 writeBit<0>(b);
86 }
87
88private:
90 static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
91 writeBit<7>(b, clockpin, datapin);
92 writeBit<6>(b, clockpin, datapin);
93 writeBit<5>(b, clockpin, datapin);
94 writeBit<4>(b, clockpin, datapin);
95 writeBit<3>(b, clockpin, datapin);
96 writeBit<2>(b, clockpin, datapin);
97 writeBit<1>(b, clockpin, datapin);
98 writeBit<0>(b, clockpin, datapin);
99 }
100
104 static void writeByte(uint8_t b, data_ptr_t datapin,
105 data_t hival, data_t loval,
106 clock_t hiclock, clock_t loclock) {
107 writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
108 writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
109 writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
110 writeBit<4>(b, datapin, hival, loval, hiclock, loclock);
111 writeBit<3>(b, datapin, hival, loval, hiclock, loclock);
112 writeBit<2>(b, datapin, hival, loval, hiclock, loclock);
113 writeBit<1>(b, datapin, hival, loval, hiclock, loclock);
114 writeBit<0>(b, datapin, hival, loval, hiclock, loclock);
115 }
116
121 static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
122 data_t hival, data_t loval,
123 clock_t hiclock, clock_t loclock) {
124 writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock);
125 writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock);
126 writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock);
127 writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock);
128 writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock);
129 writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock);
130 writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock);
131 writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock);
132 }
133
134public:
135
136#if defined(FASTLED_TEENSY4)
137 #define DELAY_NS (1000 / (SPI_SPEED/1000000))
138 #define CLOCK_HI_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
139 #define CLOCK_LO_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
140#else
142 #define MIN_DELAY ((NS(35)>3) ? (NS(35) - 3) : 1)
143
145 #define CLOCK_HI_DELAY do { delaycycles<MIN_DELAY>(); delaycycles<((SPI_SPEED > 10) ? (((SPI_SPEED-6) / 2) - MIN_DELAY) : (SPI_SPEED))>(); } while(0);
147 #define CLOCK_LO_DELAY do { delaycycles<((SPI_SPEED > 10) ? ((SPI_SPEED-6) / 2) : (SPI_SPEED))>(); } while(0);
148#endif
149
153 template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
154 //cli();
155 if(b & (1 << BIT)) {
157#ifdef ESP32
158 // try to ensure we never have adjacent write opcodes to the same register
162#else
165#endif
166 } else {
169#ifdef ESP32
170 // try to ensure we never have adjacent write opcodes to the same register
172#else
174#endif
175 }
176 //sei();
177 }
178
179private:
181 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
182 if(b & (1 << BIT)) {
183 FastPin<DATA_PIN>::hi(datapin);
186 } else {
187 FastPin<DATA_PIN>::lo(datapin);
190 }
191
192 }
193
196 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
197 data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
198 // // only need to explicitly set clock hi if clock and data are on different ports
199 if(b & (1 << BIT)) {
200 FastPin<DATA_PIN>::fastset(datapin, hival);
203 } else {
204 // FL_NOP;
205 FastPin<DATA_PIN>::fastset(datapin, loval);
208 }
209 }
210
213 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, data_ptr_t clockdatapin,
214 data_t datahiclockhi, data_t dataloclockhi,
215 data_t datahiclocklo, data_t dataloclocklo) {
216#if 0
217 writeBit<BIT>(b);
218#else
219 if(b & (1 << BIT)) {
220 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo);
221 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY;
222 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY;
223 } else {
224 // FL_NOP;
225 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo);
226 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY;
227 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY;
228 }
229#endif
230 }
231
232public:
233
239 void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<SELECT_PIN>::hi(); }
240
242 void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::lo(); }
243
248 void writeBytesValue(uint8_t value, int len) {
249 select();
250 writeBytesValueRaw(value, len);
251 release();
252 }
253
256 static void writeBytesValueRaw(uint8_t value, int len) {
257#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
258 // TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled. Need
259 // to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall
260 // back to the degenerative code below
261 while(len--) {
262 writeByte(value);
263 }
264#else
266
268 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
269 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
275 while(len--) {
276 writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo);
277 }
278
279 } else {
280 // If data and clock are on the same port then we can combine setting the data and clock pins
285
286 while(len--) {
287 writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
288 }
289 }
290#endif
291 }
292
298 template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
299 select();
300#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
301 uint8_t *end = data + len;
302 while(data != end) {
303 writeByte(D::adjust(*data++));
304 }
305#else
308
310 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
311 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
316 uint8_t *end = data + len;
317
318 while(data != end) {
319 writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
320 }
321
322 } else {
323 // FastPin<CLOCK_PIN>::hi();
324 // If data and clock are on the same port then we can combine setting the data and clock pins
329
330 uint8_t *end = data + len;
331
332 while(data != end) {
333 writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
334 }
335 // FastPin<CLOCK_PIN>::lo();
336 }
337#endif
338 D::postBlock(len);
339 release();
340 }
341
345 void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
346
347
354 template <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
355 FASTLED_UNUSED(context);
356 select();
357 int len = pixels.mLen;
358
359#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
360 // If interrupts or other things may be generating output while we're working on things, then we need
361 // to use this block
362 while(pixels.has(1)) {
363 if(FLAGS & FLAG_START_BIT) {
364 writeBit<0>(1);
365 }
366 writeByte(D::adjust(pixels.loadAndScale0()));
367 writeByte(D::adjust(pixels.loadAndScale1()));
368 writeByte(D::adjust(pixels.loadAndScale2()));
369 pixels.advanceData();
370 pixels.stepDithering();
371 }
372#else
373 // 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)
374 // then we can use a bunch of optimizations in here
376
379 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
380 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
385
386 while(pixels.has(1)) {
387 if(FLAGS & FLAG_START_BIT) {
388 writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
389 }
390 writeByte(D::adjust(pixels.loadAndScale0()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
391 writeByte(D::adjust(pixels.loadAndScale1()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
392 writeByte(D::adjust(pixels.loadAndScale2()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
393 pixels.advanceData();
394 pixels.stepDithering();
395 }
396
397 } else {
398 // If data and clock are on the same port then we can combine setting the data and clock pins
403
404 while(pixels.has(1)) {
405 if(FLAGS & FLAG_START_BIT) {
406 writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
407 }
408 writeByte(D::adjust(pixels.loadAndScale0()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
409 writeByte(D::adjust(pixels.loadAndScale1()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
410 writeByte(D::adjust(pixels.loadAndScale2()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
411 pixels.advanceData();
412 pixels.stepDithering();
413 }
414 }
415#endif
416 D::postBlock(len);
417 release();
418 }
419};
420
422
423#endif
central include file for FastLED, defines the CFastLED class/object
static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin)
writeByte() implementation with data/clock registers passed in.
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, data_ptr_t datapin, data_t hival, data_t loval, clock_t hiclock, clock_t loclock)
writeByte() implementation with the data register passed in and prebaked values for data hi w/clock h...
static FASTLED_FORCE_INLINE void writeBit(uint8_t b, data_ptr_t clockdatapin, data_t datahiclockhi, data_t dataloclockhi, data_t datahiclocklo, data_t dataloclocklo)
The version of writeBit() to use when clock and data are on the same port with precomputed values for...
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 writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin, data_t hival, data_t loval, clock_t hiclock, clock_t loclock)
writeByte() implementation with not just registers passed in, but pre-baked values for said registers...
Selectable * m_pSelect
SPI chip select.
static FASTLED_FORCE_INLINE void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin, data_t hival, data_t loval, clock_t hiclock, clock_t loclock)
The version of writeBit() to use when clock and data are on separate pins with precomputed values for...
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.
FastPin< CLOCK_PIN >::port_t clock_t
static void writeBit(uint8_t b)
Write the BIT'th bit out via SPI, setting the data pin then strobing the clock.
FastPin< DATA_PIN >::port_t data_t
static void writeByteNoWait(uint8_t b)
Write a single byte over SPI without waiting.
FastPin< DATA_PIN >::port_ptr_t data_ptr_t
FastPin< CLOCK_PIN >::port_ptr_t clock_ptr_t
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.
static FASTLED_FORCE_INLINE void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin)
Write the BIT'th bit out via SPI, setting the data pin then strobing the clock, using the passed in p...
static port_t hival()
Gets the state of the port with this pin HIGH
Definition fastpin.h:265
RwReg port_t
type for a pin read/write register, non-volatile
Definition fastpin.h:236
static void toggle()
Toggle the pin.
Definition fastpin.h:252
static port_t loval()
Gets the state of the port with this pin LOW
Definition fastpin.h:267
static port_t mask()
Get the pin mask.
Definition fastpin.h:271
volatile RwReg * port_ptr_t
type for a pin read/write register, volatile
Definition fastpin.h:235
static void lo()
Set the pin state to LOW
Definition fastpin.h:246
static port_ptr_t port()
Get the output state of the port.
Definition fastpin.h:269
static void setOutput()
Set the pin mode as OUTPUT
Definition fastpin.h:239
static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val)
Set the state of a port.
Definition fastpin.h:262
static void hi()
Set the pin state to HIGH
Definition fastpin.h:244
Abstract class for "selectable" things.
Definition fastpin.h:36
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.
#define FASTLED_FORCE_INLINE
Definition force_inline.h:6
#define FASTLED_NAMESPACE_END
Definition namespace.h:23
#define FASTLED_REGISTER
Helper macro to replace the deprecated 'register' keyword if we're using modern C++ where it's been r...
Definition register.h:8
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.
Pixel controller class.
#define FASTLED_UNUSED(x)
Definition unused.h:3