diff options
author | danielgarcia@gmail.com <danielgarcia@gmail.com@4ad4ec5c-605d-bd5c-5796-512c9b60011b> | 2013-02-18 11:20:47 +0400 |
---|---|---|
committer | danielgarcia@gmail.com <danielgarcia@gmail.com@4ad4ec5c-605d-bd5c-5796-512c9b60011b> | 2013-02-18 11:20:47 +0400 |
commit | 5555154f3ae88082deb071f0e36a4c2c39d85d98 (patch) | |
tree | 3d6e178427291ff81b3073d93c494249028e287f | |
parent | b9bfc26db78d337ec2a02e24210d9e9ce55bd3de (diff) |
Complete support for the teensy 3.0 platform:
* Compile time toggle between hardware SPI & software bit-bang'd spi
* Implementation of clock-less chip support (tm1809, ucs1903, ws2811, etc...) for teensy 3.0
* Speed parameter for spi chipsets now a value from 1-255 with speed mappings to line up to hardware SPI div values (finer tuning of SPI control when bit banging!)
* FastPIN definitions for Arm GPIO - not using bitbanding, other mechanisms still faster!
* Disable AVR USART spi support - not working at the moment
* Add __attribute__((always_inline)) everywhere, teensy 3.0 compiler requires this to force inlining
-rw-r--r-- | FastSPI_LED2.h | 8 | ||||
-rw-r--r-- | clockless.h | 48 | ||||
-rw-r--r-- | examples/Fast2Dev/Fast2Dev.ino | 36 | ||||
-rw-r--r-- | fastpin.h | 163 | ||||
-rw-r--r-- | fastspi.h | 340 |
5 files changed, 419 insertions, 176 deletions
diff --git a/FastSPI_LED2.h b/FastSPI_LED2.h index 4cb6048b..015dc6b8 100644 --- a/FastSPI_LED2.h +++ b/FastSPI_LED2.h @@ -16,7 +16,7 @@ class LPD8806_ADJUST { public: - static uint8_t adjust(register uint8_t data) { return (data>>1) | 0x80; } + __attribute__((always_inline)) inline 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, uint8_t SPI_SPEED = 0 > @@ -30,6 +30,8 @@ class LPD8806Controller : public CLEDController { public: virtual void init() { SPI::init(); + + // push out 1000 leds worth of 0's to clear out the line SPI::writeBytesValue(0x80, 1000); clearLine(1000); } @@ -58,6 +60,8 @@ class WS2801Controller : public CLEDController { public: virtual void init() { SPI::init(); + // 0 out as much as we can on the line + SPI::writeBytesValue(0, 1000); } virtual void showRGB(uint8_t *data, int nLeds) { @@ -89,7 +93,7 @@ class TM1809Controller800Mhz : public ClocklessController<DATA_PIN, NS(350), NS( #pragma message "No enough clock cycles available for the UCS103" #endif -// 380s, 380ns, 725ns +// 350n, 350ns, 550ns template <uint8_t DATA_PIN> class WS2811Controller800Mhz : public ClocklessController<DATA_PIN, NS(350), NS(350), NS(550)> {}; #if NO_TIME(350, 350, 550) diff --git a/clockless.h b/clockless.h index 631adc86..53a7760c 100644 --- a/clockless.h +++ b/clockless.h @@ -20,8 +20,11 @@ template <uint8_t DATA_PIN, int T1, int T2, int T3> class ClocklessController : public CLEDController { - uint8_t mPinMask; - volatile uint8_t *mPort; + typedef typename Pin<DATA_PIN>::port_ptr_t data_ptr_t; + typedef typename Pin<DATA_PIN>::port_t data_t; + + data_t mPinMask; + data_ptr_t mPort; public: virtual void init() { Pin<DATA_PIN>::setOutput(); @@ -29,7 +32,9 @@ public: mPort = Pin<DATA_PIN>::port(); } - template <int N>inline static void bitSetFast(register volatile uint8_t *port, register uint8_t hi, register uint8_t lo, register uint8_t b) { +#if defined(__MK20DX128__) +#else + template <int N>inline static void bitSetFast(register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t b) { // First cycle Pin<DATA_PIN>::fastset(port, hi); // 1/2 clock cycle if using out delaycycles<T1 - (_CYCLES(DATA_PIN) + 1)>(); // 1st cycle length minus 1/2 clock for out, 1 clock for sbrs @@ -45,7 +50,7 @@ public: } #define END_OF_LOOP 6 // loop compare, jump, next uint8_t load - template <int N, int ADJ>inline static void bitSetLast(register volatile uint8_t *port, register uint8_t hi, register uint8_t lo, register uint8_t b) { + template <int N, int ADJ>inline static void bitSetLast(register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t b) { // First cycle Pin<DATA_PIN>::fastset(port, hi); // 1 clock cycle if using out, 2 otherwise delaycycles<T1 - (_CYCLES(DATA_PIN) + 1)>(); // 1st cycle length minus 1 clock for out, 1 clock for sbrs @@ -59,18 +64,44 @@ public: Pin<DATA_PIN>::fastset(port, lo); // 1/2 clock cycle if using out delaycycles<T3 - (_CYCLES(DATA_PIN) + ADJ)>(); // 3rd cycle length minus 7 clocks for out, loop compare, jump, next uint8_t load } +#endif virtual void showRGB(register uint8_t *data, register int nLeds) { cli(); - register uint8_t mask = mPinMask; - register volatile uint8_t *port = mPort; + register data_t mask = mPinMask; + register data_ptr_t port = mPort; nLeds *= (3); register uint8_t *end = data + nLeds; - register uint8_t hi = *port | mask; - register uint8_t lo = *port & ~mask; + register data_t hi = *port | mask; + register data_t lo = *port & ~mask; *port = lo; +#if defined(__MK20DX128__) + register uint32_t b = *data++; + while(data != end) { + for(register uint32_t i = 7; i > 0; i--) { + Pin<DATA_PIN>::fastset(port, hi); + delaycycles<1 + T1 - 4>(); // 4 cycles - 2 store, 1 test, 1 if + if(b & 0x80) { Pin<DATA_PIN>::fastset(port, hi); } else { Pin<DATA_PIN>::fastset(port, lo); } + b <<= 1; + delaycycles<1 + T2 - 5>(); // 5 cycles, 2 store, 2 store/skip, 1 shift + Pin<DATA_PIN>::fastset(port, lo); + delaycycles<1 + T3 - 5>(); // 5 cycles, 2 store, 1 sub, 2 branch backwards + } + // extra delay because branch is faster falling through + delaycycles<1>(); + + // 8th bit, interleave loading rest of data + Pin<DATA_PIN>::fastset(port, hi); + delaycycles<1 + T1 - 4>(); + if(b & 0x80) { Pin<DATA_PIN>::fastset(port, hi); } else { Pin<DATA_PIN>::fastset(port, lo); } + delaycycles<1 + T2 - 4>(); // 4 cycles, 2 store, store/skip + Pin<DATA_PIN>::fastset(port, lo); + b = *data++; + delaycycles<1 + T3 - 8>(); // 2 store, 2 load, 1 cmp, 2 branch backwards, 1 movim + } +#else while(data != end) { register uint8_t b = *data++; bitSetFast<7>(port, hi, lo, b); @@ -82,6 +113,7 @@ public: bitSetFast<1>(port, hi, lo, b); bitSetLast<0, END_OF_LOOP>(port, hi, lo, b); } +#endif sei(); } diff --git a/examples/Fast2Dev/Fast2Dev.ino b/examples/Fast2Dev/Fast2Dev.ino index 580d67a6..7038858e 100644 --- a/examples/Fast2Dev/Fast2Dev.ino +++ b/examples/Fast2Dev/Fast2Dev.ino @@ -10,7 +10,7 @@ // #include "xxxwiring.h" // #endif -#define DEBUG +// #define DEBUG #ifdef DEBUG #define DPRINT Serial.print #define DPRINTLN Serial.println @@ -38,19 +38,22 @@ 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 +// LPD8806Controller<0, 4, 10> LED; +// LPD8806Controller<12, 13, 10, 0> LED; // 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<12, 13, 10> LED; // 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 // LPD8806Controller<7, 13, 10> LED; // LPD8806Controller<8, 1, 10> LED; +// LPD8806Controller<11, 14, 10> LED; // 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 @@ -64,7 +67,8 @@ LPD8806Controller<11, 13, 10> LED; // TM1809Controller800Mhz<6> LED; // UCS1903Controller400Mhz<7> LED; -// WS2811Controller800Mhz<6> LED; +// WS2811Controller800Mhz<12> LED; +WS2811Controller800Mhz<5> 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; @@ -89,12 +93,13 @@ LPD8806Controller<11, 13, 10> LED; void setup() { - aPin.hi(); - aPin.lo(); - Pin<2>::hi(); - Pin<2>::lo(); - + // aPin.hi(); + // aPin.lo(); + // Pin<2>::hi(); + // Pin<2>::lo(); #ifdef DEBUG + delay(5000); + Serial.begin(38400); Serial.println("resetting!"); #endif @@ -112,6 +117,11 @@ void setup() { } void loop() { +#if 0 + memset(leds, 0, NUM_LEDS * sizeof(struct CRGB)); + LED.showRGB((byte*)leds, NUM_LEDS); + delay(20); +#else for(int i = 0; i < 3; i++) { for(int iLed = 0; iLed < NUM_LEDS; iLed++) { memset(leds, 0, NUM_LEDS * sizeof(struct CRGB)); @@ -122,17 +132,21 @@ void loop() { } LED.showRGB((byte*)leds, NUM_LEDS);; + //DPRINTLN("waiting"); delay(20); } } for(int i = 0; i < 64; i++) { memset(leds, i, NUM_LEDS * 3); LED.showRGB((byte*)leds, NUM_LEDS);; - delay(20); + // DPRINTLN("waiting"); + delay(40); } for(int i = 64; i >= 0; i--) { memset(leds, i, NUM_LEDS * 3); LED.showRGB((byte*)leds, NUM_LEDS);; - delay(20); + // DPRINTLN("waiting"); + delay(40); } +#endif }
\ No newline at end of file @@ -23,54 +23,135 @@ template<uint8_t PIN> class Pin { mPort = portOutputRegister(digitalPinToPort(PIN)); } public: + typedef volatile uint8_t * port_ptr_t; + typedef uint8_t port_t; + inline static void setOutput() { _init(); pinMode(PIN, OUTPUT); } inline static void setInput() { _init(); pinMode(PIN, INPUT); } - inline static void hi() { *mPort |= mPinMask; } - inline static void lo() { *mPort &= ~mPinMask; } + inline static void hi() __attribute__ ((always_inline)) { *mPort |= mPinMask; } + inline static void lo() __attribute__ ((always_inline)) { *mPort &= ~mPinMask; } + + inline static void strobe() __attribute__ ((always_inline)) { hi(); lo(); } - inline static void hi(register volatile uint8_t * port) { *port |= mPinMask; } - inline static void lo(register volatile uint8_t * port) { *port &= ~mPinMask; } - inline static void set(register uint8_t val) { *mPort = val; } + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port |= mPinMask; } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port &= ~mPinMask; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { *mPort = val; } - inline static void fastset(register volatile uint8_t * port, register uint8_t val) { *port = val; } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } - static uint8_t hival() { return *mPort | mPinMask; } - static uint8_t loval() { return *mPort & ~mPinMask; } - static volatile uint8_t * port() { return mPort; } - static uint8_t mask() { return mPinMask; } + static port_t hival() __attribute__ ((always_inline)) { return *mPort | mPinMask; } + static port_t loval() __attribute__ ((always_inline)) { return *mPort & ~mPinMask; } + static port_ptr_t port() __attribute__ ((always_inline)) { return mPort; } + static port_t mask() __attribute__ ((always_inline)) { return mPinMask; } }; template<uint8_t PIN> uint8_t Pin<PIN>::mPinMask; template<uint8_t PIN> volatile uint8_t *Pin<PIN>::mPort; -template<uint8_t PIN, uint8_t _MASK, typename _PORT, typename _DDR, typename _PIN> class _NUPIN { +// Template definition for AVR pins, providing direct access to port/ddr/pin registers +template<uint8_t PIN, uint8_t _MASK, typename _PORT, typename _DDR, typename _PIN> class _AVRPIN { public: + typedef volatile uint8_t * port_ptr_t; + typedef uint8_t port_t; + inline static void setOutput() { _DDR::r() |= _MASK; } inline static void setInput() { _DDR::r() &= ~_MASK; } - inline static void hi() { _PORT::r() |= _MASK; } - inline static void lo() { _PORT::r() &= ~_MASK; } - inline static void set(register uint8_t val) { _PORT::r() = val; } + inline static void hi() __attribute__ ((always_inline)) { _PORT::r() |= _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _PORT::r() &= ~_MASK; } + inline static void set(register uint8_t val) __attribute__ ((always_inline)) { _PORT::r() = val; } - inline static void hi(register volatile uint8_t *port) { hi(); } - inline static void lo(register volatile uint8_t *port) { lo(); } - inline static void fastset(register volatile uint8_t *port, register uint8_t val) { set(val); } + inline static void strobe() __attribute__ ((always_inline)) { hi(); lo(); } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register uint8_t val) __attribute__ ((always_inline)) { set(val); } - inline static uint8_t hival() { return _PORT::r() | _MASK; } - inline static uint8_t loval() { return _PORT::r() & ~_MASK; } - inline static volatile uint8_t * port() { return &_PORT::r(); } - inline static uint8_t mask() { return _MASK; } + inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PORT::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } }; +// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers +template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; } + inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; } + inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); } + inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; } +}; + +// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers +template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND { +public: + typedef volatile uint32_t * port_ptr_t; + typedef uint32_t port_t; + inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; } + inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; } + + inline static void hi() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 1; } + inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; } + inline static void set(register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; } + + inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); } + + inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; } + + inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { *port = 1; } + inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { *port = 0; } + inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; } + + inline static port_t hival() __attribute__ ((always_inline)) { return 1; } + inline static port_t loval() __attribute__ ((always_inline)) { return 0; } + inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); } + inline static port_t mask() __attribute__ ((always_inline)) { return 1; } +}; + +// AVR definitions typedef volatile uint8_t & reg8_t; #define _R(T) struct __gen_struct_ ## T #define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }}; #define _IO(L) _RD8(DDR ## L); _RD8(PORT ## L); _RD8(PIN ## L); +#define _DEFPIN_AVR(PIN, MASK, L) template<> class Pin<PIN> : public _AVRPIN<PIN, MASK, _R(PORT ## L), _R(DDR ## L), _R(PIN ## L)> {}; + +// ARM definitions +#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000) +#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit))) + +typedef volatile uint32_t & reg32_t; +typedef volatile uint32_t * ptr_reg32_t; + +#define _RD32(T) struct __gen_struct_ ## T { static inline reg32_t r() { return T; } template<int BIT> static inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } }; +#define _IO32(L) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); + +#define _DEFPIN_ARM(PIN, BIT, L) template<> class Pin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \ + _R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; +// Don't use bit band'd pins for now, the compiler generates far less efficient code around them +// #define _DEFPIN_ARM(PIN, BIT, L) template<> class Pin<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \ +// _R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; -#define _DEFPIN(PIN, MASK, L) template<> class Pin<PIN> : public _NUPIN<PIN, MASK, _R(PORT ## L), _R(DDR ## L), _R(PIN ## L)> {}; /////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -81,24 +162,38 @@ typedef volatile uint8_t & reg8_t; #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) // Accelerated port definitions for arduino avrs _IO(D); _IO(B); _IO(C); -_DEFPIN( 0, 0x01, D); _DEFPIN( 1, 0x02, D); _DEFPIN( 2, 0x04, D); _DEFPIN( 3, 0x08, D); -_DEFPIN( 4, 0x10, D); _DEFPIN( 5, 0x20, D); _DEFPIN( 6, 0x40, D); _DEFPIN( 7, 0x80, D); -_DEFPIN( 8, 0x01, B); _DEFPIN( 9, 0x02, B); _DEFPIN(10, 0x04, B); _DEFPIN(11, 0x08, B); -_DEFPIN(12, 0x10, B); _DEFPIN(13, 0x20, B); _DEFPIN(14, 0x40, B); _DEFPIN(15, 0x80, B); -_DEFPIN(16, 0x01, C); _DEFPIN(17, 0x02, C); _DEFPIN(18, 0x04, C); _DEFPIN(19, 0x08, C); -_DEFPIN(20, 0x10, C); _DEFPIN(21, 0x20, C); _DEFPIN(22, 0x40, C); _DEFPIN(23, 0x80, C); +_DEFPIN_AVR( 0, 0x01, D); _DEFPIN_AVR( 1, 0x02, D); _DEFPIN_AVR( 2, 0x04, D); _DEFPIN_AVR( 3, 0x08, D); +_DEFPIN_AVR( 4, 0x10, D); _DEFPIN_AVR( 5, 0x20, D); _DEFPIN_AVR( 6, 0x40, D); _DEFPIN_AVR( 7, 0x80, D); +_DEFPIN_AVR( 8, 0x01, B); _DEFPIN_AVR( 9, 0x02, B); _DEFPIN_AVR(10, 0x04, B); _DEFPIN_AVR(11, 0x08, B); +_DEFPIN_AVR(12, 0x10, B); _DEFPIN_AVR(13, 0x20, B); _DEFPIN_AVR(14, 0x40, B); _DEFPIN_AVR(15, 0x80, B); +_DEFPIN_AVR(16, 0x01, C); _DEFPIN_AVR(17, 0x02, C); _DEFPIN_AVR(18, 0x04, C); _DEFPIN_AVR(19, 0x08, C); +_DEFPIN_AVR(20, 0x10, C); _DEFPIN_AVR(21, 0x20, C); _DEFPIN_AVR(22, 0x40, C); _DEFPIN_AVR(23, 0x80, C); // Leonardo, teensy, blinkm #elif defined(__AVR_ATmega32U4__) _IO(B); _IO(C); _IO(D); _IO(E); _IO(F); -_DEFPIN(0, 1, B); _DEFPIN(1, 2, B); _DEFPIN(2, 4, B); _DEFPIN(3, 8, B); -_DEFPIN(4, 128, B); _DEFPIN(5, 1, D); _DEFPIN(6, 2, D); _DEFPIN(7, 4, D); -_DEFPIN(8, 8, D); _DEFPIN(9, 64, C); _DEFPIN(10, 128, C); _DEFPIN(11, 64, D); -_DEFPIN(12, 128, D); _DEFPIN(13, 16, B); _DEFPIN(14, 32, B); _DEFPIN(15, 64, B); -_DEFPIN(16, 128, F); _DEFPIN(17, 64, F); _DEFPIN(18, 32, F); _DEFPIN(19, 16, F); -_DEFPIN(20, 2, F); _DEFPIN(21, 1, F); _DEFPIN(22, 16, D); _DEFPIN(23, 32, D); +_DEFPIN_AVR(0, 1, B); _DEFPIN_AVR(1, 2, B); _DEFPIN_AVR(2, 4, B); _DEFPIN_AVR(3, 8, B); +_DEFPIN_AVR(4, 128, B); _DEFPIN_AVR(5, 1, D); _DEFPIN_AVR(6, 2, D); _DEFPIN_AVR(7, 4, D); +_DEFPIN_AVR(8, 8, D); _DEFPIN_AVR(9, 64, C); _DEFPIN_AVR(10, 128, C); _DEFPIN_AVR(11, 64, D); +_DEFPIN_AVR(12, 128, D); _DEFPIN_AVR(13, 16, B); _DEFPIN_AVR(14, 32, B); _DEFPIN_AVR(15, 64, B); +_DEFPIN_AVR(16, 128, F); _DEFPIN_AVR(17, 64, F); _DEFPIN_AVR(18, 32, F); _DEFPIN_AVR(19, 16, F); +_DEFPIN_AVR(20, 2, F); _DEFPIN_AVR(21, 1, F); _DEFPIN_AVR(22, 16, D); _DEFPIN_AVR(23, 32, D); + +#elif defined(__MK20DX128__) + +_IO32(A); _IO32(B); _IO32(C); _IO32(D); _IO32(E); + +_DEFPIN_ARM(0, 16, B); _DEFPIN_ARM(1, 17, B); _DEFPIN_ARM(2, 0, D); _DEFPIN_ARM(3, 12, A); +_DEFPIN_ARM(4, 13, A); _DEFPIN_ARM(5, 7, D); _DEFPIN_ARM(6, 4, D); _DEFPIN_ARM(7, 2, D); +_DEFPIN_ARM(8, 3, D); _DEFPIN_ARM(9, 3, C); _DEFPIN_ARM(10, 4, C); _DEFPIN_ARM(11, 6, C); +_DEFPIN_ARM(12, 7, C); _DEFPIN_ARM(13, 5, C); _DEFPIN_ARM(14, 1, D); _DEFPIN_ARM(15, 0, C); +_DEFPIN_ARM(16, 0, B); _DEFPIN_ARM(17, 1, B); _DEFPIN_ARM(18, 3, B); _DEFPIN_ARM(19, 2, B); +_DEFPIN_ARM(20, 5, D); _DEFPIN_ARM(21, 6, D); _DEFPIN_ARM(22, 1, C); _DEFPIN_ARM(23, 2, C); +_DEFPIN_ARM(24, 5, A); _DEFPIN_ARM(25, 19, B); _DEFPIN_ARM(26, 1, E); _DEFPIN_ARM(27, 9, C); +_DEFPIN_ARM(28, 8, C); _DEFPIN_ARM(29, 10, C); _DEFPIN_ARM(30, 11, C); _DEFPIN_ARM(31, 0, E); +_DEFPIN_ARM(32, 18, B); _DEFPIN_ARM(33, 4, A); #else #pragma message "No pin/port mappings found, pin access will be slightly slower. See fastpin.h for info." @@ -33,21 +33,24 @@ template<int LOOP, int PAD> inline void _delaycycles() { } // usable definition -#if 1 -template<int CYCLES> inline void delaycycles() { +#if !defined(__MK20DX128__) +template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() { _delaycycles<CYCLES / 3, CYCLES % 3>(); } #else -template<int CYCLES> inline void delaycycles() { +template<int CYCLES> __attribute__((always_inline)) inline void delaycycles() { NOP; delaycycles<CYCLES-1>(); } #endif // pre-instantiations for values small enough to not need the loop -template<> inline void delaycycles<0>() {} -template<> inline void delaycycles<1>() {NOP;} -template<> inline void delaycycles<2>() {NOP;NOP;} -template<> inline void delaycycles<3>() {NOP;NOP;NOP;} +template<> __attribute__((always_inline)) inline void delaycycles<-3>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-2>() {} +template<> __attribute__((always_inline)) inline void delaycycles<-1>() {} +template<> __attribute__((always_inline)) inline void delaycycles<0>() {} +template<> __attribute__((always_inline)) inline void delaycycles<1>() {NOP;} +template<> __attribute__((always_inline)) inline void delaycycles<2>() {NOP;NOP;} +template<> __attribute__((always_inline)) inline void delaycycles<3>() {NOP;NOP;NOP;} class DATA_NOP { public: @@ -62,6 +65,15 @@ public: template <uint8_t DATA_PIN, uint8_t CLOCK_PIN, uint8_t LATCH_PIN, uint8_t SPI_SPEED> class AVRSoftwareSPIOutput { + // The data types for pointers to the pin port - typedef'd here from the Pin definition because on avr these + // are pointers to 8 bit values, while on arm they are 32 bit + typedef typename Pin<DATA_PIN>::port_ptr_t data_ptr_t; + typedef typename Pin<CLOCK_PIN>::port_ptr_t clock_ptr_t; + + // The data type for what's at a pin's port - typedef'd here from the Pin definition because on avr the ports + // are 8 bits wide while on arm they are 32. + typedef typename Pin<DATA_PIN>::port_t data_t; + typedef typename Pin<CLOCK_PIN>::port_t clock_t; public: static void init() { // set the pins to output @@ -74,15 +86,24 @@ public: writeBytesValue(0, 192*3); } + // stop the SPI output + static void stop() { } + static void wait() __attribute__((always_inline)) { } static void writeByte(uint8_t b) __attribute__((always_inline)) { - register volatile uint8_t *clockpin = Pin<CLOCK_PIN>::port(); - register volatile uint8_t *datapin = Pin<DATA_PIN>::port(); - writeByte(b, clockpin, datapin); + writeBit<7>(b); + writeBit<6>(b); + writeBit<5>(b); + writeBit<4>(b); + writeBit<3>(b); + writeBit<2>(b); + writeBit<1>(b); + writeBit<0>(b); } - - static void writeByte(uint8_t b, volatile uint8_t* clockpin, volatile uint8_t *datapin) __attribute__((always_inline)) { + +private: + static void writeByte(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) __attribute__((always_inline)) { writeBit<7>(b, clockpin, datapin); writeBit<6>(b, clockpin, datapin); writeBit<5>(b, clockpin, datapin); @@ -93,8 +114,9 @@ public: writeBit<0>(b, clockpin, datapin); } - static void writeByte(uint8_t b, volatile uint8_t *datapin, - uint8_t hival, uint8_t loval, uint8_t hiclock, uint8_t loclock) __attribute__((always_inline, hot)) { + static void writeByte(uint8_t b, data_ptr_t datapin, + data_t hival, data_t loval, + clock_t hiclock, clock_t loclock) __attribute__((always_inline, hot)) { writeBit<7>(b, datapin, hival, loval, hiclock, loclock); writeBit<6>(b, datapin, hival, loval, hiclock, loclock); writeBit<5>(b, datapin, hival, loval, hiclock, loclock); @@ -105,8 +127,9 @@ public: writeBit<0>(b, datapin, hival, loval, hiclock, loclock); } - static void writeByte(uint8_t b, volatile uint8_t* clockpin, volatile uint8_t *datapin, - uint8_t hival, uint8_t loval, uint8_t hiclock, uint8_t loclock) __attribute__((always_inline)) { + 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) __attribute__((always_inline)) { writeBit<7>(b, clockpin, datapin, hival, loval, hiclock, loclock); writeBit<6>(b, clockpin, datapin, hival, loval, hiclock, loclock); writeBit<5>(b, clockpin, datapin, hival, loval, hiclock, loclock); @@ -117,101 +140,134 @@ public: writeBit<0>(b, clockpin, datapin, hival, loval, hiclock, loclock); } - 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); +public: + #define SPI_DELAY delaycycles< (SPI_SPEED-2) / 2>(); + + template <uint8_t BIT> __attribute__((always_inline, hot)) inline static void writeBit(uint8_t b) { + if(b & (1 << BIT)) { + Pin<DATA_PIN>::hi(); + if(SPI_SPEED < 3) { + Pin<CLOCK_PIN>::strobe(); + } else { + Pin<CLOCK_PIN>::hi(); SPI_DELAY; + Pin<CLOCK_PIN>::lo(); SPI_DELAY; + } + } else { + Pin<DATA_PIN>::lo(); + if(SPI_SPEED < 3) { + Pin<CLOCK_PIN>::strobe(); + } else { + Pin<CLOCK_PIN>::hi(); SPI_DELAY; + Pin<CLOCK_PIN>::lo(); SPI_DELAY; + } + } } - template <uint8_t BIT> inline static void writeBit(uint8_t b, volatile uint8_t *clockpin, volatile uint8_t *datapin) { +private: + template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, clock_ptr_t clockpin, data_ptr_t datapin) { if(b & (1 << BIT)) { Pin<DATA_PIN>::hi(datapin); + Pin<CLOCK_PIN>::hi(clockpin); SPI_DELAY; + Pin<CLOCK_PIN>::lo(clockpin); SPI_DELAY; } else { Pin<DATA_PIN>::lo(datapin); + Pin<CLOCK_PIN>::hi(clockpin); SPI_DELAY; + Pin<CLOCK_PIN>::lo(clockpin); SPI_DELAY; } - Pin<CLOCK_PIN>::hi(clockpin); - Pin<CLOCK_PIN>::lo(clockpin); } // the version of write to use when clock and data are on separate pins with precomputed values for setting // the clock and data pins - template <uint8_t BIT> inline static void writeBit(uint8_t b, volatile uint8_t *clockpin, volatile uint8_t *datapin, - uint8_t hival, uint8_t loval, uint8_t hiclock, uint8_t loclock) { + template <uint8_t BIT> __attribute__((always_inline)) inline static 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) { // // only need to explicitly set clock hi if clock and data are on different ports if(b & (1 << BIT)) { Pin<DATA_PIN>::fastset(datapin, hival); + Pin<CLOCK_PIN>::fastset(clockpin, hiclock); SPI_DELAY; + Pin<CLOCK_PIN>::fastset(clockpin, loclock); SPI_DELAY; } else { // NOP; Pin<DATA_PIN>::fastset(datapin, loval); + Pin<CLOCK_PIN>::fastset(clockpin, hiclock); + Pin<CLOCK_PIN>::fastset(clockpin, loclock); } - - - Pin<CLOCK_PIN>::fastset(clockpin, hiclock); - Pin<CLOCK_PIN>::fastset(clockpin, loclock); } // the version of write to use when clock and data are on the same pin with precomputed values for the various // combinations - template <uint8_t BIT> inline static void writeBit(uint8_t b, volatile uint8_t *clockdatapin, - uint8_t datahiclockhi, uint8_t dataloclockhi, - uint8_t datahiclocklo, uint8_t dataloclocklo) { + template <uint8_t BIT> __attribute__((always_inline)) inline static void writeBit(uint8_t b, data_ptr_t clockdatapin, + data_t datahiclockhi, data_t dataloclockhi, + data_t datahiclocklo, data_t dataloclocklo) { if(b & (1 << BIT)) { - Pin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); - Pin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); + Pin<DATA_PIN>::fastset(clockdatapin, datahiclockhi); SPI_DELAY; + Pin<DATA_PIN>::fastset(clockdatapin, datahiclocklo); SPI_DELAY; } else { // NOP; - Pin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); - Pin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); + Pin<DATA_PIN>::fastset(clockdatapin, dataloclockhi); SPI_DELAY; + Pin<DATA_PIN>::fastset(clockdatapin, dataloclocklo); SPI_DELAY; } } +public: static void latch() { Pin<LATCH_PIN>::lo(); } static void release() { Pin<LATCH_PIN>::hi(); } static void writeBytesValue(uint8_t value, int len) { latch(); - register volatile uint8_t *clockpin = Pin<CLOCK_PIN>::port(); - register volatile uint8_t *datapin = Pin<DATA_PIN>::port(); +#if 0 + while(len--) { + writeByte(value); + } +#else + register clock_ptr_t clockpin = Pin<CLOCK_PIN>::port(); + register data_ptr_t datapin = Pin<DATA_PIN>::port(); if(Pin<DATA_PIN>::port() != Pin<CLOCK_PIN>::port()) { // If data and clock are on different ports, then writing a bit will consist of writing the value foor // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line - register uint8_t datahi = Pin<DATA_PIN>::hival(); - register uint8_t datalo = Pin<DATA_PIN>::loval(); - register uint8_t clockhi = Pin<CLOCK_PIN>::hival(); - register uint8_t clocklo = Pin<CLOCK_PIN>::loval(); + register data_t datahi = Pin<DATA_PIN>::hival(); + register data_t datalo = Pin<DATA_PIN>::loval(); + register clock_t clockhi = Pin<CLOCK_PIN>::hival(); + register clock_t clocklo = Pin<CLOCK_PIN>::loval(); while(len--) { writeByte(value, clockpin, datapin, datahi, datalo, clockhi, clocklo); } } else { // If data and clock are on the same port then we can combine setting the data and clock pins - register uint8_t datahi_clockhi = Pin<DATA_PIN>::hival() | Pin<CLOCK_PIN>::mask(); - register uint8_t datalo_clockhi = Pin<DATA_PIN>::loval() | Pin<CLOCK_PIN>::mask(); - register uint8_t datahi_clocklo = Pin<DATA_PIN>::hival() & ~Pin<CLOCK_PIN>::mask(); - register uint8_t datalo_clocklo = Pin<DATA_PIN>::loval() & ~Pin<CLOCK_PIN>::mask(); + register data_t datahi_clockhi = Pin<DATA_PIN>::hival() | Pin<CLOCK_PIN>::mask(); + register data_t datalo_clockhi = Pin<DATA_PIN>::loval() | Pin<CLOCK_PIN>::mask(); + register data_t datahi_clocklo = Pin<DATA_PIN>::hival() & ~Pin<CLOCK_PIN>::mask(); + register data_t datalo_clocklo = Pin<DATA_PIN>::loval() & ~Pin<CLOCK_PIN>::mask(); while(len--) { writeByte(value, datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); } } +#endif release(); } // write a block of len uint8_ts out template <class D> static void writeBytes(register uint8_t *data, int len) { latch(); - register volatile uint8_t *clockpin = Pin<CLOCK_PIN>::port(); - register volatile uint8_t *datapin = Pin<DATA_PIN>::port(); +#if 0 + uint8_t *end = data + len; + while(data != end) { + writeByte(D::adjust(*data++)); + } +#else + register clock_ptr_t clockpin = Pin<CLOCK_PIN>::port(); + register data_ptr_t datapin = Pin<DATA_PIN>::port(); if(Pin<DATA_PIN>::port() != Pin<CLOCK_PIN>::port()) { // If data and clock are on different ports, then writing a bit will consist of writing the value foor // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line - register uint8_t datahi = Pin<DATA_PIN>::hival(); - register uint8_t datalo = Pin<DATA_PIN>::loval(); - register uint8_t clockhi = Pin<CLOCK_PIN>::hival(); - register uint8_t clocklo = Pin<CLOCK_PIN>::loval(); + register data_t datahi = Pin<DATA_PIN>::hival(); + register data_t datalo = Pin<DATA_PIN>::loval(); + register clock_t clockhi = Pin<CLOCK_PIN>::hival(); + register clock_t clocklo = Pin<CLOCK_PIN>::loval(); uint8_t *end = data + len; while(data != end) { @@ -220,10 +276,10 @@ public: } else { // If data and clock are on the same port then we can combine setting the data and clock pins - register uint8_t datahi_clockhi = Pin<DATA_PIN>::hival() | Pin<CLOCK_PIN>::mask(); - register uint8_t datalo_clockhi = Pin<DATA_PIN>::loval() | Pin<CLOCK_PIN>::mask(); - register uint8_t datahi_clocklo = Pin<DATA_PIN>::hival() & ~Pin<CLOCK_PIN>::mask(); - register uint8_t datalo_clocklo = Pin<DATA_PIN>::loval() & ~Pin<CLOCK_PIN>::mask(); + register data_t datahi_clockhi = Pin<DATA_PIN>::hival() | Pin<CLOCK_PIN>::mask(); + register data_t datalo_clockhi = Pin<DATA_PIN>::loval() | Pin<CLOCK_PIN>::mask(); + register data_t datahi_clocklo = Pin<DATA_PIN>::hival() & ~Pin<CLOCK_PIN>::mask(); + register data_t datalo_clocklo = Pin<DATA_PIN>::loval() & ~Pin<CLOCK_PIN>::mask(); uint8_t *end = data + len; @@ -231,6 +287,7 @@ public: writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); } } +#endif release(); } @@ -241,16 +298,30 @@ public: // 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) { latch(); - register volatile uint8_t *clockpin = Pin<CLOCK_PIN>::port(); - register volatile uint8_t *datapin = Pin<DATA_PIN>::port(); + +#if 0 + // If interrupts or other things may be generating output while we're working on things, then we need + // to use this block + uint8_t *end = data + len; + while(data != end) { + data += SKIP; + writeByte(D::adjust(*data++)); + writeByte(D::adjust(*data++)); + writeByte(D::adjust(*data++)); + } +#else + // 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) + // then we can use a bunch of optimizations in here + register clock_ptr_t clockpin = Pin<CLOCK_PIN>::port(); + register data_ptr_t datapin = Pin<DATA_PIN>::port(); if(Pin<DATA_PIN>::port() != Pin<CLOCK_PIN>::port()) { // If data and clock are on different ports, then writing a bit will consist of writing the value foor // the bit (hi or low) to the data pin port, and then two writes to the clock port to strobe the clock line - register uint8_t datahi = Pin<DATA_PIN>::hival(); - register uint8_t datalo = Pin<DATA_PIN>::loval(); - register uint8_t clockhi = Pin<CLOCK_PIN>::hival(); - register uint8_t clocklo = Pin<CLOCK_PIN>::loval(); + register data_t datahi = Pin<DATA_PIN>::hival(); + register data_t datalo = Pin<DATA_PIN>::loval(); + register clock_t clockhi = Pin<CLOCK_PIN>::hival(); + register clock_t clocklo = Pin<CLOCK_PIN>::loval(); uint8_t *end = data + len; while(data != end) { @@ -262,10 +333,10 @@ public: } else { // If data and clock are on the same port then we can combine setting the data and clock pins - register uint8_t datahi_clockhi = Pin<DATA_PIN>::hival() | Pin<CLOCK_PIN>::mask(); - register uint8_t datalo_clockhi = Pin<DATA_PIN>::loval() | Pin<CLOCK_PIN>::mask(); - register uint8_t datahi_clocklo = Pin<DATA_PIN>::hival() & ~Pin<CLOCK_PIN>::mask(); - register uint8_t datalo_clocklo = Pin<DATA_PIN>::loval() & ~Pin<CLOCK_PIN>::mask(); + register data_t datahi_clockhi = Pin<DATA_PIN>::hival() | Pin<CLOCK_PIN>::mask(); + register data_t datalo_clockhi = Pin<DATA_PIN>::loval() | Pin<CLOCK_PIN>::mask(); + register data_t datahi_clocklo = Pin<DATA_PIN>::hival() & ~Pin<CLOCK_PIN>::mask(); + register data_t datalo_clocklo = Pin<DATA_PIN>::loval() & ~Pin<CLOCK_PIN>::mask(); uint8_t *end = data + len; @@ -275,9 +346,9 @@ public: writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); writeByte(D::adjust(*data++), datapin, datahi_clockhi, datalo_clockhi, datahi_clocklo, datalo_clocklo); } - - release(); } +#endif + release(); } template <uint8_t SKIP> static void writeBytes3(register uint8_t *data, int len) { writeBytes3<SKIP, DATA_NOP>(data, len); } @@ -291,6 +362,9 @@ public: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// uno/mini/duemilanove +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint8_t _LATCH_PIN, uint8_t _SPI_SPEED> class AVRUSARTSPIOutput { public: @@ -299,7 +373,7 @@ public: UBRR0 = 0; UCSR0A = 1<<TXC0; - Pin<_CLOCK_PIN>::setOutpuut(); + Pin<_CLOCK_PIN>::setOutput(); Pin<_DATA_PIN>::setOutput(); UCSR0C = _BV (UMSEL00) | _BV (UMSEL01); // Master SPI mode @@ -309,31 +383,23 @@ public: UBRR0 = 3; // 2 Mhz clock rate } + static void stop() { + // TODO: stop the uart spi output + } + 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); + template <uint8_t BIT> inline static void writeBit(uint8_t b) { + if(b && (1 << BIT)) { + Pin<_DATA_PIN>::hi(); } else { - Pin<_DATA_PIN>::lo(datapin); + Pin<_DATA_PIN>::lo(); } - 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); + Pin<_CLOCK_PIN>::hi(); + Pin<_CLOCK_PIN>::lo(); } static void latch() { Pin<_LATCH_PIN>::lo(); } @@ -356,7 +422,12 @@ public: uint8_t *end = data + len; latch(); while(data != end) { +#if defined(__MK20DX128__) writeByte(D::adjust(*data++)); +#else + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) + writeByte(D::adjust(*data++)); delaycycles<3>(); +#endif } release(); } @@ -370,10 +441,16 @@ public: latch(); while(data != end) { data += SKIP; - // a slight touch of delay here helps optimize the timing of the status register check loop +#if defined(__MK20DX128__) + writeByte(D::adjust(*data++)); + writeByte(D::adjust(*data++)); + writeByte(D::adjust(*data++)); +#else + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) writeByte(D::adjust(*data++)); delaycycles<3>(); writeByte(D::adjust(*data++)); delaycycles<3>(); writeByte(D::adjust(*data++)); delaycycles<3>(); +#endif } release(); } @@ -384,6 +461,7 @@ public: }; +#endif ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Hardware SPI support using SPDR registers and friends @@ -409,14 +487,23 @@ public: clr = SPDR; // clear SPI data register bool b2x = false; - switch(_SPI_SPEED) { - /* fosc/2 */ case 0: b2x=true; break; - /* fosc/4 */ case 1: break; - /* fosc/8 */ case 2: SPCR |= (1<<SPR0); b2x=true; break; - /* fosc/16 */ case 3: SPCR |= (1<<SPR0); break; - /* fosc/32 */ case 4: SPCR |= (1<<SPR1); b2x=true; break; - /* fosc/64 */ case 5: SPCR |= (1<<SPR1); break; - /* fosc/64 */ case 6: SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); b2x=true; break; + int hiBit = 0; + int spd = _SPI_SPEED; + while(spd >>= 1) { hiBit++; } + + // Spped mappings are a little different, here they are based on the highest bit set in the speed parameter. + // If bit 8 is set, it's at osc/128, bit 7, then osc/64, etc... down the line. + switch(hiBit) { + /* fosc/2 */ case 0: // no bits set + case 1: // speed set to 1 + case 2: // speed set to 2 + b2x=true; break; + /* fosc/4 */ case 3: break; + /* fosc/8 */ case 4: SPCR |= (1<<SPR0); b2x=true; break; + /* fosc/16 */ case 5: SPCR |= (1<<SPR0); break; + /* fosc/32 */ case 6: SPCR |= (1<<SPR1); b2x=true; break; + /* fosc/64 */ case 7: SPCR |= (1<<SPR1); break; + // /* fosc/64 */ case 6: SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); b2x=true; break; /* fosc/128 */ default: SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); break; } if(b2x) { SPSR |= (1<<SPI2X); } @@ -433,36 +520,22 @@ public: static void wait() __attribute__((always_inline)) { while(!(SPSR & (1<<SPIF))); } - // 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)) { 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); } 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) { + template <uint8_t BIT> inline static void writeBit(uint8_t b) { SPCR &= ~(1 << SPE); if(b & (1 << BIT)) { - Pin<_DATA_PIN>::hi(datapin); + Pin<_DATA_PIN>::hi(); } else { - Pin<_DATA_PIN>::lo(datapin); + Pin<_DATA_PIN>::lo(); } - Pin<_CLOCK_PIN>::hi(clockpin); - Pin<_CLOCK_PIN>::lo(clockpin); + Pin<_CLOCK_PIN>::hi(); + Pin<_CLOCK_PIN>::lo(); SPCR |= 1 << SPE; } - 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() { Pin<_LATCH_PIN>::hi(); } @@ -479,7 +552,12 @@ public: uint8_t *end = data + len; latch(); while(data != end) { - writeByteNoWait(D::adjust(*data++)); +#if defined(__MK20DX128__) + writeByte(D::adjust(*data++)); +#else + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) + writeByte(D::adjust(*data++)); delaycycles<3>(); +#endif } release(); } @@ -493,7 +571,12 @@ public: latch(); while(data != end) { data += SKIP; - // a slight touch of delay here helps optimize the timing of the status register check loop +#if defined(__MK20DX128__) + writeByte(D::adjust(*data++)); + writeByte(D::adjust(*data++)); + writeByte(D::adjust(*data++)); +#else + // a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM) if(_SPI_SPEED == 0) { writeByteNoWait(D::adjust(*data++)); delaycycles<13>(); writeByteNoWait(D::adjust(*data++)); delaycycles<13>(); @@ -503,6 +586,7 @@ public: writeByte(D::adjust(*data++)); delaycycles<3>(); writeByte(D::adjust(*data++)); delaycycles<3>(); } +#endif } release(); } @@ -513,6 +597,15 @@ public: }; +// Clock speed dividers +#define SPEED_DIV_2 2 +#define SPEED_DIV_4 4 +#define SPEED_DIV_8 8 +#define SPEED_DIV_16 16 +#define SPEED_DIV_32 32 +#define SPEED_DIV_64 64 +#define SPEED_DIV_128 128 + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -530,10 +623,10 @@ class AVRSPIOutput : public AVRSoftwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _LATCH_P 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> {}; +// #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__) @@ -542,6 +635,11 @@ class AVRSPIOutput<USART_DATA, USART_CLOCK, _LATCH, SPI_SPEED> : public AVRUSART 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> {}; +#elif defined(__MK20DX128__) // for Teensy 3.0 +#define SPI_DATA 11 +#define SPI_CLOCK 13 +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 #pragma message "No hardware SPI pins defined. All SPI access will default to bitbanged output" |