Library for the WS2812 LED Driver. Uses bit banging and nops for precise timing. Number of nops executed are configurable at run time.

Dependents:   WS2812_Example WS2812_Example_fade Lamp_03 Lamp_04 ... more

This library uses bit banging to control the LED strip. Because the mbed platform doesn't have a timer standardized that doesn't have a resolution lower a microsecond, we have to use NOPs. Also, the time it takes to toggle a GPIO pin is not consistent across platforms and it does not correlate to the system clock in any way. It is completely dependent on the platform's implementation.

I have a found the timings for a few boards. They are currently listed on the example program page below.

Import programWS2812_Example

Example for WS2812 Library

When using this on a new platform, you'll need to use a logic analyzer to determine the timings.

/media/uploads/bridadan/k64f_ws2812_timing_v2.png

Above is a screenshot from a Saleae logic analyzer. You'll notice that the "1" pulse is longer than the "0" pulse. The "Data low" pulses should be consistent. The timing requirements for these pulses can be found below (Source: http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/).

/media/uploads/bridadan/ws2812_timings.png

If you find timings for a new board you're using, please PM me or start a discussion below and I'll add it to this page!

For more on the timings and data format of the WS2812 drivers, you should read the datasheet and the article "NeoPixels Revealed: How to (not need to) generate precisely timed signals" listed below.

References:

WS2812B Datasheet: https://www.adafruit.com/datasheets/WS2812B.pdf

"NeoPixels Revealed: How to (not need to) generate precisely timed signals": http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/

Committer:
bridadan
Date:
Thu Feb 12 20:24:15 2015 +0000
Revision:
2:6e647820f587
Parent:
1:aadbf08c62a2
Updated docs

Who changed what in which revision?

