One of the many tools in an embedded software engineers toolkit is DMA or Direct-Memory Access. It’s a powerful method that allows the processor to hand off accessing memory to certain hardware subsystems ultimately freeing up a great amount of time for the main processor to do other tasks. Neopixels are also a great tool for hardware and firmware engineers alike. They are a very versatile and easily usable RGB LED which, when paired with DMA, can be a great and fun addition to any project.
What Are Neopixels?
Neopixels are digitally individually-addressable RGB leds. They can be daisy chained together which allows for the control of (theoretically) as many LEDs as you’d like. They are easily accessible with lots of example code and libraries which makes them very popular in the DIY community. On the downside, each neopixel takes up a few bytes of memory space and has a slow refresh rate (400Hz) making them not the best choice for persistence of vision projects or projects that require a high FPS.
How do Neopixels Work?
Each Neopixel consists of three leds (red, green, and blue) and a small LED control IC. Each neopixel is connected to the next over a single data pin. They use a cascade method of transferring data from one pixel to another. As the first neopixel receives each new byte of data it pushes a byte of data to the next neopixel in the series. This style of data transfer is heavily dependent on timing as there’s no clock signal. Each bit is represented by a full period of the data signal with a one or zero being dependent on the duty cycle of that signal. Once a neopixel hasn’t received any new data for a given period of time it lights up each of its LEDs accordingly.
What’s the Problem?
Neopixels are convenient and versatile in that they can be controlled over one data pin and can be strung together easily but they do come with some down sides. Firstly, each neopixel takes 3 bytes of data to control which could eat up a good chunk of memory if enough of them are being used. Depending on your system and the number of neopixels used this may or may not be a concern. The second, and more important downside is the neopixels slow refresh rate (400Hz). This makes them incapable of persistence of vision projects and projects that require high FPS. Due to them having a slower refresh rate they also take a lot of processor time to update which can cause issues if any other time sensitive tasks need to be accomplished. DMA is a great solution to this. It allows different hardware subsystems to directly access memory without the main processor having to help. DMA works great with communication and data transfer between different devices, especially in the case where a lot of data needs to be transferred.
What’s the Fix?
In the case of neopixels eating up a lot of memory the best fix for that is more memory. We advise switching to a microcontroller with more memory or even adding some extra external memory to your board. Now for the “neopixels slow refresh rate taking up too much processor time” problem, a combination of a SPI MOSI port and DMA can fix that right up. The total time to send one bit of data to a neopixel is 1.25µs, with the amount of time the signal is high and low varying depending on if a one or zero is being sent. At a speed of 6400kHz the SPI MOSI port can send one byte in 1.25µs. We can then use one byte of data sent over the SPI port to represent one bit of data for the neopixel. This means if we send 0xC0, the byte is high for 0.3125µs and low for 0.9375µs which is read as a zero by the neopixel. If 0xF8 is sent, the byte is high for 0.78125µs and low for 0.46875µs which is read as a 1 by the neopixel. Now that we have a way to easily represent ones and zeros in memory we can introduce DMA to the mix. All the main processor has to do is update the data stored in memory (keeping in mind each byte in memory is one bit to the neopixel) to get the color desired while the SPI port combined with DMA sends the data out. This will drastically reduce the time taken by the processor to control the neopixels and free the main processor up to take care of more important and time sensitive tasks. This method does take up quite a bit more memory, though, so is best for when the processor needs to accomplish many different tasks and memory space is readily available.