A conversion of the excellent Adafruit WS2801 library for Arduino to work on mbed

Dependents:   SSS_Ble

Adafruit_WS2801.cpp

Committer:
SomeRandomBloke
Date:
2014-06-03
Revision:
4:987c91c45188
Parent:
3:dfffbd9f8ac6
Child:
5:bbe85db83246

File content as of revision 4:987c91c45188:

#include "mbed.h"
#include "Adafruit_WS2801.h"


// Example to control WS2801-based RGB LED Modules in a strand or strip
// Written by Adafruit - MIT license
/*****************************************************************************/

SPI spi(p16, p15, p13); // mosi, miso, sclk

// Constructor for use with hardware SPI (specific clock/data pins):
/*Adafruit_WS2801::Adafruit_WS2801(uint16_t n, uint8_t order): clkpin(PTD4), datapin(PTA12)
{
    rgb_order = order;
    alloc(n);
    updatePins();
}*/

// Constructor for use with arbitrary clock/data pins:
Adafruit_WS2801::Adafruit_WS2801(int16_t n, PinName dpin, PinName cpin, uint8_t order) 
    : clkpin(cpin), datapin(dpin)
{
    rgb_order = order;
    alloc(n);
    width = 1;
    height = n;
    hardwareSPI = false;
//  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
Adafruit_WS2801::Adafruit_WS2801(int16_t w, int16_t h, PinName dpin, PinName cpin, uint8_t order) 
    : clkpin(cpin), datapin(dpin)
{
    rgb_order = order;
    alloc(w * h);
    width = w;
    height = h;
    hardwareSPI = false;
//  updatePins(dpin, cpin);
}

// Allocate 3 bytes per pixel, init to RGB 'off' state:
void Adafruit_WS2801::alloc(uint16_t n)
{
    begun   = false;
    numLEDs = ((pixels = (uint8_t *)calloc(n, 3)) != NULL) ? n : 0;
    hardwareSPI = true;

//    for(int bits = 0; bits <= numLEDs*24; bits++) {
//        spi.write(0x00);
//        clkpin = 0;
//        datapin = 0;
//        clkpin = 1;
//    }
//    clkpin = 0;
}

// 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
/*Adafruit_WS2801::Adafruit_WS2801(uint16_t w, uint16_t h, uint8_t order)
{
    rgb_order = order;
    alloc(w * h);
    width = w;
    height = h;
    hardwareSPI = true;
//  updatePins(dpin, cpin);
}
*/



// via Michael Vogt/neophob: empty constructor is used when strand length
// isn't known at compile-time; situations where program config might be
// read from internal flash memory or an SD card, or arrive via serial
// command.  If using this constructor, MUST follow up with updateLength()
// and updatePins() to establish the strand length and output pins!
// Also, updateOrder() to change RGB vs GRB order (RGB is default).
//Adafruit_WS2801::Adafruit_WS2801(void) : clkpin(PTD4), datapin(PTA12)
//{
//    begun     = false;
//    numLEDs   = 0;
//    pixels    = NULL;
//    rgb_order = WS2801_RGB;
//    updatePins(); // Must assume hardware SPI until pins are set
//}

// Release memory (as needed):
Adafruit_WS2801::~Adafruit_WS2801(void)
{
    if (pixels != NULL) {
        free(pixels);
    }
}

// Activate hard/soft SPI as appropriate:
void Adafruit_WS2801::begin(void)
{
    if( hardwareSPI ) {
        // Setup the spi for 8 bit data, high steady state clock,
        // second edge capture, with a 1MHz clock rate
        spi.format(8,0);
        spi.frequency(1000000);
    } else {
        datapin = 0;
        clkpin = 0;
    }
    begun = true;
}

// Change pin assignments post-constructor, switching to hardware SPI:
void Adafruit_WS2801::updatePins(void)
{
    hardwareSPI = true;
    datapin = 0;
    clkpin = 0;
}


// Change pin assignments post-constructor, using arbitrary pins:
void Adafruit_WS2801::updatePins(PinName dpin, PinName cpin)
{
    // Note: any prior clock/data pin directions are left as-is and are
    // NOT restored as inputs!

    datapin     = DigitalOut(dpin);
    clkpin      = DigitalOut(cpin);
}

// Return the number of LEDs
uint16_t Adafruit_WS2801::numPixels(void)
{
    return numLEDs;
}

// Change strand length (see notes with empty constructor, above):
void Adafruit_WS2801::updateLength(uint16_t n)
{
    if(pixels != NULL) free(pixels); // Free existing data (if any)
    // Allocate new data -- note: ALL PIXELS ARE CLEARED
    numLEDs = ((pixels = (uint8_t *)calloc(n, 3)) != NULL) ? n : 0;
    // 'begun' state does not change -- pins retain prior modes
}

// Change RGB data order (see notes with empty constructor, above):
void Adafruit_WS2801::updateOrder(uint8_t order)
{
    rgb_order = order;
    // Existing LED data, if any, is NOT reformatted to new data order.
    // Calling function should clear or fill pixel data anew.
}

void Adafruit_WS2801::show(void)
{
    uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED

    if( hardwareSPI ) {
        for(i=0; i<nl3; i++ ) {
            spi.write( pixels[i]);
        }        
        wait_ms(1); // Needed otherwise sometimes doesnt display
    } else {
        uint8_t  bit;

        // Write 24 bits per pixel:
        for(i=0; i<nl3; i++ ) {
            for(bit=0x80; bit; bit >>= 1) {
                clkpin = 0;
                datapin = (pixels[i] & bit) ? 1 : 0;
                clkpin = 1;
                wait_us(100);
            }
        }
        datapin = 0;

        clkpin = 0;
        wait_ms(1); // Data is latched by holding clock pin low for 1 millisecond
        clkpin = 1;
    }
}

// Set pixel color from separate 8-bit R, G, B components:
void Adafruit_WS2801::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b)
{
    if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
        uint8_t *p = &pixels[n * 3];
        // See notes later regarding color order
        if(rgb_order == WS2801_RGB) {
            *p++ = r;
            *p++ = g;
        } else {
            *p++ = g;
            *p++ = r;
        }
        *p++ = b;
    }
}

