hubmartin / WS2812B_STM32F4

WS2812 DMA library with low RAM needs. Up to 16 paralel outputs and thousands of LEDs on each of them
MIT License
116 stars 33 forks source link

WS2812B_NUMBER_OF_LEDS off by 2 error #1

Closed solexious closed 7 years ago

solexious commented 7 years ago

When testing the lib out with my 10 long strips I need to set WS2812B_NUMBER_OF_LEDS to be 12 for all 10 to be lit and working.

Not yet tried this with a longer strip. Still working though the library so not sure of what is causing it yet.

danilobso commented 7 years ago

@solexious Any update on this problem? I'm experiencing it too.

hubmartin commented 7 years ago

Thanks for reminder @danilobso. Fixed. But the LED strip length has to be always multiple of two.

jcrademacher commented 4 years ago

Hi Martin. I'm experiencing a similar issue. When I try to address a certain LED in a strip, it works up until the 8th strip, where the LED 2 places after the one I want to address lights up in the same color. There is some more strange behavior that is hard to describe so I've posted a picture. Note that in the picture every other strip is connected to the data line, with the strips that aren't lit up being an extension of the strips connected to the MCU.

IMG_6975

The code that is doing this is below:

while(1) {
  uint8_t led, strip;

  for(strip = 0; strip < WS2812_BUFFER_COUNT; ++strip) {
      frame_buffer[strip][4*3 + 0] = 0;
      frame_buffer[strip][4*3 + 1] = 100;
      frame_buffer[strip][4*3 + 2] = 0;
  }

  if(ws2812b.transferComplete) {
    ws2812b.startTransfer = 1;
    ws2812b_handle();
  }
}

WS2812_BUFFER_COUNT = 14. My MCU is a STM32F401CB. You can also see the light fades away down the line, then doesn't show up for the last 2 strips, but the LED 2 places away does.

hubmartin commented 4 years ago

Hello, what is the core frequency the MCU is running at? What optimization level -O... do you use to compile code? Do you have an oscilloscope so you can investigate the signal on the "off by two" strips? I'm looking for a jitter to understand if the signal/color is coming late from MCU (timing IRQ/DMA issue) or if the jitter is not present the it is probably some bug in code.

jcrademacher commented 4 years ago

Hi Martin, my clock is at 72MHz, and the optimization flag is -Og. I can grab an oscilloscope and measure those waveforms later today.

hubmartin commented 4 years ago

I'm afraid that the slow frequency and lot of outputs may exhaust the DMA bit-juggling in the DMA IRQs. Can you try to lower number of outputs or overclock MCU a bit? I've developed and tested 16 outputs witn F407 MCU and 168 MHz. What can happen is that the IRQ routine does not have time co finnish and it is triggered again. This could be debugged by LED outputs which toggle ON when DMA half or DMA full IRQ is running. Take a look at these defines in the code. https://github.com/hubmartin/WS2812B_STM32F4/blob/master/Src/ws2812b/ws2812b.c#L60 https://github.com/hubmartin/WS2812B_STM32F4/blob/master/Src/ws2812b/ws2812b.c#L398

Check the waveform here https://github.com/hubmartin/WS2812B_STM32F4#bit-banding-for-bit-juggling-in-the-irq When the blue debug pin is high, then the MCU is in IRQ. The more outputs you enable, the more time it takes in IRQ. If the debug output is (almost) all the time in high, it means the code is not fast enough. This is the cons of this library. It is not perfect for everyone. It uses less RAM but needs more processing. https://github.com/hubmartin/WS2812B_STM32F4/blob/master/README.md#pros-and-cons

hubmartin commented 4 years ago

In my F103 port (which is Cortex M3, but bit-banding is the same) are some benchmarks and at 72 MHz it could drive 10 paralel outputs. Which is in similar spot you have issues. https://github.com/hubmartin/WS2812B_STM32F103#stm32f103-benchmark

jcrademacher commented 4 years ago

Hi Martin. I maxed (but not overclocked) the MCU to 84MHz and I dropped WS2812_BUFFER_COUNT down to 3 and the problem is (sort of) fixed. You're right that the DMA IRQ is taking a long time, but it doesn't seem to be getting cut short, unless I'm interpreting these plots wrong. The first picture is the DEBUG pin triggered on entering the IRQ for 3 strips, taking about 7.6us. The second is for 14 strips taking about 29.1us.

IMG_6977

IMG_6979

The original problem I had is fixed (I would assume because of the clock increase). However, I still have a problem where I increment the led count on the strip and display one by one each led with a 500ms delay, and the 2nd and 3rd leds will turn on at the same time after the 1st LED turns on, even with only 3 strips. The rest of the LEDs turn on one by one correctly. This is strange to me because if I simply try to turn on the 2nd and 3rd LEDs individually, they work fine. It only happens when I load the 2nd into frame buffer, delay, transfer, load the third into frame buffer, then delay and transfer. The code that is doing this is below:

uint8_t led, strip;

  for(strip = 0; strip < WS2812_BUFFER_COUNT; ++strip) {
    for(led = 0; led < WS2812B_NUMBER_OF_LEDS; ++led) {
      frame_buffer[strip][led*3 + 0] = 0; //* BRIGHTNESS(ADC_values[2]));
      frame_buffer[strip][led*3 + 1] = 100; //* BRIGHTNESS(ADC_values[2]));
      frame_buffer[strip][led*3 + 2] = 0; //* BRIGHTNESS(ADC_values[2]));

      HAL_Delay(500);
      if(ws2812b.transferComplete) {
        ws2812b.startTransfer = 1;
        ws2812b_handle();
      }
    }
  }

Thank you so much for the help!

jcrademacher commented 4 years ago

@hubmartin Moving the delay after the transfer fixed this problem. All is well now at 84MHz and 14 strips, although the DMA IRQ is consuming quite a lot of CPU time.

hubmartin commented 4 years ago

I tested your code and it seems like you have HAL_Delay() on the wrong line. I can replicate the bug with your code on my hardware. The fix is to move delay to the right place.

    uint8_t led, strip;

      for(strip = 0; strip < WS2812_BUFFER_COUNT; ++strip) {
        for(led = 0; led < WS2812B_NUMBER_OF_LEDS; ++led) {
            frameBuffer[strip][led*3 + 0] = 0; //* BRIGHTNESS(ADC_values[2]));
            frameBuffer[strip][led*3 + 1] = 100; //* BRIGHTNESS(ADC_values[2]));
            frameBuffer[strip][led*3 + 2] = 0; //* BRIGHTNESS(ADC_values[2]));

          if(ws2812b.transferComplete) {
            ws2812b.startTransfer = 1;
            ws2812b_handle();
          }

          HAL_Delay(500);
        }
      }

The ws2812b data transfer is asynchrnonous in IRQ and DMA and there is no double buffering. Your original code set framebuffer to turn LED1, waited 500ms then started the transfer. But you didn't wait on transfer finish and your FOR loop immediatelly rewrites the data to second LED just before the DMA in the background finishes the first transfer.

I would also suggest to wait until the ws2812b.transferComplete to be 1 so you can be sure no DMA is running in the background.

https://photos.app.goo.gl/vEXLv4pfJS6ENeyj7

hubmartin commented 4 years ago

@jcrademacher

hubmartin commented 4 years ago

Also the 14 strips seem to be really running on the edge based on your scope trace. If your code uses other IRQs which will slow down MCU then the LEDs will flicker. I would not be afraid of overclocking 84 MHz parts to 100 MHz or more. I managed to overclock 24 MHz F100 part to 48 MHz with no issue. Don't worry about burning or frying MCU down, this is not PC :) Or try better GCC optimalization level and compare the scope debug output.

hubmartin commented 4 years ago

@jcrademacher Aha, now I see we figured that out almost in the same time :)