WS2812b DMA/Timer driver for STM32F100 using Chibios

I stumbled on someone selling 50 WS2812b leds on ebay on small boards, shipping from the UK for £17, and couldn't resist. After I got them, I soldered one up, and expected to find an easy way to get it working.

I was a bit wrong...

Here's a link describing what you need to do. These things have stringent and curious timing requirements. Even the above makes it sound easier than it really is. Anyway, I'm not the first person to try to do this: This is for Chibios, It uses two timers and out of the box works only on an STM32F3. This uses the STM32 library from ST. It uses only one timer. Curiously, it uses a word per bit, when the DMA can handle conversion from a byte to a word. Both use DMA to set the PWM pulse width for each transmitted bit, and you must first expand the bits in each pixel into a pulse width. Once you've done that, you begin sending.

What I've done is basically a port of the second approach to Chibios, although there's no code in common at all. Chibios actually requires more direct register poking than using the STM32 library.

I was quite surprised to learn how general the STM32 DMA model is. You can DMA to or from pretty much any peripheral register. You can also use almost anything to trigger each transfer as well. In this case, we are using the individual time channel to clock the DMA.

It took me a while to understand that at least for the STM32F100xx there's a completely arbitrary fixed map from trigger source to DMA channel, rather than having anything to do with the register you're writing. It's documented here, in the reference manual. Chibios also didn't help by leading me to think you could enable the DMA by setting a flag in the PWM configuration. It just gets ignored, you need to do it directly yourself.

I also tried and failed to write a fast enough interrupt handler to set the next pulse width, reading directly from the frame buffer, so you wouldn't need tons of memory. I used the Chibios wrapper to avoid writing any assembly code. On a faster micro controller I think even this should work, and handling the interrupt directly should work on the STM32F100 - you get 20 odd instructions...

Anyway, the code is here. The DMA channel is still hard coded at the time of writing, even though the timer isn't, so beware. It's stuck randomly in another project: I did it with the micro controller I had out on my desk at the time.

After all this, I'm feeling confident about returning to a previous project: Using an STM32F303 as an oscilloscope. Poking registers to get the interleaved ADC mode working is a little less intimidating now.

Popular posts from this blog

3D Printer ramblings

Balance Bot V2

Robot arm