Yet another WS2812 driver, uses the BusrtSPI library. Less features than the PixelArray library but I felt like making my own version.

Dependencies:   BurstSPI

Dependents:   WS2812Text cylon

An alternative WS2811/2812 (NeoPixel) driver using the BusrtSPI library.

Credit for the inspiration goes to Jacob Bramley for his pixelArray library that can be found here: http://developer.mbed.org/users/JacobBramley/code/PixelArray/

This version was written mainly to help me understand what was going on rather than to overcome any shortcomings in the other library and as such it lacks some features (800kHz only, no callback on each pixel etc...)

Connect the SPI output to the LED data input, other SPI pins are unused.

Note: The voltage thresholds between the LEDs and mbed devices are on paper incompatible. The datasheet for the WS2812 indicated that running at 5V it requires 4V on Din to count it as a high (the threshold is 0.8*the supply voltage). Most mbeds are lucky to output much over 3.1V. In reality things normally work OK but it depends on the mBed and batch to batch variations in the LEDs, I've seen some combinations that start to fail at an LED supply voltage of 4.4V or more. If something odd is going on try dropping the LED power supply voltage, they run fine down to 4V.

Committer:
AndyA
Date:
Wed Nov 12 16:43:33 2014 +0000
Revision:
3:3c48065d20ff
Parent:
2:1f20efb81649
Added 16bit colour support

Who changed what in which revision?

