Only update of BurstSPI-Library

Dependencies:   BurstSPI

Dependents:   WordClock TI_NEOPIXEL_SPI

Fork of PixelArray by Jacob Bramley

Files at this revision

API Documentation at this revision

Comitter:
mcapewel
Date:
Mon Aug 18 21:38:40 2014 +0000
Parent:
2:3c3c41774cdf
Child:
4:c3b314df3dfe
Commit message:
Add support for WS2811's 400kHz protocol mode.

Changed in this revision

neopixel.cpp Show annotated file Show diff for this revision Revisions of this file
neopixel.h Show annotated file Show diff for this revision Revisions of this file
--- a/neopixel.cpp	Fri Aug 01 22:17:23 2014 +0000
+++ b/neopixel.cpp	Mon Aug 18 21:38:40 2014 +0000
@@ -5,32 +5,46 @@
 namespace neopixel
 {
 
-PixelArray::PixelArray(PinName out, ByteOrder byte_order)
-    : spi_(out, NC, NC), byte_order_(byte_order)
+PixelArray::PixelArray(PinName out, ByteOrder byte_order, Protocol protocol)
+    : spi_(out, NC, NC), byte_order_(byte_order), protocol_(protocol)
 {
-    // WS281x bit encodings:
-    //  '0': ----________
-    //  '1': --------____
-    // The period is 1.25us, giving a basic frequency of 800kHz.
-    // Getting the mark-space ratio right is trickier, though. There are a number
-    // of different timings, and the correct (documented) values depend on the
-    // controller chip.
-    //
-    // The _real_ timing restrictions are much simpler though, and someone has
-    // published a lovely analysis here:
-    //   http://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/
-    //
-    // In summary:
-    // - The period should be at least 1.25us.
-    // - The '0' high time can be anywhere from 0.0625us to 0.5us.
-    // - The '1' high time should be longer than 0.625us.
-    //
-    // These constraints are easy to meet by splitting each bit into three and packing them into SPI packets.
-    //  '0': 100             mark: 0.42us, space: 0.83us
-    //  '1': 110             mark: 0.83us, space: 0.42us
-
-    spi_.frequency(2400000);  // 800kHz * 3
-    spi_.format(12);          // Send four NeoPixel bits in each packet.
+    if (protocol_ == PROTOCOL_800KHZ) {
+        // 800kHz bit encodings:
+        //  '0': ----________
+        //  '1': --------____
+        // The period is 1.25us, giving a basic frequency of 800kHz.
+        // Getting the mark-space ratio right is trickier, though. There are a number
+        // of different timings, and the correct (documented) values depend on the
+        // controller chip.
+        //
+        // The _real_ timing restrictions are much simpler though, and someone has
+        // published a lovely analysis here:
+        //   http://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/
+        //
+        // In summary:
+        // - The period should be at least 1.25us.
+        // - The '0' high time can be anywhere from 0.0625us to 0.5us.
+        // - The '1' high time should be longer than 0.625us.
+        //
+        // These constraints are easy to meet by splitting each bit into three and packing them into SPI packets.
+        //  '0': 100             mark: 0.42us, space: 0.83us
+        //  '1': 110             mark: 0.83us, space: 0.42us
+        spi_.frequency(2400000);  // 800kHz * 3
+        spi_.format(12);          // Send four NeoPixel bits in each packet.
+    } else {
+        // 400kHz bit encodings:
+        //  '0': --________
+        //  '1': -----_____
+        //
+        // Timing requirements are derived from this document:
+        //   http://www.adafruit.com/datasheets/WS2811.pdf
+        //
+        // The period is 2.5us, and we use a 10-bit packet for this encoding:
+        //  '0': 1100000000      mark: 0.5us, space: 2us
+        //  '1': 1111100000      mark: 1.25us, space: 1.25us    
+        spi_.frequency(4000000);  // 400kHz * 10
+        spi_.format(10);          // Send one NeoPixel bit in each packet.
+    }
 }
 
 static void SendFourBits(BurstSPI& spi, uint32_t bits)
@@ -49,6 +63,15 @@
     spi.fastWrite(base | ac | bd);        // 1a01b01c01d0
 }
 