UserRevisionLine numberNew contents of line
bridadan 0:0b79cafcb387 1 #include "WS2812.h"
bridadan 0:0b79cafcb387 2
bridadan 1:aadbf08c62a2 3 WS2812::WS2812(PinName pin, int size, int zeroHigh, int zeroLow, int oneHigh, int oneLow) : __gpo(pin)
bridadan 0:0b79cafcb387 4 {
bridadan 0:0b79cafcb387 5 __size = size;
bridadan 0:0b79cafcb387 6 __transmitBuf = new bool[size * FRAME_SIZE];
bridadan 1:aadbf08c62a2 7 __use_II = OFF;
bridadan 0:0b79cafcb387 8 __II = 0xFF; // set global intensity to full
bridadan 1:aadbf08c62a2 9 __outPin = pin;
bridadan 0:0b79cafcb387 10
bridadan 0:0b79cafcb387 11 // Default values designed for K64f. Assumes GPIO toggle takes ~0.4us
bridadan 1:aadbf08c62a2 12 setDelays(zeroHigh, zeroLow, oneHigh, oneLow);
bridadan 0:0b79cafcb387 13 }
bridadan 0:0b79cafcb387 14
bridadan 0:0b79cafcb387 15
bridadan 0:0b79cafcb387 16 WS2812::~WS2812()
bridadan 0:0b79cafcb387 17 {
bridadan 0:0b79cafcb387 18 delete[] __transmitBuf;
bridadan 0:0b79cafcb387 19 }
bridadan 0:0b79cafcb387 20
bridadan 0:0b79cafcb387 21 void WS2812::setDelays(int zeroHigh, int zeroLow, int oneHigh, int oneLow) {
bridadan 0:0b79cafcb387 22 __zeroHigh = zeroHigh;
bridadan 0:0b79cafcb387 23 __zeroLow = zeroLow;
bridadan 0:0b79cafcb387 24 __oneHigh = oneHigh;
bridadan 0:0b79cafcb387 25 __oneLow = oneLow;
bridadan 0:0b79cafcb387 26 }
bridadan 0:0b79cafcb387 27
bridadan 0:0b79cafcb387 28 void WS2812::__loadBuf(int buf[],int r_offset, int g_offset, int b_offset) {
bridadan 0:0b79cafcb387 29 for (int i = 0; i < __size; i++) {
bridadan 0:0b79cafcb387 30 int color = 0;
bridadan 0:0b79cafcb387 31
bridadan 0:0b79cafcb387 32 color |= ((buf[(i+g_offset)%__size] & 0x0000FF00));
bridadan 0:0b79cafcb387 33 color |= ((buf[(i+r_offset)%__size] & 0x00FF0000));
bridadan 0:0b79cafcb387 34 color |= (buf[(i+b_offset)%__size] & 0x000000FF);
bridadan 0:0b79cafcb387 35 color |= (buf[i] & 0xFF000000);
bridadan 0:0b79cafcb387 36
bridadan 0:0b79cafcb387 37 // Outut format : GGRRBB
bridadan 0:0b79cafcb387 38 // Inout format : IIRRGGBB
bridadan 0:0b79cafcb387 39 unsigned char agrb[4] = {0x0, 0x0, 0x0, 0x0};
bridadan 0:0b79cafcb387 40
bridadan 0:0b79cafcb387 41 unsigned char sf; // scaling factor for II
bridadan 0:0b79cafcb387 42
bridadan 0:0b79cafcb387 43 // extract colour fields from incoming
bridadan 0:0b79cafcb387 44 // 0 = green, 1 = red, 2 = blue, 3 = brightness
bridadan 0:0b79cafcb387 45 agrb[0] = (color & 0x0000FF00) >> 8;
bridadan 0:0b79cafcb387 46 agrb[1] = (color & 0x00FF0000) >> 16;
bridadan 0:0b79cafcb387 47 agrb[2] = color & 0x000000FF;
bridadan 0:0b79cafcb387 48 agrb[3] = (color & 0xFF000000) >> 24;
bridadan 0:0b79cafcb387 49
bridadan 1:aadbf08c62a2 50 // set the intensity scaling factor (global, per pixel, none)
bridadan 1:aadbf08c62a2 51 if (__use_II == GLOBAL) {
bridadan 0:0b79cafcb387 52 sf = __II;
bridadan 1:aadbf08c62a2 53 } else if (__use_II == PER_PIXEL) {
bridadan 0:0b79cafcb387 54 sf = agrb[3];
bridadan 0:0b79cafcb387 55 } else {
bridadan 0:0b79cafcb387 56 sf = 0xFF;
bridadan 0:0b79cafcb387 57 }
bridadan 0:0b79cafcb387 58
bridadan 0:0b79cafcb387 59 // Apply the scaling factor to each othe colour components
bridadan 0:0b79cafcb387 60 for (int clr = 0; clr < 3; clr++) {
bridadan 0:0b79cafcb387 61 agrb[clr] = ((agrb[clr] * sf) >> 8);
bridadan 0:0b79cafcb387 62
bridadan 0:0b79cafcb387 63 for (int j = 0; j < 8; j++) {
bridadan 0:0b79cafcb387 64 if (((agrb[clr] << j) & 0x80) == 0x80) {
bridadan 0:0b79cafcb387 65 // Bit is set (checks MSB fist)
bridadan 0:0b79cafcb387 66 __transmitBuf[(i * FRAME_SIZE) + (clr * 8) + j] = 1;
bridadan 0:0b79cafcb387 67 } else {
bridadan 0:0b79cafcb387 68 // Bit is clear
bridadan 0:0b79cafcb387 69 __transmitBuf[(i * FRAME_SIZE) + (clr * 8) + j] = 0;
bridadan 0:0b79cafcb387 70 }
bridadan 0:0b79cafcb387 71 }
bridadan 0:0b79cafcb387 72 }
bridadan 0:0b79cafcb387 73 }
bridadan 0:0b79cafcb387 74 }
bridadan 0:0b79cafcb387 75
bridadan 0:0b79cafcb387 76 void WS2812::write(int buf[]) {
bridadan 0:0b79cafcb387 77 write_offsets(buf, 0, 0, 0);
bridadan 0:0b79cafcb387 78 }
bridadan 0:0b79cafcb387 79
bridadan 0:0b79cafcb387 80 void WS2812::write_offsets (int buf[],int r_offset, int g_offset, int b_offset) {
bridadan 0:0b79cafcb387 81 int i, j;
bridadan 0:0b79cafcb387 82
bridadan 1:aadbf08c62a2 83 // Load the transmit buffer
bridadan 0:0b79cafcb387 84 __loadBuf(buf, r_offset, g_offset, b_offset);
bridadan 0:0b79cafcb387 85
bridadan 1:aadbf08c62a2 86 // Entering timing critical section, so disabling interrupts
bridadan 1:aadbf08c62a2 87 __disable_irq();
bridadan 1:aadbf08c62a2 88
bridadan 1:aadbf08c62a2 89 // Begin bit-banging
bridadan 0:0b79cafcb387 90 for (i = 0; i < FRAME_SIZE * __size; i++) {
bridadan 0:0b79cafcb387 91 j = 0;
bridadan 0:0b79cafcb387 92 if (__transmitBuf[i]){
bridadan 0:0b79cafcb387 93 __gpo = 1;
bridadan 0:0b79cafcb387 94 for (; j < __oneHigh; j++) {
bridadan 0:0b79cafcb387 95 __nop();
bridadan 0:0b79cafcb387 96 }
bridadan 0:0b79cafcb387 97 __gpo = 0;
bridadan 0:0b79cafcb387 98 for (; j < __oneLow; j++) {
bridadan 0:0b79cafcb387 99 __nop();
bridadan 0:0b79cafcb387 100 }
bridadan 0:0b79cafcb387 101 } else {
bridadan 0:0b79cafcb387 102 __gpo = 1;
bridadan 0:0b79cafcb387 103 for (; j < __zeroHigh; j++) {
bridadan 0:0b79cafcb387 104 __nop();
bridadan 0:0b79cafcb387 105 }
bridadan 0:0b79cafcb387 106 __gpo = 0;
bridadan 0:0b79cafcb387 107 for (; j < __zeroLow; j++) {
bridadan 0:0b79cafcb387 108 __nop();
bridadan 0:0b79cafcb387 109 }
bridadan 0:0b79cafcb387 110 }
bridadan 0:0b79cafcb387 111 }
bridadan 1:aadbf08c62a2 112
bridadan 1:aadbf08c62a2 113 // Exiting timing critical section, so enabling interrutps
bridadan 1:aadbf08c62a2 114 __enable_irq();
bridadan 0:0b79cafcb387 115 }
bridadan 0:0b79cafcb387 116
bridadan 0:0b79cafcb387 117
bridadan 1:aadbf08c62a2 118 void WS2812::useII(BrightnessControl bc)
bridadan 0:0b79cafcb387 119 {
bridadan 1:aadbf08c62a2 120 if (bc > OFF) {
bridadan 1:aadbf08c62a2 121 __use_II = bc;
bridadan 0:0b79cafcb387 122 } else {
bridadan 1:aadbf08c62a2 123 __use_II = OFF;
bridadan 0:0b79cafcb387 124 }
bridadan 0:0b79cafcb387 125 }
bridadan 0:0b79cafcb387 126
bridadan 0:0b79cafcb387 127 void WS2812::setII(unsigned char II)
bridadan 0:0b79cafcb387 128 {
bridadan 0:0b79cafcb387 129 __II = II;
bridadan 0:0b79cafcb387 130 }
bridadan 0:0b79cafcb387 131
bridadan 0:0b79cafcb387 132
bridadan 0:0b79cafcb387 133
bridadan 0:0b79cafcb387 134
bridadan 0:0b79cafcb387 135
bridadan 0:0b79cafcb387 136