UserRevisionLine numberNew contents of line
AndyA 0:b3665f91bedc 1 #ifndef __wsDrive_h__
AndyA 0:b3665f91bedc 2 #define __wsDrive_h__
AndyA 0:b3665f91bedc 3
AndyA 0:b3665f91bedc 4 #include "BurstSPI.h"
AndyA 0:b3665f91bedc 5
AndyA 1:741864ea11d4 6 /****************************************************************
AndyA 1:741864ea11d4 7 * An alternative WS2811/2812 driver using the BusrtSPI library
AndyA 1:741864ea11d4 8 * Credit for the inspiration goes to Jacob for his pixelArray library
AndyA 1:741864ea11d4 9 * http://developer.mbed.org/users/JacobBramley/code/PixelArray/
AndyA 1:741864ea11d4 10 *
AndyA 1:741864ea11d4 11 * This version was written mainly to help me understand what was going on
AndyA 1:741864ea11d4 12 * While the end result is the same the code is completely from scratch.
AndyA 1:741864ea11d4 13 *
AndyA 1:741864ea11d4 14 *****************************************************************/
AndyA 0:b3665f91bedc 15
AndyA 1:741864ea11d4 16
AndyA 1:741864ea11d4 17 /** A structure used to hold a single pixel or a pixel colour
AndyA 1:741864ea11d4 18 *
AndyA 3:3c48065d20ff 19 * Each colour can be set to any value between about -32,768 and +32,767 (the 16 bit signed number range)
AndyA 3:3c48065d20ff 20 * 0 or negative values mean that colour is off, 255 or more mean on at full brightness.
AndyA 3:3c48065d20ff 21 * In between means partially on.
AndyA 3:3c48065d20ff 22 * If you are never going to go outside the 0-255 range then use pixelInfo, it uses half the memory.
AndyA 3:3c48065d20ff 23 * however if you are calculating pixel values when superimposing two images and don't want to worry about wrap around then use this version.
AndyA 3:3c48065d20ff 24 *
AndyA 3:3c48065d20ff 25 * Note, lots of LEDs on bright will use a lot of power, make sure your supply can cope.
AndyA 3:3c48065d20ff 26 *
AndyA 3:3c48065d20ff 27 * @param G The green component
AndyA 3:3c48065d20ff 28 * @param R The red component
AndyA 3:3c48065d20ff 29 * @param B The blue component
AndyA 3:3c48065d20ff 30 */
AndyA 3:3c48065d20ff 31 typedef struct pixelInfo16 {
AndyA 3:3c48065d20ff 32 int16_t G;
AndyA 3:3c48065d20ff 33 int16_t R;
AndyA 3:3c48065d20ff 34 int16_t B;
AndyA 3:3c48065d20ff 35 } pixelInfo16;
AndyA 3:3c48065d20ff 36
AndyA 3:3c48065d20ff 37
AndyA 3:3c48065d20ff 38 /** A structure used to hold a single pixel or a pixel colour
AndyA 3:3c48065d20ff 39 *
AndyA 1:741864ea11d4 40 * Each colour can be set to any value between 0 and 255.
AndyA 1:741864ea11d4 41 * 0 = off, 255 = full brightness.
AndyA 1:741864ea11d4 42 *
AndyA 1:741864ea11d4 43 * Note, lots of LEDs on bright will use a lot of power, make sure your supply can cope.
AndyA 1:741864ea11d4 44 *
AndyA 1:741864ea11d4 45 * @param G The green component
AndyA 1:741864ea11d4 46 * @param R The red component
AndyA 1:741864ea11d4 47 * @param B The blue component
AndyA 1:741864ea11d4 48 */
AndyA 0:b3665f91bedc 49 typedef struct pixelInfo {
AndyA 0:b3665f91bedc 50 unsigned char G;
AndyA 0:b3665f91bedc 51 unsigned char R;
AndyA 0:b3665f91bedc 52 unsigned char B;
AndyA 0:b3665f91bedc 53 } pixelInfo;
AndyA 0:b3665f91bedc 54
AndyA 0:b3665f91bedc 55
AndyA 1:741864ea11d4 56 /** Drives a WS2812 LED chain
AndyA 1:741864ea11d4 57 *
AndyA 1:741864ea11d4 58 * An alternative WS2811/2812 driver using the BusrtSPI library
AndyA 1:741864ea11d4 59 *
AndyA 1:741864ea11d4 60 * Credit for the inspiration goes to Jacob for his pixelArray library
AndyA 1:741864ea11d4 61 * http://developer.mbed.org/users/JacobBramley/code/PixelArray/
AndyA 1:741864ea11d4 62 *
AndyA 1:741864ea11d4 63 * This version was written mainly to help me understand what was going on and is a little more basic.
AndyA 1:741864ea11d4 64 * While the end result is muc the same the code is completely from scratch.
AndyA 1:741864ea11d4 65 *
AndyA 1:741864ea11d4 66 * BurstSPI is used to generate the timing so support is limited to the parts supported by that library.
AndyA 1:741864ea11d4 67 *
AndyA 1:741864ea11d4 68 * Also note that while all 3 SPI pins are specified only the MOSI pin is actually needed and should connect to the data in on the LEDs
AndyA 1:741864ea11d4 69 *
AndyA 3:3c48065d20ff 70 * Values for each LED are stored in either an array of pixelInfo or pixelInfo16 structures
AndyA 3:3c48065d20ff 71 * Each LED only supports 0-255 for each colour, the limits of the pixelInfo data type.
AndyA 3:3c48065d20ff 72 *
AndyA 3:3c48065d20ff 73 * The 16 bit version is for when you are adding/subtracting values in order to avoid
AndyA 3:3c48065d20ff 74 * wraparound issues. When displayed the values outside the range are clamped to the limits.
AndyA 3:3c48065d20ff 75 * This allows tricks like having a point with negative brighnesses moving along the line to add a
AndyA 3:3c48065d20ff 76 * dark spot without having to worry about what happens if a number goes negative.
AndyA 3:3c48065d20ff 77 *
AndyA 3:3c48065d20ff 78 *
AndyA 1:741864ea11d4 79 * Example code to run a single lit led along a chain
AndyA 1:741864ea11d4 80 *
AndyA 1:741864ea11d4 81 * @code
AndyA 1:741864ea11d4 82 *
AndyA 1:741864ea11d4 83 * #include "mbed.h"
AndyA 1:741864ea11d4 84 * #include "wsDrive.h"
AndyA 1:741864ea11d4 85 *
AndyA 1:741864ea11d4 86 * // update period in ms
AndyA 1:741864ea11d4 87 * #define updatePeriod 100
AndyA 1:741864ea11d4 88 * // number of LEDs
AndyA 1:741864ea11d4 89 * #define chainLen 8
AndyA 1:741864ea11d4 90 *
AndyA 1:741864ea11d4 91 * DigitalIn dummy(MOSI,PullDown); // first activate the pulldown on the pin.
AndyA 1:741864ea11d4 92 * wsDrive ledDriver(MOSI,MISO,CLK); // create the SPI bus. You can normally list the MISO and CLK as NC but some mbed library versions don't like that
AndyA 1:741864ea11d4 93 *
AndyA 1:741864ea11d4 94 * // pixel storage buffer
AndyA 1:741864ea11d4 95 * pixelInfo pixelData[chainLen];
AndyA 1:741864ea11d4 96 *
AndyA 1:741864ea11d4 97 * Timer updateRateTimer;
AndyA 1:741864ea11d4 98 *
AndyA 1:741864ea11d4 99 * void blankBuffer(pixelInfo *Ptr)
AndyA 1:741864ea11d4 100 * {
AndyA 1:741864ea11d4 101 * memset( (void *)Ptr, 0, chainLen*sizeof(pixelInfo) );
AndyA 1:741864ea11d4 102 * }
AndyA 1:741864ea11d4 103 *
AndyA 1:741864ea11d4 104 * void setPixel(unsigned int index, pixelInfo *colourToUse) {
AndyA 1:741864ea11d4 105 * if (index < chainLen) {
AndyA 1:741864ea11d4 106 * pixelData[index].R = colourToUse->R;
AndyA 1:741864ea11d4 107 * pixelData[index].G = colourToUse->G;
AndyA 1:741864ea11d4 108 * pixelData[index].B = colourToUse->B;
AndyA 1:741864ea11d4 109 * }
AndyA 1:741864ea11d4 110 * }
AndyA 1:741864ea11d4 111 *
AndyA 1:741864ea11d4 112 * void clearPixel(unsigned int index) {
AndyA 1:741864ea11d4 113 * if (index < chainLen) {
AndyA 1:741864ea11d4 114 * pixelData[index].R = 0;
AndyA 1:741864ea11d4 115 * pixelData[index].G = 0;
AndyA 1:741864ea11d4 116 * pixelData[index].B = 0;
AndyA 1:741864ea11d4 117 * }
AndyA 1:741864ea11d4 118 * }
AndyA 1:741864ea11d4 119 *
AndyA 1:741864ea11d4 120 * int main () {
AndyA 1:741864ea11d4 121 *
AndyA 1:741864ea11d4 122 * int litLed = 0;
AndyA 1:741864ea11d4 123 *
AndyA 1:741864ea11d4 124 * pixelInfo colour;
AndyA 1:741864ea11d4 125 * colour.R = 0x80;
AndyA 1:741864ea11d4 126 * colour.G = 0x00;
AndyA 1:741864ea11d4 127 * colour.B = 0x00;
AndyA 1:741864ea11d4 128 *
AndyA 1:741864ea11d4 129 * // Tell the driver where the data is stored
AndyA 1:741864ea11d4 130 * ledDriver.setData(pixelData, chainLen);
AndyA 1:741864ea11d4 131 *
AndyA 1:741864ea11d4 132 * // Set the buffer to the pattern we want
AndyA 1:741864ea11d4 133 * blankBuffer(pixelData);
AndyA 1:741864ea11d4 134 * setPixel(litLed, &colour);
AndyA 1:741864ea11d4 135 *
AndyA 1:741864ea11d4 136 * updateRateTimer.start();
AndyA 1:741864ea11d4 137 * while (true) {
AndyA 1:741864ea11d4 138 *
AndyA 1:741864ea11d4 139 * ledDriver.sendData(); // send the LED data
AndyA 1:741864ea11d4 140 *
AndyA 1:741864ea11d4 141 * // modify the buffer ready for the next update
AndyA 1:741864ea11d4 142 * clearPixel(litLed);
AndyA 1:741864ea11d4 143 * litLed++;
AndyA 1:741864ea11d4 144 * if (litLed == chainLen)
AndyA 1:741864ea11d4 145 * litLed = 0;
AndyA 1:741864ea11d4 146 * setPixel(litLed, &colour);
AndyA 1:741864ea11d4 147 *
AndyA 1:741864ea11d4 148 * // wait until the correct time since the last update...
AndyA 1:741864ea11d4 149 * while (updateRateTimer.read_ms() < updatePeriod) {
AndyA 1:741864ea11d4 150 * }
AndyA 1:741864ea11d4 151
AndyA 1:741864ea11d4 152 * updateRateTimer.reset();
AndyA 1:741864ea11d4 153 * }
AndyA 1:741864ea11d4 154 * }
AndyA 1:741864ea11d4 155 * @endcode
AndyA 1:741864ea11d4 156 *
AndyA 1:741864ea11d4 157 * Troubleshooting:
AndyA 1:741864ea11d4 158 *
AndyA 1:741864ea11d4 159 * If the LEDs aren't lighting up correctly then check that your power supply is up to the job (or decrease the brightness you are using)
AndyA 1:741864ea11d4 160 *
AndyA 1:741864ea11d4 161 * Also check the supply voltage, on paper when running off 5V the WS2812 needs 4V on the data in pin to detect a high. Mbed based boards rarely output much over 3.1V.
AndyA 1:741864ea11d4 162 * This problem is normally indicated by the very first pattern send on power up being displayed but then no further updates being recieved. Dropping the supply voltage
AndyA 1:741864ea11d4 163 * to about 4.2 - 4.3 V will normally fix this problem without any meaningful impact on the LED output.
AndyA 1:741864ea11d4 164 *
AndyA 1:741864ea11d4 165 */
AndyA 0:b3665f91bedc 166 class wsDrive : private BurstSPI
AndyA 0:b3665f91bedc 167 {
AndyA 0:b3665f91bedc 168 public:
AndyA 1:741864ea11d4 169 /** create the driver
AndyA 1:741864ea11d4 170 */
AndyA 0:b3665f91bedc 171 wsDrive(PinName mosi, PinName miso, PinName clk);
AndyA 1:741864ea11d4 172
AndyA 1:741864ea11d4 173 /** Set the data pointer
AndyA 1:741864ea11d4 174
AndyA 1:741864ea11d4 175 Before data can be sent the driver must be given a pointer to the pixel data to use.
AndyA 1:741864ea11d4 176 Setting this is normally a one time operation unless you want to switch between buffers.
AndyA 3:3c48065d20ff 177 Calling this function will replace any prior buffers including pixelInfo16 ones.
AndyA 1:741864ea11d4 178
AndyA 1:741864ea11d4 179 @param dataStart The start of an array of pixelInfo items. This will be sent to the chain in order.
AndyA 1:741864ea11d4 180 @param dataLen The length of the array.
AndyA 1:741864ea11d4 181 */
AndyA 0:b3665f91bedc 182 void setData(pixelInfo *dataStart, uint16_t dataLen);
AndyA 1:741864ea11d4 183
AndyA 3:3c48065d20ff 184 /** Set the data pointer
AndyA 3:3c48065d20ff 185
AndyA 3:3c48065d20ff 186 Before data can be sent the driver must be given a pointer to the pixel data to use.
AndyA 3:3c48065d20ff 187 Setting this is normally a one time operation unless you want to switch between buffers.
AndyA 3:3c48065d20ff 188 Calling this function will replace any prior buffers including pixelInfo ones.
AndyA 3:3c48065d20ff 189
AndyA 3:3c48065d20ff 190 @param dataStart The start of an array of pixelInfo16 items. This will be sent to the chain in order.
AndyA 3:3c48065d20ff 191 @param dataLen The length of the array.
AndyA 3:3c48065d20ff 192 */
AndyA 3:3c48065d20ff 193 void setData(pixelInfo16 *dataStart, uint16_t dataLen);
AndyA 3:3c48065d20ff 194
AndyA 1:741864ea11d4 195
AndyA 1:741864ea11d4 196 /** Sends the data to the LEDs
AndyA 1:741864ea11d4 197 * setData() must be called prior to this.
AndyA 1:741864ea11d4 198 */
AndyA 0:b3665f91bedc 199 void sendData();
AndyA 1:741864ea11d4 200
AndyA 0:b3665f91bedc 201 private:
AndyA 0:b3665f91bedc 202
AndyA 0:b3665f91bedc 203 void sendByte(unsigned char value);
AndyA 0:b3665f91bedc 204 void sendPixel(pixelInfo *pixToSend);
AndyA 3:3c48065d20ff 205 void sendPixel(pixelInfo16 *pixToSend);
AndyA 1:741864ea11d4 206
AndyA 0:b3665f91bedc 207 pixelInfo *pixArray;
AndyA 3:3c48065d20ff 208 pixelInfo16 *pixArray16;
AndyA 0:b3665f91bedc 209 uint16_t pixelLen;
AndyA 0:b3665f91bedc 210
AndyA 0:b3665f91bedc 211 };
AndyA 0:b3665f91bedc 212
AndyA 0:b3665f91bedc 213 #endif