Skip to content

Commit

Permalink
Trinket, Gemma & Due support (experimental)
Browse files Browse the repository at this point in the history
  • Loading branch information
PaintYourDragon committed Jun 29, 2014
1 parent 91a2d7a commit ce12ff6
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 33 deletions.
117 changes: 87 additions & 30 deletions 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<<USIWM0)|(1<<USITC)); \
SPI_DELAY \
USICR = ((1<<USIWM0)|(1<<USITC)|(1<<USICLK)); \
SPI_DELAY

static void spi_out(uint8_t n) {
USIDR = n;
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
SPIBIT
}

#else

// All other boards support Full and Proper Hardware SPI

#include <SPI.h>
#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;
Expand All @@ -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);
}
Expand All @@ -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:
Expand All @@ -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) {
Expand Down Expand Up @@ -137,17 +190,21 @@ void Adafruit_WS2801::show(void) {

// Write 24 bits per pixel:
if(hardwareSPI) {
for(i=0; i<nl3; i++) {
SPDR = pixels[i];
while(!(SPSR & (1<<SPIF)));
}
for(i=0; i<nl3; i++) spi_out(pixels[i]);
} else {
for(i=0; i<nl3; i++ ) {
for(bit=0x80; bit; bit >>= 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
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion Adafruit_WS2801.h
Expand Up @@ -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);
Expand Down
10 changes: 8 additions & 2 deletions 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 <avr/power.h>
#endif

/*****************************************************************************
Example sketch for driving Adafruit WS2801 pixels!
Expand Down Expand Up @@ -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'
Expand Down

0 comments on commit ce12ff6

Please sign in to comment.