I would love to have someone help me to make it more efficient. For example, I haven't taken into account the time it takes to actually enable/disable a pin. Since these require a memory "flush" (MEMW) and some far mem stores (S32I) I know they take up a few cycles. I had a "pure" asm version but it really wasn't any more efficient and was obviously harder to read so I'm just going with this version for now.
#include <eagle_soc.h>
#include <c_types.h>
//////////
// BEGIN EspNeoPixel function.
//
#define NS_TO_CYCLES(n) (n / (1000000000L / F_CPU))
#define DELAY_NS(ns) do { __asm__ __volatile__ (".rept %0\n\t nop\n\t .endr\n\t" : : "i" (NS_TO_CYCLES(ns))); } while (0);
uint16_t enpPinMask;
//
// EspNeoPixel start.
//
// This disable interrupts and enables the pin for output.
//
// Call this before a sequence of pin updates with the pin the WS2812's are hooked up to.
// Note that this could be make to accept a pinMask in case you wanted to control multiple
// strips at the same time (albeit with the same colors).
//
// pin - The pin the WS2812's are hooked up to.
//
void enpStart(uint8_t pin) {
enpPinMask = 1 << pin;
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
noInterrupts();
}
//
// EspNeoPixel send byte.
//
// This sends a byte out to the WS2812. It should be called three times per pixel,
// once for green, once for red, and once for blue.
//
// The timing in this function is absolutely critical and could be made quicker
// if one wanted to take into account the time it takes to enable/disable the pin.
//
// This takes advantage of some of the tricks found here:
// http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
//
// val - An unsigned bit encoded with 8 bits of color information.
//
void volatile enpSendByte(uint8_t val) {
uint8_t loops;
for(loops = 0; loops < 8; loops++) {
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, enpPinMask);
if (val & 0x80) {
DELAY_NS(550);
} else {
DELAY_NS(200);
}
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, enpPinMask);
DELAY_NS(650);
val <<= 1;
}
}
//
// EspNeoPixel send pixel.
//
// This sends out three bytes (in the correct order) to light a single pixel.
//
// red - unsigned byte of red color information
// grn - unsigned byte of green color information
// blu - unsigned byte of blue color information
//
void enpSendPixel(uint8_t red, uint8_t grn, uint8_t blu) {
enpSendByte(grn);
enpSendByte(red);
enpSendByte(blu);
}
//
// EspNeoPixel stop sending data.
//
// This will re-enable interrupts and wait a small amount of time to enable the pixels.
//
void enpStop() {
interrupts();
delay(1);
}
//
// END EspNeoPixel function.
//////////
Here's an example of how to use it, again very minimal and simplistic. If a few people wanted I could setup a GitHub repo and try to keep this version maintained, although there's a few versions out there so I don't want to add to the clutter unless people want.
#define PIXELS 10
#define WS2812_PIN 12
void showColor( unsigned char r , unsigned char g , unsigned char b ) {
enpStart(WS2812_PIN);
for( int p=0; p<PIXELS; p++ ) {
enpSendPixel(r , g , b);
}
enpStop();
}
#define FADE_STEPS 255
void fadeColor(uint8_t bR, uint8_t bG, uint8_t bB, uint8_t eR, uint8_t eG, uint8_t eB, uint16_t ms) {
if (ms < FADE_STEPS) {
ms = FADE_STEPS;
}
uint16_t stepMs = ms / FADE_STEPS;
int16_t stepR = ((eR * FADE_STEPS) - (bR * FADE_STEPS)) / FADE_STEPS;
int16_t stepG = ((eG * FADE_STEPS) - (bG * FADE_STEPS)) / FADE_STEPS;
int16_t stepB = ((eB * FADE_STEPS) - (bB * FADE_STEPS)) / FADE_STEPS;
for (int i = 0; i < FADE_STEPS; i++) {
uint16_t red = bR + (stepR * i / FADE_STEPS);
uint16_t grn = bG + (stepG * i / FADE_STEPS);
uint16_t blu = bB + (stepB * i / FADE_STEPS);
showColor(red, grn, blu);
delay(stepMs);
}
}
void setup() {
}
void loop() {
fadeColor(0, 0, 0, 100, 0, 0, 2000);
fadeColor(100, 0, 0, 0, 100, 0, 2000);
fadeColor(0, 100, 0, 0, 0, 100, 2000);
fadeColor(0, 0, 100, 0, 0, 0, 2000);
}