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@13:1f65330abe92, 2013-07-23 (annotated)
- Committer:
- heroic
- Date:
- Tue Jul 23 22:32:14 2013 +0000
- Revision:
- 13:1f65330abe92
- Parent:
- 12:7ebd51549c04
- Child:
- 14:c97261a9a282
Rework timing for constant execution time; do direct bitband writes for improved speed; change interrupt handling (turns out we can't have interrupts during an entire strip push, not just between bits).; ; Now works!
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 | 13:1f65330abe92 | 30 | |
| heroic | 13:1f65330abe92 | 31 | data_mask = dat.get_mask(); |
| heroic | 13:1f65330abe92 | 32 | clock_mask = clk.get_mask(); |
| heroic | 13:1f65330abe92 | 33 | data_set = dat.get_set(); |
| heroic | 13:1f65330abe92 | 34 | data_clr = dat.get_clr(); |
| heroic | 13:1f65330abe92 | 35 | clock_set = clk.get_set(); |
| heroic | 13:1f65330abe92 | 36 | clock_clr = clk.get_clr(); |
| heroic | 13:1f65330abe92 | 37 | |
| heroic | 8:e3249c2b7607 | 38 | } |
| heroic | 8:e3249c2b7607 | 39 | |
| heroic | 8:e3249c2b7607 | 40 | /* |
| heroic | 8:e3249c2b7607 | 41 | * These chips use a one-wire protocol based on a sort of NRZ signalling- jas. |
| heroic | 8:e3249c2b7607 | 42 | */ |
| heroic | 8:e3249c2b7607 | 43 | |
| heroic | 8:e3249c2b7607 | 44 | void WS2811::write(uint8_t byte) { |
| heroic | 13:1f65330abe92 | 45 | |
| heroic | 8:e3249c2b7607 | 46 | for (int i=0; i<8; i++) { |
| heroic | 8:e3249c2b7607 | 47 | if (byte & 0x80) |
| heroic | 8:e3249c2b7607 | 48 | writebit(1); |
| heroic | 8:e3249c2b7607 | 49 | else |
| heroic | 8:e3249c2b7607 | 50 | writebit(0); |
| heroic | 8:e3249c2b7607 | 51 | byte <<= 1; |
| heroic | 8:e3249c2b7607 | 52 | } |
| heroic | 8:e3249c2b7607 | 53 | } |
| heroic | 8:e3249c2b7607 | 54 | |
| heroic | 8:e3249c2b7607 | 55 | inline void WS2811::celldelay(void) { |
| heroic | 13:1f65330abe92 | 56 | for (volatile int i = 0; i<bogocal; i++) |
| heroic | 8:e3249c2b7607 | 57 | /* do nothing */ ; |
| heroic | 8:e3249c2b7607 | 58 | } |
| heroic | 8:e3249c2b7607 | 59 | |
| heroic | 13:1f65330abe92 | 60 | inline void WS2811::writebit(bool bit) { |
| heroic | 8:e3249c2b7607 | 61 | // first cell is always 1 |
| heroic | 13:1f65330abe92 | 62 | (*data_set) = data_mask; |
| heroic | 8:e3249c2b7607 | 63 | celldelay(); |
| heroic | 8:e3249c2b7607 | 64 | if (bit) { |
| heroic | 13:1f65330abe92 | 65 | (*clock_set) = data_mask; // dummy, we don't care but must take constant time |
| heroic | 8:e3249c2b7607 | 66 | celldelay(); |
| heroic | 8:e3249c2b7607 | 67 | } else { |
| heroic | 13:1f65330abe92 | 68 | (*data_clr) = data_mask; |
| heroic | 8:e3249c2b7607 | 69 | celldelay(); |
| heroic | 8:e3249c2b7607 | 70 | } |
| heroic | 8:e3249c2b7607 | 71 | // last cell is always 0 |
| heroic | 13:1f65330abe92 | 72 | (*data_clr) = data_mask; |
| heroic | 8:e3249c2b7607 | 73 | celldelay(); |
| heroic | 8:e3249c2b7607 | 74 | } |
| heroic | 8:e3249c2b7607 | 75 | |
| heroic | 8:e3249c2b7607 | 76 | void WS2811::begin(void) { |
| heroic | 8:e3249c2b7607 | 77 | blank(); |
| heroic | 8:e3249c2b7607 | 78 | show(); |
| heroic | 8:e3249c2b7607 | 79 | } |
| heroic | 8:e3249c2b7607 | 80 | |
| heroic | 8:e3249c2b7607 | 81 | uint16_t WS2811::numPixels(void) { |
| heroic | 8:e3249c2b7607 | 82 | return numLEDs; |
| heroic | 8:e3249c2b7607 | 83 | } |
| heroic | 8:e3249c2b7607 | 84 | |
| heroic | 8:e3249c2b7607 | 85 | void WS2811::blank(void) { |
| heroic | 8:e3249c2b7607 | 86 | memset(pixels, 0x00, numLEDs * 3); |
| heroic | 8:e3249c2b7607 | 87 | } |
| heroic | 8:e3249c2b7607 | 88 | |
| heroic | 8:e3249c2b7607 | 89 | void WS2811::show(void) { |
| heroic | 8:e3249c2b7607 | 90 | uint16_t i, nl3 = numLEDs * 3; // 3 bytes per LED |
| heroic | 8:e3249c2b7607 | 91 | while (guardtime.read_us() < 50) |
| heroic | 8:e3249c2b7607 | 92 | /* spin */; |
| heroic | 13:1f65330abe92 | 93 | __disable_irq(); |
| heroic | 8:e3249c2b7607 | 94 | for (i=0; i<nl3; i++ ) { |
| heroic | 8:e3249c2b7607 | 95 | write(pixels[i]); |
| heroic | 8:e3249c2b7607 | 96 | } |
| heroic | 13:1f65330abe92 | 97 | __enable_irq(); |
| heroic | 8:e3249c2b7607 | 98 | guardtime.reset(); |
| heroic | 8:e3249c2b7607 | 99 | } |
| heroic | 8:e3249c2b7607 | 100 | |
| heroic | 8:e3249c2b7607 | 101 | |
| heroic | 8:e3249c2b7607 | 102 | uint32_t WS2811::total_luminance(void) { |
| heroic | 8:e3249c2b7607 | 103 | uint32_t running_total; |
| heroic | 8:e3249c2b7607 | 104 | running_total = 0; |
| heroic | 8:e3249c2b7607 | 105 | for (int i=0; i<numLEDs*3; i++) |
| heroic | 8:e3249c2b7607 | 106 | running_total += pixels[i]; |
| heroic | 8:e3249c2b7607 | 107 | return running_total; |
| heroic | 8:e3249c2b7607 | 108 | } |
| heroic | 8:e3249c2b7607 | 109 | |
| heroic | 8:e3249c2b7607 | 110 | // Convert R,G,B to combined 32-bit color |
| heroic | 8:e3249c2b7607 | 111 | uint32_t WS2811::Color(uint8_t r, uint8_t g, uint8_t b) { |
| heroic | 8:e3249c2b7607 | 112 | // Take the lowest 7 bits of each value and append them end to end |
| heroic | 8:e3249c2b7607 | 113 | // We have the top bit set high (its a 'parity-like' bit in the protocol |
| heroic | 8:e3249c2b7607 | 114 | // and must be set!) |
| heroic | 8:e3249c2b7607 | 115 | return ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b; |
| heroic | 8:e3249c2b7607 | 116 | } |
| heroic | 8:e3249c2b7607 | 117 | |
| heroic | 8:e3249c2b7607 | 118 | // store the rgb component in our array |
| heroic | 8:e3249c2b7607 | 119 | void WS2811::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { |
| heroic | 8:e3249c2b7607 | 120 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 121 | |
| heroic | 8:e3249c2b7607 | 122 | pixels[n*3 ] = g; |
| heroic | 8:e3249c2b7607 | 123 | pixels[n*3+1] = r; |
| heroic | 8:e3249c2b7607 | 124 | pixels[n*3+2] = b; |
| heroic | 8:e3249c2b7607 | 125 | } |
| heroic | 8:e3249c2b7607 | 126 | |
| heroic | 8:e3249c2b7607 | 127 | void WS2811::setPixelR(uint16_t n, uint8_t r) { |
| heroic | 8:e3249c2b7607 | 128 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 129 | |
| heroic | 8:e3249c2b7607 | 130 | pixels[n*3+1] = r; |
| heroic | 8:e3249c2b7607 | 131 | } |
| heroic | 8:e3249c2b7607 | 132 | |
| heroic | 8:e3249c2b7607 | 133 | void WS2811::setPixelG(uint16_t n, uint8_t g) { |
| heroic | 8:e3249c2b7607 | 134 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 135 | |
| heroic | 8:e3249c2b7607 | 136 | pixels[n*3] = g; |
| heroic | 8:e3249c2b7607 | 137 | } |
| heroic | 8:e3249c2b7607 | 138 | |
| heroic | 8:e3249c2b7607 | 139 | void WS2811::setPixelB(uint16_t n, uint8_t b) { |
| heroic | 8:e3249c2b7607 | 140 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 141 | |
| heroic | 8:e3249c2b7607 | 142 | pixels[n*3+2] = b; |
| heroic | 8:e3249c2b7607 | 143 | } |
| heroic | 8:e3249c2b7607 | 144 | |
| heroic | 12:7ebd51549c04 | 145 | void WS2811::setPackedPixels(uint8_t * buffer, uint32_t n) { |
| heroic | 12:7ebd51549c04 | 146 | if (n >= numLEDs) return; |
| heroic | 12:7ebd51549c04 | 147 | memcpy(pixels, buffer, (size_t) (n*3)); |
| heroic | 12:7ebd51549c04 | 148 | } |
| heroic | 12:7ebd51549c04 | 149 | |
| heroic | 8:e3249c2b7607 | 150 | void WS2811::setPixelColor(uint16_t n, uint32_t c) { |
| heroic | 8:e3249c2b7607 | 151 | if (n >= numLEDs) return; // '>=' because arrays are 0-indexed |
| heroic | 8:e3249c2b7607 | 152 | |
| heroic | 8:e3249c2b7607 | 153 | pixels[n*3 ] = (c >> 16); |
| heroic | 8:e3249c2b7607 | 154 | pixels[n*3+1] = (c >> 8); |
| heroic | 8:e3249c2b7607 | 155 | pixels[n*3+2] = c; |
| heroic | 8:e3249c2b7607 | 156 | } |
Ned Konz


Generic WS2811/WS2812