0
\$\begingroup\$

I was trying to light up WS2812B and it works fine when given RGB code something like this (255, 145, 67). But when I send code like (255, 67, 67) to this diode it doesn't turn on. Similarly if I want to get pure white color I cant send code (255,255,255) but instead I have to send (255,254,253) so they all would be different.

This doesn't seem to be a software problem for me but here is the program code. I'm using Atmel's ATmega328p mcu at 8MHz and code is compiled with avr-gcc compiler. Would appreciate any suggestions how to fix this!

#include <avr/io.h>
#include <util/delay.h>

#define DATA_PORT PORTD
#define DATA_PIN PD0
#define DATA_DDR DDRD


void fill_array(int* arr, int color)
{
    for(int i = 0; i < 8; ++i)
    arr[i] = color & (1 << (7 - i));
}

void set_color(int* arr, int red, int green, int blue)
{
    fill_array(arr, green);
    fill_array(&arr[8], red);
    fill_array(&arr[16], blue);
}

int main(void)
{

    int rgb_array[24];
    set_color(rgb_array, 0,255,0);

    DATA_DDR = (1 << DATA_PIN);
    DATA_PORT &= ~(1 << DATA_PIN);
    _delay_us(100);

    for(int i = 0; i < 24; ++i)
        if(rgb_array[i]){    //send one
                           //1 clock cycle is 0.125us           
            DATA_PORT = 1; __builtin_avr_delay_cycles(6); DATA_PORT = 0;
        }
        else{  //send zero
            DATA_PORT = 1; __builtin_avr_delay_cycles(3); DATA_PORT = 0;
        }

    DATA_PORT &= ~(1 << DATA_PIN); //reset signal
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Check the end of your code. The 'for' code is missing the closing '}'. That's not the reason for your question though. \$\endgroup\$ – Transistor Nov 13 '17 at 18:08
  • 1
    \$\begingroup\$ it's missing an opening { too \$\endgroup\$ – BeB00 Nov 13 '17 at 18:13
  • 1
    \$\begingroup\$ Thanks for checking out my question but that for also doesn't have opening '{' so it doesn't need closing '}' in this case. I just wanted the code to be shorted before posting here \$\endgroup\$ – etrusks Nov 13 '17 at 18:14
  • \$\begingroup\$ In PWM mode counter has to go up all the way to 0xff so its 255 clock cycles but running chip at 8MHz its not an option coz each clock cycle is 0.125us but generated "1's" length has to be as short as 0.4us. I was trying to use CTC mode but frequency is too low because I cant change TOP value within the interrupt to 3 without timer being already past this value by the time I exit the interrupt. \$\endgroup\$ – etrusks Nov 13 '17 at 19:09
  • \$\begingroup\$ @etrusks, you are correct about the brackets, but if you ever add anything to the loop, you'll have to remember to add the brackets, or it could completely change the way the loop works. To add clarity and not get yourself into a bind later, it's good practice to have the brackets in there. \$\endgroup\$ – computercarguy Nov 14 '17 at 3:08
1
\$\begingroup\$

I suspect the problem is an electrical one: The WS2812B dim by using PWM, i.e. switching LEDs on and off. Switching causes the current draw to vary and causes noise on the power line. Now, if two of them have the same setting and switch at the same time, noise adds up causing an error in the diodes' controller. Try adding a 100 nF (or similar) capacitor close to the LED between VCC and GND.

Regarding timing, the WS2812B are much easier to control than the timing diagram suggests. My experience is, as long as you make sure that the high period is between 0.25 and 0.5 µs for a '0' and longer than 0.7 µs for a '1'; and the low period is shorter than the reset time, everything works well.

The following is unproven, but I strongly believe that the logic inside the controller to receive data is a simple "wait for input going high. wait 0.6 µs. check if input is still high ('1') or low again ('0'). repeat."

\$\endgroup\$
  • \$\begingroup\$ Thanks you very much man, this sounds good! I'll only have the chance to test it out sometime in wednesday coz the project is not in my house. I'll let everybody know how it goes than, but I really appreciate your answer man! :) \$\endgroup\$ – etrusks Nov 13 '17 at 20:41
  • \$\begingroup\$ Be sure to change "ns to us" @asdfex and consider that the WS2812B has a 800kHz clock \$\endgroup\$ – Sunnyskyguy EE75 Nov 18 '17 at 22:28
  • \$\begingroup\$ @TonyStewart.EEsince'75 Thanks for pointing out the ns/µs mistake. Where did you get the information about a 800 kHz clock? Datasheet just states a 800 kBit/s data rate. (Interestingly the data sheet is not very consistent and in an other place they state it can be as high as 1.5 MHz, which doesn't make sense given the other timings... "TH+TL=1.25μs±600ns") \$\endgroup\$ – asdfex Nov 19 '17 at 11:16
  • \$\begingroup\$ I read that from internal datasheet notes, but application is probably flexible using delay line one shot comparator on the high pulse leaving margin for repeater on low side. so higher data rate is possible. So edge triggered one shot decides inside if it is a 1 or 0 based on + pulse width alone. \$\endgroup\$ – Sunnyskyguy EE75 Nov 19 '17 at 16:23
0
\$\begingroup\$

enter image description here

Figure 1. The WS2812B timing diagram.

Looking at your code it appears that your code allows pulses to be multiples of 0.125 µs. This will give you timing posibilities of 0.125, 0.250, 0.275, 0.500, 0.625, 0.750, 0.875, 1.000 and 1.125 µs. You can see that, while the 150 ns tolerence caters for this, that none of the timings is ideal.

_delay_us(100);

for(int i = 0; i < 24; ++i)
    if(rgb_array[i]){    //send one
                       //1 clock cycle is 0.125us           
        DATA_PORT = 1; __builtin_avr_delay_cycles(6); DATA_PORT = 0;
    }
    else{  //send zero
        DATA_PORT = 1; __builtin_avr_delay_cycles(3); DATA_PORT = 0;
    }
}

My other suspicion is that you have delays for the '1' signals but have no delay for the '0' signal. I expected:

    if(rgb_array[i]){    //send one
        DATA_PORT = 1; 
        __builtin_avr_delay_cycles(6);     //T1H
        DATA_PORT = 0;
        __builtin_avr_delay_cycles(3);     //T1L
    }
    else{  //send zero
        DATA_PORT = 1; 
        __builtin_avr_delay_cycles(3);     //T0H
        DATA_PORT = 0;
        __builtin_avr_delay_cycles(6);     //T0L
    }

Have I spotted the error?

\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer man! Yes i know about that but the for loop itself takes time as well as reading value from the array and if/else statement so actually delay for 0 is already longer than it should be. But it still works except for those cases when 2 of RGB values are equal. Thats the weird part \$\endgroup\$ – etrusks Nov 13 '17 at 18:43
  • \$\begingroup\$ I was actually watching video before making this project and the guy in the video pointed out exactly this thing you just mentioned using oscilloscope. Values for "1" were close to ok but the timing for "0" was even 3 times longer than it should be for signal "1" and it still worked. He said he doesn't know why but WS2812B still accepted the signal probably because the high signal had about the right timing \$\endgroup\$ – etrusks Nov 13 '17 at 18:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.