STM32F042 driving a WS2812B using SPI


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 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 🙂

6 thoughts on “STM32F042 driving a WS2812B using SPI

  1. mehranvz July 10, 2020 / 6:34 pm

    Dude I finally made one using ur IDEA!! TNX for sharing ur creativity!


    • fduignan July 10, 2020 / 6:40 pm

      Happy to help 👍


  2. Shrey Ravindra December 7, 2020 / 10:37 am

    Hey, thanks for the lovely project. When I just use your logic of SPI and configure SPI to run on 3Mbps then things are not working as expected. I will debug a bit more today to understand the frequency. I think i am messing it up somehow


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s