C code to work with WS2812 NeoPixel LEDs
David Betz
Posts: 14,516
Here is a sample program that uses the ws2812 LED control C code that I wrote based on JonnyMac's Spin code. Actually, the PASM driver is nearly identical to Jon's. I don't have anywhere near as many effects as he does but they're relatively easy to add. I'm planning to expand this to support my WS2812 Christmas ornament project.
I'll also attach a zip file containing all of the code and a Makefile to build it.
Old version with PropGCC Makefile: ws2812.zip
Newer version as a Simple Library for use with SimpleIDE: libws2812.zip (obsolete, use the one below)
Updated version that supports suppressing R/G swapping for use with the ws2811 as well as the ws2812: libws2812.zip
Yet another update switching to JonnyMac's "one shot" driver with some corresponding API changes: libws2812.zip <<< use this version
I'll also attach a zip file containing all of the code and a Makefile to build it.
Old version with PropGCC Makefile: ws2812.zip
Newer version as a Simple Library for use with SimpleIDE: libws2812.zip (obsolete, use the one below)
Updated version that supports suppressing R/G swapping for use with the ws2811 as well as the ws2812: libws2812.zip
Yet another update switching to JonnyMac's "one shot" driver with some corresponding API changes: libws2812.zip <<< use this version
#include <propeller.h> #include "ws2812.h" // led chain #define LED_PIN 13 #define LED_COUNT 4 // 4 Parallax WS2812B Fun Boards uint32_t ledColors[LED_COUNT]; // LED driver state ws2812_t driver; // pattern for chase uint32_t pattern[] = { COLOR_RED, COLOR_ORANGE, COLOR_YELLOW, COLOR_GREEN, COLOR_BLUE, COLOR_INDIGO }; #define pattern_count (sizeof(pattern) / sizeof(pattern[0])) // ticks per millisecond int ticks_per_ms; // forward declarations void alternate(int count, int delay); void chase(int count, int delay); void pause(int ms); int main(void) { // calibrate the pause function // CLKFREQ is the clock frequency of the processor // typically this is 80mhz // dividing by 1000 gives the number of clock ticks per millisecond ticks_per_ms = CLKFREQ / 1000; // load the LED driver if (ws2812b_init(&driver) < 0) return 1; // repeat the patterns for (;;) { // alternate inner and outer colors alternate(8, 500); // chase chase(32, 200); } return 0; } void alternate(int count, int delay) { // start with the outer two LEDs green and the inner two red ledColors[0] = COLOR_GREEN; ledColors[1] = COLOR_RED; ledColors[2] = COLOR_RED; ledColors[3] = COLOR_GREEN; // repeat count times or forever if count < 0 while (count < 0 || --count >= 0) { // swap the inner and outer colors ledColors[0] = ledColors[1]; ledColors[1] = ledColors[3]; ledColors[2] = ledColors[3]; ledColors[3] = ledColors[0]; // update the LED chain ws2812_refresh(&driver, LED_PIN, ledColors, LED_COUNT); // delay between frames pause(delay); } } // the chase effect was translated from Spin code by Jon MacPhalen void chase(int count, int delay) { int base = 0; int idx, i; // repeat count times or forever if count < 0 while (count < 0 || --count >= 0) { // fill the chain with the pattern idx = base; // start at base for (i = 0; i < LED_COUNT; ++i) { // loop through connected leds ledColors[i] = pattern[idx]; // update channel color if (++idx >= pattern_count) // past end of list? idx = 0; // yes, reset } if (++base >= pattern_count) // update the base for the next time base = 0; // update the LED chain ws2812_refresh(&driver, LED_PIN, ledColors, LED_COUNT); // delay between frames pause(delay); } } void pause(int ms) { waitcnt(CNT + ms * ticks_per_ms); }
Comments
- Earl
That's interesting to know -- may have to give it a try. That said... I understand David's point-of-view and agree with it; when I finally commit to C, I want to be writing code as it is intended to be written in that language.
BTW... I'm thrilled so many people are having fun with the WS28xx drivers I've written. I was just in Steve Wang's ("Predator") shop yesterday working with his team trying to simulate fire with high intestity LEDs and steam (we have a working solution) -- controlled with a Propeller, of course. Who says LEDs aren't cool?
While it is nice that the tool works well in so many cases which is a great achievement, the resulting code in no way reflects the way C source should be written.
There was one guy here who tried using spin2cpp output as source to be managed by source control and be a test subject for a review process. Certainly not an appropriate use.
We shot some test video on phones which looks better than real life! We're using 1W amber LEDs with a few reds through in, and I have a simple flame algorithm that I'm running on multiple channels (from an EFX-TEK HC-8+). I wish I could use WS2812s on this but I don't think they're bright enough, and we'd have to build special boards to achieve the required density. That said, I may order a couple of those Adafruit strips and try them when the shop re-opens in a couple weeks.
I completely understand. Auto-generated code, especially translating between languages, is rarely optimal, and is often hard to read. That said, I had a NeoPixel ring in my hot little hands, and time restrictions - I wanted to make a Christmas ornament. The protocol requirements for the WS2812 are pretty exacting, and I'm a PropGCC newb, so the black-box approach was the way to go. Not advocating that way in every case - just wanted to let people know it worked.
- Earl
Actually, I think if I was just planning to use JonnyMac's object as-is I would probably have just left it in Spin. I did run his Spin code and Duane Degn's and both were impressive.
I wrote a couple of my own routines to:
- Do a red/green swirl color comet tail chase
- Blink 3-5 random lights in the ring with white, doing a gradual up/down ramp on the intensity
- Do a red/green full ring color mix with alternating colors
The routines aren't that good :-) But if there's interest, I'll post them.
- Earl
BTW, most of the objects in libpropeller with ASM were converted with spin2cpp, including the same FFDS1 that the "one guy" tried converting. I did do quite a bit of work after the conversion to make sure things ran smoothly, but it's still in the pedigree. I think the objects turned out ok.
spin2cpp converted objects:
MCP3208
PWM32
FSRW
FFDS1
As David Betz mentioned, it needs to be done from the command prompt (quite easy!). Since this is your first time though, there's one quick step involved first: telling the command prompt where PropGCC and Make are located.
Instructions for this are all over the internet, including plastered right onto the Google search results: https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#safe=off&q=how to set environment variables windows
The variable name you need to add is "PATH" and the value needs to be set to wherever propeller-elf-gcc.exe is installed. For instance, I have PropGCC installed to C:\propgcc, so I would set it like so:
IF there is already a variable called path, simply add your value to end of it with a single semicolon separating your value from the old one (no spaces)
Woohoo! All done with setup.
Now, open a file browser navigate to the contents of that zip file. Hold "shift" and right click in the window (not on top of a file or folder). There should be an option in the menu that says "Open command window here". Click it. Type "make" and press enter. All done
https://code.google.com/p/propgcc/
A few possibilities....
If you changed the environment variable (or created it) after opening the terminal, that won't work. You'll need to close the terminal and then reopen it.
Can you show us the output of ?
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\Andyn.NWUPGRADESPLUS>echo %PATH%
C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\windows\system32;
C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0\;
C:\Program Files\WIDCOMM\Bluetooth Software\;C:\Program Files\Windows Live\Share
d;;C:\Program Files\SimpleIDE\propeller-gcc\bin;C:\Program Files\Microsoft SQL S
erver\100\Tools\Binn\;C:\Program Files\Microsoft SQL Server\100\DTS\Binn\;C:\Pro
gram Files\SimpleIDE\propeller-gcc\bin
C:\Users\Andyn.NWUPGRADESPLUS>
I notice a newline that probably shouldn't be there following the PropGCC installation - also, no semicolon following it. You should closely inspect the value of the PATH variable and fix it. To make this easier, you might copy the value and paste it into Notepad or your favorite text editor, make the modifications, and then paste it back into the value box.
C:\Users\Andyn.NWUPGRADESPLUS>echo %PATH%
C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\windows\system32;
C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0\;
C:\Program Files\WIDCOMM\Bluetooth Software\;C:\Program Files\Windows Live\Share
d;;C:\Program Files\SimpleIDE\propeller-gcc\bin;C:\Program Files\Microsoft SQL S
erver\100\Tools\Binn\;C:\Program Files\Microsoft SQL Server\100\DTS\Binn\;C:\Pro
gram Files\SimpleIDE\propeller-gcc\bin
Looks reasonable to me. Is "make.exe" in that folder? It should be. But with PATH being set correctly, I can't imagine any other reason why cmd wouldn't find it.
I don't know why make.exe wouldn't be in C:\Program Files\SimpleIDE\propeller-gcc\bin. It should be. If it isn't. try downloading a new version of PropGCC (i have a package with 1.9.0 here) and replace C:\Program Files\SimpleIDE\propeller-gcc with the new version. Make sure the folder is still named the same thing of course.
C:\Users\Andyn.NWUPGRADESPLUS\Downloads\ws2812>make
process_begin: CreateProcess(NULL, spin2cpp --dat -o ws2812_driver.dat ws2812_dr
iver.spin, ...) failed.
make (e=2): The system cannot find the file specified.
make: *** [ws2812_driver.dat] Error 2