// Set pixel color from separate 8-bit R, G, B components using x,y coordinate system:
void Adafruit_WS2801::setPixelColor(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b)
{
    if( x < 0 || x >= width || y < 0 || y >= height )
        return;         // Dont try to update a pixel that doesnt exist

    bool evenRow = ((y % 2) == 0);
    // calculate x offset first
    uint16_t offset = x % width;

    if (!evenRow) {
        offset = (width-1) - offset;
    }
    // add y offset
    offset += y * width;
    setPixelColor(offset, r, g, b);
}

// Set pixel color from 'packed' 32-bit RGB value:
void Adafruit_WS2801::setPixelColor(uint16_t n, uint32_t c)
{
    if( n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
        uint8_t *p = &pixels[n * 3];
        // To keep the show() loop as simple & fast as possible, the
        // internal color representation is native to different pixel
        // types.  For compatibility with existing code, 'packed' RGB
        // values passed in or out are always 0xRRGGBB order.
        if(rgb_order == WS2801_RGB) {
            *p++ = c >> 16; // Red
            *p++ = c >>  8; // Green
        } else {
            *p++ = c >>  8; // Green
            *p++ = c >> 16; // Red
        }
        *p++ = c;         // Blue
    }
}

// Set pixel color from 'packed' 32-bit RGB value using x,y coordinate system:
void Adafruit_WS2801::setPixelColor(int16_t x, int16_t y, uint32_t c)
{
    if( x < 0 || x >= width || y < 0 || y >= height )
        return;         // Dont try to update a pixel that doesnt exist
        
    bool evenRow = ((y % 2) == 0);
    // calculate x offset first
    uint16_t offset = x % width;
    if (!evenRow) {
        offset = (width-1) - offset;
    }
    // add y offset
    offset += y * width;
    setPixelColor(offset, c);
}

