Nucleo-transfer
Dependencies: BurstSPI
Dependents: Nucleo-transfer Nucleo-transfer Nucleo-transfer
Fork of PixelArray by
neopixel.cpp@6:b995e738b29d, 2017-11-23 (annotated)
- Committer:
- Ishy
- Date:
- Thu Nov 23 15:00:13 2017 +0000
- Revision:
- 6:b995e738b29d
- Parent:
- 4:c3b314df3dfe
test
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
JacobBramley | 2:3c3c41774cdf | 1 | #include <stdint.h> |
JacobBramley | 2:3c3c41774cdf | 2 | #include "mbed.h" |
JacobBramley | 2:3c3c41774cdf | 3 | #include "neopixel.h" |
JacobBramley | 2:3c3c41774cdf | 4 | |
JacobBramley | 2:3c3c41774cdf | 5 | namespace neopixel |
JacobBramley | 2:3c3c41774cdf | 6 | { |
JacobBramley | 2:3c3c41774cdf | 7 | |
mcapewel | 3:6f392fcb1d3b | 8 | PixelArray::PixelArray(PinName out, ByteOrder byte_order, Protocol protocol) |
mcapewel | 3:6f392fcb1d3b | 9 | : spi_(out, NC, NC), byte_order_(byte_order), protocol_(protocol) |
JacobBramley | 2:3c3c41774cdf | 10 | { |
mcapewel | 3:6f392fcb1d3b | 11 | if (protocol_ == PROTOCOL_800KHZ) { |
mcapewel | 3:6f392fcb1d3b | 12 | // 800kHz bit encodings: |
mcapewel | 3:6f392fcb1d3b | 13 | // '0': ----________ |
mcapewel | 3:6f392fcb1d3b | 14 | // '1': --------____ |
mcapewel | 3:6f392fcb1d3b | 15 | // The period is 1.25us, giving a basic frequency of 800kHz. |
mcapewel | 3:6f392fcb1d3b | 16 | // Getting the mark-space ratio right is trickier, though. There are a number |
mcapewel | 3:6f392fcb1d3b | 17 | // of different timings, and the correct (documented) values depend on the |
mcapewel | 3:6f392fcb1d3b | 18 | // controller chip. |
mcapewel | 3:6f392fcb1d3b | 19 | // |
mcapewel | 3:6f392fcb1d3b | 20 | // The _real_ timing restrictions are much simpler though, and someone has |
mcapewel | 3:6f392fcb1d3b | 21 | // published a lovely analysis here: |
mcapewel | 3:6f392fcb1d3b | 22 | // http://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/ |
mcapewel | 3:6f392fcb1d3b | 23 | // |
mcapewel | 3:6f392fcb1d3b | 24 | // In summary: |
mcapewel | 3:6f392fcb1d3b | 25 | // - The period should be at least 1.25us. |
mcapewel | 3:6f392fcb1d3b | 26 | // - The '0' high time can be anywhere from 0.0625us to 0.5us. |
mcapewel | 3:6f392fcb1d3b | 27 | // - The '1' high time should be longer than 0.625us. |
mcapewel | 3:6f392fcb1d3b | 28 | // |
mcapewel | 3:6f392fcb1d3b | 29 | // These constraints are easy to meet by splitting each bit into three and packing them into SPI packets. |
mcapewel | 3:6f392fcb1d3b | 30 | // '0': 100 mark: 0.42us, space: 0.83us |
mcapewel | 3:6f392fcb1d3b | 31 | // '1': 110 mark: 0.83us, space: 0.42us |
mcapewel | 3:6f392fcb1d3b | 32 | spi_.frequency(2400000); // 800kHz * 3 |
mcapewel | 3:6f392fcb1d3b | 33 | spi_.format(12); // Send four NeoPixel bits in each packet. |
mcapewel | 3:6f392fcb1d3b | 34 | } else { |
mcapewel | 3:6f392fcb1d3b | 35 | // 400kHz bit encodings: |
mcapewel | 3:6f392fcb1d3b | 36 | // '0': --________ |
mcapewel | 3:6f392fcb1d3b | 37 | // '1': -----_____ |
mcapewel | 3:6f392fcb1d3b | 38 | // |
mcapewel | 3:6f392fcb1d3b | 39 | // Timing requirements are derived from this document: |
mcapewel | 3:6f392fcb1d3b | 40 | // http://www.adafruit.com/datasheets/WS2811.pdf |
mcapewel | 3:6f392fcb1d3b | 41 | // |
mcapewel | 3:6f392fcb1d3b | 42 | // The period is 2.5us, and we use a 10-bit packet for this encoding: |
mcapewel | 3:6f392fcb1d3b | 43 | // '0': 1100000000 mark: 0.5us, space: 2us |
mcapewel | 3:6f392fcb1d3b | 44 | // '1': 1111100000 mark: 1.25us, space: 1.25us |
mcapewel | 3:6f392fcb1d3b | 45 | spi_.frequency(4000000); // 400kHz * 10 |
mcapewel | 3:6f392fcb1d3b | 46 | spi_.format(10); // Send one NeoPixel bit in each packet. |
mcapewel | 3:6f392fcb1d3b | 47 | } |
JacobBramley | 2:3c3c41774cdf | 48 | } |
JacobBramley | 2:3c3c41774cdf | 49 | |
JacobBramley | 2:3c3c41774cdf | 50 | static void SendFourBits(BurstSPI& spi, uint32_t bits) |
JacobBramley | 2:3c3c41774cdf | 51 | { |
JacobBramley | 2:3c3c41774cdf | 52 | // Encode '0' bits as 100 and '1' bits as 110. |
JacobBramley | 2:3c3c41774cdf | 53 | // We have this bit pattern: 00000000abcd |
JacobBramley | 2:3c3c41774cdf | 54 | // We want this bit pattern: 1a01b01c01d0 |
JacobBramley | 2:3c3c41774cdf | 55 | uint32_t ac = (bits * 0x088) & // 0abcdabcd000 |
JacobBramley | 2:3c3c41774cdf | 56 | 0x410; // 0a00000c0000 |
JacobBramley | 2:3c3c41774cdf | 57 | |
JacobBramley | 2:3c3c41774cdf | 58 | uint32_t bd = (bits * 0x022) & // 000abcdabcd0 |
JacobBramley | 2:3c3c41774cdf | 59 | 0x082; // 0000b00000d0 |
JacobBramley | 2:3c3c41774cdf | 60 | |
JacobBramley | 2:3c3c41774cdf | 61 | static uint32_t const base = 04444; // 100100100100 |
JacobBramley | 2:3c3c41774cdf | 62 | |
JacobBramley | 2:3c3c41774cdf | 63 | spi.fastWrite(base | ac | bd); // 1a01b01c01d0 |
JacobBramley | 2:3c3c41774cdf | 64 | } |
JacobBramley | 2:3c3c41774cdf | 65 | |
mcapewel | 3:6f392fcb1d3b | 66 | static void SendEightBits(BurstSPI& spi, uint8_t bits) |
mcapewel | 3:6f392fcb1d3b | 67 | { |
mcapewel | 3:6f392fcb1d3b | 68 | int zero = 0x300; // Encode zero as 0b1100000000 |
mcapewel | 3:6f392fcb1d3b | 69 | int one = 0x3e0; // Encode one as 0b1111100000 |
mcapewel | 4:c3b314df3dfe | 70 | for (int i = 128; i >= 1; i >>= 1) { |
mcapewel | 3:6f392fcb1d3b | 71 | spi.fastWrite((bits & i) ? one : zero); |
mcapewel | 3:6f392fcb1d3b | 72 | } |
mcapewel | 3:6f392fcb1d3b | 73 | } |
mcapewel | 3:6f392fcb1d3b | 74 | |
JacobBramley | 2:3c3c41774cdf | 75 | void PixelArray::send_pixel(Pixel& pixel) |
JacobBramley | 2:3c3c41774cdf | 76 | { |
JacobBramley | 2:3c3c41774cdf | 77 | // Pixels are sent as follows: |
JacobBramley | 2:3c3c41774cdf | 78 | // - The first transmitted pixel is the pixel closest to the transmitter. |
JacobBramley | 2:3c3c41774cdf | 79 | // - The most significant bit is always sent first. |
JacobBramley | 2:3c3c41774cdf | 80 | // |
JacobBramley | 2:3c3c41774cdf | 81 | // g7,g6,g5,g4,g3,g2,g1,g0,r7,r6,r5,r4,r3,r2,r1,r0,b7,b6,b5,b4,b3,b2,b1,b0 |
JacobBramley | 2:3c3c41774cdf | 82 | // \_____________________________________________________________________/ |
JacobBramley | 2:3c3c41774cdf | 83 | // | _________________... |
JacobBramley | 2:3c3c41774cdf | 84 | // | / __________________... |
JacobBramley | 2:3c3c41774cdf | 85 | // | / / ___________________... |
JacobBramley | 2:3c3c41774cdf | 86 | // | / / / |
JacobBramley | 2:3c3c41774cdf | 87 | // GRB,GRB,GRB,GRB,... |
mcapewel | 3:6f392fcb1d3b | 88 | // |
mcapewel | 3:6f392fcb1d3b | 89 | // For BYTE_ORDER_RGB, the order of the first two bytes are reversed. |
JacobBramley | 2:3c3c41774cdf | 90 | |
mcapewel | 3:6f392fcb1d3b | 91 | uint8_t byte0 = (byte_order_ == BYTE_ORDER_RGB) ? pixel.red : pixel.green; |
mcapewel | 3:6f392fcb1d3b | 92 | uint8_t byte1 = (byte_order_ == BYTE_ORDER_RGB) ? pixel.green : pixel.red; |
mcapewel | 3:6f392fcb1d3b | 93 | |
mcapewel | 3:6f392fcb1d3b | 94 | if (protocol_ == PROTOCOL_800KHZ) { |
mcapewel | 3:6f392fcb1d3b | 95 | SendFourBits(spi_, (byte0 >> 4) & 0xf); |
mcapewel | 3:6f392fcb1d3b | 96 | SendFourBits(spi_, (byte0 >> 0) & 0xf); |
mcapewel | 3:6f392fcb1d3b | 97 | SendFourBits(spi_, (byte1 >> 4) & 0xf); |
mcapewel | 3:6f392fcb1d3b | 98 | SendFourBits(spi_, (byte1 >> 0) & 0xf); |
JacobBramley | 2:3c3c41774cdf | 99 | SendFourBits(spi_, (pixel.blue >> 4) & 0xf); |
JacobBramley | 2:3c3c41774cdf | 100 | SendFourBits(spi_, (pixel.blue >> 0) & 0xf); |
JacobBramley | 2:3c3c41774cdf | 101 | } else { |
mcapewel | 3:6f392fcb1d3b | 102 | SendEightBits(spi_, byte0); |
mcapewel | 3:6f392fcb1d3b | 103 | SendEightBits(spi_, byte1); |
mcapewel | 3:6f392fcb1d3b | 104 | SendEightBits(spi_, pixel.blue); |
JacobBramley | 2:3c3c41774cdf | 105 | } |
JacobBramley | 2:3c3c41774cdf | 106 | } |
JacobBramley | 2:3c3c41774cdf | 107 | |
JacobBramley | 2:3c3c41774cdf | 108 | void PixelArray::update(Pixel buffer[], uint32_t length) |
JacobBramley | 2:3c3c41774cdf | 109 | { |
JacobBramley | 2:3c3c41774cdf | 110 | for (size_t i = 0; i < length; i++) { |
JacobBramley | 2:3c3c41774cdf | 111 | send_pixel(buffer[i]); |
JacobBramley | 2:3c3c41774cdf | 112 | } |
JacobBramley | 2:3c3c41774cdf | 113 | |
JacobBramley | 2:3c3c41774cdf | 114 | wait_us(latch_time_us_); |
JacobBramley | 2:3c3c41774cdf | 115 | } |
JacobBramley | 2:3c3c41774cdf | 116 | |
JacobBramley | 2:3c3c41774cdf | 117 | void PixelArray::update(PixelGenerator generator, uint32_t length, uintptr_t extra) |
JacobBramley | 2:3c3c41774cdf | 118 | { |
JacobBramley | 2:3c3c41774cdf | 119 | for (size_t i = 0; i < length; i++) { |
JacobBramley | 2:3c3c41774cdf | 120 | Pixel out; |
JacobBramley | 2:3c3c41774cdf | 121 | generator(&out, i, extra); |
JacobBramley | 2:3c3c41774cdf | 122 | send_pixel(out); |
JacobBramley | 2:3c3c41774cdf | 123 | } |
JacobBramley | 2:3c3c41774cdf | 124 | |
JacobBramley | 2:3c3c41774cdf | 125 | wait_us(latch_time_us_); |
JacobBramley | 2:3c3c41774cdf | 126 | } |
JacobBramley | 2:3c3c41774cdf | 127 | |
JacobBramley | 2:3c3c41774cdf | 128 | } |