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:
authordanielgarcia@gmail.com <danielgarcia@gmail.com@4ad4ec5c-605d-bd5c-5796-512c9b60011b>2013-02-18 11:20:47 +0400
committerdanielgarcia@gmail.com <danielgarcia@gmail.com@4ad4ec5c-605d-bd5c-5796-512c9b60011b>2013-02-18 11:20:47 +0400
commit5555154f3ae88082deb071f0e36a4c2c39d85d98 (patch)
tree3d6e178427291ff81b3073d93c494249028e287f
parentb9bfc26db78d337ec2a02e24210d9e9ce55bd3de (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.h8
-rw-r--r--clockless.h48
-rw-r--r--examples/Fast2Dev/Fast2Dev.ino36
-rw-r--r--fastpin.h163
-rw-r--r--fastspi.h340
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
diff --git a/fastpin.h b/fastpin.h
index 04fd08a9..38ff8df5 100644
--- a/fastpin.h
+++ b/fastpin.h
@@ -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."
diff --git a/fastspi.h b/fastspi.h
index 65f3bba2..ba106b50 100644
--- a/fastspi.h
+++ b/fastspi.h
@@ -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"