/WS2812B_STM32_Libmaple

WS2812B (Neopixel) library for Arduino STM32 (Libmaple core)

Primary LanguageC++

WS2812B_STM32_Libmaple

WS2812B (Neopixel) library for Arduino STM32 (Libmaple core)

Written by Roger Clark www.rogerclark.net, from first principals

This library uses SPI DMA to control a strip of WS2812B (NeoPixel) LEDS

It should be generally compatible with the Adafruit NeoPixel library, except I have not had chance to implement one or two of the lesser used functions

Connect Data In of the strip to SPI1 MOSI

This library has only been tested on the WS2812B LED. It may not work with the older WS2812 or other types of addressable RGB LED, becuase it relies on a division multiple of the 72Mhz clock frequence on the STM32 SPI to generate the correct width T0H pulse, of 400ns +/- 150nS SPI DIV32 gives a pulse width of 444nS which is well within spec for the WS2812B but is probably too long for the WS2812 which needs a 350ns pulse for T0H

##Technical details

The library uses SPI to send the mark/space encoded data to the LED's

Each mark/space squarewave is generated by 3 bits of data A pixel 0 value is achieved by sending the bit pattern 100 A pixel 1 value is achieved by sending the bit pattern 110

Where the duration of each bit is 444nS Hence 100 generates a mark space value of 444/888nS and 110 generates a mark space value of 888/444nS

This method results in the smallest storage requirement and the fastest send times, however because the 8 bit pixel channel data is encoded in 24 bits, (3 bytes) the values required in each of the 3 bytes to represent a specific value is not easy to generate.

The bit pattern in the 3 bytes is 88877766 65554443 33222111

For speed of operation the values reqired for each byte for each of the 256 possible values is held in 3 separate 256 byte LUTS which were pre-computed by this function (which generates the full 24 bit pattern for a given input value (0-255)

uint32_t convert(uint8_t data)
{
  uint32_t out=0;
  for(uint8_t mask = 0x80; mask; mask >>= 1)  
  {
    out=out<<3;
    if (data & mask)
    {
      out = out | 0B110;//Bit high
    }
    else
    {
      out = out | 0B100;// bit low
    }
  }
  return out;
}

The STM32F103 has plenty of flash space (either 64 or 128k), so I used 256 byte LUTs even though the number of unique values in each LUT is only 8,4 and 8 bytes respectively. However to use small LUTS requires shifting and masking of the input data, and the code was written with a preference for speed over binary size

The encoded pixel buffer is 2 bytes longer than the actual encoded data. The first and last encoded bytes are all zeros. This is because the SPI hardware seems to preload MOSI with its output value before the start of the DMA transfer, which causes the first encoded pulse to be around 50ns longer than the subsequent bits, (around 490 or 500ns) This had the effect of causing the first LED to always think the MS bit of the green channel was set to High.

So having the first encoded byte of zeros, is a work-around , as although the first encoded bit is still 490nS wide its a logic zero and is therefore not visible, becuase the default state is of the SPI when not transmitting data is logic zero The last byte was also set to all zeros, as occasionally MOSI seemed to be left set to logic high on completion of the SPI DMA send

Adding these 2 bytes does slightly slow down the transfer, as it add 444ns * 8 = just over 3.5uS to both end. But the WS2812B theoretically requires a reset time of more than 50uS between transmissions, so 3.5uS can be part of that time. In reality the WS2812B seems to only need around 6uS of reset time, so for all practical purposes, there no delays are needed at all in the library to enforce the reset time, as the overead of the function call and the SPI DMA setup plus the 3.5uS gives the enough reset time.