Loading files
This 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
#endiff1596c8d0This file is pinned to the selected release. Download the original to open it in your design tool.