The WS2812B is an RGB LED with a built in serial interface. They are available from a number of sources. The ones used in this project were obtained from dx.com and cost about 6 Euro for a pack of 5. These are mounted on a nice little breakout board. Pins can be soldered on allowing them to be plugged into a breadboard as shown above.
Driving a WS2812B requires some delicate timing. It has a serial interface that represents 1’s and 0’s using a particular waveform rather than a simple high or low. A ‘1’ is represented by a high-low pulse with the high pulse lasting about twice as long as the low pulse. A ‘0’ is represented with a high-low pulse also though in this case, the low pulse is twice the width of the high pulse. The datasheet also specifies maximum and minimum widths for pulses. Roughly speaking, the high-low waveform must be completed in about a microsecond. I noticed that there were some solutions online that used SPI to generate the serial waveforms and decided to try it out on the STM32F042 Nucleo.
The first step was to figure out the mapping of RGB colour values to SPI bytes. Note: The WS2812B orders the colours as Green Red Blue so I will be using the term “GRB colour value” below. The WS2812B must be sent a 24bit colour value followed by a low signal of a least 50 microseconds. Conceptually I think of this as filling a 24 bit shift register and then latching the contents through to the LED’s. The data is translated to SPI bytes as shown below:
The WS2812B datasheet contains the following specifications for the waveforms.
WS2812B Logic 1:
A high pulse of 0.8us +/-150ns followed by a low pulse of 0.4us +/-150ns
Total width : 1.25us +/-600ns
WS2812B Logic 0:
A high pulse of 0.4us +/-150ns followed by a low pulse of 0.8us +/-150ns
Total width : 1.25us +/-600ns
If the SPI bus is run at 3MHz, one SPI bit lasts 0.333us, two SPI bits take 0.667us. These times are within the WS2812B specification for narrow and wide pulse widths. A colour bit value of ‘1’ in the application layer is mapped to an SPI bit pattern of ‘110’; while a colour bit value of ‘0’ in the application layer is mapped to an SPI bit pattern of ‘100’. Each application colour byte therefore maps to 3 SPI bytes. A 3 byte Green-Red-Blue colour value in the application layer is converted to a 9 byte SPI sequence and output to the WS2812B.
The SPI output (MOSI) is taken from the pin labelled “A6” on the Nucleo board and connected to Din (Data In) on the WS2812B breakout board.
A demonstrator application generates a Green/Red/Blue colour value that changes over time. This value is converted to a 9 byte SPI array and output to the WS2812B. A video showing the colour progression is available on YouTube . Note. The LED is covered with a piece of quartz to dim it a little (the camera didn’t like it being so bright).
Code is available on GitHub and was compiled using arm-gcc 4.9.3. OpenOCD was used to download and debug. Next step is to drive a string of these using DMA and SPI 🙂