Library for the WS2812 LED Driver. Uses bit banging and nops for precise timing. Number of nops executed are configurable at run time.
WS2812.cpp@1:aadbf08c62a2, 2015-02-12 (annotated)
- Committer:
- bridadan
- Date:
- Thu Feb 12 20:20:10 2015 +0000
- Revision:
- 1:aadbf08c62a2
- Parent:
- 0:0b79cafcb387
Updated documentation and cleaned up code
Who changed what in which revision?
User | Revision | Line number | New 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 |