From b9bfc26db78d337ec2a02e24210d9e9ce55bd3de Mon Sep 17 00:00:00 2001 From: "danielgarcia@gmail.com" Date: Sat, 9 Feb 2013 07:53:18 +0000 Subject: * Fix latch handling in the SPI classes (lo when transmitting, hi when not) * Make it so that the template parameters for adjusting timing are based on NS timing values of the underlying chip * preliminary code (untested) for putting output over the USART chip instead of the SPI chip. --- FastSPI_LED2.h | 31 ++++++--- clockless.h | 6 ++ examples/Fast2Dev/Fast2Dev.ino | 28 ++++---- 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 +template class LPD8806Controller : public CLEDController { - typedef AVRSPIOutput SPI; + typedef AVRSPIOutput SPI; void clearLine(int nLeds) { int n = ((nLeds + 63) >> 6); @@ -51,9 +51,9 @@ public: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -template +template class WS2801Controller : public CLEDController { - typedef AVRSPIOutput SPI; + typedef AVRSPIOutput SPI; public: virtual void init() { @@ -69,7 +69,6 @@ public: } }; - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Clockless template instantiations @@ -78,18 +77,30 @@ public: // 500ns, 1500ns, 500ns template -class UCS1903Controller400Mhz : public ClocklessController {}; +class UCS1903Controller400Mhz : public ClocklessController {}; +#if NO_TIME(500, 1500, 500) +#pragma message "No enough clock cycles available for the UCS103" +#endif // 312.5ns, 312.5ns, 325ns template -class TM1809Controller800Mhz : public ClocklessController {}; +class TM1809Controller800Mhz : public ClocklessController {}; +#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 -class WS2811Controller800Mhz : public ClocklessController {}; +class WS2811Controller800Mhz : public ClocklessController {}; +#if NO_TIME(350, 350, 550) +#pragma message "No enough clock cycles available for the UCS103" +#endif // 750NS, 750NS, 750NS template -class TM1803Controller400Mhz : public ClocklessController {}; +class TM1803Controller400Mhz : public ClocklessController {}; +#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 // 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::setOutput(); Pin::setOutput(); 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::hi(); } - static void release() { Pin::lo(); } + static void latch() { Pin::lo(); } + static void release() { Pin::hi(); } static void writeBytesValue(uint8_t value, int len) { latch(); @@ -281,6 +285,105 @@ public: static void writeBytes3(register uint8_t *data, int len) { writeBytes3<0, DATA_NOP>(data, len); } }; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware SPI support using USART registers and friends +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +class AVRUSARTSPIOutput { +public: + static void init() { + Pin<_LATCH_PIN>::setOutput(); + UBRR0 = 0; + UCSR0A = 1<::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< 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 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(b, clockpin, datapin); + } + + static void latch() { Pin<_LATCH_PIN>::lo(); } + static void release() { + // wait for all transmissions to finish + while ((UCSR0A & (1 <::hi(); + } + + static void writeBytesValue(uint8_t value, int len) { + latch(); + while(len--) { + writeByte(value); + } + release(); + } + + // Write a block of n uint8_ts out + template 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, 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 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 static void writeBytes3(register uint8_t *data, int len) { writeBytes3(data, len); } + template 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< inline static void writeBit(uint8_t b, volatile uint8_t *clockpin, volatile uint8_t *datapin) { SPCR &= ~(1 << SPE); @@ -352,12 +459,12 @@ public: template 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(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 -class AVRSPIOutput : public AVRHardwareSPIOutput {}; +template +class AVRSPIOutput : public AVRHardwareSPIOutput {}; + +#define USART_DATA 0 +#define USART_CLOCK 4 +template +class AVRSPIOutput : public AVRUSARTSPIOutput {}; // Leonardo, teensy, blinkm #elif defined(__AVR_ATmega32U4__) #define SPI_DATA 2 #define SPI_CLOCK 1 -#define SPI_LATCH 0 -template -class AVRSPIOutput : public AVRHardwareSPIOutput {}; +template +class AVRSPIOutput : public AVRHardwareSPIOutput {}; #else -- cgit v1.2.3