Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
7ebe18978This file is pinned to the selected release. Download the original to open it in your design tool.
/*
* Core driver routines for WS2812 LED strips
* This is a stripped down and modified version of cpldcpu's light_ws2812 library.
*
* Author: Tim (cpldcpu@gmail.com), Patrick Pedersen (ctx.xda@gmail.com)
*
* License: GNU GPL v2+ (see License.txt)
*/
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#include "config.h"
#include "ws2812.h"
#if STRIP_TYPE == WS2812
/*
This routine writes an array of bytes with RGB values to the Dataout pin
using the fast 800kHz clockless WS2811/2812 protocol.
*/
// Timing in ns
#define w_zeropulse 350
#define w_onepulse 900
#define w_totalperiod 1250
// Fixed cycles used by the inner loop
#define w_fixedlow 3
#define w_fixedhigh 6
#define w_fixedtotal 10
// Insert NOPs to match the timing, if possible
#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000)
#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000)
#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000)
// w1 - nops between rising edge and falling edge - low
#define w1 (w_zerocycles-w_fixedlow)
// w2 nops between fe low and fe high
#define w2 (w_onecycles-w_fixedhigh-w1)
// w3 nops to complete loop
#define w3 (w_totalcycles-w_fixedtotal-w1-w2)
#if w1>0
#define w1_nops w1
#else
#define w1_nops 0
#endif
// The only critical timing parameter is the minimum pulse length of the "0"
// Warn or throw error if this timing can not be met with current F_CPU settings.
#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
#if w_lowtime>550
#error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
#elif w_lowtime>450
#warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
#warning "Please consider a higher clockspeed, if possible"
#endif
#if w2>0
#define w2_nops w2
#else
#define w2_nops 0
#endif
#if w3>0
#define w3_nops w3
#else
#define w3_nops 0
#endif
#define w_nop1 "nop \n\t"
#define w_nop2 "rjmp .+0 \n\t"
#define w_nop4 w_nop2 w_nop2
#define w_nop8 w_nop4 w_nop4
#define w_nop16 w_nop8 w_nop8
static uint8_t _sreg_prev, _maskhi, _masklo;
/* ws2812_prep_tx
* --------------
* Description:
* Prepares for a data transmission to the WS2812 strip.
* Always call this function before calling ws2812_tx_byte()!
*/
void ws2812_prep_tx()
{
_masklo = ~WS2812_DIN_MSK & WS2812_DIN_PORT;
_maskhi = WS2812_DIN_MSK | WS2812_DIN_PORT;
_sreg_prev=SREG;
cli();
}
/* ws2812_wait_rst
* ---------------
* Description:
* Waits for the WS2812 to reset.
*/
void ws2812_wait_rst()
{
#if defined(WS2812_RESET_TIME) && WS2812_RESET_TIME > 0
_delay_us(WS2812_RESET_TIME);
#endif
}
/* ws2812_end_tx
* -------------
* Description:
* Ends data transmission with the WS2812 by
* restoring the status register to its previous state
* and re-enabling interrupts. Always call this function
* after data transmission is complete!
*/
void ws2812_end_tx()
{
SREG=_sreg_prev;
ws2812_wait_rst();
sei();
}
/* ws2812_tx_byte
* --------------
* Description:
* Transmitts a byte of data to the WS2812.
* Toggling this function three times consecutively, sets a pixel.
* After every third consecutive transmission, the next pixel is
* written to. Transmissions MUST take place consecutively, else
* there's a risk that the timing fails and the strip will either
* be written to incorrectly, or not be written to at all.
*/
#pragma GCC push_options
#pragma GCC optimize ("O0")
void ws2812_tx_byte(uint8_t data)
{
uint8_t ctr;
asm volatile(
" ldi %0,8 \n\t"
"loop%=: \n\t"
" st X,%3 \n\t" // '1' [02] '0' [02] - re
#if (w1_nops&1)
w_nop1
#endif
#if (w1_nops&2)
w_nop2
#endif
#if (w1_nops&4)
w_nop4
#endif
#if (w1_nops&8)
w_nop8
#endif
#if (w1_nops&16)
w_nop16
#endif
" sbrs %1,7 \n\t" // '1' [04] '0' [03]
" st X,%4 \n\t" // '1' [--] '0' [05] - fe-low
" lsl %1 \n\t" // '1' [05] '0' [06]
#if (w2_nops&1)
w_nop1
#endif
#if (w2_nops&2)
w_nop2
#endif
#if (w2_nops&4)
w_nop4
#endif
#if (w2_nops&8)
w_nop8
#endif
#if (w2_nops&16)
w_nop16
#endif
" brcc skipone%= \n\t" // '1' [+1] '0' [+2] -
" st X,%4 \n\t" // '1' [+3] '0' [--] - fe-high
"skipone%=: " // '1' [+3] '0' [+2] -
#if (w3_nops&1)
w_nop1
#endif
#if (w3_nops&2)
w_nop2
#endif
#if (w3_nops&4)
w_nop4
#endif
#if (w3_nops&8)
w_nop8
#endif
#if (w3_nops&16)
w_nop16
#endif
" dec %0 \n\t" // '1' [+4] '0' [+3]
" brne loop%=\n\t" // '1' [+5] '0' [+4]
: "=&d" (ctr)
: "r" (data), "x" ((uint8_t *) &WS2812_DIN_PORT), "r" (_maskhi), "r" (_masklo)
);
}
#pragma GCC pop_options
#endif