Skip to content

Instantly share code, notes, and snippets.

@thexeno
Last active January 7, 2020 16:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thexeno/24e3d7c8cbaba84bd3c3d028cfbc1522 to your computer and use it in GitHub Desktop.
Save thexeno/24e3d7c8cbaba84bd3c3d028cfbc1522 to your computer and use it in GitHub Desktop.
Test firmware for the Atmega328P to communicate with the WS2812B, in line with its relative datasheet Version 5. Two chunks of data are sent, one per pixel, considering the overhead of double buffering of the PWM module.
/*
* main.c
* Author : Enrico
*
*
*/
#define F_CPU 16000000UL
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/sfr_defs.h>
#include <stdio.h>
#define BIT_HIGH 242
#define BIT_LOW 249
uint32_t color_data = 0x00005888;
uint32_t buff_data;
uint8_t rx_dummy;
uint8_t serial_data[55]; // just some bytes more to debug boundaries
volatile bit_counter = 0;
ISR(TIMER0_OVF_vect)
{
TCCR0B &= ~((1 << CS00) | (1 << CS01) | (1 << CS02)); // stop timer
TCNT0 = 0; // reset timer
OCR0B = serial_data[bit_counter++]; // update dc here to keep last bit ending with value low
if (bit_counter <= 49) // compensate for first cycle at 255. 48 cycles in total for 2 chunks,
// +2 for initial low value and final double buffering which introduce latency of one ISR
{
TCCR0B |= 1; // continue transfer
}
// after 50 pulses pwm pin is low because the pwm cycle is restored to initial value and with no compare match since timer value is 0, hence pin is still low
}
int main(void)
{
uint16_t i, j;
uint8_t temp = 0;
//for now just fixed in a certain way (start at 0x0000)
temp = MCUCR;
/* Enable change of Interrupt Vectors */
MCUCR = temp|(1<<IVCE);
/* Move interrupts to 0x0002 */
MCUCR = temp & (~(1<<IVSEL));
// Enable data pin, initial low
DDRD |= (1 << PIND5);
PORTD &= ~(1 << PORTD5);
// Fast PWM, no prescaler, invert and output low with max duty cycle
// Is very important that the PWM will end the cycle with the pwm pin low and start with the pwm pin low, to assert the LED reset
TCCR0A |= ((1 << WGM00) | (1 << WGM01) | (1 << COM0B1) | (1 << COM0B0));
TCCR0B |= (1 << WGM02);
OCR0B = 255; // duty cycle, start low
// update in ISR after timer times out
TIMSK0 |= (1 << TOIE0);
OCR0A = 255; // period
sei();
// prepare data from the color_data
// buff so is possible to add more chunks and preserve the shifted data
buff_data = color_data;
for (i = 0; i < 24; i++)
{
if (buff_data & 0x01)
{
serial_data[i] = BIT_HIGH;
}
else
{
serial_data[i] = BIT_LOW;
}
buff_data = buff_data >> 1;
}
// second chunk to be shifted just to probe if the signal is correctly output from the LED
buff_data = 0x00505000;
for (; i < 49; i++) // the additional 49th bit will not be shifted out, but used to put the compare output low and triggering the reset on the WS2812
{
if (buff_data & 0x01)
{
serial_data[i] = BIT_HIGH;
}
else
{
serial_data[i] = BIT_LOW;
}
buff_data = buff_data >> 1;
}
color_data = 0x00800000;
TCCR0B |= 1;
while (1)
{
if (bit_counter > 49)
{
OCR0B = 255;
_delay_ms(50);
bit_counter = 0;
buff_data = color_data;
for (i = 0; i < 24; i++)
{
if (buff_data & 0x01)
{
serial_data[i] = BIT_HIGH;
}
else
{
serial_data[i] = BIT_LOW;
}
buff_data = buff_data >> 1;
}
buff_data = color_data >> 1;
for (; i < 49; i++)
{
if (buff_data & 0x01)
{
serial_data[i] = BIT_HIGH;
}
else
{
serial_data[i] = BIT_LOW;
}
buff_data = buff_data >> 1;
}
serial_data[i-1] = 255;
color_data = color_data >> 1;
if (color_data == 0)
{
color_data = 0x00800000;
}
TCCR0B |= 1;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment