Close

Got some WS2812 LEDs working

A project log for AVRxDA28 Target

A Dev board for the new AVR DA series (and even DB series) microcontrollers

mcunerdmcu_nerd 06/25/2021 at 20:030 Comments

The nice thing about these chips is that the internal oscillator can run up to 24MHz. I decided to take a crack at making a function to control some WS2812 LEDs with just C code (the the exception of calling _delay_us().)  Most libraries for driving these LEDs with AVR chips make use of assembly because timing tends to be an issue with relatively slow clock speeds. Since there is no separate input clock pin, the pulse lengths determine if a bit is a one or zero. Below is some code I whipped up to just turn on the green LED at full brightness:

#define F_CPU 24000000UL

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

#define zero_high .338
#define zero_low .912

#define one_high .68
#define one_low .57

int main(void){

_PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_24M_gc);

PORTC.DIR|=PIN3_bm;
_delay_ms(2000);
while(1){
//for 1
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);
PORTC.OUT|=PIN3_bm;
_delay_us(one_high);
PORTC.OUT=0;
_delay_us(one_low);

//for zero
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
//last color
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
PORTC.OUT|=8;
_delay_us(zero_high);
PORTC.OUT=0;
_delay_us(zero_low);
_delay_us(300);
}


return(0);
}

The code kinda worked. All three LEDs came on at full brightness.  The root problem was that the chip was interpreting every bit as a "1".  I was curious that if doing bit-wise operations (such as PORTC.OUT|=8;)   was taking up too many cycles so I removed the bit-wise operations and tried instead just setting the entire port register to a number (PORTC.OUT=8;). That worked!  I then decided to make the code a bit more usable by creating an actual function to set colors and once again all three LEDs came on at full brightness. 

I figured I could compensate for the extra cycles used by reducing the delay values.  I stumbled on this article here and used the minimum values listed for the updated simplified timing constraints table and it worked.  I did try adding back in the bit-wise operations but had the same issue again. The solution is likely to reduce delays a bit more to compensate.  I got my function to work with two WS2812 chained together.  The code of this writing uses pin 16, but due to not using bit-wise operations, it interferes with pin 17.  I'll post the fairly rough code on the project page.  It would be nice if these chips had a separate clock pin, but since they're so cheap it makes sense to spend the effort in to putting up with the timing annoyances.

Discussions