FastLED  3.1
fastspi_bitbang.h
1 #ifndef __INC_FASTSPI_BITBANG_H
2 #define __INC_FASTSPI_BITBANG_H
3 
4 #include "FastLED.h"
5 
6 #include "fastled_delay.h"
7 
8 FASTLED_NAMESPACE_BEGIN
9 
11 //
12 // Software SPI (aka bit-banging) support - with aggressive optimizations for when the clock and data pin are on the same port
13 //
14 // TODO: Replace the select pin definition with a set of pins, to allow using mux hardware for routing in the future
15 //
17 
18 template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t SPI_SPEED>
20  // The data types for pointers to the pin port - typedef'd here from the Pin definition because on avr these
21  // are pointers to 8 bit values, while on arm they are 32 bit
22  typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
23  typedef typename FastPin<CLOCK_PIN>::port_ptr_t clock_ptr_t;
24 
25  // The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports
26  // are 8 bits wide while on arm they are 32.
27  typedef typename FastPin<DATA_PIN>::port_t data_t;
28  typedef typename FastPin<CLOCK_PIN>::port_t clock_t;
29  Selectable *m_pSelect;
30 
31 public:
32  AVRSoftwareSPIOutput() { m_pSelect = NULL; }
33  AVRSoftwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
34  void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
35 
36  void init() {
37  // set the pins to output and make sure the select is released (which apparently means hi? This is a bit
38  // confusing to me)
41  release();
42  }
43 
44  // stop the SPI output. Pretty much a NOP with software, as there's no registers to kick
45  static void stop() { }
46 
47  // wait until the SPI subsystem is ready for more data to write. A NOP when bitbanging
48  static void wait() __attribute__((always_inline)) { }
49  static void waitFully() __attribute__((always_inline)) { wait(); }
50 
51  static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); }
52  static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { writeByte(b); wait(); }
53 
54  static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
55 
56  // naive writeByte implelentation, simply calls writeBit on the 8 bits in the byte.
57  static void writeByte(uint8_t b) {
58  writeBit<7>(b);
59  writeBit<6>(b);
60  writeBit<5>(b);
61  writeBit<4>(b);
62  writeBit<3>(b);
63  writeBit<2>(b);
64  writeBit<1>(b);
65  writeBit<0>(b);
66  }
67 
68 private:
69  // writeByte implementation with data/clock registers passed in.
70  static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
71  writeBit<7>(b, clockpin, datapin);
72  writeBit<6>(b, clockpin, datapin);
73  writeBit<5>(b, clockpin, datapin);
74  writeBit<4>(b, clockpin, datapin);
75  writeBit<3>(b, clockpin, datapin);
76  writeBit<2>(b, clockpin, datapin);
77  writeBit<1>(b, clockpin, datapin);
78  writeBit<0>(b, clockpin, datapin);
79  }
80 
81  // writeByte implementation with the data register passed in and prebaked values for data hi w/clock hi and
82  // low and data lo w/clock hi and lo. This is to be used when clock and data are on the same GPIO register,
83  // can get close to getting a bit out the door in 2 clock cycles!
84  static void writeByte(uint8_t b, data_ptr_t datapin,
85  data_t hival, data_t loval,
86  clock_t hiclock, clock_t loclock) {
87  writeBit<7>(b, datapin, hival, loval, hiclock, loclock);
88  writeBit<6>(b, datapin, hival, loval, hiclock, loclock);
89  writeBit<5>(b, datapin, hival, loval, hiclock, loclock);
90  writeBit<4>(b, datapin, hival, loval, hiclock, loclock);
91  writeBit<3>(b, datapin, hival, loval, hiclock, loclock);
92  writeBit<2>(b, datapin, hival, loval, hiclock, loclock);
93  writeBit<1>(b, datapin, hival, loval, hiclock, loclock);
94  writeBit<0>(b, datapin, hival, loval, hiclock, loclock);
95  }
96 
97  // writeByte implementation with not just registers passed in, but pre-baked values for said registers for
98  // data hi/lo and clock hi/lo values. Note: weird things will happen if this method is called in cases where
99  // the data and clock pins are on the same port! Don't do that!
100  static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
101  data_t hival, data_t loval,
102  clock_t hiclock, clock_t loclock) {
103  writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock);
104  writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock);
105  writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock);
106  writeBit<4>(b, clockpin, datapin, hival, loval, hiclock, loclock);
107  writeBit<3>(b, clockpin, datapin, hival, loval, hiclock, loclock);
108  writeBit<2>(b, clockpin, datapin, hival, loval, hiclock, loclock);
109  writeBit<1>(b, clockpin, datapin, hival, loval, hiclock, loclock);
110  writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock);
111  }
112 
113 public:
114 
115  // We want to make sure that the clock pulse is held high for a nininum of 35ns.
116  #define MIN_DELAY (NS(35) - 3)
117 
118  #define CLOCK_HI_DELAY delaycycles<MIN_DELAY>(); delaycycles<(((SPI_SPEED-6) / 2) - MIN_DELAY)>();
119  #define CLOCK_LO_DELAY delaycycles<(((SPI_SPEED-6) / 4))>();
120 
121  // write the BIT'th bit out via spi, setting the data pin then strobing the clcok
122  template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) {
123  //cli();
124  if(b & (1 << BIT)) {
126  FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
127  FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY;
128  } else {
130  FastPin<CLOCK_PIN>::hi(); CLOCK_HI_DELAY;
131  FastPin<CLOCK_PIN>::lo(); CLOCK_LO_DELAY;
132  }
133  //sei();
134  }
135 
136 private:
137  // write the BIT'th bit out via spi, setting the data pin then strobing the clock, using the passed in pin registers to accelerate access if needed
138  template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) {
139  if(b & (1 << BIT)) {
140  FastPin<DATA_PIN>::hi(datapin);
141  FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
142  FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY;
143  } else {
144  FastPin<DATA_PIN>::lo(datapin);
145  FastPin<CLOCK_PIN>::hi(clockpin); CLOCK_HI_DELAY;
146  FastPin<CLOCK_PIN>::lo(clockpin); CLOCK_LO_DELAY;
147  }
148 
149  }
150 
151  // the version of write to use when clock and data are on separate pins with precomputed values for setting
152  // the clock and data pins
153  template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin,
154  data_t hival, data_t loval, clock_t hiclock, clock_t loclock) {
155  // // only need to explicitly set clock hi if clock and data are on different ports
156  if(b & (1 << BIT)) {
157  FastPin<DATA_PIN>::fastset(datapin, hival);
158  FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
159  FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY;
160  } else {
161  // NOP;
162  FastPin<DATA_PIN>::fastset(datapin, loval);
163  FastPin<CLOCK_PIN>::fastset(clockpin, hiclock); CLOCK_HI_DELAY;
164  FastPin<CLOCK_PIN>::fastset(clockpin, loclock); CLOCK_LO_DELAY;
165  }
166  }
167 
168  // the version of write to use when clock and data are on the same port with precomputed values for the various
169  // combinations
170  template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, data_ptr_t clockdatapin,
171  data_t datahiclockhi, data_t dataloclockhi,
172  data_t datahiclocklo, data_t dataloclocklo) {
173 #if 0
174  writeBit<BIT>(b);
175 #else
176  if(b & (1 << BIT)) {
177  FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo);
178  FastPin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); CLOCK_HI_DELAY;
179  FastPin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); CLOCK_LO_DELAY;
180  } else {
181  // NOP;
182  FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo);
183  FastPin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); CLOCK_HI_DELAY;
184  FastPin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); CLOCK_LO_DELAY;
185  }
186 #endif
187  }
188 public:
189 
190  // select the SPI output (TODO: research whether this really means hi or lo. Alt TODO: move select responsibility out of the SPI classes
191  // entirely, make it up to the caller to remember to lock/select the line?)
192  void select() { if(m_pSelect != NULL) { m_pSelect->select(); } } // FastPin<SELECT_PIN>::hi(); }
193 
194  // release the SPI line
195  void release() { if(m_pSelect != NULL) { m_pSelect->release(); } } // FastPin<SELECT_PIN>::lo(); }
196 
197  // Write out len bytes of the given value out over SPI. Useful for quickly flushing, say, a line of 0's down the line.
198  void writeBytesValue(uint8_t value, int len) {
199  select();
200  writeBytesValueRaw(value, len);
201  release();
202  }
203 
204  static void writeBytesValueRaw(uint8_t value, int len) {
205 #ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
206  // TODO: Weird things may happen if software bitbanging SPI output and other pins on the output reigsters are being twiddled. Need
207  // to allow specifying whether or not exclusive i/o access is allowed during this process, and if i/o access is not allowed fall
208  // back to the degenerative code below
209  while(len--) {
210  writeByte(value);
211  }
212 #else
213  register data_ptr_t datapin = FastPin<DATA_PIN>::port();
214 
216  // If data and clock are on different ports, then writing a bit will consist of writing the value foor
217  // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
218  register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
219  register data_t datahi = FastPin<DATA_PIN>::hival();
220  register data_t datalo = FastPin<DATA_PIN>::loval();
221  register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
222  register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
223  while(len--) {
224  writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo);
225  }
226 
227  } else {
228  // If data and clock are on the same port then we can combine setting the data and clock pins
229  register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
230  register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
231  register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
232  register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
233 
234  while(len--) {
235  writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
236  }
237  }
238 #endif
239  }
240 
241  // write a block of len uint8_ts out. Need to type this better so that explicit casts into the call aren't required.
242  // note that this template version takes a class parameter for a per-byte modifier to the data.
243  template <class D> void writeBytes(register uint8_t *data, int len) {
244  select();
245 #ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
246  uint8_t *end = data + len;
247  while(data != end) {
248  writeByte(D::adjust(*data++));
249  }
250 #else
251  register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
252  register data_ptr_t datapin = FastPin<DATA_PIN>::port();
253 
255  // If data and clock are on different ports, then writing a bit will consist of writing the value foor
256  // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
257  register data_t datahi = FastPin<DATA_PIN>::hival();
258  register data_t datalo = FastPin<DATA_PIN>::loval();
259  register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
260  register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
261  uint8_t *end = data + len;
262 
263  while(data != end) {
264  writeByte(D::adjust(*data++), clockpin, datapin, datahi, datalo, clockhi, clocklo);
265  }
266 
267  } else {
268  // FastPin<CLOCK_PIN>::hi();
269  // If data and clock are on the same port then we can combine setting the data and clock pins
270  register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
271  register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
272  register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
273  register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
274 
275  uint8_t *end = data + len;
276 
277  while(data != end) {
278  writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
279  }
280  // FastPin<CLOCK_PIN>::lo();
281  }
282 #endif
283  D::postBlock(len);
284  release();
285  }
286 
287  // default version of writing a block of data out to the SPI port, with no data modifications being made
288  void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
289 
290 
291  // write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
292  // parameters indicate how many uint8_ts to skip at the beginning of each grouping, as well as a class specifying a per
293  // byte of data modification to be made. (See DATA_NOP above)
294  template <uint8_t FLAGS, class D, EOrder RGB_ORDER> __attribute__((noinline)) void writePixels(PixelController<RGB_ORDER> pixels) {
295  select();
296  int len = pixels.mLen;
297 
298 #ifdef FAST_SPI_INTERRUPTS_WRITE_PINS
299  // If interrupts or other things may be generating output while we're working on things, then we need
300  // to use this block
301  while(pixels.has(1)) {
302  if(FLAGS & FLAG_START_BIT) {
303  writeBit<0>(1);
304  }
305  writeByte(D::adjust(pixels.loadAndScale0()));
306  writeByte(D::adjust(pixels.loadAndScale1()));
307  writeByte(D::adjust(pixels.loadAndScale2()));
308  pixels.advanceData();
309  pixels.stepDithering();
310  }
311 #else
312  // 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)
313  // then we can use a bunch of optimizations in here
314  register data_ptr_t datapin = FastPin<DATA_PIN>::port();
315 
317  register clock_ptr_t clockpin = FastPin<CLOCK_PIN>::port();
318  // If data and clock are on different ports, then writing a bit will consist of writing the value foor
319  // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line
320  register data_t datahi = FastPin<DATA_PIN>::hival();
321  register data_t datalo = FastPin<DATA_PIN>::loval();
322  register clock_t clockhi = FastPin<CLOCK_PIN>::hival();
323  register clock_t clocklo = FastPin<CLOCK_PIN>::loval();
324 
325  while(pixels.has(1)) {
326  if(FLAGS & FLAG_START_BIT) {
327  writeBit<0>(1, clockpin, datapin, datahi, datalo, clockhi, clocklo);
328  }
329  writeByte(D::adjust(pixels.loadAndScale0()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
330  writeByte(D::adjust(pixels.loadAndScale1()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
331  writeByte(D::adjust(pixels.loadAndScale2()), clockpin, datapin, datahi, datalo, clockhi, clocklo);
332  pixels.advanceData();
333  pixels.stepDithering();
334  }
335 
336  } else {
337  // If data and clock are on the same port then we can combine setting the data and clock pins
338  register data_t datahi_clockhi = FastPin<DATA_PIN>::hival() | FastPin<CLOCK_PIN>::mask();
339  register data_t datalo_clockhi = FastPin<DATA_PIN>::loval() | FastPin<CLOCK_PIN>::mask();
340  register data_t datahi_clocklo = FastPin<DATA_PIN>::hival() & ~FastPin<CLOCK_PIN>::mask();
341  register data_t datalo_clocklo = FastPin<DATA_PIN>::loval() & ~FastPin<CLOCK_PIN>::mask();
342 
343  while(pixels.has(1)) {
344  if(FLAGS & FLAG_START_BIT) {
345  writeBit<0>(1, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
346  }
347  writeByte(D::adjust(pixels.loadAndScale0()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
348  writeByte(D::adjust(pixels.loadAndScale1()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
349  writeByte(D::adjust(pixels.loadAndScale2()), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo);
350  pixels.advanceData();
351  pixels.stepDithering();
352  }
353  }
354 #endif
355  D::postBlock(len);
356  release();
357  }
358 };
359 
360 FASTLED_NAMESPACE_END
361 
362 #endif
central include file for FastLED, defines the CFastLED class/object
Utility functions and classes for managing delaycycles.
The simplest level of Pin class.
Definition: fastpin.h:160