+static void SendEightBits(BurstSPI& spi, uint8_t bits)
+{
+    int zero = 0x300;  // Encode zero as 0b1100000000
+    int one = 0x3e0;   // Encode one as 0b1111100000
+    for (int i = 1; i < 256; i *= 2) {
+        spi.fastWrite((bits & i) ? one : zero);
+    }
+}
+
 void PixelArray::send_pixel(Pixel& pixel)
 {
     // Pixels are sent as follows:
@@ -62,21 +85,23 @@
     //                           |    /   /   ___________________...
     //                           |   /   /   /
     //                          GRB,GRB,GRB,GRB,...
+    //
+    // For BYTE_ORDER_RGB, the order of the first two bytes are reversed.
 
-    if (byte_order_ == BYTE_ORDER_RGB) {
-        SendFourBits(spi_, (pixel.red >> 4) & 0xf);
-        SendFourBits(spi_, (pixel.red >> 0) & 0xf);
-        SendFourBits(spi_, (pixel.green >> 4) & 0xf);
-        SendFourBits(spi_, (pixel.green >> 0) & 0xf);
+    uint8_t byte0 = (byte_order_ == BYTE_ORDER_RGB) ? pixel.red : pixel.green;
+    uint8_t byte1 = (byte_order_ == BYTE_ORDER_RGB) ? pixel.green : pixel.red;
+
+    if (protocol_ == PROTOCOL_800KHZ) {
+        SendFourBits(spi_, (byte0 >> 4) & 0xf);
+        SendFourBits(spi_, (byte0 >> 0) & 0xf);
+        SendFourBits(spi_, (byte1 >> 4) & 0xf);
+        SendFourBits(spi_, (byte1 >> 0) & 0xf);
         SendFourBits(spi_, (pixel.blue >> 4) & 0xf);
         SendFourBits(spi_, (pixel.blue >> 0) & 0xf);
     } else {
-        SendFourBits(spi_, (pixel.green >> 4) & 0xf);
-        SendFourBits(spi_, (pixel.green >> 0) & 0xf);
-        SendFourBits(spi_, (pixel.red >> 4) & 0xf);
-        SendFourBits(spi_, (pixel.red >> 0) & 0xf);
-        SendFourBits(spi_, (pixel.blue >> 4) & 0xf);
-        SendFourBits(spi_, (pixel.blue >> 0) & 0xf);
+        SendEightBits(spi_, byte0);
+        SendEightBits(spi_, byte1);
+        SendEightBits(spi_, pixel.blue);
     }
 }
 
--- a/neopixel.h	Fri Aug 01 22:17:23 2014 +0000
+++ b/neopixel.h	Mon Aug 18 21:38:40 2014 +0000
@@ -31,6 +31,16 @@
     BYTE_ORDER_RGB,
 };
 
+/** Set the protocol mode.
+ *
+ * The protocol is named after the clock, as though WS8211 supports only the
+ * 400kHz clock, WS8212 supports both.
+ */
+enum Protocol {
+    PROTOCOL_800KHZ,
+    PROTOCOL_400KHZ,
+};
+
 typedef void (*PixelGenerator)(Pixel* out, uint32_t index, uintptr_t extra);
 
 /** Control an array or chain of NeoPixel-compatible RGB LEDs.
@@ -91,7 +101,8 @@
      * @param byte_order The order in which to transmit colour channels.
      */
     PixelArray(PinName out,
-               ByteOrder byte_order = BYTE_ORDER_GRB);
+               ByteOrder byte_order = BYTE_ORDER_GRB,
+               Protocol protocol = PROTOCOL_800KHZ);
 
     /** Update the pixel display from a buffer.
      *
@@ -135,6 +146,7 @@
 private:
     BurstSPI spi_;
     ByteOrder byte_order_;
+    Protocol protocol_;
     
     static int const latch_time_us_ = 50;