diff options
-rw-r--r-- | FastSPI_LED2.h | 31 | ||||
-rw-r--r-- | clockless.h | 6 | ||||
-rw-r--r-- | examples/Fast2Dev/Fast2Dev.ino | 28 | ||||
-rw-r--r-- | fastspi.h | 154 |
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 @@ -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 |