Your new topic does not fit any of the above??? Check first. Then post here. Thanks.

Moderator: igrr

User avatar
By Jason Stapels
#16464 I thought I would share my code since it seems to be working fine and it's very simple. I used the WS2812b spec's as a reference on the timings so I know it could be made quicker, but so far this code has been working just fine. I haven't had any weird glitches or problems with the first pixel.

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.

Code: Select all#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.

Code: Select all#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);
}
User avatar
By Makuna
#16474
Jason Stapels wrote: I haven't had any weird glitches or problems with the first pixel.


Do you have WiFi turned on with this code running? If not turn it on and connect it.
Are you just setting the pixels once? Put the code in a loop with some sort of animation (moving a series of colors along the leds with most of them off).

I started with a very similar routine and it also had glitches and further, the timing was inconsistent (measured by a Logic Analyzer). But I am running with the Wifi connected and a very active animation.
User avatar
By Jason Stapels
#16484
Makuna wrote:I started with a very similar routine and it also had glitches and further, the timing was inconsistent (measured by a Logic Analyzer). But I am running with the Wifi connected and a very active animation.

So I did a bunch of different tests with WiFi and doing solid color updates (rather than tons of fading) and I did notice a problem with the first pixel. So I did a quick makeshift 3V -> 5V level shifter and the problem with the first pixel went away***, even when using WiFi.

*** - Actually, I have an issue with the first pixel when I turn on the unit, but I'm pretty sure that's because my ghetto level shifter default to high. But I'll keep playing with it to see, I just can't imagine why my timing would vary unless the internal oscillator is all over the place.
User avatar
By Markus Gritsch
#16487
Jason Stapels wrote:I just can't imagine why my timing would vary unless the internal oscillator is all over the place.

It probably depends on whether the code which is executed is already in the instruction cache or must be fetched from the flash memory.