This is the first step in a multi step process to update how NeoPixels are dealt with in the OPP framework. This is going to be pretty techy centric, so I apologize in advance. (Truth be told, I really have no idea who reads this blog, so maybe those two people like techy topics. I just don’t know).
The original implementation of lighting NeoPixels using the PSoC used a SPI bus to create the protocol. A single bit sent to a NeoPixel is a waveform which is high for 417ns, low or high depending on if the bit is 1 or 0 for the next 417ns, and low for the last 417ns. If you add these 3 portions of the waveform together, a single bit takes 1.25 us to send. The SPI bus is one of the simplest buses. To send a 1, it sends a high for a clock cycle. To send a 0, it sends a low for a clock cycle. If I set the clock of the SPI so a full clock cycle is 417ns, I can have the hardware send a bit stream at the correct rate. There is one problem. Instead of sending a single bit, I have to send the framing portion of the protocol, so I end up sending 1×0. (If I want to send a 1, I send 110. If I want to send a 0, I send 100. Simple). Here’s the problem. Making 3 bits out of 1 bit kind of stinks because it keeps crossing byte boundaries and such. Really quite annoying.
One way to reduce this annoyance is to just store everything as 3 bits in RAM. That means that each Neopixel requires 9 bytes (24 bits/Neopixel * 3 = 72 bits or 9 bytes). As mentioned in the last post, the PSoC 4200 only has 4K of RAM, so that disappears pretty fast.
Another way is to generate the framing on the fly. So every time that a bit is sent to a NeoPixel it is pre-pended with a 1 and post-pended with a 0. This takes much more processing time, but saves RAM. (This was the original implementation, and the code that contains all the bit shifts and boundary checking is magnificent. Completely unreadable).
Neither of these solutions are optimal. (Heck, they all really kind of stink). Enter the little PLD that is part of the PSoC 4200. Why not create a little state machine to pull bytes from a FIFO and create the framing in hardware. That way, the processor doesn’t have to do any of the bit banging stuff. As an added bonus, the PSoC has internal FIFOs that can be set for either 24 bits (RGB Neopixels) or 32 bits (RGBW Neopixels). In that way, the processor just has to keep the FIFO from under-flowing and can use a single write to send a complete NeoPixel update value. Very clean.
At this point, I’ve created a library that does all the NeoPixel heavy lifting with the state machine included and an interrupt to keep the FIFO from under-flowing. In the OPP code, I will simply pre-allocate 3 bytes for every NeoPixel in memory, then as MPF or whatever framework wants to update the value of a NeoPixel, it just has to send the index, and the new value. At this point I’ve just finished the library, and with the previous NeoPixel incarnation, I had already set up commands to change NeoPixel values. I might update those commands to be less restrictive.
Using this library, the load on the processor should be as small as possible using the PSoC4. Only thing that would be better is to buy a PSoC5 and set up a DMA to fill the FIFO. That would mean the updates could happen without any processor intervention at all except for starting the DMA.
Here is a link to the Cypress question, and the final library solution that I posted. Since I’m going to integrate it into the OPP firmware, there is probably little reason to click this link, but it does have the library. Cypress Developer Community Link