diff --git a/Adafruit_WS2801.cpp b/Adafruit_WS2801.cpp index a3e9a31..927bdec 100644 --- a/Adafruit_WS2801.cpp +++ b/Adafruit_WS2801.cpp @@ -1,10 +1,50 @@ -#include "SPI.h" #include "Adafruit_WS2801.h" // Example to control WS2801-based RGB LED Modules in a strand or strip // Written by Adafruit - MIT license /*****************************************************************************/ +#ifdef __AVR_ATtiny85__ + +// Teensy/Gemma-specific stuff for hardware-assisted SPI @ 1 MHz + +#if(F_CPU > 8000000L) + #define SPI_DELAY asm volatile("rjmp .+0\nrjmp .+0"); // Burn 4 cycles +#elif(F_CPU > 4000000L) + #define SPI_DELAY asm volatile("rjmp .+0"); // Burn 2 cycles +#else + #define SPI_DELAY // Run max speed +#endif + +#define SPIBIT \ + USICR = ((1< +#define spi_out(n) (void)SPI.transfer(n) + +#endif + +/*****************************************************************************/ + // Constructor for use with hardware SPI (specific clock/data pins): Adafruit_WS2801::Adafruit_WS2801(uint16_t n, uint8_t order) { rgb_order = order; @@ -19,14 +59,15 @@ Adafruit_WS2801::Adafruit_WS2801(uint16_t n, uint8_t dpin, uint8_t cpin, uint8_t updatePins(dpin, cpin); } -// Constructor for use with a matrix configuration, specify w, h for size of matrix -// assumes configuration where string starts at coordinate 0,0 and continues to w-1,0, w-1,1 -// and on to 0,1, 0,2 and on to w-1,2 and so on. Snaking back and forth till the end. -// other function calls with provide access to pixels via an x,y coordinate system +// Constructor for use with a matrix configuration, specify w, h for size +// of matrix. Assumes configuration where string starts at coordinate 0,0 +// and continues to w-1,0, w-1,1 and on to 0,1, 0,2 and on to w-1,2 and +// so on. Snaking back and forth till the end. Other function calls +// provide access to pixels via an x,y coordinate system Adafruit_WS2801::Adafruit_WS2801(uint16_t w, uint16_t h, uint8_t dpin, uint8_t cpin, uint8_t order) { rgb_order = order; alloc(w * h); - width = w; + width = w; height = h; updatePins(dpin, cpin); } @@ -53,9 +94,7 @@ Adafruit_WS2801::Adafruit_WS2801(void) { // Release memory (as needed): Adafruit_WS2801::~Adafruit_WS2801(void) { - if (pixels != NULL) { - free(pixels); - } + if(pixels) free(pixels); } // Activate hard/soft SPI as appropriate: @@ -71,45 +110,59 @@ void Adafruit_WS2801::begin(void) { // Change pin assignments post-constructor, switching to hardware SPI: void Adafruit_WS2801::updatePins(void) { - hardwareSPI = true; + pinMode(datapin, INPUT); // Restore data and clock pins to inputs + pinMode(clkpin , INPUT); datapin = clkpin = 0; + hardwareSPI = true; // If begin() was previously invoked, init the SPI hardware now: if(begun == true) startSPI(); // Otherwise, SPI is NOT initted until begin() is explicitly called. - - // Note: any prior clock/data pin directions are left as-is and are - // NOT restored as inputs! } // Change pin assignments post-constructor, using arbitrary pins: void Adafruit_WS2801::updatePins(uint8_t dpin, uint8_t cpin) { - if(begun == true) { // If begin() was previously invoked... // If previously using hardware SPI, turn that off: - if(hardwareSPI == true) SPI.end(); - // Regardless, now enable output on 'soft' SPI pins: - pinMode(dpin, OUTPUT); + if(hardwareSPI) { +#ifdef __AVR_ATtiny85__ + DDRB &= ~(_BV(PORTB1) | _BV(PORTB2)); +#else + SPI.end(); +#endif + } else { + pinMode(datapin, INPUT); // Restore prior data and clock pins to inputs + pinMode(clkpin , INPUT); + } + pinMode(dpin, OUTPUT); // Enable output on 'soft' SPI pins: pinMode(cpin, OUTPUT); - } // Otherwise, pins are not set to outputs until begin() is called. - - // Note: any prior clock/data pin directions are left as-is and are - // NOT restored as inputs! + } - hardwareSPI = false; datapin = dpin; clkpin = cpin; +#ifdef __AVR__ clkport = portOutputRegister(digitalPinToPort(cpin)); clkpinmask = digitalPinToBitMask(cpin); dataport = portOutputRegister(digitalPinToPort(dpin)); datapinmask = digitalPinToBitMask(dpin); +#endif + hardwareSPI = false; } // Enable SPI hardware and set up protocol details: void Adafruit_WS2801::startSPI(void) { - SPI.begin(); - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - SPI.setClockDivider(SPI_CLOCK_DIV16); // 1 MHz max, else flicker +#ifdef __AVR_ATtiny85__ + PORTB &= ~(_BV(PORTB1) | _BV(PORTB2)); // Outputs + DDRB |= _BV(PORTB1) | _BV(PORTB2); // DO (NOT MOSI) + SCK +#else + SPI.begin(); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + #if defined(__AVR__) || defined(CORE_TEENSY) + SPI.setClockDivider(SPI_CLOCK_DIV16); // 1MHz max, else flicker + #else + SPI.setClockDivider((F_CPU + 500000L) / 1000000L); + #endif +#endif } uint16_t Adafruit_WS2801::numPixels(void) { @@ -137,17 +190,21 @@ void Adafruit_WS2801::show(void) { // Write 24 bits per pixel: if(hardwareSPI) { - for(i=0; i>= 1) { +#ifdef __AVR__ if(pixels[i] & bit) *dataport |= datapinmask; else *dataport &= ~datapinmask; *clkport |= clkpinmask; *clkport &= ~clkpinmask; +#else + if(pixels[i] & bit) digitalWrite(datapin, HIGH); + else digitalWrite(datapin, LOW); + digitalWrite(clkpin, HIGH); + digitalWrite(clkpin, LOW); +#endif } } } diff --git a/Adafruit_WS2801.h b/Adafruit_WS2801.h index 343caf4..cc0ca31 100644 --- a/Adafruit_WS2801.h +++ b/Adafruit_WS2801.h @@ -55,10 +55,13 @@ class Adafruit_WS2801 { uint8_t *pixels, // Holds color values for each LED (3 bytes each) rgb_order, // Color order; RGB vs GRB (or others, if needed in future) - clkpin , datapin, // Clock & data pin numbers + clkpin , datapin; // Clock & data pin numbers +#ifdef __AVR__ + uint8_t clkpinmask, datapinmask; // Clock & data PORT bitmasks volatile uint8_t *clkport , *dataport; // Clock & data PORT registers +#endif void alloc(uint16_t n), startSPI(void); diff --git a/examples/strandtest/strandtest.pde b/examples/strandtest/strandtest.pde index 87470b6..16e5c1d 100644 --- a/examples/strandtest/strandtest.pde +++ b/examples/strandtest/strandtest.pde @@ -1,5 +1,8 @@ -#include "SPI.h" #include "Adafruit_WS2801.h" +#include "SPI.h" // Comment out this line if using Trinket or Gemma +#ifdef __AVR_ATtiny85__ + #include +#endif /***************************************************************************** Example sketch for driving Adafruit WS2801 pixels! @@ -50,7 +53,10 @@ Adafruit_WS2801 strip = Adafruit_WS2801(25, dataPin, clockPin); //Adafruit_WS2801 strip = Adafruit_WS2801(25, WS2801_GRB); void setup() { - +#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L) + clock_prescale_set(clock_div_1); // Enable 16 MHz on Trinket +#endif + strip.begin(); // Update LED contents, to start they are all 'off'