OctoWS2811-style DMA for single output without affecting other pins on the port?

Status
Not open for further replies.

Jopj

Member
Hi all,

I have built a autopilot/flight controller for RC vehicles using the Teensy 3.6, which is currently flying a small quadcopter. It has some WS2812 leds connected to pin 2 of the Teensy which I currently drive with FastLED, but since there's so much stuff going on in flight this causes a lot of flickering from interrupts disrupting the timing. I am looking for some way to drive the LED's without that limitation, specifically on pin 2 which doesn't have serial or SPI peripherals on it.

To that end I've been looking at the OctoWS2811 code, and like the approach! The problem is that it's doing 8-bit DMA transfers directly to the GPIO control registers, affecting all 8 port D pins. Setting the rest of the pins as inputs would prevent anything from happening, but my board requires some of them to be outputs. I thought about reading the current state of the GPIOD_PDOR register and modifying the buffer contents to result in the right state for the "unwanted" pins, but even if there'd be a good way to do it (interrupt before DMA transfer instead of after?), it would require CPU time taking away most of the benefit in addition to just feeling silly.

I'm not very familiar yet with the inner workings of the chip and it's capabilities so I'd appreciate opinions on whether it's even possible to have non-blocking WS2811 waveform on pin 2 of the teensy 3.6 without affecting other pins I know the Betaflight flight control software somehow drives these leds using DMA without blocking 8 ports with somewhat similar STM chips, but I haven't been able to decrypt that code yet..

Thanks,
Jopj
 
Yes, the WS2812Serial library does this, all DMA for no CPU usage while transmitting to the LEDs. But you can't just choose any pin. Only the serial TX pins can be used.

In Arduino, click File > Examples > WS2812Serial.
 
Thanks for your response,

I took a look at that already, but since there's no serial hardware on pin 2, I can't use that approach. I guess I could break the trace of the PCB and jumper one of the serial pins there, but they too are already in use :(
 
I think I found a way to do this: Using OctoWS2811 code as a base I'm changing the second DMA channel (which does the low transition on the signal representing logic state) destination from the GPIOD_PDOR register to GPIOD_PCOR. The former sets all outputs to some logic state, the latter only clears and does nothing on logic zero. The starting high and ending low transition DMA's already use the set and clear registers. Then, by masking the buffer so that only those bits corresponding to wanted led strips have anything in them, there should be no interference on the other OctoWS2811 pins. Since the logic is now inverted (logic one makes the signal low early meaning logic low for leds), I needed to also change how the buffer bit shifts go. Yay!

This does waste memory if any other amount than full 8 outputs are used, but I just need the non-blocking performance on pin 2 for my project :D
 
Although a niche use case, here is the resulting modified library in case someone finds it useful: link
I haven't tested it that much yet but it appears to work, there is no output except on those channels explicitly enabled.
 
Status
Not open for further replies.
Back
Top