Test program for my Multi_WS2811 library that started out as a fork of heroic/WS2811. My library uses hardware DMA on the FRDM-KL25Z to drive up to 16 strings of WS2811 or WS2812 LEDs in parallel.
Dependencies: Multi_WS2811 mbed MMA8451Q
Fork of WS2811 by
NOTE: I have accidentally pushed changes for another fork of this program that I used in the recent Georgetown Carnival Power Tool Races. When I get some time, I will restore the test program to its original glory.
You can see my power tool racer (Nevermore's Revenge) here
This tests my FRDM-KL25Z multi-string WS2811/WS2812 library. It uses the accelerometer to change the rainbow phase on two strings of LEDs as well as the touch sense to change brightness.
A video of this program in operation is here.
Here is the library that I developed to run the LEDs:
Import libraryMulti_WS2811
Library allowing up to 16 strings of 60 WS2811 or WS2812 LEDs to be driven from a single FRDM-KL25Z board. Uses hardware DMA to do a full 800 KHz rate without much CPU burden.
Revision 15:331e139672b5, committed 2013-12-05
- Comitter:
- bikeNomad
- Date:
- Thu Dec 05 15:12:47 2013 +0000
- Parent:
- 14:c97261a9a282
- Commit message:
- inlined bit writes
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LedStrip.cpp Thu Dec 05 15:12:47 2013 +0000 @@ -0,0 +1,80 @@ +#include "LedStrip.h" + +LedStrip::LedStrip(int n) +{ + // Allocate 3 bytes per pixel: + numLEDs = n; + pixels = (uint8_t *)malloc(numPixelBytes()); + if (pixels) { + memset(pixels, 0x00, numPixelBytes()); // Init to RGB 'off' state + } +} + +LedStrip::~LedStrip() +{ + free(pixels); +} + +uint32_t LedStrip::total_luminance(void) +{ + uint32_t running_total; + running_total = 0; + for (int i=0; i< numPixelBytes(); i++) + running_total += pixels[i]; + return running_total; +} + +// Convert R,G,B to combined 32-bit color +uint32_t LedStrip::Color(uint8_t r, uint8_t g, uint8_t b) +{ + // Take the lowest 7 bits of each value and append them end to end + // We have the top bit set high (its a 'parity-like' bit in the protocol + // and must be set!) + return ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b; +} + +// store the rgb component in our array +void LedStrip::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) +{ + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3 ] = g; + pixels[n*3+1] = r; + pixels[n*3+2] = b; +} + +void LedStrip::setPixelR(uint16_t n, uint8_t r) +{ + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3+1] = r; +} + +void LedStrip::setPixelG(uint16_t n, uint8_t g) +{ + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3] = g; +} + +void LedStrip::setPixelB(uint16_t n, uint8_t b) +{ + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3+2] = b; +} + +void LedStrip::setPackedPixels(uint8_t * buffer, uint32_t n) +{ + if (n >= numLEDs) return; + memcpy(pixels, buffer, (size_t) (n*3)); +} + +void LedStrip::setPixelColor(uint16_t n, uint32_t c) +{ + if (n >= numLEDs) return; // '>=' because arrays are 0-indexed + + pixels[n*3 ] = (c >> 16); + pixels[n*3+1] = (c >> 8); + pixels[n*3+2] = c; +}
--- a/LedStrip.h Sun Jul 28 08:38:18 2013 +0000 +++ b/LedStrip.h Thu Dec 05 15:12:47 2013 +0000 @@ -9,22 +9,33 @@ // of strip may be used in a single array or container. #include "mbed.h" + #ifndef LEDSTRIP_H #define LEDSTRIP_H -class LedStrip { - public: - virtual void begin(void)=0; - virtual void show(void)=0; - virtual void blank(void)=0; - virtual void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b)=0; - virtual void setPackedPixels(uint8_t * buffer, uint32_t n)=0; - virtual void setPixelB(uint16_t n, uint8_t b)=0; - virtual void setPixelG(uint16_t n, uint8_t g)=0; - virtual void setPixelR(uint16_t n, uint8_t r)=0; - virtual void setPixelColor(uint16_t n, uint32_t c)=0; - virtual uint16_t numPixels(void)=0; - virtual uint32_t Color(uint8_t, uint8_t, uint8_t)=0; - virtual uint32_t total_luminance(void); +class LedStrip +{ +public: + LedStrip(int n); + ~LedStrip(); + + virtual void begin(void)=0; + virtual void show(void)=0; + virtual void blank(void)=0; + + uint16_t numPixels(void) { return numLEDs; } + uint16_t numPixelBytes(void) { return numLEDs * 3; } + virtual uint32_t Color(uint8_t, uint8_t, uint8_t); + virtual uint32_t total_luminance(void); + virtual void setPixelB(uint16_t n, uint8_t b); + virtual void setPixelG(uint16_t n, uint8_t g); + virtual void setPixelR(uint16_t n, uint8_t r); + virtual void setPixelColor(uint16_t n, uint32_t c); + virtual void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); + virtual void setPackedPixels(uint8_t * buffer, uint32_t n); + +protected: + uint8_t *pixels; // Holds LED color values + uint16_t numLEDs; // Number of RGB LEDs in strand }; #endif \ No newline at end of file
--- a/WS2811.cpp Sun Jul 28 08:38:18 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,157 +0,0 @@ -// 800 KHz WS2811 driver, kinda. -// -// Parameterized and modified to use soft SPI. -// Jas Strong <jasmine@electronpusher.org> -/*****************************************************************************/ - -#include "LedStrip.h" -#include "WS2811.h" - -extern int onewire_speed_multiplier; // speed setting for ws2811 et al. - - -WS2811::WS2811(PinName dataPin, PinName clockPin, int n) : - dat(dataPin), - clk(clockPin) { - // Allocate 3 bytes per pixel: - numLEDs = n; - pixels = (uint8_t *)malloc(numLEDs * 3); - if (pixels) { - memset(pixels, 0x00, numLEDs * 3); // Init to RGB 'off' state - } - // calibrate delay loops for NRZ - int i; - guardtime.start(); - for (i=0; i<1000; i++) - /* do nothing */; - i=guardtime.read_us(); - printf("ws2811: 1000 iters took %d usec.\n", i); - bogocal = (1000 / (onewire_speed_multiplier * i * 4.2)); // iterations per bitcell (417 nsec) - printf("ws2811: calibrating to %d bogojiffies.\n", bogocal); - - data_mask = dat.get_mask(); - clock_mask = clk.get_mask(); - data_set = dat.get_set(); - data_clr = dat.get_clr(); - clock_set = clk.get_set(); - clock_clr = clk.get_clr(); - -} - -/* - * These chips use a one-wire protocol based on a sort of NRZ signalling- jas. - */ - -void WS2811::write(uint8_t byte) { - - for (int i=0; i<8; i++) { - if (byte & 0x80) - writebit(1); - else - writebit(0); - byte <<= 1; - } -} - -inline void WS2811::celldelay(void) { - for (volatile int i = 0; i<bogocal; i++) - /* do nothing */ ; -} - -inline void WS2811::writebit(bool bit) { - // first cell is always 1 - (*data_set) = data_mask; - celldelay(); - if (bit) { - (*clock_set) = data_mask; // dummy, we don't care but must take constant time - celldelay(); - } else { - (*data_clr) = data_mask; - celldelay(); - } - // last cell is always 0 - (*data_clr) = data_mask; - celldelay(); -} - -void WS2811::begin(void) { - blank(); - show(); -} - -uint16_t WS2811::numPixels(void) { - return numLEDs; -} - -void WS2811::blank(void) { - memset(pixels, 0x00, numLEDs * 3); -} - -void WS2811::show(void) { - uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED - while (guardtime.read_us() < 50) - /* spin */; - __disable_irq(); - for (i=0; i<nl3; i++ ) { - write(pixels[i]); - } - __enable_irq(); - guardtime.reset(); -} - - -uint32_t WS2811::total_luminance(void) { - uint32_t running_total; - running_total = 0; - for (int i=0; i<numLEDs*3; i++) - running_total += pixels[i]; - return running_total; -} - -// Convert R,G,B to combined 32-bit color -uint32_t WS2811::Color(uint8_t r, uint8_t g, uint8_t b) { - // Take the lowest 7 bits of each value and append them end to end - // We have the top bit set high (its a 'parity-like' bit in the protocol - // and must be set!) - return ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b; -} - -// store the rgb component in our array -void WS2811::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3 ] = g; - pixels[n*3+1] = r; - pixels[n*3+2] = b; -} - -void WS2811::setPixelR(uint16_t n, uint8_t r) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3+1] = r; -} - -void WS2811::setPixelG(uint16_t n, uint8_t g) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3] = g; -} - -void WS2811::setPixelB(uint16_t n, uint8_t b) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3+2] = b; -} - -void WS2811::setPackedPixels(uint8_t * buffer, uint32_t n) { - if (n >= numLEDs) return; - memcpy(pixels, buffer, (size_t) (n*3)); -} - -void WS2811::setPixelColor(uint16_t n, uint32_t c) { - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - - pixels[n*3 ] = (c >> 16); - pixels[n*3+1] = (c >> 8); - pixels[n*3+2] = c; -}
--- a/WS2811.h Sun Jul 28 08:38:18 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -// Mbed library to control WS2801-based RGB LED Strips -// some portions (c) 2011 Jelmer Tiete -// This library is ported from the Arduino implementation of Adafruit Industries -// found at: http://github.com/adafruit/LPD8806 -// and their strips: http://www.adafruit.com/products/306 -// Released under the MIT License: http://mbed.org/license/mit -// -/*****************************************************************************/ - -// Heavily modified by Jas Strong, 2012-10-04 -// Changed to use a virtual base class and to use software SPI. - -#include "mbed.h" -#include "LedStrip.h" -#ifndef MBED_WS2811_H -#define MBED_WS2811_H - - -class WS2811 : public LedStrip { - - public: - - WS2811(PinName dataPin, PinName clockPin, int n); - virtual void begin(void); - virtual void show(void); - virtual void blank(void); - virtual void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); - virtual void setPackedPixels(uint8_t * buffer, uint32_t n); - virtual void setPixelB(uint16_t n, uint8_t b); - virtual void setPixelG(uint16_t n, uint8_t g); - virtual void setPixelR(uint16_t n, uint8_t r); - virtual void setPixelColor(uint16_t n, uint32_t c); - virtual uint16_t numPixels(void); - virtual uint32_t Color(uint8_t, uint8_t, uint8_t); - virtual uint32_t total_luminance(void); - - private: - DigitalOut dat; - DigitalOut clk; - __IO uint32_t *data_set; - __IO uint32_t *clock_set; - __IO uint32_t *data_clr; - __IO uint32_t *clock_clr; - uint32_t clock_mask; - uint32_t data_mask; - void write(uint8_t byte); - void writebit(bool bit); - void celldelay(void); - uint8_t *pixels; // Holds LED color values - uint16_t numLEDs; // Number of RGB LEDs in strand - Timer guardtime; - uint32_t bogocal; - -}; -#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WS2812.cpp Thu Dec 05 15:12:47 2013 +0000 @@ -0,0 +1,82 @@ +// 800 KHz WS2812 driver, kinda. +// +// Parameterized and modified to use soft SPI. +// Jas Strong <jasmine@electronpusher.org> +// Modified to use hard SPI by Ned Konz <ned@bike-nomad.com> +/*****************************************************************************/ + +#include "LedStrip.h" +#include "WS2812.h" + +WS2812::WS2812(int n, PinName mosi, PinName sclk) : + LedStrip(n), + spi(mosi, NC, sclk) +{ +#ifdef TARGET_KL25Z + spi.format(8, 3); +#else + spi.format(16, 3); +#endif + spi.frequency(800e3 * 16); // 12.8 MHz => 800KHz bit rate +} + +/* + * These chips use a one-wire protocol based on a sort of NRZ signalling- jas. + */ + +inline void WS2812::writebit(bool bit) +{ + if (bit) { +#ifdef TARGET_KL25Z + spi.write(0xff); // 8 high + spi.write(0x00); // 8 low +#else + spi.write(0xff00); +#endif + } else { +#ifdef TARGET_KL25Z + spi.write(0xe0); // 3 high, 5 low + spi.write(0x00); // +8 low +#else + spi.write(0xe000); +#endif + } +} + +void WS2812::write(uint8_t byte) +{ + writebit(byte & 0x80); + writebit(byte & 0x40); + writebit(byte & 0x20); + writebit(byte & 0x10); + writebit(byte & 0x08); + writebit(byte & 0x04); + writebit(byte & 0x02); + writebit(byte & 0x01); +} + +void WS2812::begin(void) +{ + blank(); + show(); +} + +void WS2812::blank(void) +{ + memset(pixels, 0x00, numPixelBytes()); +} + +void WS2812::show(void) +{ + uint16_t i, nl3 = numPixelBytes(); // 3 bytes per LED + while (guardtime.read_us() < 50) + /* spin */; + __disable_irq(); + for (i=0; i<nl3; i++ ) { + write(pixels[i]); + } + __enable_irq(); + guardtime.reset(); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WS2812.h Thu Dec 05 15:12:47 2013 +0000 @@ -0,0 +1,42 @@ +// Mbed library to control WS2801-based RGB LED Strips +// some portions (c) 2011 Jelmer Tiete +// This library is ported from the Arduino implementation of Adafruit Industries +// found at: http://github.com/adafruit/LPD8806 +// and their strips: http://www.adafruit.com/products/306 +// Released under the MIT License: http://mbed.org/license/mit +// +/*****************************************************************************/ + +// Heavily modified by Jas Strong, 2012-10-04 +// Changed to use a virtual base class and to use software SPI. + +#include "mbed.h" +#include "LedStrip.h" +#ifndef MBED_WS2811_H +#define MBED_WS2811_H + +#ifdef TARGET_KL25Z +#define MOSI_DEFAULT PTD2 +#define SCLK_DEFAULT PTC5 +#else +#endif + +class WS2812 : public LedStrip +{ +public: + WS2812(int n, PinName mosi = MOSI_DEFAULT, PinName sclk = SCLK_DEFAULT); + virtual void begin(void); + virtual void show(void); + virtual void blank(void); + +private: + SPI spi; // to do actual communication + + Timer guardtime; + uint32_t bogocal; + + void write(uint8_t byte); + void writebit(bool bit); + void celldelay(void); +}; +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Dec 05 15:12:47 2013 +0000 @@ -0,0 +1,7 @@ +#include "mbed.h" +#include "WS2812.h" + +int main(void) +{ + WS2812 lightStrip(60*4, PTD2, PTD3); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Thu Dec 05 15:12:47 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/9c8f0e3462fb \ No newline at end of file