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#include "fl/int.h"
14
16
18
26template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, fl::u32 SPI_SPEED>
28 // The data types for pointers to the pin port - typedef'd here from the ::Pin definition because on AVR these
29 // are pointers to 8 bit values, while on ARM they are 32 bit
32
33 // The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports
34 // are 8 bits wide while on arm they are 32.
38
39public:
43 AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
44
47 void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
48
50 void init() {
51 // set the pins to output and make sure the select is released (which apparently means hi? This is a bit
52 // confusing to me)
55 release();
56 }
57
60 static void stop() { }
61
64 static void wait() __attribute__((always_inline)) { }
66 static void waitFully() __attribute__((always_inline)) { wait(); }
67
69 static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
71 static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
72
74 static void writeWord(fl::u16 w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
75
78 static void writeByte(uint8_t b) {
79 writeBit<7>(b);
80 writeBit<6>(b);
81 writeBit<5>(b);
82 writeBit<4>(b);
83 writeBit<3>(b);
84 writeBit<2>(b);
85 writeBit<1>(b);
86 writeBit<0>(b);
87 }
88
89private:
91 static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
92 writeBit<7>(b, clockpin, datapin);
93 writeBit<6>(b, clockpin, datapin);
94 writeBit<5>(b, clockpin, datapin);
95 writeBit<4>(b, clockpin, datapin);
96 writeBit<3>(b, clockpin, datapin);
97 writeBit<2>(b, clockpin, datapin);
98 writeBit<1>(b, clockpin, datapin);
99 writeBit<0>(b, clockpin, datapin);
100 }
101
105 static void writeByte(uint8_t b, data_ptr_t datapin,
106 data_t hival, data_t loval,
107 clock_t hiclock, clock_t loclock) {
108 writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
109 writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
110 writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
111 writeBit<4>(b, datapin, hival, loval, hiclock, loclock);
112 writeBit<3>(b, datapin, hival, loval, hiclock, loclock);
113 writeBit<2>(b, datapin, hival, loval, hiclock, loclock);
114 writeBit<1>(b, datapin, hival, loval, hiclock, loclock);
115 writeBit<0>(b, datapin, hival, loval, hiclock, loclock);
116 }
117
122 static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
123 data_t hival, data_t loval,
124 clock_t hiclock, clock_t loclock) {
125 writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock);
126 writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock);
127 writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock);
128 writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock);
129 writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock);
130 writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock);
131 writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock);
132 writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock);
133 }
134
135public:
136
137#if defined(FASTLED_TEENSY4)
138 #define DELAY_NS (1000 / (SPI_SPEED/1000000))
139 #define CLOCK_HI_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
140 #define CLOCK_LO_DELAY do { delayNanoseconds((DELAY_NS/4)); } while(0);
141#else
143 #define MIN_DELAY ((NS(35)>3) ? (NS(35) - 3) : 1)
144
146 #define CLOCK_HI_DELAY do { delaycycles<MIN_DELAY>(); delaycycles<((SPI_SPEED > 10) ? (((SPI_SPEED-6) / 2) - MIN_DELAY) : (SPI_SPEED))>(); } while(0);
148 #define CLOCK_LO_DELAY do { delaycycles<((SPI_SPEED > 10) ? ((SPI_SPEED-6) / 2) : (SPI_SPEED))>(); } while(0);
149#endif
150
154 template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
155 //cli();
156 if(b & (1 << BIT)) {
158#ifdef ESP32
159 // try to ensure we never have adjacent write opcodes to the same register
163#else
166#endif
167 } else {
170#ifdef ESP32
171 // try to ensure we never have adjacent write opcodes to the same register
173#else
175#endif
176 }
177 //sei();
178 }
179
180private:
182 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
183 if(b & (1 << BIT)) {
184 FastPin<DATA_PIN>::hi(datapin);
187 } else {
188 FastPin<DATA_PIN>::lo(datapin);
191 }
192
193 }
194
197 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
198 data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
199 // // only need to explicitly set clock hi if clock and data are on different ports
200 if(b & (1 << BIT)) {
201 FastPin<DATA_PIN>::fastset(datapin, hival);
204 } else {
205 // FL_NOP;
206 FastPin<DATA_PIN>::fastset(datapin, loval);
209 }
210 }
211
214 template <uint8_t BIT> FASTLED_FORCE_INLINE static void writeBit(uint8_t b, data_ptr_t clockdatapin,
215 data_t datahiclockhi, data_t dataloclockhi,
216 data_t datahiclocklo, data_t dataloclocklo) {
217#if 0
218 writeBit<BIT>(b);
219#else
220 if(b & (1 << BIT)) {
221 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo);
222 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY;
223 FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY;
224 } else {
225 // FL_NOP;
226 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo);
227 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY;
228 FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY;
229 }
230#endif
231 }
232
233public:
234
240 void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<SELECT_PIN>::hi(); }
241
243 void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::lo(); }
244
249 void writeBytesValue(uint8_t value, int len) {
250 select();
251 writeBytesValueRaw(value, len);
252 release();
253 }
254
257 static void writeBytesValueRaw(uint8_t value, int len) {
258#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
259 // TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled. Need
260 // to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall
261 // back to the degenerative code below
262 while(len--) {
263 writeByte(value);
264 }
265#else
267
269 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
270 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
276 while(len--) {
277 writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo);
278 }
279
280 } else {
281 // If data and clock are on the same port then we can combine setting the data and clock pins
286
287 while(len--) {
288 writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
289 }
290 }
291#endif
292 }
293
299 template <class D> void writeBytes(FASTLED_REGISTER uint8_t *data, int len) {
300 select();
301#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
302 uint8_t *end = data + len;
303 while(data != end) {
304 writeByte(D::adjust(*data++));
305 }
306#else
309
311 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
312 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
317 uint8_t *end = data + len;
318
319 while(data != end) {
320 writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
321 }
322
323 } else {
324 // FastPin<CLOCK_PIN>::hi();
325 // If data and clock are on the same port then we can combine setting the data and clock pins
330
331 uint8_t *end = data + len;
332
333 while(data != end) {
334 writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
335 }
336 // FastPin<CLOCK_PIN>::lo();
337 }
338#endif
339 D::postBlock(len);
340 release();
341 }
342
346 void writeBytes(FASTLED_REGISTER uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
347
348
355 template <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels, void* context = NULL) {
356 FASTLED_UNUSED(context);
357 select();
358 int len = pixels.mLen;
359
360#ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
361 // If interrupts or other things may be generating output while we're working on things, then we need
362 // to use this block
363 while(pixels.has(1)) {
364 if(FLAGS & FLAG_START_BIT) {
365 writeBit<0>(1);
366 }
367 writeByte(D::adjust(pixels.loadAndScale0()));
368 writeByte(D::adjust(pixels.loadAndScale1()));
369 writeByte(D::adjust(pixels.loadAndScale2()));
370 pixels.advanceData();
371 pixels.stepDithering();
372 }
373#else
374 // 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)
375 // then we can use a bunch of optimizations in here
377
380 // If data and clock are on different ports, then writing a bit will consist of writing the value foor
381 // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
386
387 while(pixels.has(1)) {
388 if(FLAGS & FLAG_START_BIT) {
389 writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
390 }
391 writeByte(D::adjust(pixels.loadAndScale0()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
392 writeByte(D::adjust(pixels.loadAndScale1()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
393 writeByte(D::adjust(pixels.loadAndScale2()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
394 pixels.advanceData();
395 pixels.stepDithering();
396 }
397
398 } else {
399 // If data and clock are on the same port then we can combine setting the data and clock pins
404
405 while(pixels.has(1)) {
406 if(FLAGS & FLAG_START_BIT) {
407 writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
408 }
409 writeByte(D::adjust(pixels.loadAndScale0()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
410 writeByte(D::adjust(pixels.loadAndScale1()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
411 writeByte(D::adjust(pixels.loadAndScale2()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
412 pixels.advanceData();
413 pixels.stepDithering();
414 }
415 }
416#endif
417 D::postBlock(len);
418 release();
419 }
420};
421
423
424#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.
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 writeWord(fl::u16 w)
Write a word (two bytes) over SPI.
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:339
RwReg port_t
type for a pin read/write register, non-volatile
Definition fastpin.h:310
static void toggle()
Toggle the pin.
Definition fastpin.h:326
static port_t loval()
Gets the state of the port with this pin LOW
Definition fastpin.h:341
static port_t mask()
Get the pin mask.
Definition fastpin.h:345
volatile RwReg * port_ptr_t
type for a pin read/write register, volatile
Definition fastpin.h:309
static void lo()
Set the pin state to LOW
Definition fastpin.h:320
static port_ptr_t port()
Get the output state of the port.
Definition fastpin.h:343
static void setOutput()
Set the pin mode as OUTPUT
Definition fastpin.h:313
static void fastset(FASTLED_REGISTER port_ptr_t port, FASTLED_REGISTER port_t val)
Set the state of a port.
Definition fastpin.h:336
static void hi()
Set the pin state to HIGH
Definition fastpin.h:318
Abstract class for "selectable" things.
Definition fastpin.h:38
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_NAMESPACE_BEGIN
Definition namespace.h:22
#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:9
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:4