A conversion of the excellent Adafruit WS2801 library for Arduino to work on mbed
Adafruit_WS2801.cpp
- Committer:
- krissl
- Date:
- 2019-04-20
- Revision:
- 5:bbe85db83246
- Parent:
- 4:987c91c45188
- Child:
- 6:a7cf522f7ec8
File content as of revision 5:bbe85db83246:
#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); } } } */