// Query color from previously-set pixel (returns packed 32-bit RGB value)
uint32_t Adafruit_WS2801::getPixelColor(uint16_t n)
{
    if(n < numLEDs) {
        uint16_t ofs = n * 3;
        // To keep the show() loop as simple & fast as possible, the
        // internal color representation is native to different pixel
        // types.  For compatibility with existing code, 'packed' RGB
        // values passed in or out are always 0xRRGGBB order.
        return (rgb_order == WS2801_RGB) ?
               ((uint32_t)pixels[ofs] << 16) | ((uint16_t) pixels[ofs + 1] <<  8) | pixels[ofs + 2] :
               (pixels[ofs] <<  8) | ((uint32_t)pixels[ofs + 1] << 16) | pixels[ofs + 2];
    }

    return 0; // Pixel # is out of bounds
}

// bresenham's algorithm - thx wikpedia
void Adafruit_WS2801::drawLine(int16_t x0, int16_t y0, 
                int16_t x1, int16_t y1, 
                uint32_t color) {
  int16_t steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    ws_swap(x0, y0);
    ws_swap(x1, y1);
  }

  if (x0 > x1) {
    ws_swap(x0, x1);
    ws_swap(y0, y1);
  }

  int16_t dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);

  int16_t err = dx / 2;
  int16_t ystep;

  if (y0 < y1) {
    ystep = 1;
  } else {
    ystep = -1;
  }

  for (; x0<=x1; x0++) {
    if (steep) {
      setPixelColor(y0, x0, color);
    } else {
      setPixelColor(x0, y0, color);
    }
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}

void Adafruit_WS2801::drawFastVLine(int16_t x, int16_t y, 
                 int16_t h, uint32_t color) {
  // stupidest version - update in subclasses if desired!
  drawLine(x, y, x, y+h-1, color);
}


void Adafruit_WS2801::drawFastHLine(int16_t x, int16_t y, 
                 int16_t w, uint32_t color) {
  // stupidest version - update in subclasses if desired!
  drawLine(x, y, x+w-1, y, color);
}


// draw a rectangle
void Adafruit_WS2801::drawRect(int16_t x, int16_t y, 
                int16_t w, int16_t h, uint32_t color) {
  drawFastHLine(x, y, w, color);
  drawFastHLine(x, y+h-1, w, color);
  drawFastVLine(x, y, h, color);
  drawFastVLine(x+w-1, y, h, color);
}

// draw a circle outline
void Adafruit_WS2801::drawCircle(int16_t x0, int16_t y0, int16_t r, uint32_t color) {
  int16_t f = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x = 0;
  int16_t y = r;

  setPixelColor(x0, y0+r, color);
  setPixelColor(x0, y0-r, color);
  setPixelColor(x0+r, y0, color);
  setPixelColor(x0-r, y0, color);

  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;
  
    setPixelColor(x0 + x, y0 + y, color);
    setPixelColor(x0 - x, y0 + y, color);
    setPixelColor(x0 + x, y0 - y, color);
    setPixelColor(x0 - x, y0 - y, color);
    setPixelColor(x0 + y, y0 + x, color);
    setPixelColor(x0 - y, y0 + x, color);
    setPixelColor(x0 + y, y0 - x, color);
    setPixelColor(x0 - y, y0 - x, color);
    
  }
}
/*
void Adafruit_WS2801::fillCircle(int16_t x0, int16_t y0, int16_t r, uint32_t color) {
  drawFastVLine(x0, y0-r, 2*r+1, color);
  fillCircleHelper(x0, y0, r, 3, 0, color);
}

// used to do circles and roundrects!
void Adafruit_WS2801::fillCircleHelper(int16_t x0, int16_t y0, int16_t r,
                    uint8_t cornername, int16_t delta, uint32_t color) {

  int16_t f     = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x     = 0;
  int16_t y     = r;

  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;

    if (cornername & 0x1) {
      drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
      drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
    }
    if (cornername & 0x2) {
      drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
      drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
    }
  }
}
*/