neopixel crashing the the board?

Posted on
  • Is there a bug in the souce?

    https://github.com/espruino/Espruino/blo­b/master/libs/neopixel/jswrap_neopixel.c­#L246

    start = _getCycleCount();                        // get start time of this bit
    while (_getCycleCount()-start < t) ;             // busy-wait
    

    The function _getCycleCount() returns ccount with is a counter increasing steady from zero to max, but once max is reached it is set to zero again. In the case if start is set to max (or a value almost max) then _getCycleCount()-start will be always bigger than t.
    I'd say a fix would be to calculate the cycles to transfer a bit, subtract this value from the max ccount giving the compare value for ccount. And if _getCycleCount() is higher than the compare value just output low until _getCycleCount() is zero again. Keeping the output low for the duration of a very short time (a single bit) should not affect the neopixels at all, according to https://wp.josh.com/2014/05/13/ws2812-ne­opixels-are-not-so-finicky-once-you-get-­to-know-them/
    Or any other idea?

  • Can you please share what you are working on?

  • Actually a remote control for a rc car, a Willy's MB (JJRC Q65). The original has 5 different light modes and a complex switch to control them. I'll to use Neopixels instead of LEDs to simulate the light modes, and could also simulate a broken light. I tested it already with a led strip, but finally I want to switch to SK6805-2427. This, however requires a pcb and reflow or hot air soldering, as these LEDs have tiny contacts.
    An ESP8266 with a web server will become the receiver, to control servos, ESC and neopixels. The car (driving and lights) will be controlled over wifi with a mobile phone as controller. Finally I'll build a PCB with the ESP8266 12-F with logical level converters to 5V (needed for neopixels), but without USB port (to make it small).

  • Cool 😎

    So what is crashing ?

  • I suspect the neopixel code in post #1 to crash the ESP8266 if _getCycleCount() returns a value close or the exact maximum of ccount. And wondered what the best fix would be.

  • Mon 2019.08.12

    title    'neopixel crashing the the board?'

    Just had time to take a peek at this. Puzzled by the title. Is it accurate that connecting the Neopixels are causing the board to crash as is indicated, or is it speculation that the suggestion there is an overflow of that register, that might, and I add might, cause an issue with timing?



    An observation here. Many of us have used Neopixels successfully for around ~five years, and over the last two years since my introduction to the world of Espruino and observing forum posts, this would be an absolute first, if this were truly 'crashing' your board as you put it.


    If one takes a look at the function _getCycleCount() just the contents of that register are being returned.

    L197    asm volatile("rsr %0,ccount":"=a" (ccount));

    While I didn't create this, not do I completely understand how the register is used elsewhere in the source, it doesn't even appear that it would be an issue. My guess is the original author is resetting that register prior to data output to the pin. I'll S.W.A.G. it and guess in the SPI write mechanism.



    Using this case:

    L210  uint8_t tOne = 90; // one bit, high typ 800ns

    The value of 't' is equal to 90 and has an equivalent duration of ~0.8msec

    L247   while (_getCycleCount()-start < t) ; // busy-wait

    Assigning a value to start, then immediately taking another reading will make the result of that comparison extremely small. Then we wait in the loop counting up towards 90 the 800nsec equivalent. Clocking at the speeds indicated, even if there were overflow (my guess what is being referred to, ref. max value), the interval to wait for that condition to clear won't even be perceptibly noticed. We are looking at an interval of one in one thousand! In fact, being close to an overflow condition should only add an additional loop or two, as when the overflow occurs, the equation approximately a ~zero count is evaluated and this is also less than the 90 or 800nsec comparison value. Hardly noticeable. Comment at L222 explains why the bits need to initially be set to zero. I don't see an issue.


    Have you looked at the bits being clocked out with a logic analyzer?

  • Could it be that the crash is power supply related... typical for (any) ESP-8266 boards (and driving 'nice' modern LED loads)... Could you elaborate a bit more on the type of crash you experience?

  • @Robin: My bad, it would not hang and crash the ESP8266, but the HIGH or LOW pulse duration would be too short. Most likely resulting in a wrong color or an incomplete lit strip.

  • So, we may file this under speculation then?

    'pulse duration would be too short'

    I don't believe this applies as the pulse train is a fixed duration based on four bits each. Looking for where I read that. Those two lines apply to a waiting state.

    Related to this creating the pulse train, and likely how Neopixels were done before the tutorials.

    http://www.espruino.com/Reference#t_l_SP­I_send4bit

    One reference by @allObjects #2 but not the one I'm after:

    http://forum.espruino.com/conversations/­330532/

    We are all end users acquiring new skills while experimenting with our new ideas.

    One can certainly learn the hows and whys while peeking under the covers. But it was the post title along with the explanations that just weren't adding up.

    I'm curious what prompted the initial post. It struck me as a bit odd to single out those two lines of code, as most (myself included) typically aren't concerned with how things work under the hood. I actually hadn't looked at the Neopixel code until this thread. What sent me off in detective land was based on a daily hammering on the limits with Neopixels as I heard rumblings that Javascript was never going to be fast enough. I put that noise to rest over a year ago building a module to produce animations using the WS2812B x 60 strips and running on the Pico. Had to take a break as the source was growing just too large to run on the MDBT42Q and Pixl even with 'inlineC' sections. The limiting factor was more of the incorrect use of setInterval(). Produced some amazing animations along with sequencing and color changing. As there hasn't been a recent demand for examples, I may get back to a tutorial this winter.

  • Hi Robin

    for me it looks like it'll block the while loop. You said the C could would be sending just a very short pulse instead, it's only a minor bug.

    Just to expain my understanding of the code I've attached a simplified JavaScript program emulating the code:

    • A while loop that doesn't contain any code itself (just assume it would transmit a bit)
    • Said while loop shall complete after 10 ticks (t=10)
    • The program would repeat forever (as it was transmitting an infinite number of bits)

    The program works fine, until ccount is reset back to 0. At this point the while loop hangs, it never quits. This would crash the ESP8266 (reset triggered by wifi/watchdog).

    A fix for the JavaScript program would be to uncomment line 17 ("ccountChk = 255-t-5;", with "t" being the waiting time and "5" being an estimated amount of ticks needed to run the program code itself, with good margin). Since the fix itself takes some time to process it might be ok to change the while loop counter "t" from "10" to "9" (in this example it doesn't makes sense. But in the original code you can reduce the while counter due to the delay introduced by a similar fix).
    Anyway, before writing a fix I think it's best to get a common understanding if there's a bug, and what the best fix would be. I wouldn't want to add a speculative fix for a potential bug that isn't fully understood.

    //
    //step debugger
    //
    
    //init variables
    var t="na";
    var start="na";
    var pc = 0;          //program counter
    var ccount = 0;      //a 8 bit ccount implementaton
    var ccountMax = 255; //a 8 bit ccount implementaton
    var program = [];    //program code, addressed by program counter
    var ccountChk = 255-10-5;
    
    //program code
    program[0] = "t = 10;";
    program[1] = "start = ccount;";               //start=_getCycleCount();
    //program[1] = "if (ccount>ccountChk) { pc--; start =-1; } else { start = ccount; };"; 
    program[2] = "if (ccount-start < t) { pc--; } //while(_getCycleCount()-start < t);";
    program[3] = "pc=0; //while loop completed, repeat the program (to send next bit)";
    
    //step debugger, executes one line of the program code each call
    stepDebugger = function() {
      console.log("A PC=" + pc + ", ccount=" + ccount + ", t=" + t + ", start=" + start + ", program: " + program[pc]);
      eval(program[pc]);  //execute one line of code
      pc++;               //set pc to next line
      if (pc > program.length) { console.log('program ended'); }
      ccount++;           //increase ccount, reset to zero if it's countMax
      if (ccount>ccountMax) { ccount=0; }
      console.log("B PC=" + pc + ", ccount=" + ccount + ", t=" + t);
    }
    
    //automatically call the debugger every 500ms
    stop = setInterval(stepDebugger, 500);
    
    //clearInterval(stop)
    //the while loop (program[2]) exits every 10 ticks.
    //bug: once ccount==start=253 the while loop "hangs" forever
    
  • @allObjects

    Could it be that the crash is power supply related... typical for (any) ESP-8266 boards (and driving 'nice' modern LED loads)... Could you elaborate a bit more on the type of crash you experience?

    The question is related to the C source only, not any practical application or crash.

  • I'd propose the fix the code like this, however I didn't touch C for ages.

    Line 1: set start = 0 for the first run (cache loading)
    Line 23: wait if ccount>(0xFFFFFFFF-1000)
    Line 24: set start = ccount, in the range [0 .. (0xFFFFFFFF-1000)]

    I selected the number 1000 just to show the case, in reality it should correspond to the number of ticks needed to transfer a single bit (tOne + tLow (incl. program overhead)). Maybe 300 is more reasonable.

    
      start =  0;                     //fast pre-roll
      tZero = 63;                  //T0H = 400ns for WS2812B
      tOne = 121;                  //T1H = 800ns for WS2812B
      tLow =  181;                //PERIOD = 1200ns for WS2812B
      
      ets_intr_lock();                // disable most interrupts
      while(1) {
        GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask);  // Set high
        uint32_t t;
        if (pix & mask) t = tOne;
        else            t = tZero;
        while (_getCycleCount()-start < t) ;             // busy-wait (high)
        GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask);  // Set low
        start = _getCycleCount();
        while (_getCycleCount()-start < tLow) ;          // busy-wait (low)
        mask = mask>>1;                                  // Next bit
        if (mask == 0) {                                 // Next byte
          if (p >= end) break;                           // at end, we're done
          pix = *p++;
          mask = 0x80;
        }
        pinMask = _BV(pin);                              // correct pinMask after pre-roll
        while (_getCycleCount() > 0xFFFFFC17) ;          // ccount overflow handler
        start = _getCycleCount();                        // get start time of this bit
      }
      ets_intr_unlock();
    
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

neopixel crashing the the board?

Posted by Avatar for maze1980 @maze1980

Actions