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.
WS2811.cpp@11:d127f93be182, 2013-02-20 (annotated)
- Committer:
- heroic
- Date:
- Wed Feb 20 03:38:25 2013 +0000
- Revision:
- 11:d127f93be182
- Parent:
- 10:62368b801d16
- Child:
- 12:7ebd51549c04
Fix timing on ws2811.
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| heroic | 10:62368b801d16 | 1 | // 800 KHz WS2811 driver, kinda. |
| heroic | 8:e3249c2b7607 | 2 | // |
| heroic | 8:e3249c2b7607 | 3 | // Parameterized and modified to use soft SPI. |
| heroic | 8:e3249c2b7607 | 4 | // Jas Strong <jasmine@electronpusher.org> |
| heroic | 8:e3249c2b7607 | 5 | /*****************************************************************************/ |
| heroic | 8:e3249c2b7607 | 6 | |
| heroic | 8:e3249c2b7607 | 7 | #include "LedStrip.h" |
| heroic | 8:e3249c2b7607 | 8 | #include "WS2811.h" |
| heroic | 8:e3249c2b7607 | 9 | |
| heroic | 10:62368b801d16 | 10 | extern int onewire_speed_multiplier; // speed setting for ws2811 et al. |
| heroic | 10:62368b801d16 | 11 | |
| heroic | 10:62368b801d16 | 12 | |
| heroic | 8:e3249c2b7607 | 13 | WS2811::WS2811(PinName dataPin, PinName clockPin, int n) : |
| heroic | 8:e3249c2b7607 | 14 | dat(dataPin), |
| heroic | 8:e3249c2b7607 | 15 | clk(clockPin) { |
| heroic | 8:e3249c2b7607 | 16 | // Allocate 3 bytes per pixel: |
| heroic | 8:e3249c2b7607 | 17 | numLEDs = n; |
| heroic | 8:e3249c2b7607 | 18 | if ((pixels = (uint8_t *)malloc(numLEDs * 3))) { |
| heroic | 8:e3249c2b7607 | 19 | memset(pixels, 0x00, numLEDs * 3); // Init to RGB 'off' state |
| heroic | 8:e3249c2b7607 | 20 | } |
| heroic | 8:e3249c2b7607 | 21 | // calibrate delay loops for NRZ |
| heroic | 8:e3249c2b7607 | 22 | int i; |
| heroic | 8:e3249c2b7607 | 23 | guardtime.start(); |
| heroic | 8:e3249c2b7607 | 24 | for (i=0; i<1000; i++) |
| heroic | 8:e3249c2b7607 | 25 | /* do nothing */; |
| heroic | 8:e3249c2b7607 | 26 | i=guardtime.read_us(); |
| heroic | 8:e3249c2b7607 | 27 | printf("ws2811: 1000 iters took %d usec.\n", i); |
| heroic | 11:d127f93be182 | 28 | bogocal = (1000 / (onewire_speed_multiplier * i * 4.2)); // iterations per bitcell (417 nsec) |
| heroic | 8:e3249c2b7607 | 29 | printf("ws2811: calibrating to %d bogojiffies.\n", bogocal); |
| heroic | 8:e3249c2b7607 | 30 | } |
| heroic | 8:e3249c2b7607 | 31 | |
| heroic | 8:e3249c2b7607 | 32 | /* |
| heroic | 8:e3249c2b7607 | 33 | * These chips use a one-wire protocol based on a sort of NRZ signalling- jas. |
| heroic | 8:e3249c2b7607 | 34 | */ |
| heroic | 8:e3249c2b7607 | 35 | |
| heroic | 8:e3249c2b7607 | 36 | void WS2811::write(uint8_t byte) { |
| heroic | 9:dd524af149e6 | 37 | __disable_irq(); |
| heroic | 8:e3249c2b7607 | 38 | for (int i=0; i<8; i++) { |
| heroic | 8:e3249c2b7607 | 39 | if (byte & 0x80) |
| heroic | 8:e3249c2b7607 | 40 | writebit(1); |
| heroic | 8:e3249c2b7607 | 41 | else |
| heroic | 8:e3249c2b7607 | 42 | writebit(0); |
| heroic | 8:e3249c2b7607 | 43 | byte <<= 1; |
| heroic | 8:e3249c2b7607 | 44 | } |
| heroic | 9:dd524af149e6 | 45 | __enable_irq(); |
| heroic | 8:e3249c2b7607 | 46 | } |
| heroic | 8:e3249c2b7607 | 47 | |
| heroic | 8:e3249c2b7607 | 48 | inline void WS2811::celldelay(void) { |
| heroic | 8:e3249c2b7607 | 49 | for (int i = 0; i<bogocal; i++) |
| heroic | 8:e3249c2b7607 | 50 | /* do nothing */ ; |
| heroic | 8:e3249c2b7607 | 51 | } |
| heroic | 8:e3249c2b7607 | 52 | |
| heroic | 8:e3249c2b7607 | 53 | void WS2811::writebit(bool bit) { |
| heroic | 8:e3249c2b7607 | 54 | // first cell is always 1 |
| heroic | 8:e3249c2b7607 | 55 | dat = 1; |
| heroic | 8:e3249c2b7607 | 56 | celldelay(); |
| heroic | 8:e3249c2b7607 | 57 | if (bit) { |
| heroic | 8:e3249c2b7607 | 58 | celldelay(); |
| heroic | 8:e3249c2b7607 | 59 | } else { |
| heroic | 8:e3249c2b7607 | 60 | dat=0; |
| heroic | 8:e3249c2b7607 | 61 | celldelay(); |
| heroic | 8:e3249c2b7607 | 62 | } |
| heroic | 8:e3249c2b7607 | 63 | // last cell is always 0 |
| heroic | 8:e3249c2b7607 | 64 | dat=0; |
| heroic | 8:e3249c2b7607 | 65 | celldelay(); |
| heroic | 8:e3249c2b7607 | 66 | } |
| heroic | 8:e3249c2b7607 | 67 | |
| heroic | 8:e3249c2b7607 | 68 | void WS2811::begin(void) { |
| heroic | 8:e3249c2b7607 | 69 | blank(); |
| heroic | 8:e3249c2b7607 | 70 | show(); |
| heroic | 8:e3249c2b7607 | 71 | } |
| heroic | 8:e3249c2b7607 | 72 | |
| heroic | 8:e3249c2b7607 | 73 | uint16_t WS2811::numPixels(void) { |
| heroic | 8:e3249c2b7607 | 74 | return numLEDs; |
| heroic | 8:e3249c2b7607 | 75 | } |
| heroic | 8:e3249c2b7607 | 76 | |
| heroic | 8:e3249c2b7607 | 77 | void WS2811::blank(void) { |
| heroic | 8:e3249c2b7607 | 78 | memset(pixels, 0x00, numLEDs * 3); |
| heroic | 8:e3249c2b7607 | 79 | } |
| heroic | 8:e3249c2b7607 | 80 | |
| heroic | 8:e3249c2b7607 | 81 | void WS2811::show(void) { |
| heroic | 8:e3249c2b7607 | 82 | uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED |
| heroic | 8:e3249c2b7607 | 83 | while (guardtime.read_us() < 50) |
| heroic | 8:e3249c2b7607 | 84 | /* spin */; |
| heroic | 8:e3249c2b7607 | 85 | for (i=0; i<nl3; i++ ) { |
| heroic | 8:e3249c2b7607 | 86 | write(pixels[i]); |
| heroic | 8:e3249c2b7607 | 87 | } |
| heroic | 8:e3249c2b7607 | 88 | |
| heroic | 8:e3249c2b7607 | 89 | guardtime.reset(); |
| heroic | 8:e3249c2b7607 | 90 | } |
| heroic | 8:e3249c2b7607 | 91 | |
| heroic | 8:e3249c2b7607 | 92 | |
| heroic | 8:e3249c2b7607 | 93 | uint32_t WS2811::total_luminance(void) { |
| heroic | 8:e3249c2b7607 | 94 | uint32_t running_total; |
| heroic | 8:e3249c2b7607 | 95 | running_total = 0; |
| heroic | 8:e3249c2b7607 | 96 | for (int i=0; i<numLEDs*3; i++) |
| heroic | 8:e3249c2b7607 | 97 | running_total += pixels[i]; |
| heroic | 8:e3249c2b7607 | 98 | return running_total; |
| heroic | 8:e3249c2b7607 | 99 | } |
| heroic | 8:e3249c2b7607 | 100 | |
| heroic | 8:e3249c2b7607 | 101 | // Convert R,G,B to combined 32-bit color |
| heroic | 8:e3249c2b7607 | 102 | uint32_t WS2811::Color(uint8_t r, uint8_t g, uint8_t b) { |
| heroic | 8:e3249c2b7607 | 103 | // Take the lowest 7 bits of each value and append them end to end |
| heroic | 8:e3249c2b7607 | 104 | // We have the top bit set high (its a 'parity-like' bit in the protocol |
| heroic | 8:e3249c2b7607 | 105 | // and must be set!) |
| heroic | 8:e3249c2b7607 | 106 | return ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b; |
| heroic | 8:e3249c2b7607 | 107 | } |
| heroic | 8:e3249c2b7607 | 108 | |
| heroic | 8:e3249c2b7607 | 109 | // store the rgb component in our array |
| heroic | 8:e3249c2b7607 | 110 | void WS2811::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { |
| heroic | 8:e3249c2b7607 | 111 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 112 | |
| heroic | 8:e3249c2b7607 | 113 | pixels[n*3 ] = g; |
| heroic | 8:e3249c2b7607 | 114 | pixels[n*3+1] = r; |
| heroic | 8:e3249c2b7607 | 115 | pixels[n*3+2] = b; |
| heroic | 8:e3249c2b7607 | 116 | } |
| heroic | 8:e3249c2b7607 | 117 | |
| heroic | 8:e3249c2b7607 | 118 | void WS2811::setPixelR(uint16_t n, uint8_t r) { |
| heroic | 8:e3249c2b7607 | 119 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 120 | |
| heroic | 8:e3249c2b7607 | 121 | pixels[n*3+1] = r; |
| heroic | 8:e3249c2b7607 | 122 | } |
| heroic | 8:e3249c2b7607 | 123 | |
| heroic | 8:e3249c2b7607 | 124 | void WS2811::setPixelG(uint16_t n, uint8_t g) { |
| heroic | 8:e3249c2b7607 | 125 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 126 | |
| heroic | 8:e3249c2b7607 | 127 | pixels[n*3] = g; |
| heroic | 8:e3249c2b7607 | 128 | } |
| heroic | 8:e3249c2b7607 | 129 | |
| heroic | 8:e3249c2b7607 | 130 | void WS2811::setPixelB(uint16_t n, uint8_t b) { |
| heroic | 8:e3249c2b7607 | 131 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 132 | |
| heroic | 8:e3249c2b7607 | 133 | pixels[n*3+2] = b; |
| heroic | 8:e3249c2b7607 | 134 | } |
| heroic | 8:e3249c2b7607 | 135 | |
| heroic | 8:e3249c2b7607 | 136 | void WS2811::setPixelColor(uint16_t n, uint32_t c) { |
| heroic | 8:e3249c2b7607 | 137 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 138 | |
| heroic | 8:e3249c2b7607 | 139 | pixels[n*3 ] = (c >> 16); |
| heroic | 8:e3249c2b7607 | 140 | pixels[n*3+1] = (c >> 8); |
| heroic | 8:e3249c2b7607 | 141 | pixels[n*3+2] = c; |
| heroic | 8:e3249c2b7607 | 142 | } |
Ned Konz


Generic WS2811/WS2812