Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/FastLED/FastLED.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FastSPI_LED2.h31
-rw-r--r--clockless.h6
-rw-r--r--examples/Fast2Dev/Fast2Dev.ino28
-rw-r--r--fastspi.h154
4 files changed, 176 insertions, 43 deletions
diff --git a/FastSPI_LED2.h b/FastSPI_LED2.h
index 1ede0f47..4cb6048b 100644
--- a/FastSPI_LED2.h
+++ b/FastSPI_LED2.h
@@ -19,9 +19,9 @@ public:
static uint8_t adjust(register uint8_t data) { return (data>>1) | 0x80; }
};
-template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t LATCH_PIN>
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t LATCH_PIN, uint8_t SPI_SPEED = 0 >
class LPD8806Controller : public CLEDController {
- typedef AVRSPIOutput<DATA_PIN, CLOCK_PIN, LATCH_PIN, 0> SPI;
+ typedef AVRSPIOutput<DATA_PIN, CLOCK_PIN, LATCH_PIN, SPI_SPEED> SPI;
void clearLine(int nLeds) {
int n = ((nLeds + 63) >> 6);
@@ -51,9 +51,9 @@ public:
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t LATCH_PIN>
+template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t LATCH_PIN, uint8_t SPI_SPEED = 1>
class WS2801Controller : public CLEDController {
- typedef AVRSPIOutput<DATA_PIN, CLOCK_PIN, LATCH_PIN, 0> SPI;
+ typedef AVRSPIOutput<DATA_PIN, CLOCK_PIN, LATCH_PIN, SPI_SPEED> SPI;
public:
virtual void init() {
@@ -69,7 +69,6 @@ public:
}
};
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Clockless template instantiations
@@ -78,18 +77,30 @@ public:
// 500ns, 1500ns, 500ns
template <uint8_t DATA_PIN>
-class UCS1903Controller400Mhz : public ClocklessController<DATA_PIN, 8, 24, 8> {};
+class UCS1903Controller400Mhz : public ClocklessController<DATA_PIN, NS(500), NS(1500), NS(500)> {};
+#if NO_TIME(500, 1500, 500)
+#pragma message "No enough clock cycles available for the UCS103"
+#endif
// 312.5ns, 312.5ns, 325ns
template <uint8_t DATA_PIN>
-class TM1809Controller800Mhz : public ClocklessController<DATA_PIN, 5, 5, 10> {};
+class TM1809Controller800Mhz : public ClocklessController<DATA_PIN, NS(350), NS(350), NS(550)> {};
+#if NO_TIME(350, 350, 550)
+#pragma message "No enough clock cycles available for the UCS103"
+#endif
-// 312.5ns, 312.5ns, 325ns
+// 380s, 380ns, 725ns
template <uint8_t DATA_PIN>
-class WS2811Controller800Mhz : public ClocklessController<DATA_PIN, 5, 5, 10> {};
+class WS2811Controller800Mhz : public ClocklessController<DATA_PIN, NS(350), NS(350), NS(550)> {};
+#if NO_TIME(350, 350, 550)
+#pragma message "No enough clock cycles available for the UCS103"
+#endif
// 750NS, 750NS, 750NS
template <uint8_t DATA_PIN>
-class TM1803Controller400Mhz : public ClocklessController<DATA_PIN, 12, 12, 12> {};
+class TM1803Controller400Mhz : public ClocklessController<DATA_PIN, NS(750), NS(750), NS(750)> {};
+#if NO_TIME(750, 750, 750)
+#pragma message "No enough clock cycles available for the UCS103"
+#endif
#endif \ No newline at end of file
diff --git a/clockless.h b/clockless.h
index 6a62a52c..631adc86 100644
--- a/clockless.h
+++ b/clockless.h
@@ -4,6 +4,12 @@
#include "controller.h"
#include <avr/interrupt.h> // for cli/se definitions
+// Macro to convert from nano-seconds to clocks
+#define NS(_NS) (_NS / (1000 / (F_CPU / 1000000L)))
+
+// Macro for making sure there's enough time available
+#define NO_TIME(A, B, C) (NS(A) < 3 || NS(B) < 2 || NS(C) < 6)
+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
diff --git a/examples/Fast2Dev/Fast2Dev.ino b/examples/Fast2Dev/Fast2Dev.ino
index fe382c35..580d67a6 100644
--- a/examples/Fast2Dev/Fast2Dev.ino
+++ b/examples/Fast2Dev/Fast2Dev.ino
@@ -38,14 +38,14 @@ struct CRGB leds[NUM_LEDS];
// Hardware SPI - .652ms for an 86 led frame @8Mhz (3.1Mbps?), .913ms @4Mhz 1.434ms @2Mhz
// Hardware SPIr2 - .539ms @8Mhz, .799 @4Mhz, 1.315ms @2Mhz
// With the wait ordering reversed, .520ms at 8Mhz, .779ms @4Mhz, 1.3ms @2Mhz
-// LPD8806Controller<11, 13, 10> LED;
+LPD8806Controller<11, 13, 10> LED;
// LPD8806Controller<2, 1, 0> LED; // teensy pins
// Same Port, non-hardware SPI - 1.2ms for an 86 led frame, 1.12ms with large switch
// r2 - .939ms without large switch .823ms with large switch
// r3 - .824ms removing 0 balancing nop, .823 with large switch removing balancing
//LPD8806Controller<12, 13, 10> LED;
-LPD8806Controller<14, 1, 10> LED; // teensy pins
+// LPD8806Controller<14, 1, 10> LED; // teensy pins
// Different Port, non-hardware SPI - 1.47ms for an 86 led frame
// Different PortR2, non-hardware SPI - 1.07ms for an 86 led frame
@@ -54,7 +54,7 @@ LPD8806Controller<14, 1, 10> LED; // teensy pins
// Hardware SPI - .652ms for an 86 led frame @8Mhz (3.1Mbps?), .913ms @4Mhz 1.434ms @2Mhz
// With the wait ordering reversed, .520ms at 8Mhz, .779ms @4Mhz, 1.3ms @2Mhz
-// WS2801Controller<11, 13, 10> LED;
+// WS2801Controller<11, 13, 10, 0> LED;
// Same Port, non-hardware SPI - 1.2ms for an 86 led frame, 1.12ms with large switch
// WS2801Controller<12, 13, 10> LED;
@@ -64,7 +64,7 @@ LPD8806Controller<14, 1, 10> LED; // teensy pins
// TM1809Controller800Mhz<6> LED;
// UCS1903Controller400Mhz<7> LED;
-// WS2811Controller800Mhz<8> LED;
+// WS2811Controller800Mhz<6> LED;
// TM1803Controller400Mhz<5> LED;
// struct aLED { void init() { FastSPI_LED.setLeds(NUM_LEDS, (unsigned char*)leds); FastSPI_LED.setPin(8); FastSPI_LED.setChipset(CFastSPI_LED::SPI_LPD8806); FastSPI_LED.init(); FastSPI_LED.start();}; void showRGB(byte *, int) { FastSPI_LED.show();} } LED;
@@ -125,14 +125,14 @@ void loop() {
delay(20);
}
}
- // for(int i = 0; i < 64; i++) {
- // memset(leds, i, NUM_LEDS * 3);
- // LED.showRGB((byte*)leds, NUM_LEDS);;
- // delay(20);
- // }
- // for(int i = 64; i >= 0; i--) {
- // memset(leds, i, NUM_LEDS * 3);
- // LED.showRGB((byte*)leds, NUM_LEDS);;
- // delay(20);
- // }
+ for(int i = 0; i < 64; i++) {
+ memset(leds, i, NUM_LEDS * 3);
+ LED.showRGB((byte*)leds, NUM_LEDS);;
+ delay(20);
+ }
+ for(int i = 64; i >= 0; i--) {
+ memset(leds, i, NUM_LEDS * 3);
+ LED.showRGB((byte*)leds, NUM_LEDS);;
+ delay(20);
+ }
} \ No newline at end of file
diff --git a/fastspi.h b/fastspi.h
index 60c70a96..65f3bba2 100644
--- a/fastspi.h
+++ b/fastspi.h
@@ -68,6 +68,10 @@ public:
Pin<DATA_PIN>::setOutput();
Pin<LATCH_PIN>::setOutput();
Pin<CLOCK_PIN>::setOutput();
+ release();
+
+ // Flush out a bunch of 0's to clear out the line
+ writeBytesValue(0, 192*3);
}
static void wait() __attribute__((always_inline)) { }
@@ -162,8 +166,8 @@ public:
}
}
- static void latch() { Pin<LATCH_PIN>::hi(); }
- static void release() { Pin<LATCH_PIN>::lo(); }
+ static void latch() { Pin<LATCH_PIN>::lo(); }
+ static void release() { Pin<LATCH_PIN>::hi(); }
static void writeBytesValue(uint8_t value, int len) {
latch();
@@ -283,6 +287,105 @@ public:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
+// Hardware SPI support using USART registers and friends
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _LATCH_PIN, uint8_t _SPI_SPEED>
+class AVRUSARTSPIOutput {
+public:
+ static void init() {
+ Pin<_LATCH_PIN>::setOutput();
+ UBRR0 = 0;
+ UCSR0A = 1<<TXC0;
+
+ Pin<_CLOCK_PIN>::setOutpuut();
+ Pin<_DATA_PIN>::setOutput();
+
+ UCSR0C = _BV (UMSEL00) | _BV (UMSEL01); // Master SPI mode
+ UCSR0B = _BV (TXEN0) | _BV (RXEN0); // transmit enable and receive enable
+
+ // must be done last, see page 206
+ UBRR0 = 3; // 2 Mhz clock rate
+ }
+
+ static void wait() __attribute__((always_inline)) { while(!(UCSR0A & (1<<UDRE0))); }
+
+ static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR0 = b; }
+ static void writeByte(uint8_t b, volatile uint8_t *, volatile uint8_t *,
+ uint8_t , uint8_t , uint8_t , uint8_t ) __attribute__((always_inline)) { writeByte(b); }
+ static void writeByte(uint8_t b, volatile uint8_t *,
+ uint8_t , uint8_t , uint8_t , uint8_t ) __attribute__((always_inline)) { writeByte(b); }
+
+ static void writeByte(uint8_t b, volatile uint8_t*, volatile uint8_t*) __attribute__((always_inline)) { writeByte(b); }
+
+ template <uint8_t BIT> inline static void writeBit(uint8_t b, volatile uint8_t *clockpin, volatile uint8_t *datapin) {
+ if(b & (1 << BIT)) {
+ Pin<_DATA_PIN>::hi(datapin);
+ } else {
+ Pin<_DATA_PIN>::lo(datapin);
+ }
+
+ Pin<_CLOCK_PIN>::hi(clockpin);
+ Pin<_CLOCK_PIN>::lo(clockpin);
+ }
+
+ template <uint8_t BIT> inline static void writeBit(uint8_t b) {
+ register volatile uint8_t *clockpin = Pin<_CLOCK_PIN>::port();
+ register volatile uint8_t *datapin = Pin<_DATA_PIN>::port();
+ writeBit<BIT>(b, clockpin, datapin);
+ }
+
+ static void latch() { Pin<_LATCH_PIN>::lo(); }
+ static void release() {
+ // wait for all transmissions to finish
+ while ((UCSR0A & (1 <<TXC0)) == 0) {}
+ Pin<_LATCH_PIN>::hi();
+ }
+
+ static void writeBytesValue(uint8_t value, int len) {
+ latch();
+ while(len--) {
+ writeByte(value);
+ }
+ release();
+ }
+
+ // Write a block of n uint8_ts out
+ template <class D> static void writeBytes(register uint8_t *data, int len) {
+ uint8_t *end = data + len;
+ latch();
+ while(data != end) {
+ writeByte(D::adjust(*data++));
+ }
+ release();
+ }
+
+ static void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
+
+ // write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
+ // parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
+ template <uint8_t SKIP, class D> static void writeBytes3(register uint8_t *data, int len) {
+ uint8_t *end = data + len;
+ latch();
+ while(data != end) {
+ data += SKIP;
+ // a slight touch of delay here helps optimize the timing of the status register check loop
+ writeByte(D::adjust(*data++)); delaycycles<3>();
+ writeByte(D::adjust(*data++)); delaycycles<3>();
+ writeByte(D::adjust(*data++)); delaycycles<3>();
+ }
+ release();
+ }
+
+ template <uint8_t SKIP> static void writeBytes3(register uint8_t *data, int len) { writeBytes3<SKIP, DATA_NOP>(data, len); }
+ template <class D> static void writeBytes3(register uint8_t *data, int len) { writeBytes3<0, D>(data, len); }
+ static void writeBytes3(register uint8_t *data, int len) { writeBytes3<0, DATA_NOP>(data, len); }
+
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
// Hardware SPI support using SPDR registers and friends
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -297,6 +400,7 @@ public:
Pin<_DATA_PIN>::setOutput();
Pin<_LATCH_PIN>::setOutput();
Pin<_CLOCK_PIN>::setOutput();
+ release();
SPCR |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear out the prescalar bits
@@ -319,10 +423,12 @@ public:
else { SPSR &= ~ (1<<SPI2X); }
// push 192 0s to prime the spi stuff
+ latch();
SPDR = 0;
for(int i = 0; i < 191; i++) {
- writeByte(0);
+ writeByte(0); writeByte(0); writeByte(0);
}
+ release();
}
static void wait() __attribute__((always_inline)) { while(!(SPSR & (1<<SPIF))); }
@@ -330,12 +436,13 @@ public:
// All the write functions stub out and degrade to the SPDR write. The various parameter combinations are attempts to support
// bitbanging optimizations
static void writeByte(uint8_t b, volatile uint8_t *, volatile uint8_t *,
- uint8_t , uint8_t , uint8_t , uint8_t ) __attribute__((always_inline)) { wait(); SPDR=b; }
+ uint8_t , uint8_t , uint8_t , uint8_t ) __attribute__((always_inline)) { writeByte(b); }
static void writeByte(uint8_t b, volatile uint8_t *,
- uint8_t , uint8_t , uint8_t , uint8_t ) __attribute__((always_inline)) { wait(); SPDR=b; }
+ uint8_t , uint8_t , uint8_t , uint8_t ) __attribute__((always_inline)) { writeByte(b); }
- static void writeByte(uint8_t b, volatile uint8_t*, volatile uint8_t*) __attribute__((always_inline)) { wait(); SPDR=b; }
+ static void writeByte(uint8_t b, volatile uint8_t*, volatile uint8_t*) __attribute__((always_inline)) { writeByte(b); }
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPDR=b; }
+ static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; }
template <uint8_t BIT> inline static void writeBit(uint8_t b, volatile uint8_t *clockpin, volatile uint8_t *datapin) {
SPCR &= ~(1 << SPE);
@@ -352,12 +459,12 @@ public:
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
register volatile uint8_t *clockpin = Pin<_CLOCK_PIN>::port();
- register volatile uint8_t *datapin = Pin<_CLOCK_PIN>::port();
+ register volatile uint8_t *datapin = Pin<_DATA_PIN>::port();
writeBit<BIT>(b, clockpin, datapin);
}
- static void latch() { Pin<_LATCH_PIN>::hi(); }
- static void release() { Pin<_LATCH_PIN>::lo(); }
+ static void latch() { Pin<_LATCH_PIN>::lo(); }
+ static void release() { Pin<_LATCH_PIN>::hi(); }
static void writeBytesValue(uint8_t value, int len) {
latch();
@@ -372,7 +479,7 @@ public:
uint8_t *end = data + len;
latch();
while(data != end) {
- writeByte(D::adjust(*data++));
+ writeByteNoWait(D::adjust(*data++));
}
release();
}
@@ -387,9 +494,15 @@ public:
while(data != end) {
data += SKIP;
// a slight touch of delay here helps optimize the timing of the status register check loop
- writeByte(D::adjust(*data++)); delaycycles<3>();
- writeByte(D::adjust(*data++)); delaycycles<3>();
- writeByte(D::adjust(*data++)); delaycycles<3>();
+ if(_SPI_SPEED == 0) {
+ writeByteNoWait(D::adjust(*data++)); delaycycles<13>();
+ writeByteNoWait(D::adjust(*data++)); delaycycles<13>();
+ writeByteNoWait(D::adjust(*data++)); delaycycles<9>();
+ } else {
+ writeByte(D::adjust(*data++)); delaycycles<3>();
+ writeByte(D::adjust(*data++)); delaycycles<3>();
+ writeByte(D::adjust(*data++)); delaycycles<3>();
+ }
}
release();
}
@@ -414,17 +527,20 @@ class AVRSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _LATCH_P
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
#define SPI_DATA 11
#define SPI_CLOCK 13
-#define SPI_LATCH 10
-template<uint8_t SPI_SPEED>
-class AVRSPIOutput<SPI_DATA, SPI_CLOCK, SPI_LATCH, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_LATCH, SPI_SPEED> {};
+template<uint8_t _LATCH, uint8_t SPI_SPEED>
+class AVRSPIOutput<SPI_DATA, SPI_CLOCK, _LATCH, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, _LATCH, SPI_SPEED> {};
+
+#define USART_DATA 0
+#define USART_CLOCK 4
+template<uint8_t _LATCH, uint8_t SPI_SPEED>
+class AVRSPIOutput<USART_DATA, USART_CLOCK, _LATCH, SPI_SPEED> : public AVRUSARTSPIOutput<USART_DATA, USART_CLOCK, _LATCH, SPI_SPEED> {};
// Leonardo, teensy, blinkm
#elif defined(__AVR_ATmega32U4__)
#define SPI_DATA 2
#define SPI_CLOCK 1
-#define SPI_LATCH 0
-template<uint8_t SPI_SPEED>
-class AVRSPIOutput<SPI_DATA, SPI_CLOCK, SPI_LATCH, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, SPI_LATCH, SPI_SPEED> {};
+template<uint8_t _LATCH, uint8_t SPI_SPEED>
+class AVRSPIOutput<SPI_DATA, SPI_CLOCK, _LATCH, SPI_SPEED> : public AVRHardwareSPIOutput<SPI_DATA, SPI_CLOCK, _LATCH, SPI_SPEED> {};
#else