GD32/STM32 : WS2812B using Timer + DMA
Spi not good enough for you?
The easy way to use WS2812B programmable RGB leds is using the SPI MOSI pin.
That works fine most of the times with a couple of gotchas :
- The SPI divider is not very flexible on the GD32/STM32 chips, you can only divide by a power of 2. So if your APB clock is not "near" enought the WS2812B 400 us tick you are in trouble or must use complicated schemes.
- Using a SPI "locks" the alternate functions of ~ 4 pins. So you can't use them for PMW or any other alternate functions, even if you dont actually use them.
- The SPI pin is the one getting killed most often on my boards. I have a few MCUs that are working fine except the spi is fried.
- Also the 2nd /3rd SPI are using APB1, so not ok in terms of clock.
In short, Spi0 @ 72 Mhz is ok, other setup is to be looked closely at.
What's the other option ?
The other option is to use PWM + DMA.
We program a 1.2 us PWM mode 0 on a pin.
We change the duty at every cycle to create 0s and 1s.
For example, If the PWM is using a reload value of 100, we'll use 33 for a zero and 66 for a one as compare value for the PWM.
So we'll fill a 16 bits array of 33 and 66 to create the desired waveform, and transfer a new compare value at each PWM cycle.
DMA half transfer for the win
The main idea is as follows :
- Our internal buffer is worth 2 leds, i.e. 48 bits => 48 x 16 bits =96 bytes
- We program a circular DMA transfer from our internal buffer to the CHCVs timer compare register
- We enable the "half" and "full" transfer interrupts
- When one occurs, it means the other half is being transfered, so we can fill the current half with the next LED value
- When the last has been written we must wait 2 dma half cycles for it to be fully transferred
Pros & cons
Pros :
- We can use any PWM capable pin linked to a DMA capable timer
- We lock down only one PIN alternate function
- The state at rest of the pin is zero
- The timing is much more flexible as we can divide the APB clock by any number between 0 and 65535
- The translation between "flat" value and PWM value is trivial
Cons:
- Each led is 24 bits, each bits is converted to a 16 bits PWM compare value. That means that if we precompute everything, each Led will consume 48 bytes.
- If we do it on the fly, we only consume 96 bytes whatever the # of leds *BUT* we have a dma interrupt every 33 us. The computation takes about 4us (~ 2 us on a G32F303 @ 72 Mhz).
- We probably lock a full timer, it's not easy to share different channels of a given timer
Possible improvement:
Using 8 bits->16 bits dma should work most of the times, that would divide by 2 the needed amount of data when precalculated.
Comments
Post a Comment