mirror of
https://github.com/gunner47/GyverLamp.git
synced 2025-10-25 05:40:54 +03:00
Обновление библиотек
This commit is contained in:
389
libraries/FastLED/platforms/arm/common/m0clockless.h
Normal file
389
libraries/FastLED/platforms/arm/common/m0clockless.h
Normal file
@@ -0,0 +1,389 @@
|
||||
#ifndef __INC_M0_CLOCKLESS_H
|
||||
#define __INC_M0_CLOCKLESS_H
|
||||
|
||||
struct M0ClocklessData {
|
||||
uint8_t d[3];
|
||||
uint8_t e[3];
|
||||
uint8_t adj;
|
||||
uint8_t pad;
|
||||
uint32_t s[3];
|
||||
};
|
||||
|
||||
|
||||
template<int HI_OFFSET, int LO_OFFSET, int T1, int T2, int T3, EOrder RGB_ORDER, int WAIT_TIME>int
|
||||
showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, uint32_t num_leds, struct M0ClocklessData *pData) {
|
||||
// Lo register variables
|
||||
register uint32_t scratch=0;
|
||||
register struct M0ClocklessData *base = pData;
|
||||
register volatile uint32_t *port = _port;
|
||||
register uint32_t d=0;
|
||||
register uint32_t counter=num_leds;
|
||||
register uint32_t bn=0;
|
||||
register uint32_t b=0;
|
||||
register uint32_t bitmask = _bitmask;
|
||||
|
||||
// high register variable
|
||||
register const uint8_t *leds = _leds;
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
pData->s[0]++;
|
||||
pData->s[1]++;
|
||||
pData->s[2]++;
|
||||
#endif
|
||||
asm __volatile__ (
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// asm macro definitions - used to assemble the clockless output
|
||||
//
|
||||
".ifnotdef fl_delay_def;"
|
||||
#ifdef FASTLED_ARM_M0_PLUS
|
||||
" .set fl_is_m0p, 1;"
|
||||
" .macro m0pad;"
|
||||
" nop;"
|
||||
" .endm;"
|
||||
#else
|
||||
" .set fl_is_m0p, 0;"
|
||||
" .macro m0pad;"
|
||||
" .endm;"
|
||||
#endif
|
||||
" .set fl_delay_def, 1;"
|
||||
" .set fl_delay_mod, 4;"
|
||||
" .if fl_is_m0p == 1;"
|
||||
" .set fl_delay_mod, 3;"
|
||||
" .endif;"
|
||||
" .macro fl_delay dtime, reg=r0;"
|
||||
" .if (\\dtime > 0);"
|
||||
" .set dcycle, (\\dtime / fl_delay_mod);"
|
||||
" .set dwork, (dcycle * fl_delay_mod);"
|
||||
" .set drem, (\\dtime - dwork);"
|
||||
" .rept (drem);"
|
||||
" nop;"
|
||||
" .endr;"
|
||||
" .if dcycle > 0;"
|
||||
" mov \\reg, #dcycle;"
|
||||
" delayloop_\\@:;"
|
||||
" sub \\reg, #1;"
|
||||
" bne delayloop_\\@;"
|
||||
" .if fl_is_m0p == 0;"
|
||||
" nop;"
|
||||
" .endif;"
|
||||
" .endif;"
|
||||
" .endif;"
|
||||
" .endm;"
|
||||
|
||||
" .macro mod_delay dtime,b1,b2,reg;"
|
||||
" .set adj, (\\b1 + \\b2);"
|
||||
" .if adj < \\dtime;"
|
||||
" .set dtime2, (\\dtime - adj);"
|
||||
" fl_delay dtime2, \\reg;"
|
||||
" .endif;"
|
||||
" .endm;"
|
||||
|
||||
// check the bit and drop the line low if it isn't set
|
||||
" .macro qlo4 b,bitmask,port,loff ;"
|
||||
" lsl \\b, #1 ;"
|
||||
" bcs skip_\\@ ;"
|
||||
" str \\bitmask, [\\port, \\loff] ;"
|
||||
" skip_\\@: ;"
|
||||
" m0pad;"
|
||||
" .endm ;"
|
||||
|
||||
// set the pin hi or low (determined by the offset passed in )
|
||||
" .macro qset2 bitmask,port,loff;"
|
||||
" str \\bitmask, [\\port, \\loff];"
|
||||
" m0pad;"
|
||||
" .endm;"
|
||||
|
||||
// Load up the next led byte to work with, put it in bn
|
||||
" .macro loadleds3 leds, bn, rled, scratch;"
|
||||
" mov \\scratch, \\leds;"
|
||||
" ldrb \\bn, [\\scratch, \\rled];"
|
||||
" .endm;"
|
||||
|
||||
// check whether or not we should dither
|
||||
" .macro loaddither7 bn,d,base,rdither;"
|
||||
" ldrb \\d, [\\base, \\rdither];"
|
||||
" lsl \\d, #24;" //; shift high for the qadd w/bn
|
||||
" lsl \\bn, #24;" //; shift high for the qadd w/d
|
||||
" bne chkskip_\\@;" //; if bn==0, clear d;"
|
||||
" eor \\d, \\d;" //; clear d;"
|
||||
" m0pad;"
|
||||
" chkskip_\\@:;"
|
||||
" .endm;"
|
||||
|
||||
// Do the qadd8 for dithering -- there's two versions of this. The m0 version
|
||||
// takes advantage of the 3 cycle branch to do two things after the branch,
|
||||
// while keeping timing constant. The m0+, however, branches in 2 cycles, so
|
||||
// we have to work around that a bit more. This is one of the few times
|
||||
// where the m0 will actually be _more_ efficient than the m0+
|
||||
" .macro dither5 bn,d;"
|
||||
" .syntax unified;"
|
||||
" .if fl_is_m0p == 0;"
|
||||
" adds \\bn, \\d;" // do the add
|
||||
" bcc dither5_1_\\@;"
|
||||
" mvns \\bn, \\bn;" // set the low 24bits ot 1's
|
||||
" lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
|
||||
" dither5_1_\\@:;"
|
||||
" nop;" // nop to keep timing in line
|
||||
" .else;"
|
||||
" adds \\bn, \\d;" // do the add"
|
||||
" bcc dither5_2_\\@;"
|
||||
" mvns \\bn, \\bn;" // set the low 24bits ot 1's
|
||||
" dither5_2_\\@:;"
|
||||
" bcc dither5_3_\\@;"
|
||||
" lsls \\bn, \\bn, #24;" // move low 8 bits to the high bits
|
||||
" dither5_3_\\@:;"
|
||||
" .endif;"
|
||||
" .syntax divided;"
|
||||
" .endm;"
|
||||
|
||||
// Do our scaling
|
||||
" .macro scale4 bn, base, scale, scratch;"
|
||||
" ldr \\scratch, [\\base, \\scale];"
|
||||
" lsr \\bn, \\bn, #24;" // bring bn back down to its low 8 bits
|
||||
" mul \\bn, \\scratch;" // do the multiply
|
||||
" .endm;"
|
||||
|
||||
// swap bn into b
|
||||
" .macro swapbbn1 b,bn;"
|
||||
" lsl \\b, \\bn, #16;" // put the 8 bits we want for output high
|
||||
" .endm;"
|
||||
|
||||
// adjust the dithering value for the next time around (load e from memory
|
||||
// to do the math)
|
||||
" .macro adjdither7 base,d,rled,eoffset,scratch;"
|
||||
" ldrb \\d, [\\base, \\rled];"
|
||||
" ldrb \\scratch,[\\base,\\eoffset];" // load e
|
||||
" .syntax unified;"
|
||||
" subs \\d, \\scratch, \\d;" // d=e-d
|
||||
" .syntax divided;"
|
||||
" strb \\d, [\\base, \\rled];" // save d
|
||||
" .endm;"
|
||||
|
||||
// increment the led pointer (base+6 has what we're incrementing by)
|
||||
" .macro incleds3 leds, base, scratch;"
|
||||
" ldrb \\scratch, [\\base, #6];" // load incremen
|
||||
" add \\leds, \\leds, \\scratch;" // update leds pointer
|
||||
" .endm;"
|
||||
|
||||
// compare and loop
|
||||
" .macro cmploop5 counter,label;"
|
||||
" .syntax unified;"
|
||||
" subs \\counter, #1;"
|
||||
" .syntax divided;"
|
||||
" beq done_\\@;"
|
||||
" m0pad;"
|
||||
" b \\label;"
|
||||
" done_\\@:;"
|
||||
" .endm;"
|
||||
|
||||
" .endif;"
|
||||
);
|
||||
|
||||
#define M0_ASM_ARGS : \
|
||||
[leds] "+h" (leds), \
|
||||
[counter] "+l" (counter), \
|
||||
[scratch] "+l" (scratch), \
|
||||
[d] "+l" (d), \
|
||||
[bn] "+l" (bn), \
|
||||
[b] "+l" (b) \
|
||||
: \
|
||||
[port] "l" (port), \
|
||||
[base] "l" (base), \
|
||||
[bitmask] "l" (bitmask), \
|
||||
[hi_off] "I" (HI_OFFSET), \
|
||||
[lo_off] "I" (LO_OFFSET), \
|
||||
[led0] "I" (RO(0)), \
|
||||
[led1] "I" (RO(1)), \
|
||||
[led2] "I" (RO(2)), \
|
||||
[e0] "I" (3+RO(0)), \
|
||||
[e1] "I" (3+RO(1)), \
|
||||
[e2] "I" (3+RO(2)), \
|
||||
[scale0] "I" (4*(2+RO(0))), \
|
||||
[scale1] "I" (4*(2+RO(1))), \
|
||||
[scale2] "I" (4*(2+RO(2))), \
|
||||
[T1] "I" (T1), \
|
||||
[T2] "I" (T2), \
|
||||
[T3] "I" (T3) \
|
||||
:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// now for some convinience macros to make building our lines a bit cleaner
|
||||
#define LOOP " loop_%=:"
|
||||
#define HI2 " qset2 %[bitmask], %[port], %[hi_off];"
|
||||
#define _D1 " mod_delay %c[T1],2,0,%[scratch];"
|
||||
#define QLO4 " qlo4 %[b],%[bitmask],%[port], %[lo_off];"
|
||||
#define LOADLEDS3(X) " loadleds3 %[leds], %[bn], %[led" #X "] ,%[scratch];"
|
||||
#define _D2(ADJ) " mod_delay %c[T2],4," #ADJ ",%[scratch];"
|
||||
#define LO2 " qset2 %[bitmask], %[port], %[lo_off];"
|
||||
#define _D3(ADJ) " mod_delay %c[T3],2," #ADJ ",%[scratch];"
|
||||
#define LOADDITHER7(X) " loaddither7 %[bn], %[d], %[base], %[led" #X "];"
|
||||
#define DITHER5 " dither5 %[bn], %[d];"
|
||||
#define SCALE4(X) " scale4 %[bn], %[base], %[scale" #X "], %[scratch];"
|
||||
#define SWAPBBN1 " swapbbn1 %[b], %[bn];"
|
||||
#define ADJDITHER7(X) " adjdither7 %[base],%[d],%[led" #X "],%[e" #X "],%[scratch];"
|
||||
#define INCLEDS3 " incleds3 %[leds],%[base],%[scratch];"
|
||||
#define CMPLOOP5 " cmploop5 %[counter], loop_%=;"
|
||||
#define NOTHING ""
|
||||
|
||||
#if (defined(SEI_CHK) && (FASTLED_ALLOW_INTERRUPTS == 1))
|
||||
// We're allowing interrupts and have hardware timer support defined -
|
||||
// track the loop outside the asm code, to allow inserting the interrupt
|
||||
// overrun checks.
|
||||
asm __volatile__ (
|
||||
// pre-load byte 0
|
||||
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
|
||||
M0_ASM_ARGS);
|
||||
|
||||
do {
|
||||
asm __volatile__ (
|
||||
// Write out byte 0, prepping byte 1
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 1, prepping byte 2
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 2, prepping byte 0
|
||||
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5)
|
||||
|
||||
M0_ASM_ARGS
|
||||
);
|
||||
SEI_CHK; INNER_SEI; --counter; CLI_CHK;
|
||||
} while(counter);
|
||||
#elif (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
// We're allowing interrupts - track the loop outside the asm code, and
|
||||
// re-enable interrupts in between each iteration.
|
||||
asm __volatile__ (
|
||||
// pre-load byte 0
|
||||
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
|
||||
M0_ASM_ARGS);
|
||||
|
||||
do {
|
||||
asm __volatile__ (
|
||||
// Write out byte 0, prepping byte 1
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 1, prepping byte 2
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 2, prepping byte 0
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5)
|
||||
|
||||
M0_ASM_ARGS
|
||||
);
|
||||
|
||||
uint32_t ticksBeforeInterrupts = SysTick->VAL;
|
||||
sei();
|
||||
--counter;
|
||||
cli();
|
||||
|
||||
// If more than 45 uSecs have elapsed, give up on this frame and start over.
|
||||
// Note: this isn't completely correct. It's possible that more than one
|
||||
// millisecond will elapse, and so SysTick->VAL will lap
|
||||
// ticksBeforeInterrupts.
|
||||
// Note: ticksBeforeInterrupts DECREASES
|
||||
const uint32_t kTicksPerMs = VARIANT_MCK / 1000;
|
||||
const uint32_t kTicksPerUs = kTicksPerMs / 1000;
|
||||
const uint32_t kTicksIn45us = kTicksPerUs * 45;
|
||||
|
||||
const uint32_t currentTicks = SysTick->VAL;
|
||||
|
||||
if (ticksBeforeInterrupts < currentTicks) {
|
||||
// Timer started over
|
||||
if ((ticksBeforeInterrupts + (kTicksPerMs - currentTicks)) > kTicksIn45us) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if ((ticksBeforeInterrupts - currentTicks) > kTicksIn45us) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} while(counter);
|
||||
#else
|
||||
// We're not allowing interrupts - run the entire loop in asm to keep things
|
||||
// as tight as possible. In an ideal world, we should be pushing out ws281x
|
||||
// leds (or other 3-wire leds) with zero gaps between pixels.
|
||||
asm __volatile__ (
|
||||
// pre-load byte 0
|
||||
LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1
|
||||
|
||||
// loop over writing out the data
|
||||
LOOP
|
||||
// Write out byte 0, prepping byte 1
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 1, prepping byte 2
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0)
|
||||
|
||||
// Write out byte 2, prepping byte 0
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0)
|
||||
HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) CMPLOOP5
|
||||
|
||||
M0_ASM_ARGS
|
||||
);
|
||||
#endif
|
||||
return num_leds;
|
||||
}
|
||||
|
||||
#endif
|
||||
61
libraries/FastLED/platforms/arm/d21/clockless_arm_d21.h
Normal file
61
libraries/FastLED/platforms/arm/d21/clockless_arm_d21.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_D21
|
||||
#define __INC_CLOCKLESS_ARM_D21
|
||||
|
||||
#include "../common/m0clockless.h"
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
cli();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mScale[0];
|
||||
data.s[1] = pixels.mScale[1];
|
||||
data.s[2] = pixels.mScale[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
|
||||
return showLedData<8,4,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_CLOCKLESS_ARM_D21
|
||||
7
libraries/FastLED/platforms/arm/d21/fastled_arm_d21.h
Normal file
7
libraries/FastLED/platforms/arm/d21/fastled_arm_d21.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __INC_FASTLED_ARM_D21_H
|
||||
#define __INC_FASTLED_ARM_D21_H
|
||||
|
||||
#include "fastpin_arm_d21.h"
|
||||
#include "clockless_arm_d21.h"
|
||||
|
||||
#endif
|
||||
232
libraries/FastLED/platforms/arm/d21/fastpin_arm_d21.h
Normal file
232
libraries/FastLED/platforms/arm/d21/fastpin_arm_d21.h
Normal file
@@ -0,0 +1,232 @@
|
||||
#ifndef __INC_FASTPIN_ARM_SAM_H
|
||||
#define __INC_FASTPIN_ARM_SAM_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
|
||||
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, int _GRP> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
#if 0
|
||||
inline static void setOutput() {
|
||||
if(_BIT<8) {
|
||||
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
|
||||
} else {
|
||||
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
|
||||
}
|
||||
}
|
||||
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
#endif
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTSET.reg = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTCLR.reg = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUT.reg = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { PORT_IOBUS->Group[_GRP].OUTTGL.reg = _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return PORT_IOBUS->Group[_GRP].OUT.reg & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUT.reg; }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTSET.reg; }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT_IOBUS->Group[_GRP].OUTCLR.reg; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } };
|
||||
|
||||
#define _FL_IO(L) _RD32(GPIO ## L)
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, L> {};
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS)
|
||||
|
||||
#define MAX_PIN 17
|
||||
_FL_DEFPIN( 8,23,1);
|
||||
_FL_DEFPIN( 0, 9,1); _FL_DEFPIN( 1, 8,1); _FL_DEFPIN( 2, 2,1); _FL_DEFPIN( 3, 3,1);
|
||||
_FL_DEFPIN( 6, 5,0); _FL_DEFPIN( 9, 6,0); _FL_DEFPIN(10, 7,0); _FL_DEFPIN(12, 2,0);
|
||||
_FL_DEFPIN(A6, 9,1); _FL_DEFPIN(A7, 8,1); _FL_DEFPIN(A5, 2,1); _FL_DEFPIN(A4, 3,1);
|
||||
_FL_DEFPIN(A1, 5,0); _FL_DEFPIN(A2, 6,0); _FL_DEFPIN(A3, 7,0); _FL_DEFPIN(A0, 2,0);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
|
||||
#elif defined(ADAFRUIT_HALLOWING)
|
||||
|
||||
#define MAX_PIN 20
|
||||
// 0 & 1
|
||||
_FL_DEFPIN( 0, 9, 0); _FL_DEFPIN( 1, 10, 0);
|
||||
// 2, 3, 4
|
||||
_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 11, 0); _FL_DEFPIN( 4, 8, 0);
|
||||
// 5, 6, 7
|
||||
_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 18, 0); _FL_DEFPIN( 7, 0, 0);
|
||||
// 8, 9, 10
|
||||
_FL_DEFPIN( 8, 12, 0); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0);
|
||||
// 11, 12, 13
|
||||
_FL_DEFPIN(11, 21, 0); _FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0);
|
||||
// 14, 15, 16 (A0 - A2)
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 8, 1); _FL_DEFPIN(16, 9, 1);
|
||||
// 17, 18, 19 (A3 - A5)
|
||||
_FL_DEFPIN(17, 4, 0); _FL_DEFPIN(18, 5, 0); _FL_DEFPIN(19, 6, 0);
|
||||
|
||||
#define SPI_DATA PIN_SPI_MOSI
|
||||
#define SPI_CLOCK PIN_SPI_SCK
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
|
||||
#elif defined(ARDUINO_SAMD_ZERO)
|
||||
|
||||
#define MAX_PIN 42
|
||||
_FL_DEFPIN( 0,10,0); _FL_DEFPIN( 1,11,0); _FL_DEFPIN( 2, 8,0); _FL_DEFPIN( 3, 9,0);
|
||||
_FL_DEFPIN( 4,14,0); _FL_DEFPIN( 5,15,0); _FL_DEFPIN( 6,20,0); _FL_DEFPIN( 7,21,0);
|
||||
_FL_DEFPIN( 8, 6,0); _FL_DEFPIN( 9, 7,0); _FL_DEFPIN(10,18,0); _FL_DEFPIN(11,16,0);
|
||||
_FL_DEFPIN(12,19,0); _FL_DEFPIN(13,17,0); _FL_DEFPIN(14, 2,0); _FL_DEFPIN(15, 8,1);
|
||||
_FL_DEFPIN(16, 9,1); _FL_DEFPIN(17, 4,0); _FL_DEFPIN(18, 5,0); _FL_DEFPIN(19, 2,1);
|
||||
_FL_DEFPIN(20,22,0); _FL_DEFPIN(21,23,0); _FL_DEFPIN(22,12,0); _FL_DEFPIN(23,11,1);
|
||||
_FL_DEFPIN(24,10,1); _FL_DEFPIN(25, 3,1); _FL_DEFPIN(26,27,0); _FL_DEFPIN(27,28,0);
|
||||
_FL_DEFPIN(28,24,0); _FL_DEFPIN(29,25,0); _FL_DEFPIN(30,22,1); _FL_DEFPIN(31,23,1);
|
||||
_FL_DEFPIN(32,22,0); _FL_DEFPIN(33,23,0); _FL_DEFPIN(34,19,0); _FL_DEFPIN(35,16,0);
|
||||
_FL_DEFPIN(36,18,0); _FL_DEFPIN(37,17,0); _FL_DEFPIN(38,13,0); _FL_DEFPIN(39,21,0);
|
||||
_FL_DEFPIN(40, 6,0); _FL_DEFPIN(41, 7,0); _FL_DEFPIN(42, 3,0);
|
||||
|
||||
#define SPI_DATA 24
|
||||
#define SPI_CLOCK 23
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SODAQ_AUTONOMO)
|
||||
|
||||
#define MAX_PIN 56
|
||||
_FL_DEFPIN( 0, 9,0); _FL_DEFPIN( 1,10,0); _FL_DEFPIN( 2,11,0); _FL_DEFPIN( 3,10,1);
|
||||
_FL_DEFPIN( 4,11,1); _FL_DEFPIN( 5,12,1); _FL_DEFPIN( 6,13,1); _FL_DEFPIN( 7,14,1);
|
||||
_FL_DEFPIN( 8,15,1); _FL_DEFPIN( 9,14,0); _FL_DEFPIN(10,15,0); _FL_DEFPIN(11,16,0);
|
||||
_FL_DEFPIN(12,17,0); _FL_DEFPIN(13,18,0); _FL_DEFPIN(14,19,0); _FL_DEFPIN(15,16,1);
|
||||
_FL_DEFPIN(16, 8,0); _FL_DEFPIN(17,28,0); _FL_DEFPIN(18,17,1); _FL_DEFPIN(19, 2,0);
|
||||
_FL_DEFPIN(20, 6,0); _FL_DEFPIN(21, 5,0); _FL_DEFPIN(22, 4,0); _FL_DEFPIN(23, 9,1);
|
||||
_FL_DEFPIN(24, 8,1); _FL_DEFPIN(25, 7,1); _FL_DEFPIN(26, 6,1); _FL_DEFPIN(27, 5,1);
|
||||
_FL_DEFPIN(28, 4,1); _FL_DEFPIN(29, 7,0); _FL_DEFPIN(30, 3,1); _FL_DEFPIN(31, 2,1);
|
||||
_FL_DEFPIN(32, 1,1); _FL_DEFPIN(33, 0,1); _FL_DEFPIN(34, 3,0); _FL_DEFPIN(35, 3,0);
|
||||
_FL_DEFPIN(36,30,1); _FL_DEFPIN(37,31,1); _FL_DEFPIN(38,22,1); _FL_DEFPIN(39,23,1);
|
||||
_FL_DEFPIN(40,12,0); _FL_DEFPIN(41,13,0); _FL_DEFPIN(42,22,0); _FL_DEFPIN(43,23,0);
|
||||
_FL_DEFPIN(44,20,0); _FL_DEFPIN(45,21,0); _FL_DEFPIN(46,27,0); _FL_DEFPIN(47,24,0);
|
||||
_FL_DEFPIN(48,25,0); _FL_DEFPIN(49,13,1); _FL_DEFPIN(50,14,1); _FL_DEFPIN(51,17,0);
|
||||
_FL_DEFPIN(52,18,0); _FL_DEFPIN(53,12,1); _FL_DEFPIN(54,13,1); _FL_DEFPIN(55,14,1);
|
||||
_FL_DEFPIN(56,15,1);
|
||||
|
||||
#define SPI_DATA 44
|
||||
#define SPI_CLOCK 45
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SAMD_WINO)
|
||||
|
||||
#define MAX_PIN 22
|
||||
_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 16, 0); _FL_DEFPIN( 3, 17, 0);
|
||||
_FL_DEFPIN( 4, 18, 0); _FL_DEFPIN( 5, 19, 0); _FL_DEFPIN( 6, 24, 0); _FL_DEFPIN( 7, 25, 0);
|
||||
_FL_DEFPIN( 8, 27, 0); _FL_DEFPIN( 9, 28, 0); _FL_DEFPIN( 10, 30, 0); _FL_DEFPIN( 11, 31, 0);
|
||||
_FL_DEFPIN( 12, 15, 0); _FL_DEFPIN( 13, 14, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 3, 0);
|
||||
_FL_DEFPIN( 16, 4, 0); _FL_DEFPIN( 17, 5, 0); _FL_DEFPIN( 18, 6, 0); _FL_DEFPIN( 19, 7, 0);
|
||||
_FL_DEFPIN( 20, 8, 0); _FL_DEFPIN( 21, 9, 0); _FL_DEFPIN( 22, 10, 0); _FL_DEFPIN( 23, 11, 0);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SAMD_MKR1000)
|
||||
|
||||
#define MAX_PIN 22
|
||||
_FL_DEFPIN( 0, 22, 0); _FL_DEFPIN( 1, 23, 0); _FL_DEFPIN( 2, 10, 0); _FL_DEFPIN( 3, 11, 0);
|
||||
_FL_DEFPIN( 4, 10, 1); _FL_DEFPIN( 5, 11, 1); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0);
|
||||
_FL_DEFPIN( 8, 16, 0); _FL_DEFPIN( 9, 17, 0); _FL_DEFPIN( 10, 19, 0); _FL_DEFPIN( 11, 8, 0);
|
||||
_FL_DEFPIN( 12, 9, 0); _FL_DEFPIN( 13, 23, 1); _FL_DEFPIN( 14, 22, 1); _FL_DEFPIN( 15, 2, 0);
|
||||
_FL_DEFPIN( 16, 2, 1); _FL_DEFPIN( 17, 3, 1); _FL_DEFPIN( 18, 4, 0); _FL_DEFPIN( 19, 5, 0);
|
||||
_FL_DEFPIN( 20, 6, 0); _FL_DEFPIN( 21, 7, 0);
|
||||
|
||||
#define SPI_DATA 8
|
||||
#define SPI_CLOCK 9
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_SAMD_NANO_33_IOT)
|
||||
|
||||
#define MAX_PIN 25
|
||||
_FL_DEFPIN( 0, 11, 0); _FL_DEFPIN( 1, 10, 0); _FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 9, 0);
|
||||
_FL_DEFPIN( 4, 8, 0); _FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0);
|
||||
_FL_DEFPIN( 8, 6, 0); _FL_DEFPIN( 9, 7, 0); _FL_DEFPIN( 10, 18, 0); _FL_DEFPIN( 11, 16, 0);
|
||||
_FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0); _FL_DEFPIN( 14, 2, 0); _FL_DEFPIN( 15, 8, 1);
|
||||
_FL_DEFPIN( 16, 9, 1); _FL_DEFPIN( 17, 4, 0); _FL_DEFPIN( 18, 5, 0); _FL_DEFPIN( 19, 2, 1);
|
||||
_FL_DEFPIN( 20, 22, 0); _FL_DEFPIN( 21, 23, 0); _FL_DEFPIN( 22, 12, 0); _FL_DEFPIN( 23, 10, 1);
|
||||
_FL_DEFPIN( 24, 11, 1);
|
||||
|
||||
#define SPI_DATA 23
|
||||
#define SPI_CLOCK 24
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_GEMMA_M0)
|
||||
|
||||
#define MAX_PIN 4
|
||||
_FL_DEFPIN( 0, 4, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 5, 0);
|
||||
_FL_DEFPIN( 3, 0, 0); _FL_DEFPIN( 4, 1, 0);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_TRINKET_M0)
|
||||
|
||||
#define MAX_PIN 7
|
||||
_FL_DEFPIN( 0, 8, 0); _FL_DEFPIN( 1, 2, 0); _FL_DEFPIN( 2, 9, 0);
|
||||
_FL_DEFPIN( 3, 7, 0); _FL_DEFPIN( 4, 6, 0); _FL_DEFPIN( 7, 0, 0); _FL_DEFPIN( 8, 1, 0);
|
||||
|
||||
#define SPI_DATA 4
|
||||
#define SPI_CLOCK 3
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_ITSYBITSY_M0)
|
||||
|
||||
#define MAX_PIN 16
|
||||
_FL_DEFPIN( 2, 14, 0); _FL_DEFPIN( 3, 9, 0); _FL_DEFPIN( 4, 8, 0);
|
||||
_FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 20, 0); _FL_DEFPIN( 7, 21, 0);
|
||||
_FL_DEFPIN( 8, 6, 0); _FL_DEFPIN( 9, 7, 0); _FL_DEFPIN( 10, 18, 0);
|
||||
_FL_DEFPIN( 11, 16, 0); _FL_DEFPIN( 12, 19, 0); _FL_DEFPIN( 13, 17, 0);
|
||||
_FL_DEFPIN( 29, 10, 0); // MOSI
|
||||
_FL_DEFPIN( 30, 11, 0); // SCK
|
||||
_FL_DEFPIN( 40, 0, 0); //APA102 Clock
|
||||
_FL_DEFPIN( 41, 0, 1) //APA102 Data
|
||||
|
||||
#define SPI_DATA 29
|
||||
#define SPI_CLOCK 30
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_SAM_H
|
||||
26
libraries/FastLED/platforms/arm/d21/led_sysdefs_arm_d21.h
Normal file
26
libraries/FastLED/platforms/arm/d21/led_sysdefs_arm_d21.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_D21_H
|
||||
#define __INC_LED_SYSDEFS_ARM_D21_H
|
||||
|
||||
|
||||
#define FASTLED_ARM
|
||||
#define FASTLED_ARM_M0_PLUS
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
|
||||
#endif
|
||||
4
libraries/FastLED/platforms/arm/d51/README.txt
Normal file
4
libraries/FastLED/platforms/arm/d51/README.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
FastLED updates for adafruit FEATHER M4 and fixes to ITSBITSY M4 compiles
|
||||
SAMD51
|
||||
|
||||
only tested on FEATHER M4 with DOTSTAR and neopixel strips
|
||||
128
libraries/FastLED/platforms/arm/d51/clockless_arm_d51.h
Normal file
128
libraries/FastLED/platforms/arm/d51/clockless_arm_d51.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_D51
|
||||
#define __INC_CLOCKLESS_ARM_D51
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for SAMD51
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#define ARM_DEMCR (*(volatile uint32_t *)0xE000EDFC) // Debug Exception and Monitor Control
|
||||
#define ARM_DEMCR_TRCENA (1 << 24) // Enable debugging & monitoring blocks
|
||||
#define ARM_DWT_CTRL (*(volatile uint32_t *)0xE0001000) // DWT control register
|
||||
#define ARM_DWT_CTRL_CYCCNTENA (1 << 0) // Enable cycle count
|
||||
#define ARM_DWT_CYCCNT (*(volatile uint32_t *)0xE0001004) // Cycle count register
|
||||
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) {
|
||||
for(register uint32_t i = BITS-1; i > 0; i--) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
register data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
register data_t hi = *port | FastPin<DATA_PIN>::mask();;
|
||||
register data_t lo = *port & ~FastPin<DATA_PIN>::mask();;
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
register uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
7
libraries/FastLED/platforms/arm/d51/fastled_arm_d51.h
Normal file
7
libraries/FastLED/platforms/arm/d51/fastled_arm_d51.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __INC_FASTLED_ARM_D51_H
|
||||
#define __INC_FASTLED_ARM_D51_H
|
||||
|
||||
#include "fastpin_arm_d51.h"
|
||||
#include "clockless_arm_d51.h"
|
||||
|
||||
#endif
|
||||
139
libraries/FastLED/platforms/arm/d51/fastpin_arm_d51.h
Normal file
139
libraries/FastLED/platforms/arm/d51/fastpin_arm_d51.h
Normal file
@@ -0,0 +1,139 @@
|
||||
#ifndef __INC_FASTPIN_ARM_D51_H
|
||||
#define __INC_FASTPIN_ARM_D51_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
|
||||
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, int _GRP> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
#if 0
|
||||
inline static void setOutput() {
|
||||
if(_BIT<8) {
|
||||
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
|
||||
} else {
|
||||
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
|
||||
}
|
||||
}
|
||||
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
#endif
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTSET.reg = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTCLR.reg = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { PORT->Group[_GRP].OUT.reg = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { PORT->Group[_GRP].OUTTGL.reg = _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return PORT->Group[_GRP].OUT.reg & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUT.reg; }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTSET.reg; }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &PORT->Group[_GRP].OUTCLR.reg; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile PortGroup * r() { return T; } };
|
||||
|
||||
#define _FL_IO(L) _RD32(GPIO ## L)
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, L> {};
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(ADAFRUIT_ITSYBITSY_M4_EXPRESS)
|
||||
|
||||
#define MAX_PIN 19
|
||||
// D0-D13, including D6+D8 (DotStar CLK + DATA)
|
||||
_FL_DEFPIN( 0, 16, 0); _FL_DEFPIN( 1, 17, 0); _FL_DEFPIN( 2, 7, 0); _FL_DEFPIN( 3, 22, 1);
|
||||
_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 15, 0); _FL_DEFPIN( 6, 2, 1); _FL_DEFPIN( 7, 18, 0);
|
||||
_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0);
|
||||
_FL_DEFPIN(12, 23, 0); _FL_DEFPIN(13, 22, 0);
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1);
|
||||
_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0);
|
||||
|
||||
// 23..25 MISO/SCK/MOSI
|
||||
_FL_DEFPIN(23, 23, 1); _FL_DEFPIN(24, 1, 0); _FL_DEFPIN(25, 0, 0);
|
||||
|
||||
#define SPI_DATA 25
|
||||
#define SPI_CLOCK 24
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
// Actual pin definitions
|
||||
#elif defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE)
|
||||
|
||||
#define MAX_PIN 20
|
||||
// D0-D13, including D6+D8 (DotStar CLK + DATA)
|
||||
_FL_DEFPIN( 0, 23, 0); _FL_DEFPIN( 1, 22, 0); _FL_DEFPIN( 2, 17, 1); _FL_DEFPIN( 3, 16, 1);
|
||||
_FL_DEFPIN( 4, 13, 1); _FL_DEFPIN( 5, 14, 1); _FL_DEFPIN( 6, 15, 1); _FL_DEFPIN( 7, 12, 1);
|
||||
_FL_DEFPIN( 8, 21, 0); _FL_DEFPIN( 9, 20, 0); _FL_DEFPIN(10, 18, 0); _FL_DEFPIN(11, 19, 0);
|
||||
_FL_DEFPIN(12, 17, 0); _FL_DEFPIN(13, 16, 0);
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 6, 0); _FL_DEFPIN(17, 0, 1);
|
||||
_FL_DEFPIN(18, 8, 1); _FL_DEFPIN(19, 9, 1);
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(22, 2, 1); _FL_DEFPIN(23, 3, 1);
|
||||
|
||||
// 23..25 MISO/SCK/MOSI
|
||||
_FL_DEFPIN(24, 14, 0); _FL_DEFPIN(25, 13, 0); _FL_DEFPIN(26, 12, 0);
|
||||
|
||||
#define SPI_DATA 26
|
||||
#define SPI_CLOCK 25
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ADAFRUIT_FEATHER_M4_EXPRESS)
|
||||
|
||||
#define MAX_PIN 19
|
||||
// D0-D13, including D8 (neopixel) no pins 2 3
|
||||
_FL_DEFPIN( 0, 17, 1); _FL_DEFPIN( 1, 16, 1);
|
||||
_FL_DEFPIN( 4, 14, 0); _FL_DEFPIN( 5, 16, 0); _FL_DEFPIN( 6, 18, 0);
|
||||
_FL_DEFPIN( 8, 3, 1); _FL_DEFPIN( 9, 19, 0); _FL_DEFPIN(10, 20, 0); _FL_DEFPIN(11, 21, 0);
|
||||
_FL_DEFPIN(12, 22, 0); _FL_DEFPIN(13, 23, 0);
|
||||
// A0-A5
|
||||
_FL_DEFPIN(14, 2, 0); _FL_DEFPIN(15, 5, 0); _FL_DEFPIN(16, 8, 1); _FL_DEFPIN(17, 9, 1);
|
||||
_FL_DEFPIN(18, 4, 0); _FL_DEFPIN(19, 6, 0); /* A6 is present in variant.h but couldn't find it on the schematic */
|
||||
// SDA/SCL
|
||||
_FL_DEFPIN(21, 12, 0); _FL_DEFPIN(22, 13, 0);
|
||||
// 23..25 MISO/MOSI/SCK
|
||||
_FL_DEFPIN(23, 22, 1); _FL_DEFPIN(24, 23, 1); _FL_DEFPIN(25, 17, 0);
|
||||
|
||||
#define SPI_DATA 24
|
||||
#define SPI_CLOCK 25
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_D51_H
|
||||
27
libraries/FastLED/platforms/arm/d51/led_sysdefs_arm_d51.h
Normal file
27
libraries/FastLED/platforms/arm/d51/led_sysdefs_arm_d51.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_D51_H
|
||||
#define __INC_LED_SYSDEFS_ARM_D51_H
|
||||
|
||||
|
||||
#define FASTLED_ARM
|
||||
// Note this is an M4, not an M0+, but this enables the shared m0clockless.h
|
||||
#define FASTLED_ARM_M0_PLUS
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 0
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
|
||||
#endif
|
||||
124
libraries/FastLED/platforms/arm/k20/clockless_arm_k20.h
Normal file
124
libraries/FastLED/platforms/arm/k20/clockless_arm_k20.h
Normal file
@@ -0,0 +1,124 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_K20_H
|
||||
#define __INC_CLOCKLESS_ARM_K20_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) {
|
||||
for(register uint32_t i = BITS-1; i > 0; i--) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
register data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
register data_t hi = *port | FastPin<DATA_PIN>::mask();;
|
||||
register data_t lo = *port & ~FastPin<DATA_PIN>::mask();;
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
register uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
330
libraries/FastLED/platforms/arm/k20/clockless_block_arm_k20.h
Normal file
330
libraries/FastLED/platforms/arm/k20/clockless_block_arm_k20.h
Normal file
@@ -0,0 +1,330 @@
|
||||
#ifndef __INC_BLOCK_CLOCKLESS_ARM_K20_H
|
||||
#define __INC_BLOCK_CLOCKLESS_ARM_K20_H
|
||||
|
||||
// Definition for a single channel clockless controller for the k20 family of chips, like that used in the teensy 3.0/3.1
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
#define FASTLED_HAS_BLOCKLESS 1
|
||||
|
||||
#define PORTC_FIRST_PIN 15
|
||||
#define PORTD_FIRST_PIN 2
|
||||
#define HAS_PORTDC 1
|
||||
|
||||
#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFFF))
|
||||
|
||||
#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
|
||||
#define USED_LANES ((FIRST_PIN==2) ? MIN(LANES,8) : MIN(LANES,12))
|
||||
|
||||
#include <kinetis.h>
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 40>
|
||||
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
|
||||
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * LANES; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC
|
||||
switch(USED_LANES) {
|
||||
case 12: FastPin<30>::setOutput();
|
||||
case 11: FastPin<29>::setOutput();
|
||||
case 10: FastPin<27>::setOutput();
|
||||
case 9: FastPin<28>::setOutput();
|
||||
case 8: FastPin<12>::setOutput();
|
||||
case 7: FastPin<11>::setOutput();
|
||||
case 6: FastPin<13>::setOutput();
|
||||
case 5: FastPin<10>::setOutput();
|
||||
case 4: FastPin<9>::setOutput();
|
||||
case 3: FastPin<23>::setOutput();
|
||||
case 2: FastPin<22>::setOutput();
|
||||
case 1: FastPin<15>::setOutput();
|
||||
}
|
||||
} else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD
|
||||
switch(USED_LANES) {
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
}
|
||||
mPinMask = FastPin<FIRST_PIN>::mask();
|
||||
mPort = FastPin<FIRST_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[12];
|
||||
uint16_t shorts[6];
|
||||
uint32_t raw[3];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER, LANES, PORT_MASK> &pixels) { // , register uint32_t & b2) {
|
||||
register Lines b2;
|
||||
if(USED_LANES>8) {
|
||||
transpose8<1,2>(b.bytes,b2.bytes);
|
||||
transpose8<1,2>(b.bytes+8,b2.bytes+1);
|
||||
} else {
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
}
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(register uint32_t i = 0; i < (USED_LANES/2); i++) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale<PX>(pixels,i+(USED_LANES/2),d,scale);
|
||||
}
|
||||
|
||||
// if folks use an odd numnber of lanes, get the last byte's value here
|
||||
if(USED_LANES & 0x01) {
|
||||
b.bytes[USED_LANES-1] = pixels.template loadAndScale<PX>(pixels,USED_LANES-1,d,scale);
|
||||
}
|
||||
|
||||
for(register uint32_t i = USED_LANES/2; i < 8; i++) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
// b2.bytes[0] = 0;
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
register Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < USED_LANES; i++) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
allpixels.stepDithering();
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
#define PMASK ((1<<(LANES))-1)
|
||||
#define PMASK_HI (PMASK>>8 & 0xFF)
|
||||
#define PMASK_LO (PMASK & 0xFF)
|
||||
|
||||
template <uint8_t LANES, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class SixteenWayInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PMASK> {
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!");
|
||||
// FastPin<30>::setOutput();
|
||||
// FastPin<29>::setOutput();
|
||||
// FastPin<27>::setOutput();
|
||||
// FastPin<28>::setOutput();
|
||||
switch(LANES) {
|
||||
case 16: FastPin<12>::setOutput();
|
||||
case 15: FastPin<11>::setOutput();
|
||||
case 14: FastPin<13>::setOutput();
|
||||
case 13: FastPin<10>::setOutput();
|
||||
case 12: FastPin<9>::setOutput();
|
||||
case 11: FastPin<23>::setOutput();
|
||||
case 10: FastPin<22>::setOutput();
|
||||
case 9: FastPin<15>::setOutput();
|
||||
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PMASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[16];
|
||||
uint16_t shorts[8];
|
||||
uint32_t raw[4];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER,LANES, PMASK> &pixels) { // , register uint32_t & b2) {
|
||||
register Lines b2;
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
transpose8x1(b.bytes+8,b2.bytes+8);
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(register uint32_t i = 0; (i < LANES) && (i < 8); i++) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<PORTD_FIRST_PIN>::sport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::sport() = PMASK_HI;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PMASK_LO);
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = ((~b2.bytes[15-i]) & PMASK_HI);
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = PMASK_HI;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
if(LANES==16 || (LANES>8 && ((i+8) < LANES))) {
|
||||
b.bytes[i+8] = pixels.template loadAndScale<PX>(pixels,i+8,d,scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, PMASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
register Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < LANES; i++) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
allpixels.stepDithering();
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
sei();
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
13
libraries/FastLED/platforms/arm/k20/fastled_arm_k20.h
Normal file
13
libraries/FastLED/platforms/arm/k20/fastled_arm_k20.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef __INC_FASTLED_ARM_K20_H
|
||||
#define __INC_FASTLED_ARM_K20_H
|
||||
|
||||
// Include the k20 headers
|
||||
#include "fastpin_arm_k20.h"
|
||||
#include "fastspi_arm_k20.h"
|
||||
#include "octows2811_controller.h"
|
||||
#include "ws2812serial_controller.h"
|
||||
#include "smartmatrix_t3.h"
|
||||
#include "clockless_arm_k20.h"
|
||||
#include "clockless_block_arm_k20.h"
|
||||
|
||||
#endif
|
||||
120
libraries/FastLED/platforms/arm/k20/fastpin_arm_k20.h
Normal file
120
libraries/FastLED/platforms/arm/k20/fastpin_arm_k20.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef __FASTPIN_ARM_K20_H
|
||||
#define __FASTPIN_ARM_K20_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be sloightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
|
||||
/// does a poor job of optimizing around these accesses so they are not being used just yet.
|
||||
template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 1; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return 1; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
|
||||
};
|
||||
|
||||
// Macros for k20 pin access/definition
|
||||
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
|
||||
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
|
||||
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
|
||||
#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR));
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; \
|
||||
template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
|
||||
|
||||
// Actual pin definitions
|
||||
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
#define MAX_PIN 33
|
||||
_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 12, A);
|
||||
_FL_DEFPIN(4, 13, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D);
|
||||
_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
|
||||
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
|
||||
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
|
||||
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
|
||||
_FL_DEFPIN(24, 5, A); _FL_DEFPIN(25, 19, B); _FL_DEFPIN(26, 1, E); _FL_DEFPIN(27, 9, C);
|
||||
_FL_DEFPIN(28, 8, C); _FL_DEFPIN(29, 10, C); _FL_DEFPIN(30, 11, C); _FL_DEFPIN(31, 0, E);
|
||||
_FL_DEFPIN(32, 18, B); _FL_DEFPIN(33, 4, A);
|
||||
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
#define SPI1 (*(SPI_t *)0x4002D000)
|
||||
|
||||
#define SPI2_DATA 7
|
||||
#define SPI2_CLOCK 14
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#define ARM_HARDWARE_SPI
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_K20
|
||||
466
libraries/FastLED/platforms/arm/k20/fastspi_arm_k20.h
Normal file
466
libraries/FastLED/platforms/arm/k20/fastspi_arm_k20.h
Normal file
@@ -0,0 +1,466 @@
|
||||
#ifndef __INC_FASTSPI_ARM_H
|
||||
#define __INC_FASTSPI_ARM_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
// Version 1.20 renamed SPI_t to KINETISK_SPI_t
|
||||
#if TEENSYDUINO >= 120
|
||||
#define SPI_t KINETISK_SPI_t
|
||||
#endif
|
||||
|
||||
#ifndef KINETISK_SPI0
|
||||
#define KINETISK_SPI0 SPI0
|
||||
#endif
|
||||
|
||||
#ifndef SPI_PUSHR_CONT
|
||||
#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
|
||||
#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
|
||||
#define SPI_PUSHR_EOQ SPIX.PUSHR_EOQ
|
||||
#define SPI_PUSHR_CTCNT SPIX.PUSHR_CTCNT
|
||||
#define SPI_PUSHR_PCS(X) SPIX.PUSHR_PCS(X)
|
||||
#endif
|
||||
|
||||
// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now,
|
||||
// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit
|
||||
// set (the zero-th bit). Unclear if I will want this to change at some point.
|
||||
template<int VAL, int BIT> class BitWork {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork<VAL, BIT-1>::highestBit(); }
|
||||
};
|
||||
template<int VAL> class BitWork<VAL, 0> {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return 0; }
|
||||
};
|
||||
|
||||
#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
|
||||
|
||||
#define USE_CONT 0
|
||||
// intra-frame backup data
|
||||
struct SPIState {
|
||||
uint32_t _ctar0,_ctar1;
|
||||
uint32_t pins[4];
|
||||
};
|
||||
|
||||
// extern SPIState gState;
|
||||
|
||||
|
||||
// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
|
||||
template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
|
||||
switch(VAL) {
|
||||
// Handle the dbl clock cases
|
||||
case 0: case 1:
|
||||
case 2: preScalar = 0; scalar = 0; dbl = 1; break;
|
||||
case 3: preScalar = 1; scalar = 0; dbl = 1; break;
|
||||
case 5: preScalar = 2; scalar = 0; dbl = 1; break;
|
||||
case 7: preScalar = 3; scalar = 0; dbl = 1; break;
|
||||
|
||||
// Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
|
||||
// below)
|
||||
case 9: preScalar = 1; scalar = 2; dbl = 1; break;
|
||||
case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 15: preScalar = 2; scalar = 2; dbl = 1; break;
|
||||
case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
|
||||
case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
|
||||
default: {
|
||||
int p2 = BitWork<VAL/2, 15>::highestBit();
|
||||
int p3 = BitWork<VAL/3, 15>::highestBit();
|
||||
int p5 = BitWork<VAL/5, 15>::highestBit();
|
||||
int p7 = BitWork<VAL/7, 15>::highestBit();
|
||||
|
||||
int w2 = 2 * (1 << p2);
|
||||
int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
|
||||
int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
|
||||
int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
|
||||
|
||||
int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
|
||||
|
||||
if(w2 == maxval) { preScalar = 0; scalar = p2; }
|
||||
else if(w3 == maxval) { preScalar = 1; scalar = p3; }
|
||||
else if(w5 == maxval) { preScalar = 2; scalar = p5; }
|
||||
else if(w7 == maxval) { preScalar = 3; scalar = p7; }
|
||||
|
||||
dbl = 0;
|
||||
if(scalar == 0) { dbl = 1; }
|
||||
else if(scalar < 3) { scalar--; }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#define SPIX (*(SPI_t*)pSPIX)
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
|
||||
class ARMHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
SPIState gState;
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa),
|
||||
// and likewise enabling pin 14 disables pin 13 (and vice versa)
|
||||
inline void enable_pins(void) __attribute__((always_inline)) {
|
||||
//serial_print("enable_pins\n");
|
||||
switch(_DATA_PIN) {
|
||||
case 7:
|
||||
CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 11:
|
||||
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13:
|
||||
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 14:
|
||||
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using
|
||||
inline void disable_pins(void) __attribute__((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) {
|
||||
if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if(mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR0 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar;
|
||||
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR1 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR1 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR1 = ctar;
|
||||
SPIX.MCR = mcr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
|
||||
uint32_t _PBR = 0;
|
||||
uint32_t _BR = 0;
|
||||
uint32_t _CSSCK = 0;
|
||||
uint32_t _DBR = 0;
|
||||
|
||||
// if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
|
||||
// else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
|
||||
|
||||
getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
|
||||
_CSSCK = _BR;
|
||||
|
||||
uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
|
||||
#if USE_CONT == 1
|
||||
ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
#endif
|
||||
|
||||
if(_DBR) {
|
||||
ctar0 |= SPI_CTAR_DBR;
|
||||
ctar1 |= SPI_CTAR_DBR;
|
||||
}
|
||||
|
||||
update_ctars(ctar0,ctar1);
|
||||
}
|
||||
|
||||
void inline save_spi_state() __attribute__ ((always_inline)) {
|
||||
// save ctar data
|
||||
gState._ctar0 = SPIX.CTAR0;
|
||||
gState._ctar1 = SPIX.CTAR1;
|
||||
|
||||
// save data for the not-us pins
|
||||
gState.pins[0] = CORE_PIN7_CONFIG;
|
||||
gState.pins[1] = CORE_PIN11_CONFIG;
|
||||
gState.pins[2] = CORE_PIN13_CONFIG;
|
||||
gState.pins[3] = CORE_PIN14_CONFIG;
|
||||
}
|
||||
|
||||
void inline restore_spi_state() __attribute__ ((always_inline)) {
|
||||
// restore ctar data
|
||||
update_ctars(gState._ctar0,gState._ctar1);
|
||||
|
||||
// restore data for the not-us pins (not necessary because disable_pins will do this)
|
||||
// CORE_PIN7_CONFIG = gState.pins[0];
|
||||
// CORE_PIN11_CONFIG = gState.pins[1];
|
||||
// CORE_PIN13_CONFIG = gState.pins[2];
|
||||
// CORE_PIN14_CONFIG = gState.pins[3];
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
ARMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
|
||||
void init() {
|
||||
// set the pins to output
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// Enable SPI0 clock
|
||||
uint32_t sim6 = SIM_SCGC6;
|
||||
if((SPI_t*)pSPIX == &KINETISK_SPI0) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI0)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
} else if((SPI_t*)pSPIX == &SPI1) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI1)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI1;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure SPI as the master and enable
|
||||
SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
|
||||
SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
|
||||
|
||||
// pin/spi configuration happens on select
|
||||
}
|
||||
|
||||
static void waitFully() __attribute__((always_inline)) {
|
||||
// Wait for the last byte to get shifted into the register
|
||||
bool empty = false;
|
||||
|
||||
do {
|
||||
cli();
|
||||
if ((SPIX.SR & 0xF000) > 0) {
|
||||
// reset the TCF flag
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
} else {
|
||||
empty = true;
|
||||
}
|
||||
sei();
|
||||
} while (!empty);
|
||||
|
||||
// wait for the TCF flag to get set
|
||||
while (!(SPIX.SR & SPI_SR_TCF));
|
||||
SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF);
|
||||
}
|
||||
|
||||
static bool needwait() __attribute__((always_inline)) { return (SPIX.SR & 0x4000); }
|
||||
static void wait() __attribute__((always_inline)) { while( (SPIX.SR & 0x4000) ); }
|
||||
static void wait1() __attribute__((always_inline)) { while( (SPIX.SR & 0xF000) >= 0x2000); }
|
||||
|
||||
enum ECont { CONT, NOCONT };
|
||||
enum EWait { PRE, POST, NONE };
|
||||
enum ELast { NOTLAST, LAST };
|
||||
|
||||
#if USE_CONT == 1
|
||||
#define CM CONT
|
||||
#else
|
||||
#define CM NOCONT
|
||||
#endif
|
||||
#define WM PRE
|
||||
|
||||
template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
|
||||
public:
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
cli();
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
sei();
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
cli();
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(0) | (b & 0xFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
sei();
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
};
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; sei(); wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
|
||||
static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF; sei();}
|
||||
|
||||
static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); wait(); }
|
||||
static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { cli(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF; sei(); }
|
||||
|
||||
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 1 bit transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
writeWord( (b & (1 << BIT)) != 0);
|
||||
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
save_spi_state();
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
setSPIRate();
|
||||
enable_pins();
|
||||
}
|
||||
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
disable_pins();
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
restore_spi_state();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
// could be optimized to write 16bit words out instead of 8bit bytes
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
// Setup the pixel controller
|
||||
if((FLAGS & FLAG_START_BIT) == 0) {
|
||||
//If no start bit stupiditiy, write out as many 16-bit blocks as we can
|
||||
while(pixels.has(2)) {
|
||||
// Load and write out the first two bytes
|
||||
if(WM == NONE) { wait1(); }
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
|
||||
// Load and write out the next two bytes (step dithering, advance data in between since we
|
||||
// cross pixels here)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale2()) << 8 | D::adjust(pixels.stepAdvanceAndLoadAndScale0()));
|
||||
|
||||
// Load and write out the next two bytes
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale1()) << 8 | D::adjust(pixels.loadAndScale2()));
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
|
||||
if(pixels.has(1)) {
|
||||
if(WM == NONE) { wait1(); }
|
||||
// write out the rest as alternating 16/8-bit blocks (likely to be just one)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
Write<CM, WM, NOTLAST>::writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
} else if(FLAGS & FLAG_START_BIT) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
writeWord( 0x100 | D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
|
||||
// restore ctar1
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
release();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
46
libraries/FastLED/platforms/arm/k20/led_sysdefs_arm_k20.h
Normal file
46
libraries/FastLED/platforms/arm/k20/led_sysdefs_arm_k20.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_K20_H
|
||||
#define __INC_LED_SYSDEFS_ARM_K20_H
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#define FASTLED_ARM
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 96000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
extern volatile uint32_t systick_millis_count;
|
||||
# define MS_COUNTER systick_millis_count
|
||||
|
||||
|
||||
// Default to using PROGMEM, since TEENSY3 provides it
|
||||
// even though all it does is ignore it. Just being
|
||||
// conservative here in case TEENSY3 changes.
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
66
libraries/FastLED/platforms/arm/k20/octows2811_controller.h
Normal file
66
libraries/FastLED/platforms/arm/k20/octows2811_controller.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef __INC_OCTOWS2811_CONTROLLER_H
|
||||
#define __INC_OCTOWS2811_CONTROLLER_H
|
||||
|
||||
#ifdef USE_OCTOWS2811
|
||||
|
||||
// #include "OctoWS2811.h"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template<EOrder RGB_ORDER = GRB, uint8_t CHIP = WS2811_800kHz>
|
||||
class COctoWS2811Controller : public CPixelLEDController<RGB_ORDER, 8, 0xFF> {
|
||||
OctoWS2811 *pocto;
|
||||
uint8_t *drawbuffer,*framebuffer;
|
||||
|
||||
void _init(int nLeds) {
|
||||
if(pocto == NULL) {
|
||||
drawbuffer = (uint8_t*)malloc(nLeds * 8 * 3);
|
||||
framebuffer = (uint8_t*)malloc(nLeds * 8 * 3);
|
||||
|
||||
// byte ordering is handled in show by the pixel controller
|
||||
int config = WS2811_RGB;
|
||||
config |= CHIP;
|
||||
|
||||
pocto = new OctoWS2811(nLeds, framebuffer, drawbuffer, config);
|
||||
|
||||
pocto->begin();
|
||||
}
|
||||
}
|
||||
public:
|
||||
COctoWS2811Controller() { pocto = NULL; }
|
||||
virtual int size() { return CLEDController::size() * 8; }
|
||||
|
||||
virtual void init() { /* do nothing yet */ }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[8];
|
||||
uint32_t raw[2];
|
||||
} Lines;
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> & pixels) {
|
||||
_init(pixels.size());
|
||||
|
||||
uint8_t *pData = drawbuffer;
|
||||
while(pixels.has(1)) {
|
||||
Lines b;
|
||||
|
||||
for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale0(i); }
|
||||
transpose8x1_MSB(b.bytes,pData); pData += 8;
|
||||
for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale1(i); }
|
||||
transpose8x1_MSB(b.bytes,pData); pData += 8;
|
||||
for(int i = 0; i < 8; i++) { b.bytes[i] = pixels.loadAndScale2(i); }
|
||||
transpose8x1_MSB(b.bytes,pData); pData += 8;
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
|
||||
pocto->show();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
55
libraries/FastLED/platforms/arm/k20/smartmatrix_t3.h
Normal file
55
libraries/FastLED/platforms/arm/k20/smartmatrix_t3.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef __INC_SMARTMATRIX_T3_H
|
||||
#define __INC_SMARTMATRIX_T3_H
|
||||
|
||||
#ifdef SmartMatrix_h
|
||||
#include <SmartMatrix.h>
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
extern SmartMatrix *pSmartMatrix;
|
||||
|
||||
// note - dmx simple must be included before FastSPI for this code to be enabled
|
||||
class CSmartMatrixController : public CPixelLEDController<RGB_ORDER> {
|
||||
SmartMatrix matrix;
|
||||
|
||||
public:
|
||||
// initialize the LED controller
|
||||
virtual void init() {
|
||||
// Initialize 32x32 LED Matrix
|
||||
matrix.begin();
|
||||
matrix.setBrightness(255);
|
||||
matrix.setColorCorrection(ccNone);
|
||||
|
||||
// Clear screen
|
||||
clearLeds(0);
|
||||
matrix.swapBuffers();
|
||||
pSmartMatrix = &matrix;
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
if(SMART_MATRIX_CAN_TRIPLE_BUFFER) {
|
||||
rgb24 *md = matrix.getRealBackBuffer();
|
||||
} else {
|
||||
rgb24 *md = matrix.backBuffer();
|
||||
}
|
||||
while(pixels.has(1)) {
|
||||
md->red = pixels.loadAndScale0();
|
||||
md->green = pixels.loadAndScale1();
|
||||
md->blue = pixels.loadAndScale2();
|
||||
md++;
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
matrix.swapBuffers();
|
||||
if(SMART_MATRIX_CAN_TRIPLE_BUFFER && pixels.advanceBy() > 0) {
|
||||
matrix.setBackBuffer(pixels.mData);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef __INC_WS2812SERIAL_CONTROLLER_H
|
||||
#define __INC_WS2812SERIAL_CONTROLLER_H
|
||||
|
||||
#ifdef USE_WS2812SERIAL
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template<int DATA_PIN, EOrder RGB_ORDER>
|
||||
class CWS2812SerialController : public CPixelLEDController<RGB_ORDER, 8, 0xFF> {
|
||||
WS2812Serial *pserial;
|
||||
uint8_t *drawbuffer,*framebuffer;
|
||||
|
||||
void _init(int nLeds) {
|
||||
if (pserial == NULL) {
|
||||
drawbuffer = (uint8_t*)malloc(nLeds * 3);
|
||||
framebuffer = (uint8_t*)malloc(nLeds * 12);
|
||||
pserial = new WS2812Serial(nLeds, framebuffer, drawbuffer, DATA_PIN, WS2812_RGB);
|
||||
pserial->begin();
|
||||
}
|
||||
}
|
||||
public:
|
||||
CWS2812SerialController() { pserial = NULL; }
|
||||
|
||||
virtual void init() { /* do nothing yet */ }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, 8, 0xFF> & pixels) {
|
||||
_init(pixels.size());
|
||||
|
||||
uint8_t *p = drawbuffer;
|
||||
|
||||
while(pixels.has(1)) {
|
||||
*p++ = pixels.loadAndScale0();
|
||||
*p++ = pixels.loadAndScale1();
|
||||
*p++ = pixels.loadAndScale2();
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
pserial->show();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // USE_WS2812SERIAL
|
||||
#endif // __INC_WS2812SERIAL_CONTROLLER_H
|
||||
124
libraries/FastLED/platforms/arm/k66/clockless_arm_k66.h
Normal file
124
libraries/FastLED/platforms/arm/k66/clockless_arm_k66.h
Normal file
@@ -0,0 +1,124 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_K66_H
|
||||
#define __INC_CLOCKLESS_ARM_K66_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) {
|
||||
for(register uint32_t i = BITS-1; i > 0; i--) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
register data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
register data_t hi = *port | FastPin<DATA_PIN>::mask();;
|
||||
register data_t lo = *port & ~FastPin<DATA_PIN>::mask();;
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
register uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
344
libraries/FastLED/platforms/arm/k66/clockless_block_arm_k66.h
Normal file
344
libraries/FastLED/platforms/arm/k66/clockless_block_arm_k66.h
Normal file
@@ -0,0 +1,344 @@
|
||||
#ifndef __INC_BLOCK_CLOCKLESS_ARM_K66_H
|
||||
#define __INC_BLOCK_CLOCKLESS_ARM_K66_H
|
||||
|
||||
// Definition for a single channel clockless controller for the k66 family of chips, like that used in the teensy 3.6
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY3)
|
||||
#define FASTLED_HAS_BLOCKLESS 1
|
||||
|
||||
#define PORTB_FIRST_PIN 0
|
||||
#define PORTC_FIRST_PIN 15
|
||||
#define PORTD_FIRST_PIN 2
|
||||
#define HAS_PORTDC 1
|
||||
|
||||
#define LANE_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFFF))
|
||||
#define PORT_SHIFT(P) ((P) << ((FIRST_PIN==0) ? 16 : 0))
|
||||
#define PORT_MASK PORT_SHIFT(LANE_MASK)
|
||||
|
||||
#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
|
||||
#define USED_LANES ((FIRST_PIN!=15) ? MIN(LANES,8) : MIN(LANES,12))
|
||||
|
||||
#include <kinetis.h>
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 40>
|
||||
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, LANE_MASK> {
|
||||
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * LANES; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, LANE_MASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
if(FIRST_PIN == PORTC_FIRST_PIN) { // PORTC
|
||||
switch(USED_LANES) {
|
||||
case 12: FastPin<30>::setOutput();
|
||||
case 11: FastPin<29>::setOutput();
|
||||
case 10: FastPin<27>::setOutput();
|
||||
case 9: FastPin<28>::setOutput();
|
||||
case 8: FastPin<12>::setOutput();
|
||||
case 7: FastPin<11>::setOutput();
|
||||
case 6: FastPin<13>::setOutput();
|
||||
case 5: FastPin<10>::setOutput();
|
||||
case 4: FastPin<9>::setOutput();
|
||||
case 3: FastPin<23>::setOutput();
|
||||
case 2: FastPin<22>::setOutput();
|
||||
case 1: FastPin<15>::setOutput();
|
||||
}
|
||||
} else if(FIRST_PIN == PORTD_FIRST_PIN) { // PORTD
|
||||
switch(USED_LANES) {
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
} else if (FIRST_PIN == PORTB_FIRST_PIN) { // PORTB
|
||||
switch (USED_LANES) {
|
||||
case 8: FastPin<45>::setOutput();
|
||||
case 7: FastPin<44>::setOutput();
|
||||
case 6: FastPin<46>::setOutput();
|
||||
case 5: FastPin<43>::setOutput();
|
||||
case 4: FastPin<30>::setOutput();
|
||||
case 3: FastPin<29>::setOutput();
|
||||
case 2: FastPin<1>::setOutput();
|
||||
case 1: FastPin<0>::setOutput();
|
||||
}
|
||||
}
|
||||
mPinMask = FastPin<FIRST_PIN>::mask();
|
||||
mPort = FastPin<FIRST_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[12];
|
||||
uint16_t shorts[6];
|
||||
uint32_t raw[3];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER, LANES, LANE_MASK> &pixels) { // , register uint32_t & b2) {
|
||||
register Lines b2;
|
||||
if(USED_LANES>8) {
|
||||
transpose8<1,2>(b.bytes,b2.bytes);
|
||||
transpose8<1,2>(b.bytes+8,b2.bytes+1);
|
||||
} else {
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
}
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(register uint32_t i = 0; i < (USED_LANES/2); i++) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
*FastPin<FIRST_PIN>::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
b.bytes[i+(USED_LANES/2)] = pixels.template loadAndScale<PX>(pixels,i+(USED_LANES/2),d,scale);
|
||||
}
|
||||
|
||||
// if folks use an odd numnber of lanes, get the last byte's value here
|
||||
if(USED_LANES & 0x01) {
|
||||
b.bytes[USED_LANES-1] = pixels.template loadAndScale<PX>(pixels,USED_LANES-1,d,scale);
|
||||
}
|
||||
|
||||
for(register uint32_t i = USED_LANES/2; i < 8; i++) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+(2*(F_CPU/24000000))));
|
||||
if(USED_LANES>8) {
|
||||
*FastPin<FIRST_PIN>::cport() = ((~b2.shorts[i]) & PORT_MASK);
|
||||
} else {
|
||||
// b2.bytes[0] = 0;
|
||||
*FastPin<FIRST_PIN>::cport() = (PORT_SHIFT(~b2.bytes[7-i]) & PORT_MASK);
|
||||
}
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, LANE_MASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
register Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < USED_LANES; i++) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-5)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
allpixels.stepDithering();
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
#define PMASK ((1<<(LANES))-1)
|
||||
#define PMASK_HI (PMASK>>8 & 0xFF)
|
||||
#define PMASK_LO (PMASK & 0xFF)
|
||||
|
||||
template <uint8_t LANES, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class SixteenWayInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PMASK> {
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<PORTC_FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
static_assert(LANES <= 16, "Maximum of 16 lanes for Teensy parallel controllers!");
|
||||
// FastPin<30>::setOutput();
|
||||
// FastPin<29>::setOutput();
|
||||
// FastPin<27>::setOutput();
|
||||
// FastPin<28>::setOutput();
|
||||
switch(LANES) {
|
||||
case 16: FastPin<12>::setOutput();
|
||||
case 15: FastPin<11>::setOutput();
|
||||
case 14: FastPin<13>::setOutput();
|
||||
case 13: FastPin<10>::setOutput();
|
||||
case 12: FastPin<9>::setOutput();
|
||||
case 11: FastPin<23>::setOutput();
|
||||
case 10: FastPin<22>::setOutput();
|
||||
case 9: FastPin<15>::setOutput();
|
||||
|
||||
case 8: FastPin<5>::setOutput();
|
||||
case 7: FastPin<21>::setOutput();
|
||||
case 6: FastPin<20>::setOutput();
|
||||
case 5: FastPin<6>::setOutput();
|
||||
case 4: FastPin<8>::setOutput();
|
||||
case 3: FastPin<7>::setOutput();
|
||||
case 2: FastPin<14>::setOutput();
|
||||
case 1: FastPin<2>::setOutput();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PMASK> & pixels) {
|
||||
mWait.wait();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[16];
|
||||
uint16_t shorts[8];
|
||||
uint32_t raw[4];
|
||||
} Lines;
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, PixelController<RGB_ORDER,LANES, PMASK> &pixels) { // , register uint32_t & b2) {
|
||||
register Lines b2;
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
transpose8x1(b.bytes+8,b2.bytes+8);
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(register uint32_t i = 0; (i < LANES) && (i < 8); i++) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + (T1+T2+T3)-3;
|
||||
*FastPin<PORTD_FIRST_PIN>::sport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::sport() = PMASK_HI;
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T2+T3+6));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = ((~b2.bytes[7-i]) & PMASK_LO);
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = ((~b2.bytes[15-i]) & PMASK_HI);
|
||||
|
||||
while((next_mark - ARM_DWT_CYCCNT) > (T3));
|
||||
*FastPin<PORTD_FIRST_PIN>::cport() = PMASK_LO;
|
||||
*FastPin<PORTC_FIRST_PIN>::cport() = PMASK_HI;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
if(LANES==16 || (LANES>8 && ((i+8) < LANES))) {
|
||||
b.bytes[i+8] = pixels.template loadAndScale<PX>(pixels,i+8,d,scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, PMASK> &allpixels) {
|
||||
// Get access to the clock
|
||||
ARM_DEMCR |= ARM_DEMCR_TRCENA;
|
||||
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
|
||||
ARM_DWT_CYCCNT = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
allpixels.preStepFirstByteDithering();
|
||||
register Lines b0;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(int i = 0; i < LANES; i++) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + (T1+T2+T3);
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
allpixels.stepDithering();
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return ARM_DWT_CYCCNT; }
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
|
||||
#if 0 && (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
sei();
|
||||
|
||||
return ARM_DWT_CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
14
libraries/FastLED/platforms/arm/k66/fastled_arm_k66.h
Normal file
14
libraries/FastLED/platforms/arm/k66/fastled_arm_k66.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __INC_FASTLED_ARM_K66_H
|
||||
#define __INC_FASTLED_ARM_K66_H
|
||||
|
||||
// Include the k66 headers
|
||||
#include "fastpin_arm_k66.h"
|
||||
#include "fastspi_arm_k66.h"
|
||||
#include "../k20/octows2811_controller.h"
|
||||
#include "../k20/ws2812serial_controller.h"
|
||||
#include "../k20/smartmatrix_t3.h"
|
||||
#include "clockless_arm_k66.h"
|
||||
#include "clockless_block_arm_k66.h"
|
||||
|
||||
#endif
|
||||
|
||||
128
libraries/FastLED/platforms/arm/k66/fastpin_arm_k66.h
Normal file
128
libraries/FastLED/platforms/arm/k66/fastpin_arm_k66.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#ifndef __FASTPIN_ARM_K66_H
|
||||
#define __FASTPIN_ARM_K66_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
/// Template definition for teensy 3.0 style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
|
||||
/// does a poor job of optimizing around these accesses so they are not being used just yet.
|
||||
template<uint8_t PIN, int _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN_BITBAND {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 1; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { *_PTOR::template rx<_BIT>() = 1; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return 1; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
|
||||
};
|
||||
|
||||
// Macros for k20 pin access/definition
|
||||
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
|
||||
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
|
||||
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
|
||||
#define _FL_IO(L,C) _RD32(GPIO ## L ## _PDOR); _RD32(GPIO ## L ## _PSOR); _RD32(GPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(GPIO ## L ## _PDIR); _RD32(GPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(GPIO ## L ## _PDOR));
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; \
|
||||
template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {};
|
||||
|
||||
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
#define MAX_PIN 63
|
||||
_FL_DEFPIN( 0, 16, B); _FL_DEFPIN( 1, 17, B); _FL_DEFPIN( 2, 0, D); _FL_DEFPIN( 3, 12, A);
|
||||
_FL_DEFPIN( 4, 13, A); _FL_DEFPIN( 5, 7, D); _FL_DEFPIN( 6, 4, D); _FL_DEFPIN( 7, 2, D);
|
||||
_FL_DEFPIN( 8, 3, D); _FL_DEFPIN( 9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
|
||||
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
|
||||
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
|
||||
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
|
||||
_FL_DEFPIN(24, 26, E); _FL_DEFPIN(25, 5, A); _FL_DEFPIN(26, 14, A); _FL_DEFPIN(27, 15, A);
|
||||
_FL_DEFPIN(28, 16, A); _FL_DEFPIN(29, 18, B); _FL_DEFPIN(30, 19, B); _FL_DEFPIN(31, 10, B);
|
||||
_FL_DEFPIN(32, 11, B); _FL_DEFPIN(33, 24, E); _FL_DEFPIN(34, 25, E); _FL_DEFPIN(35, 8, C);
|
||||
_FL_DEFPIN(36, 9, C); _FL_DEFPIN(37, 10, C); _FL_DEFPIN(38, 11, C); _FL_DEFPIN(39, 17, A);
|
||||
_FL_DEFPIN(40, 28, A); _FL_DEFPIN(41, 29, A); _FL_DEFPIN(42, 26, A); _FL_DEFPIN(43, 20, B);
|
||||
_FL_DEFPIN(44, 22, B); _FL_DEFPIN(45, 23, B); _FL_DEFPIN(46, 21, B); _FL_DEFPIN(47, 8, D);
|
||||
_FL_DEFPIN(48, 9, D); _FL_DEFPIN(49, 4, B); _FL_DEFPIN(50, 5, B); _FL_DEFPIN(51, 14, D);
|
||||
_FL_DEFPIN(52, 13, D); _FL_DEFPIN(53, 12, D); _FL_DEFPIN(54, 15, D); _FL_DEFPIN(55, 11, D);
|
||||
_FL_DEFPIN(56, 10, E); _FL_DEFPIN(57, 11, E); _FL_DEFPIN(58, 0, E); _FL_DEFPIN(59, 1, E);
|
||||
_FL_DEFPIN(60, 2, E); _FL_DEFPIN(61, 3, E); _FL_DEFPIN(62, 4, E); _FL_DEFPIN(63, 5, E);
|
||||
|
||||
|
||||
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
|
||||
#define SPI2_DATA 7
|
||||
#define SPI2_CLOCK 14
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#define ARM_HARDWARE_SPI
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_K66
|
||||
470
libraries/FastLED/platforms/arm/k66/fastspi_arm_k66.h
Normal file
470
libraries/FastLED/platforms/arm/k66/fastspi_arm_k66.h
Normal file
@@ -0,0 +1,470 @@
|
||||
#ifndef __INC_FASTSPI_ARM_H
|
||||
#define __INC_FASTSPI_ARM_H
|
||||
|
||||
//
|
||||
// copied from k20 code
|
||||
// changed SPI1 define to KINETISK_SPI1
|
||||
// TODO: add third alternative MOSI pin (28) and CLOCK pin (27)
|
||||
// TODO: add alternative pins for SPI1
|
||||
// TODO: add SPI2 output
|
||||
//
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_TEENSY3) && defined(CORE_TEENSY)
|
||||
|
||||
// Version 1.20 renamed SPI_t to KINETISK_SPI_t
|
||||
#if TEENSYDUINO >= 120
|
||||
#define SPI_t KINETISK_SPI_t
|
||||
#endif
|
||||
|
||||
#ifndef KINETISK_SPI0
|
||||
#define KINETISK_SPI0 SPI0
|
||||
#endif
|
||||
|
||||
#ifndef SPI_PUSHR_CONT
|
||||
#define SPI_PUSHR_CONT SPIX.PUSHR_CONT
|
||||
#define SPI_PUSHR_CTAS(X) SPIX.PUSHR_CTAS(X)
|
||||
#define SPI_PUSHR_EOQ SPIX.PUSHR_EOQ
|
||||
#define SPI_PUSHR_CTCNT SPIX.PUSHR_CTCNT
|
||||
#define SPI_PUSHR_PCS(X) SPIX.PUSHR_PCS(X)
|
||||
#endif
|
||||
|
||||
// Template function that, on compilation, expands to a constant representing the highest bit set in a byte. Right now,
|
||||
// if no bits are set (value is 0), it returns 0, which is also the value returned if the lowest bit is the only bit
|
||||
// set (the zero-th bit). Unclear if I will want this to change at some point.
|
||||
template<int VAL, int BIT> class BitWork {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return (VAL & 1 << BIT) ? BIT : BitWork<VAL, BIT-1>::highestBit(); }
|
||||
};
|
||||
template<int VAL> class BitWork<VAL, 0> {
|
||||
public:
|
||||
static int highestBit() __attribute__((always_inline)) { return 0; }
|
||||
};
|
||||
|
||||
#define MAX(A, B) (( (A) > (B) ) ? (A) : (B))
|
||||
|
||||
#define USE_CONT 0
|
||||
// intra-frame backup data
|
||||
struct SPIState {
|
||||
uint32_t _ctar0,_ctar1;
|
||||
uint32_t pins[4];
|
||||
};
|
||||
|
||||
// extern SPIState gState;
|
||||
|
||||
|
||||
// Templated function to translate a clock divider value into the prescalar, scalar, and clock doubling setting for the world.
|
||||
template <int VAL> void getScalars(uint32_t & preScalar, uint32_t & scalar, uint32_t & dbl) {
|
||||
switch(VAL) {
|
||||
// Handle the dbl clock cases
|
||||
case 0: case 1:
|
||||
case 2: preScalar = 0; scalar = 0; dbl = 1; break;
|
||||
case 3: preScalar = 1; scalar = 0; dbl = 1; break;
|
||||
case 5: preScalar = 2; scalar = 0; dbl = 1; break;
|
||||
case 7: preScalar = 3; scalar = 0; dbl = 1; break;
|
||||
|
||||
// Handle the scalar value 6 cases (since it's not a power of two, it won't get caught
|
||||
// below)
|
||||
case 9: preScalar = 1; scalar = 2; dbl = 1; break;
|
||||
case 18: case 19: preScalar = 1; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 15: preScalar = 2; scalar = 2; dbl = 1; break;
|
||||
case 30: case 31: preScalar = 2; scalar = 2; dbl = 0; break;
|
||||
|
||||
case 21: case 22: case 23: preScalar = 3; scalar = 2; dbl = 1; break;
|
||||
case 42: case 43: case 44: case 45: case 46: case 47: preScalar = 3; scalar = 2; dbl = 0; break;
|
||||
default: {
|
||||
int p2 = BitWork<VAL/2, 15>::highestBit();
|
||||
int p3 = BitWork<VAL/3, 15>::highestBit();
|
||||
int p5 = BitWork<VAL/5, 15>::highestBit();
|
||||
int p7 = BitWork<VAL/7, 15>::highestBit();
|
||||
|
||||
int w2 = 2 * (1 << p2);
|
||||
int w3 = (VAL/3) > 0 ? 3 * (1 << p3) : 0;
|
||||
int w5 = (VAL/5) > 0 ? 5 * (1 << p5) : 0;
|
||||
int w7 = (VAL/7) > 0 ? 7 * (1 << p7) : 0;
|
||||
|
||||
int maxval = MAX(MAX(w2, w3), MAX(w5, w7));
|
||||
|
||||
if(w2 == maxval) { preScalar = 0; scalar = p2; }
|
||||
else if(w3 == maxval) { preScalar = 1; scalar = p3; }
|
||||
else if(w5 == maxval) { preScalar = 2; scalar = p5; }
|
||||
else if(w7 == maxval) { preScalar = 3; scalar = p7; }
|
||||
|
||||
dbl = 0;
|
||||
if(scalar == 0) { dbl = 1; }
|
||||
else if(scalar < 3) { scalar--; }
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#define SPIX (*(SPI_t*)pSPIX)
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
|
||||
class ARMHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
SPIState gState;
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code -- note, enabling pin 7 disables pin 11 (and vice versa),
|
||||
// and likewise enabling pin 14 disables pin 13 (and vice versa)
|
||||
inline void enable_pins(void) __attribute__((always_inline)) {
|
||||
//serial_print("enable_pins\n");
|
||||
switch(_DATA_PIN) {
|
||||
case 7:
|
||||
CORE_PIN7_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 11:
|
||||
CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13:
|
||||
CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
case 14:
|
||||
CORE_PIN14_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
|
||||
CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from the teensy3 SPSR emulation code. We disable the pins that we're using, and restore the state on the pins that we aren't using
|
||||
inline void disable_pins(void) __attribute__((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN11_CONFIG = gState.pins[1]; break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN7_CONFIG = gState.pins[0]; break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN14_CONFIG = gState.pins[3]; break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); CORE_PIN13_CONFIG = gState.pins[2]; break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctars(uint32_t ctar0, uint32_t ctar1) __attribute__((always_inline)) {
|
||||
if(SPIX.CTAR0 == ctar0 && SPIX.CTAR1 == ctar1) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if(mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar0;
|
||||
SPIX.CTAR1 = ctar1;
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar0(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR0 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR0 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR0 = ctar;
|
||||
|
||||
SPIX.MCR = mcr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_ctar1(uint32_t ctar) __attribute__((always_inline)) {
|
||||
if (SPIX.CTAR1 == ctar) return;
|
||||
uint32_t mcr = SPIX.MCR;
|
||||
if (mcr & SPI_MCR_MDIS) {
|
||||
SPIX.CTAR1 = ctar;
|
||||
} else {
|
||||
SPIX.MCR = mcr | SPI_MCR_MDIS | SPI_MCR_HALT;
|
||||
SPIX.CTAR1 = ctar;
|
||||
SPIX.MCR = mcr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
// Configure CTAR0, defaulting to 8 bits and CTAR1, defaulting to 16 bits
|
||||
uint32_t _PBR = 0;
|
||||
uint32_t _BR = 0;
|
||||
uint32_t _CSSCK = 0;
|
||||
uint32_t _DBR = 0;
|
||||
|
||||
// if(_SPI_CLOCK_DIVIDER >= 256) { _PBR = 0; _BR = _CSSCK = 7; _DBR = 0; } // osc/256
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 128) { _PBR = 0; _BR = _CSSCK = 6; _DBR = 0; } // osc/128
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 64) { _PBR = 0; _BR = _CSSCK = 5; _DBR = 0; } // osc/64
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 32) { _PBR = 0; _BR = _CSSCK = 4; _DBR = 0; } // osc/32
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 16) { _PBR = 0; _BR = _CSSCK = 3; _DBR = 0; } // osc/16
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 8) { _PBR = 0; _BR = _CSSCK = 1; _DBR = 0; } // osc/8
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 7) { _PBR = 3; _BR = _CSSCK = 0; _DBR = 1; } // osc/7
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 5) { _PBR = 2; _BR = _CSSCK = 0; _DBR = 1; } // osc/5
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 4) { _PBR = 0; _BR = _CSSCK = 0; _DBR = 0; } // osc/4
|
||||
// else if(_SPI_CLOCK_DIVIDER >= 3) { _PBR = 1; _BR = _CSSCK = 0; _DBR = 1; } // osc/3
|
||||
// else { _PBR = 0; _BR = _CSSCK = 0; _DBR = 1; } // osc/2
|
||||
|
||||
getScalars<_SPI_CLOCK_DIVIDER>(_PBR, _BR, _DBR);
|
||||
_CSSCK = _BR;
|
||||
|
||||
uint32_t ctar0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
uint32_t ctar1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(_PBR) | SPI_CTAR_BR(_BR) | SPI_CTAR_CSSCK(_CSSCK);
|
||||
|
||||
#if USE_CONT == 1
|
||||
ctar0 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
ctar1 |= SPI_CTAR_CPHA | SPI_CTAR_CPOL;
|
||||
#endif
|
||||
|
||||
if(_DBR) {
|
||||
ctar0 |= SPI_CTAR_DBR;
|
||||
ctar1 |= SPI_CTAR_DBR;
|
||||
}
|
||||
|
||||
update_ctars(ctar0,ctar1);
|
||||
}
|
||||
|
||||
void inline save_spi_state() __attribute__ ((always_inline)) {
|
||||
// save ctar data
|
||||
gState._ctar0 = SPIX.CTAR0;
|
||||
gState._ctar1 = SPIX.CTAR1;
|
||||
|
||||
// save data for the not-us pins
|
||||
gState.pins[0] = CORE_PIN7_CONFIG;
|
||||
gState.pins[1] = CORE_PIN11_CONFIG;
|
||||
gState.pins[2] = CORE_PIN13_CONFIG;
|
||||
gState.pins[3] = CORE_PIN14_CONFIG;
|
||||
}
|
||||
|
||||
void inline restore_spi_state() __attribute__ ((always_inline)) {
|
||||
// restore ctar data
|
||||
update_ctars(gState._ctar0,gState._ctar1);
|
||||
|
||||
// restore data for the not-us pins (not necessary because disable_pins will do this)
|
||||
// CORE_PIN7_CONFIG = gState.pins[0];
|
||||
// CORE_PIN11_CONFIG = gState.pins[1];
|
||||
// CORE_PIN13_CONFIG = gState.pins[2];
|
||||
// CORE_PIN14_CONFIG = gState.pins[3];
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
ARMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
|
||||
void init() {
|
||||
// set the pins to output
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// Enable SPI0 clock
|
||||
uint32_t sim6 = SIM_SCGC6;
|
||||
if((SPI_t*)pSPIX == &KINETISK_SPI0) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI0)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI0;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
} else if((SPI_t*)pSPIX == &KINETISK_SPI1) {
|
||||
if (!(sim6 & SIM_SCGC6_SPI1)) {
|
||||
//serial_print("init1\n");
|
||||
SIM_SCGC6 = sim6 | SIM_SCGC6_SPI1;
|
||||
SPIX.CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(1) | SPI_CTAR_BR(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure SPI as the master and enable
|
||||
SPIX.MCR |= SPI_MCR_MSTR; // | SPI_MCR_CONT_SCKE);
|
||||
SPIX.MCR &= ~(SPI_MCR_MDIS | SPI_MCR_HALT);
|
||||
|
||||
// pin/spi configuration happens on select
|
||||
}
|
||||
|
||||
static void waitFully() __attribute__((always_inline)) {
|
||||
// Wait for the last byte to get shifted into the register
|
||||
bool empty = false;
|
||||
|
||||
do {
|
||||
cli();
|
||||
if ((SPIX.SR & 0xF000) > 0) {
|
||||
// reset the TCF flag
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
} else {
|
||||
empty = true;
|
||||
}
|
||||
sei();
|
||||
} while (!empty);
|
||||
|
||||
// wait for the TCF flag to get set
|
||||
while (!(SPIX.SR & SPI_SR_TCF));
|
||||
SPIX.SR |= (SPI_SR_TCF | SPI_SR_EOQF);
|
||||
}
|
||||
|
||||
static bool needwait() __attribute__((always_inline)) { return (SPIX.SR & 0x4000); }
|
||||
static void wait() __attribute__((always_inline)) { while( (SPIX.SR & 0x4000) ); }
|
||||
static void wait1() __attribute__((always_inline)) { while( (SPIX.SR & 0xF000) >= 0x2000); }
|
||||
|
||||
enum ECont { CONT, NOCONT };
|
||||
enum EWait { PRE, POST, NONE };
|
||||
enum ELast { NOTLAST, LAST };
|
||||
|
||||
#if USE_CONT == 1
|
||||
#define CM CONT
|
||||
#else
|
||||
#define CM NOCONT
|
||||
#endif
|
||||
#define WM PRE
|
||||
|
||||
template<ECont CONT_STATE, EWait WAIT_STATE, ELast LAST_STATE> class Write {
|
||||
public:
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(1) | (w & 0xFFFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) {
|
||||
if(WAIT_STATE == PRE) { wait(); }
|
||||
SPIX.PUSHR = ((LAST_STATE == LAST) ? SPI_PUSHR_EOQ : 0) |
|
||||
((CONT_STATE == CONT) ? SPI_PUSHR_CONT : 0) |
|
||||
SPI_PUSHR_CTAS(0) | (b & 0xFF);
|
||||
SPIX.SR |= SPI_SR_TCF;
|
||||
if(WAIT_STATE == POST) { wait(); }
|
||||
}
|
||||
};
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeWordNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF);SPIX.SR |= SPI_SR_TCF; wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
static void writeWordCont(uint16_t w) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeWordContNoWait(uint16_t w) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1) | (w & 0xFFFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
static void writeByteCont(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
static void writeByteContPostWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;wait(); }
|
||||
static void writeByteContNoWait(uint8_t b) __attribute__((always_inline)) { SPIX.PUSHR = SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0) | (b & 0xFF); SPIX.SR |= SPI_SR_TCF;}
|
||||
|
||||
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 1 bit transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(0);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
writeWord( (b & (1 << BIT)) != 0);
|
||||
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
save_spi_state();
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
setSPIRate();
|
||||
enable_pins();
|
||||
}
|
||||
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
disable_pins();
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
restore_spi_state();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { Write<CM, WM, NOTLAST>::writeByte(value); }
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
// could be optimized to write 16bit words out instead of 8bit bytes
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
// Setup the pixel controller
|
||||
if((FLAGS & FLAG_START_BIT) == 0) {
|
||||
//If no start bit stupiditiy, write out as many 16-bit blocks as we can
|
||||
while(pixels.has(2)) {
|
||||
// Load and write out the first two bytes
|
||||
if(WM == NONE) { wait1(); }
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
|
||||
// Load and write out the next two bytes (step dithering, advance data in between since we
|
||||
// cross pixels here)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale2()) << 8 | D::adjust(pixels.stepAdvanceAndLoadAndScale0()));
|
||||
|
||||
// Load and write out the next two bytes
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale1()) << 8 | D::adjust(pixels.loadAndScale2()));
|
||||
pixels.stepDithering();
|
||||
pixels.advanceData();
|
||||
}
|
||||
|
||||
if(pixels.has(1)) {
|
||||
if(WM == NONE) { wait1(); }
|
||||
// write out the rest as alternating 16/8-bit blocks (likely to be just one)
|
||||
Write<CM, WM, NOTLAST>::writeWord(D::adjust(pixels.loadAndScale0()) << 8 | D::adjust(pixels.loadAndScale1()));
|
||||
Write<CM, WM, NOTLAST>::writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
} else if(FLAGS & FLAG_START_BIT) {
|
||||
uint32_t ctar1_save = SPIX.CTAR1;
|
||||
|
||||
// Clear out the FMSZ bits, reset them for 9 bits transferd for the start bit
|
||||
uint32_t ctar1 = (ctar1_save & (~SPI_CTAR_FMSZ(15))) | SPI_CTAR_FMSZ(8);
|
||||
update_ctar1(ctar1);
|
||||
|
||||
while(pixels.has(1)) {
|
||||
writeWord( 0x100 | D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
|
||||
// restore ctar1
|
||||
update_ctar1(ctar1_save);
|
||||
}
|
||||
release();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
46
libraries/FastLED/platforms/arm/k66/led_sysdefs_arm_k66.h
Normal file
46
libraries/FastLED/platforms/arm/k66/led_sysdefs_arm_k66.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_K66_H
|
||||
#define __INC_LED_SYSDEFS_ARM_K66_H
|
||||
|
||||
#define FASTLED_TEENSY3
|
||||
#define FASTLED_ARM
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 192000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
extern volatile uint32_t systick_millis_count;
|
||||
# define MS_COUNTER systick_millis_count
|
||||
|
||||
|
||||
// Default to using PROGMEM, since TEENSY3 provides it
|
||||
// even though all it does is ignore it. Just being
|
||||
// conservative here in case TEENSY3 changes.
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
65
libraries/FastLED/platforms/arm/kl26/clockless_arm_kl26.h
Normal file
65
libraries/FastLED/platforms/arm/kl26/clockless_arm_kl26.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_KL26
|
||||
#define __INC_CLOCKLESS_ARM_KL26
|
||||
|
||||
#include "../common/m0clockless.h"
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
cli();
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
if(!clocks) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
clocks = showRGBInternal(pixels);
|
||||
}
|
||||
long microsTaken = CLKS_TO_MICROS(clocks * ((T1 + T2 + T3) * 24));
|
||||
MS_COUNTER += (microsTaken / 1000);
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mScale[0];
|
||||
data.s[1] = pixels.mScale[1];
|
||||
data.s[2] = pixels.mScale[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
|
||||
return showLedData<4,8,T1,T2,T3,RGB_ORDER, WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
|
||||
// return 0; // 0x00FFFFFF - _VAL;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_CLOCKLESS_ARM_KL26
|
||||
10
libraries/FastLED/platforms/arm/kl26/fastled_arm_kl26.h
Normal file
10
libraries/FastLED/platforms/arm/kl26/fastled_arm_kl26.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __INC_FASTLED_ARM_KL26_H
|
||||
#define __INC_FASTLED_ARM_KL26_H
|
||||
|
||||
// Include the k20 headers
|
||||
#include "fastpin_arm_kl26.h"
|
||||
#include "fastspi_arm_kl26.h"
|
||||
#include "clockless_arm_kl26.h"
|
||||
#include "../k20/ws2812serial_controller.h"
|
||||
|
||||
#endif
|
||||
88
libraries/FastLED/platforms/arm/kl26/fastpin_arm_kl26.h
Normal file
88
libraries/FastLED/platforms/arm/kl26/fastpin_arm_kl26.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef __FASTPIN_ARM_KL26_H
|
||||
#define __FASTPIN_ARM_KL26_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be sloightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/// Template definition for teensy LC style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PTOR, typename _PDIR, typename _PDDR> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PTOR::r() = _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
// Macros for kl26 pin access/definition
|
||||
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
|
||||
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
|
||||
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
|
||||
#define _FL_IO(L,C) _RD32(FGPIO ## L ## _PDOR); _RD32(FGPIO ## L ## _PSOR); _RD32(FGPIO ## L ## _PCOR); _RD32(GPIO ## L ## _PTOR); _RD32(FGPIO ## L ## _PDIR); _RD32(FGPIO ## L ## _PDDR); _FL_DEFINE_PORT3(L,C,_R(FGPIO ## L ## _PDOR));
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << BIT, _R(FGPIO ## L ## _PDOR), _R(FGPIO ## L ## _PSOR), _R(FGPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(FGPIO ## L ## _PDIR), _R(FGPIO ## L ## _PDDR)> {}; \
|
||||
/* template<> class FastPinBB<PIN> : public _ARMPIN_BITBAND<PIN, BIT, _R(GPIO ## L ## _PDOR), _R(GPIO ## L ## _PSOR), _R(GPIO ## L ## _PCOR), \
|
||||
_R(GPIO ## L ## _PTOR), _R(GPIO ## L ## _PDIR), _R(GPIO ## L ## _PDDR)> {}; */
|
||||
|
||||
_FL_IO(A,0); _FL_IO(B,1); _FL_IO(C,2); _FL_IO(D,3); _FL_IO(E,4);
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(FASTLED_TEENSYLC) && defined(CORE_TEENSY)
|
||||
|
||||
#define MAX_PIN 26
|
||||
_FL_DEFPIN(0, 16, B); _FL_DEFPIN(1, 17, B); _FL_DEFPIN(2, 0, D); _FL_DEFPIN(3, 1, A);
|
||||
_FL_DEFPIN(4, 2, A); _FL_DEFPIN(5, 7, D); _FL_DEFPIN(6, 4, D); _FL_DEFPIN(7, 2, D);
|
||||
_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 3, C); _FL_DEFPIN(10, 4, C); _FL_DEFPIN(11, 6, C);
|
||||
_FL_DEFPIN(12, 7, C); _FL_DEFPIN(13, 5, C); _FL_DEFPIN(14, 1, D); _FL_DEFPIN(15, 0, C);
|
||||
_FL_DEFPIN(16, 0, B); _FL_DEFPIN(17, 1, B); _FL_DEFPIN(18, 3, B); _FL_DEFPIN(19, 2, B);
|
||||
_FL_DEFPIN(20, 5, D); _FL_DEFPIN(21, 6, D); _FL_DEFPIN(22, 1, C); _FL_DEFPIN(23, 2, C);
|
||||
_FL_DEFPIN(24, 20, E); _FL_DEFPIN(25, 21, E); _FL_DEFPIN(26, 30, E);
|
||||
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
// #define SPI1 (*(SPI_t *)0x4002D000)
|
||||
|
||||
#define SPI2_DATA 0
|
||||
#define SPI2_CLOCK 20
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_K20
|
||||
252
libraries/FastLED/platforms/arm/kl26/fastspi_arm_kl26.h
Normal file
252
libraries/FastLED/platforms/arm/kl26/fastspi_arm_kl26.h
Normal file
@@ -0,0 +1,252 @@
|
||||
#ifndef __INC_FASTSPI_ARM_KL26_H
|
||||
#define __INC_FASTSPI_ARM_KL26_h
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template <int VAL> void getScalars(uint8_t & sppr, uint8_t & spr) {
|
||||
if(VAL > 4096) { sppr=7; spr=8; }
|
||||
else if(VAL > 3584) { sppr=6; spr=8; }
|
||||
else if(VAL > 3072) { sppr=5; spr=8; }
|
||||
else if(VAL > 2560) { sppr=4; spr=8; }
|
||||
else if(VAL > 2048) { sppr=7; spr=7; }
|
||||
else if(VAL > 2048) { sppr=3; spr=8; }
|
||||
else if(VAL > 1792) { sppr=6; spr=7; }
|
||||
else if(VAL > 1536) { sppr=5; spr=7; }
|
||||
else if(VAL > 1536) { sppr=2; spr=8; }
|
||||
else if(VAL > 1280) { sppr=4; spr=7; }
|
||||
else if(VAL > 1024) { sppr=7; spr=6; }
|
||||
else if(VAL > 1024) { sppr=3; spr=7; }
|
||||
else if(VAL > 1024) { sppr=1; spr=8; }
|
||||
else if(VAL > 896) { sppr=6; spr=6; }
|
||||
else if(VAL > 768) { sppr=5; spr=6; }
|
||||
else if(VAL > 768) { sppr=2; spr=7; }
|
||||
else if(VAL > 640) { sppr=4; spr=6; }
|
||||
else if(VAL > 512) { sppr=7; spr=5; }
|
||||
else if(VAL > 512) { sppr=3; spr=6; }
|
||||
else if(VAL > 512) { sppr=1; spr=7; }
|
||||
else if(VAL > 512) { sppr=0; spr=8; }
|
||||
else if(VAL > 448) { sppr=6; spr=5; }
|
||||
else if(VAL > 384) { sppr=5; spr=5; }
|
||||
else if(VAL > 384) { sppr=2; spr=6; }
|
||||
else if(VAL > 320) { sppr=4; spr=5; }
|
||||
else if(VAL > 256) { sppr=7; spr=4; }
|
||||
else if(VAL > 256) { sppr=3; spr=5; }
|
||||
else if(VAL > 256) { sppr=1; spr=6; }
|
||||
else if(VAL > 256) { sppr=0; spr=7; }
|
||||
else if(VAL > 224) { sppr=6; spr=4; }
|
||||
else if(VAL > 192) { sppr=5; spr=4; }
|
||||
else if(VAL > 192) { sppr=2; spr=5; }
|
||||
else if(VAL > 160) { sppr=4; spr=4; }
|
||||
else if(VAL > 128) { sppr=7; spr=3; }
|
||||
else if(VAL > 128) { sppr=3; spr=4; }
|
||||
else if(VAL > 128) { sppr=1; spr=5; }
|
||||
else if(VAL > 128) { sppr=0; spr=6; }
|
||||
else if(VAL > 112) { sppr=6; spr=3; }
|
||||
else if(VAL > 96) { sppr=5; spr=3; }
|
||||
else if(VAL > 96) { sppr=2; spr=4; }
|
||||
else if(VAL > 80) { sppr=4; spr=3; }
|
||||
else if(VAL > 64) { sppr=7; spr=2; }
|
||||
else if(VAL > 64) { sppr=3; spr=3; }
|
||||
else if(VAL > 64) { sppr=1; spr=4; }
|
||||
else if(VAL > 64) { sppr=0; spr=5; }
|
||||
else if(VAL > 56) { sppr=6; spr=2; }
|
||||
else if(VAL > 48) { sppr=5; spr=2; }
|
||||
else if(VAL > 48) { sppr=2; spr=3; }
|
||||
else if(VAL > 40) { sppr=4; spr=2; }
|
||||
else if(VAL > 32) { sppr=7; spr=1; }
|
||||
else if(VAL > 32) { sppr=3; spr=2; }
|
||||
else if(VAL > 32) { sppr=1; spr=3; }
|
||||
else if(VAL > 32) { sppr=0; spr=4; }
|
||||
else if(VAL > 28) { sppr=6; spr=1; }
|
||||
else if(VAL > 24) { sppr=5; spr=1; }
|
||||
else if(VAL > 24) { sppr=2; spr=2; }
|
||||
else if(VAL > 20) { sppr=4; spr=1; }
|
||||
else if(VAL > 16) { sppr=7; spr=0; }
|
||||
else if(VAL > 16) { sppr=3; spr=1; }
|
||||
else if(VAL > 16) { sppr=1; spr=2; }
|
||||
else if(VAL > 16) { sppr=0; spr=3; }
|
||||
else if(VAL > 14) { sppr=6; spr=0; }
|
||||
else if(VAL > 12) { sppr=5; spr=0; }
|
||||
else if(VAL > 12) { sppr=2; spr=1; }
|
||||
else if(VAL > 10) { sppr=4; spr=0; }
|
||||
else if(VAL > 8) { sppr=3; spr=0; }
|
||||
else if(VAL > 8) { sppr=1; spr=1; }
|
||||
else if(VAL > 8) { sppr=0; spr=2; }
|
||||
else if(VAL > 6) { sppr=2; spr=0; }
|
||||
else if(VAL > 4) { sppr=1; spr=0; }
|
||||
else if(VAL > 4) { sppr=0; spr=1; }
|
||||
else /* if(VAL > 2) */ { sppr=0; spr=0; }
|
||||
}
|
||||
|
||||
|
||||
#define SPIX (*(KINETISL_SPI_t*)pSPIX)
|
||||
#define ARM_HARDWARE_SPI
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER, uint32_t pSPIX>
|
||||
class ARMHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
|
||||
static inline void enable_pins(void) __attribute__((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 0: CORE_PIN0_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 1: CORE_PIN1_CONFIG = PORT_PCR_MUX(5); break;
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 8: CORE_PIN8_CONFIG = PORT_PCR_MUX(5); break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 12: CORE_PIN12_CONFIG = PORT_PCR_MUX(5); break;
|
||||
case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_MUX(2); break;
|
||||
case 20: CORE_PIN20_CONFIG = PORT_PCR_MUX(2); break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void disable_pins(void) __attribute((always_inline)) {
|
||||
switch(_DATA_PIN) {
|
||||
case 0: CORE_PIN0_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 1: CORE_PIN1_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 7: CORE_PIN7_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 8: CORE_PIN8_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 11: CORE_PIN11_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 12: CORE_PIN12_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 21: CORE_PIN21_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
}
|
||||
|
||||
switch(_CLOCK_PIN) {
|
||||
case 13: CORE_PIN13_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 14: CORE_PIN14_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
case 20: CORE_PIN20_CONFIG = PORT_PCR_SRE | PORT_PCR_MUX(1); break;
|
||||
}
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
uint8_t sppr, spr;
|
||||
getScalars<_SPI_CLOCK_DIVIDER>(sppr, spr);
|
||||
|
||||
// Set the speed
|
||||
SPIX.BR = SPI_BR_SPPR(sppr) | SPI_BR_SPR(spr);
|
||||
|
||||
// Also, force 8 bit transfers (don't want to juggle 8/16 since that flushes the world)
|
||||
SPIX.C2 = 0;
|
||||
SPIX.C1 |= SPI_C1_SPE;
|
||||
}
|
||||
|
||||
public:
|
||||
ARMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
ARMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() {
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// Enable the SPI clocks
|
||||
uint32_t sim4 = SIM_SCGC4;
|
||||
if ((pSPIX == 0x40076000) && !(sim4 & SIM_SCGC4_SPI0)) {
|
||||
SIM_SCGC4 = sim4 | SIM_SCGC4_SPI0;
|
||||
}
|
||||
|
||||
if ( (pSPIX == 0x40077000) && !(sim4 & SIM_SCGC4_SPI1)) {
|
||||
SIM_SCGC4 = sim4 | SIM_SCGC4_SPI1;
|
||||
}
|
||||
|
||||
SPIX.C1 = SPI_C1_MSTR | SPI_C1_SPE;
|
||||
SPIX.C2 = 0;
|
||||
SPIX.BR = SPI_BR_SPPR(1) | SPI_BR_SPR(0);
|
||||
}
|
||||
|
||||
// latch the CS select
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
setSPIRate();
|
||||
enable_pins();
|
||||
}
|
||||
|
||||
|
||||
// release the CS select
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
disable_pins();
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
}
|
||||
|
||||
// Wait for the world to be clear
|
||||
static void wait() __attribute__((always_inline)) { while(!(SPIX.S & SPI_S_SPTEF)); }
|
||||
|
||||
// wait until all queued up data has been written
|
||||
void waitFully() { wait(); }
|
||||
|
||||
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) { /* TODO */ }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPIX.DL = b; }
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w & 0xFF); }
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
setSPIRate();
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
setSPIRate();
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
// could be optimized to write 16bit words out instead of 8bit bytes
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
int len = pixels.mLen;
|
||||
|
||||
select();
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
} else {
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
47
libraries/FastLED/platforms/arm/kl26/led_sysdefs_arm_kl26.h
Normal file
47
libraries/FastLED/platforms/arm/kl26/led_sysdefs_arm_kl26.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_KL26_H
|
||||
#define __INC_LED_SYSDEFS_ARM_KL26_H
|
||||
|
||||
#define FASTLED_TEENSYLC
|
||||
#define FASTLED_ARM
|
||||
#define FASTLED_ARM_M0_PLUS
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
#define FASTLED_SPI_BYTE_ONLY
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
// #define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 96000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
extern volatile uint32_t systick_millis_count;
|
||||
# define MS_COUNTER systick_millis_count
|
||||
|
||||
// Default to using PROGMEM since TEENSYLC provides it
|
||||
// even though all it does is ignore it. Just being
|
||||
// conservative here in case TEENSYLC changes.
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,214 @@
|
||||
#ifndef __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H
|
||||
#define __INC_BLOCK_CLOCKLESS_ARM_MXRT1062_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the teensy4
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY4)
|
||||
|
||||
#define __FL_T4_MASK ((1<<(LANES))-1)
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class FlexibleInlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, __FL_T4_MASK> {
|
||||
|
||||
uint8_t m_bitOffsets[16];
|
||||
uint8_t m_nActualLanes;
|
||||
uint8_t m_nLowBit;
|
||||
uint8_t m_nHighBit;
|
||||
uint32_t m_nWriteMask;
|
||||
uint8_t m_nOutBlocks;
|
||||
uint32_t m_offsets[3];
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
|
||||
virtual int size() { return CLEDController::size() * m_nActualLanes; }
|
||||
|
||||
// For each pin, if we've hit our lane count, break, otherwise set the pin to output,
|
||||
// store the bit offset in our offset array, add this pin to the write mask, and if this
|
||||
// pin ends a block sequence, then break out of the switch as well
|
||||
#define _BLOCK_PIN(P) case P: { \
|
||||
if(m_nActualLanes == LANES) break; \
|
||||
FastPin<P>::setOutput(); \
|
||||
m_bitOffsets[m_nActualLanes++] = FastPin<P>::pinbit(); \
|
||||
m_nWriteMask |= FastPin<P>::mask(); \
|
||||
if( P == 27 || P == 7 || P == 30) break; \
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
// pre-initialize
|
||||
memset(m_bitOffsets,0,16);
|
||||
m_nActualLanes = 0;
|
||||
m_nLowBit = 33;
|
||||
m_nHighBit = 0;
|
||||
m_nWriteMask = 0;
|
||||
|
||||
// setup the bits and data tracking for parallel output
|
||||
switch(FIRST_PIN) {
|
||||
// GPIO6 block output
|
||||
_BLOCK_PIN( 1);
|
||||
_BLOCK_PIN( 0);
|
||||
_BLOCK_PIN(24);
|
||||
_BLOCK_PIN(25);
|
||||
_BLOCK_PIN(19);
|
||||
_BLOCK_PIN(18);
|
||||
_BLOCK_PIN(14);
|
||||
_BLOCK_PIN(15);
|
||||
_BLOCK_PIN(17);
|
||||
_BLOCK_PIN(16);
|
||||
_BLOCK_PIN(22);
|
||||
_BLOCK_PIN(23);
|
||||
_BLOCK_PIN(20);
|
||||
_BLOCK_PIN(21);
|
||||
_BLOCK_PIN(26);
|
||||
_BLOCK_PIN(27);
|
||||
// GPIO7 block output
|
||||
_BLOCK_PIN(10);
|
||||
_BLOCK_PIN(12);
|
||||
_BLOCK_PIN(11);
|
||||
_BLOCK_PIN(13);
|
||||
_BLOCK_PIN( 6);
|
||||
_BLOCK_PIN( 9);
|
||||
_BLOCK_PIN(32);
|
||||
_BLOCK_PIN( 8);
|
||||
_BLOCK_PIN( 7);
|
||||
// GPIO 37 block output
|
||||
_BLOCK_PIN(37);
|
||||
_BLOCK_PIN(36);
|
||||
_BLOCK_PIN(35);
|
||||
_BLOCK_PIN(34);
|
||||
_BLOCK_PIN(39);
|
||||
_BLOCK_PIN(38);
|
||||
_BLOCK_PIN(28);
|
||||
_BLOCK_PIN(31);
|
||||
_BLOCK_PIN(30);
|
||||
}
|
||||
|
||||
for(int i = 0; i < m_nActualLanes; i++) {
|
||||
if(m_bitOffsets[i] < m_nLowBit) { m_nLowBit = m_bitOffsets[i]; }
|
||||
if(m_bitOffsets[i] > m_nHighBit) { m_nHighBit = m_bitOffsets[i]; }
|
||||
}
|
||||
|
||||
m_nOutBlocks = (m_nHighBit + 8)/8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, __FL_T4_MASK> & pixels) {
|
||||
mWait.wait();
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 0
|
||||
uint32_t clocks = showRGBInternal(pixels);
|
||||
// Adjust the timer
|
||||
long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
#else
|
||||
showRGBInternal(pixels);
|
||||
#endif
|
||||
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[32];
|
||||
uint8_t bg[4][8];
|
||||
uint16_t shorts[16];
|
||||
uint32_t raw[8];
|
||||
} _outlines;
|
||||
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register _outlines & b, PixelController<RGB_ORDER, LANES, __FL_T4_MASK> &pixels) {
|
||||
_outlines b2;
|
||||
transpose8x1(b.bg[3], b2.bg[3]);
|
||||
transpose8x1(b.bg[2], b2.bg[2]);
|
||||
transpose8x1(b.bg[1], b2.bg[1]);
|
||||
transpose8x1(b.bg[0], b2.bg[0]);
|
||||
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
int x = 0;
|
||||
for(uint32_t i = 8; i > 0;) {
|
||||
i--;
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
*FastPin<FIRST_PIN>::sport() = m_nWriteMask;
|
||||
next_mark = ARM_DWT_CYCCNT + m_offsets[0];
|
||||
|
||||
uint32_t out = (b2.bg[3][i] << 24) | (b2.bg[2][i] << 16) | (b2.bg[1][i] << 8) | b2.bg[0][i];
|
||||
|
||||
out = ((~out) & m_nWriteMask);
|
||||
while((next_mark - ARM_DWT_CYCCNT) > m_offsets[1]);
|
||||
*FastPin<FIRST_PIN>::cport() = out;
|
||||
|
||||
out = m_nWriteMask;
|
||||
while((next_mark - ARM_DWT_CYCCNT) > m_offsets[2]);
|
||||
*FastPin<FIRST_PIN>::cport() = out;
|
||||
|
||||
// Read and store up to two bytes
|
||||
if (x < m_nActualLanes) {
|
||||
b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels,x,d,scale);
|
||||
x++;
|
||||
if (x < m_nActualLanes) {
|
||||
b.bytes[m_bitOffsets[x]] = pixels.template loadAndScale<PX>(pixels,x,d,scale);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t showRGBInternal(PixelController<RGB_ORDER,LANES, __FL_T4_MASK> &allpixels) {
|
||||
allpixels.preStepFirstByteDithering();
|
||||
_outlines b0;
|
||||
uint32_t start = ARM_DWT_CYCCNT;
|
||||
|
||||
for(int i = 0; i < m_nActualLanes; i++) {
|
||||
b0.bytes[m_bitOffsets[i]] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
cli();
|
||||
m_offsets[0] = _FASTLED_NS_TO_DWT(T1+T2+T3);
|
||||
m_offsets[1] = _FASTLED_NS_TO_DWT(T2+T3);
|
||||
m_offsets[2] = _FASTLED_NS_TO_DWT(T3);
|
||||
uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD));
|
||||
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + m_offsets[0];
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
allpixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; }
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b0, allpixels);
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
}
|
||||
|
||||
sei();
|
||||
|
||||
return ARM_DWT_CYCCNT - start;
|
||||
}
|
||||
};
|
||||
|
||||
template<template<uint8_t DATA_PIN, EOrder RGB_ORDER> class CHIPSET, uint8_t DATA_PIN, int NUM_LANES, EOrder RGB_ORDER=GRB>
|
||||
class __FIBCC : public FlexibleInlineBlockClocklessController<NUM_LANES,DATA_PIN,CHIPSET<DATA_PIN,RGB_ORDER>::__T1(),CHIPSET<DATA_PIN,RGB_ORDER>::__T2(),CHIPSET<DATA_PIN,RGB_ORDER>::__T3(),RGB_ORDER,CHIPSET<DATA_PIN,RGB_ORDER>::__XTRA0(),CHIPSET<DATA_PIN,RGB_ORDER>::__FLIP(),CHIPSET<DATA_PIN,RGB_ORDER>::__WAIT_TIME()> {};
|
||||
|
||||
#define __FASTLED_HAS_FIBCC 1
|
||||
|
||||
#endif //defined(FASTLED_TEENSY4)
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_MXRT1062_H
|
||||
#define __INC_CLOCKLESS_ARM_MXRT1062_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the teensy4
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
#if defined(FASTLED_TEENSY4)
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
#define _FASTLED_NS_TO_DWT(_NS) (((F_CPU_ACTUAL>>16)*(_NS)) / (1000000000UL>>16))
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
uint32_t off[3];
|
||||
|
||||
public:
|
||||
static constexpr int __DATA_PIN() { return DATA_PIN; }
|
||||
static constexpr int __T1() { return T1; }
|
||||
static constexpr int __T2() { return T2; }
|
||||
static constexpr int __T3() { return T3; }
|
||||
static constexpr EOrder __RGB_ORDER() { return RGB_ORDER; }
|
||||
static constexpr int __XTRA0() { return XTRA0; }
|
||||
static constexpr bool __FLIP() { return FLIP; }
|
||||
static constexpr int __WAIT_TIME() { return WAIT_TIME; }
|
||||
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
FastPin<DATA_PIN>::lo();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
template<int BITS> __attribute__ ((always_inline)) inline void writeBits(register uint32_t & next_mark, register uint32_t & b) {
|
||||
for(register uint32_t i = BITS-1; i > 0; i--) {
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + off[0];
|
||||
FastPin<DATA_PIN>::hi();
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[1]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(ARM_DWT_CYCCNT < next_mark);
|
||||
next_mark = ARM_DWT_CYCCNT + off[1];
|
||||
FastPin<DATA_PIN>::hi();
|
||||
|
||||
if(b&0x80) {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
} else {
|
||||
while((next_mark - ARM_DWT_CYCCNT) > off[2]);
|
||||
FastPin<DATA_PIN>::lo();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
uint32_t start = ARM_DWT_CYCCNT;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
register uint32_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
off[0] = _FASTLED_NS_TO_DWT(T1+T2+T3);
|
||||
off[1] = _FASTLED_NS_TO_DWT(T2+T3);
|
||||
off[2] = _FASTLED_NS_TO_DWT(T3);
|
||||
uint32_t wait_off = _FASTLED_NS_TO_DWT((WAIT_TIME-INTERRUPT_THRESHOLD));
|
||||
|
||||
uint32_t next_mark = ARM_DWT_CYCCNT + off[0];
|
||||
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(ARM_DWT_CYCCNT > next_mark) {
|
||||
if((ARM_DWT_CYCCNT-next_mark) > wait_off) { sei(); return ARM_DWT_CYCCNT - start; }
|
||||
}
|
||||
#endif
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return ARM_DWT_CYCCNT - start;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef __INC_FASTLED_ARM_MXRT1062_H
|
||||
#define __INC_FASTLED_ARM_MXRT1062_H
|
||||
|
||||
#include "fastpin_arm_mxrt1062.h"
|
||||
#include "fastspi_arm_mxrt1062.h"
|
||||
#include "clockless_arm_mxrt1062.h"
|
||||
#include "block_clockless_arm_mxrt1062.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef __FASTPIN_ARM_MXRT1062_H
|
||||
#define __FASTPIN_ARM_MXRT1062_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
/// Template definition for teensy 4.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. It calls through to pinMode for setting input/output on pins
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
template<uint8_t PIN, uint32_t _BIT, uint32_t _MASK, typename _GPIO_DR, typename _GPIO_DR_SET, typename _GPIO_DR_CLEAR, typename _GPIO_DR_TOGGLE> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _GPIO_DR_SET::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _GPIO_DR_CLEAR::r() = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO_DR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _GPIO_DR_TOGGLE::r() = _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO_DR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO_DR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO_DR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO_DR_SET::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO_DR_CLEAR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
inline static uint32_t pinbit() __attribute__ ((always_inline)) { return _BIT; }
|
||||
};
|
||||
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } };
|
||||
#define _FL_IO(L) _RD32(GPIO ## L ## _DR); _RD32(GPIO ## L ## _DR_SET); _RD32(GPIO ## L ## _DR_CLEAR); _RD32(GPIO ## L ## _DR_TOGGLE); _FL_DEFINE_PORT(L, _R(GPIO ## L ## _DR));
|
||||
|
||||
// From the teensy core - it looks like there's the "default set" of port registers at GPIO1-5 - but then there
|
||||
// are a mirrored set for GPIO1-4 at GPIO6-9, which in the teensy core is referred to as "fast" - while the pin definitiosn
|
||||
// at https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=193716&viewfull=1#post193716
|
||||
// refer to GPIO1-4, we're going to use GPIO6-9 in the definitions below because the fast registers are what
|
||||
// the teensy core is using internally
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, _R(GPIO ## L ## _DR), _R(GPIO ## L ## _DR_SET), _R(GPIO ## L ## _DR_CLEAR), _R(GPIO ## L ## _DR_TOGGLE)> {};
|
||||
|
||||
#if defined(FASTLED_TEENSY4) && defined(CORE_TEENSY)
|
||||
_FL_IO(1); _FL_IO(2); _FL_IO(3); _FL_IO(4); _FL_IO(5);
|
||||
_FL_IO(6); _FL_IO(7); _FL_IO(8); _FL_IO(9);
|
||||
|
||||
#define MAX_PIN 39
|
||||
_FL_DEFPIN( 0, 3,6); _FL_DEFPIN( 1, 2,6); _FL_DEFPIN( 2, 4,9); _FL_DEFPIN( 3, 5,9);
|
||||
_FL_DEFPIN( 4, 6,9); _FL_DEFPIN( 5, 8,9); _FL_DEFPIN( 6,10,7); _FL_DEFPIN( 7,17,7);
|
||||
_FL_DEFPIN( 8,16,7); _FL_DEFPIN( 9,11,7); _FL_DEFPIN(10, 0,7); _FL_DEFPIN(11, 2,7);
|
||||
_FL_DEFPIN(12, 1,7); _FL_DEFPIN(13, 3,7); _FL_DEFPIN(14,18,6); _FL_DEFPIN(15,19,6);
|
||||
_FL_DEFPIN(16,23,6); _FL_DEFPIN(17,22,6); _FL_DEFPIN(18,17,6); _FL_DEFPIN(19,16,6);
|
||||
_FL_DEFPIN(20,26,6); _FL_DEFPIN(21,27,6); _FL_DEFPIN(22,24,6); _FL_DEFPIN(23,25,6);
|
||||
_FL_DEFPIN(24,12,6); _FL_DEFPIN(25,13,6); _FL_DEFPIN(26,30,6); _FL_DEFPIN(27,31,6);
|
||||
_FL_DEFPIN(28,18,8); _FL_DEFPIN(29,31,9); _FL_DEFPIN(30,23,8); _FL_DEFPIN(31,22,8);
|
||||
_FL_DEFPIN(32,12,7); _FL_DEFPIN(33, 7,9); _FL_DEFPIN(34,15,8); _FL_DEFPIN(35,14,8);
|
||||
_FL_DEFPIN(36,13,8); _FL_DEFPIN(37,12,8); _FL_DEFPIN(38,17,8); _FL_DEFPIN(39,16,8);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#define ARM_HARDWARE_SPI
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
|
||||
#define SPI1_DATA 26
|
||||
#define SPI1_CLOCK 27
|
||||
|
||||
#define SPI2_DATA 35
|
||||
#define SPI2_CLOCK 37
|
||||
|
||||
#endif // defined FASTLED_TEENSY4
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINSs
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
140
libraries/FastLED/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h
Normal file
140
libraries/FastLED/platforms/arm/mxrt1062/fastspi_arm_mxrt1062.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#ifndef __INC_FASTSPI_ARM_MXRT1062_H
|
||||
#define __INC_FASTSPI_ARM_MXRT1062_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined (FASTLED_TEENSY4) && defined(ARM_HARDWARE_SPI)
|
||||
#include <SPI.h>
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_RATE, SPIClass & _SPIObject, int _SPI_INDEX>
|
||||
class Teesy4HardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
uint32_t m_bitCount;
|
||||
uint32_t m_bitData;
|
||||
inline IMXRT_LPSPI_t & port() __attribute__((always_inline)) {
|
||||
switch(_SPI_INDEX) {
|
||||
case 0:
|
||||
return IMXRT_LPSPI4_S;
|
||||
case 1:
|
||||
return IMXRT_LPSPI3_S;
|
||||
case 2:
|
||||
return IMXRT_LPSPI1_S;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Teesy4HardwareSPIOutput() { m_pSelect = NULL; m_bitCount = 0;}
|
||||
Teesy4HardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; m_bitCount = 0;}
|
||||
|
||||
// set the object representing the selectable -- ignore for now
|
||||
void setSelect(Selectable *pSelect) { /* TODO */ }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() { _SPIObject.begin(); }
|
||||
|
||||
// latch the CS select
|
||||
void inline select() __attribute__((always_inline)) {
|
||||
// begin the SPI transaction
|
||||
_SPIObject.beginTransaction(SPISettings(_SPI_CLOCK_RATE, MSBFIRST, SPI_MODE0));
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
}
|
||||
|
||||
// release the CS select
|
||||
void inline release() __attribute__((always_inline)) {
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
_SPIObject.endTransaction();
|
||||
}
|
||||
|
||||
// wait until all queued up data has been written
|
||||
static void waitFully() { /* TODO */ }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register) -
|
||||
void inline writeByte(uint8_t b) __attribute__((always_inline)) {
|
||||
if(m_bitCount == 0) {
|
||||
_SPIObject.transfer(b);
|
||||
} else {
|
||||
// There's been a bit of data written, add that to the output as well
|
||||
uint32_t outData = (m_bitData << 8) | b;
|
||||
uint32_t tcr = port().TCR;
|
||||
port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8+m_bitCount) - 1); // turn on 9 bit mode
|
||||
port().TDR = outData; // output 9 bit data.
|
||||
while ((port().RSR & LPSPI_RSR_RXEMPTY)) ; // wait while the RSR fifo is empty...
|
||||
port().TCR = (tcr & 0xfffff000) | LPSPI_TCR_FRAMESZ((8) - 1); // turn back on 8 bit mode
|
||||
port().RDR;
|
||||
m_bitCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
void inline writeWord(uint16_t w) __attribute__((always_inline)) {
|
||||
writeByte(((w>>8) & 0xFF));
|
||||
_SPIObject.transfer(w & 0xFF);
|
||||
}
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { _SPIObject.transfer(value); }
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select(); writeBytesValueRaw(value, len); release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
// could be optimized to write 16bit words out instead of 8bit bytes
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
template <uint8_t BIT> inline void writeBit(uint8_t b) {
|
||||
m_bitData = (m_bitData<<1) | ((b&(1<<BIT)) != 0);
|
||||
// If this is the 8th bit we've collected, just write it out raw
|
||||
register uint32_t bc = m_bitCount;
|
||||
bc = (bc + 1) & 0x07;
|
||||
if (!bc) {
|
||||
m_bitCount = 0;
|
||||
_SPIObject.transfer(m_bitData);
|
||||
}
|
||||
m_bitCount = bc;
|
||||
}
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_MXRT1062_H
|
||||
#define __INC_LED_SYSDEFS_ARM_MXRT1062_H
|
||||
|
||||
#define FASTLED_TEENSY4
|
||||
#define FASTLED_ARM
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
#if (F_CPU == 96000000)
|
||||
#define CLK_DBL 1
|
||||
#endif
|
||||
|
||||
// Get some system include files
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
#if defined(ARDUINO) // && ARDUINO < 150
|
||||
typedef volatile uint32_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint32_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
#endif
|
||||
|
||||
// extern volatile uint32_t systick_millis_count;
|
||||
// # define MS_COUNTER systick_millis_count
|
||||
|
||||
// Teensy4 provides progmem
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
83
libraries/FastLED/platforms/arm/nrf51/clockless_arm_nrf51.h
Normal file
83
libraries/FastLED/platforms/arm/nrf51/clockless_arm_nrf51.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_NRF51
|
||||
#define __INC_CLOCKLESS_ARM_NRF51
|
||||
|
||||
#if defined(NRF51)
|
||||
|
||||
#include <nrf51_bitfields.h>
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS==1)
|
||||
#define SEI_CHK LED_TIMER->CC[0] = (WAIT_TIME * (F_CPU/1000000)); LED_TIMER->TASKS_CLEAR; LED_TIMER->EVENTS_COMPARE[0] = 0;
|
||||
#define CLI_CHK cli(); if(LED_TIMER->EVENTS_COMPARE[0]) { LED_TIMER->TASKS_STOP = 1; return 0; }
|
||||
#define INNER_SEI sei();
|
||||
#else
|
||||
#define SEI_CHK
|
||||
#define CLI_CHK
|
||||
#define INNER_SEI delaycycles<1>();
|
||||
#endif
|
||||
|
||||
|
||||
#include "../common/m0clockless.h"
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 75>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
cli();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mScale[0];
|
||||
data.s[1] = pixels.mScale[1];
|
||||
data.s[2] = pixels.mScale[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
|
||||
|
||||
// timer mode w/prescaler of 0
|
||||
LED_TIMER->MODE = TIMER_MODE_MODE_Timer;
|
||||
LED_TIMER->PRESCALER = 0;
|
||||
LED_TIMER->EVENTS_COMPARE[0] = 0;
|
||||
LED_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
|
||||
LED_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
|
||||
LED_TIMER->TASKS_START = 1;
|
||||
|
||||
int ret = showLedData<4,8,T1,T2,T3,RGB_ORDER,WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
|
||||
|
||||
LED_TIMER->TASKS_STOP = 1;
|
||||
return ret; // 0x00FFFFFF - _VAL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // NRF51
|
||||
#endif // __INC_CLOCKLESS_ARM_NRF51
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef __INC_FASTLED_ARM_NRF51_H
|
||||
#define __INC_FASTLED_ARM_NRF51_H
|
||||
|
||||
// Include the k20 headers
|
||||
#include "fastpin_arm_nrf51.h"
|
||||
#include "fastspi_arm_nrf51.h"
|
||||
#include "clockless_arm_nrf51.h"
|
||||
|
||||
#endif
|
||||
119
libraries/FastLED/platforms/arm/nrf51/fastpin_arm_nrf51.h
Normal file
119
libraries/FastLED/platforms/arm/nrf51/fastpin_arm_nrf51.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef __FASTPIN_ARM_NRF51_H
|
||||
#define __FASTPIN_ARM_NRF51_H
|
||||
|
||||
#if defined(NRF51)
|
||||
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
#if 0
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _DIRSET, typename _DIRCLR, typename _OUTSET, typename _OUTCLR, typename _OUT> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { _DIRSET::r() = _MASK; }
|
||||
inline static void setInput() { _DIRCLR::r() = _MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _OUTSET::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _OUTCLR::r() = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _OUT::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _OUT::r() ^= _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _OUT::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _OUT::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_OUT::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#define ADDR(X) *(volatile uint32_t*)X
|
||||
#define NR_GPIO_ADDR(base,offset) (*(volatile uint32_t *))((uint32_t)(base + offset))
|
||||
#define NR_DIRSET ADDR(0x50000518UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x518)
|
||||
#define NR_DIRCLR ADDR(0x5000051CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x51C)
|
||||
#define NR_OUTSET ADDR(0x50000508UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x508)
|
||||
#define NR_OUTCLR ADDR(0x5000050CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x50C)
|
||||
#define NR_OUT ADDR(0x50000504UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x504)
|
||||
|
||||
#define _RD32_NRF(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; }};
|
||||
|
||||
_RD32_NRF(NR_DIRSET);
|
||||
_RD32_NRF(NR_DIRCLR);
|
||||
_RD32_NRF(NR_OUTSET);
|
||||
_RD32_NRF(NR_OUTCLR);
|
||||
_RD32_NRF(NR_OUT);
|
||||
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN, \
|
||||
_R(NR_DIRSET), _R(NR_DIRCLR), _R(NR_OUTSET), _R(NR_OUTCLR), _R(NR_OUT)> {};
|
||||
#else
|
||||
|
||||
typedef struct { /*!< GPIO Structure */
|
||||
// __I uint32_t RESERVED0[321];
|
||||
__IO uint32_t OUT; /*!< Write GPIO port. */
|
||||
__IO uint32_t OUTSET; /*!< Set individual bits in GPIO port. */
|
||||
__IO uint32_t OUTCLR; /*!< Clear individual bits in GPIO port. */
|
||||
__I uint32_t IN; /*!< Read GPIO port. */
|
||||
__IO uint32_t DIR; /*!< Direction of GPIO pins. */
|
||||
__IO uint32_t DIRSET; /*!< DIR set register. */
|
||||
__IO uint32_t DIRCLR; /*!< DIR clear register. */
|
||||
__I uint32_t RESERVED1[120];
|
||||
__IO uint32_t PIN_CNF[32]; /*!< Configuration of GPIO pins. */
|
||||
} FL_NRF_GPIO_Type;
|
||||
|
||||
#define FL_NRF_GPIO_BASE 0x50000504UL
|
||||
#define FL_NRF_GPIO ((FL_NRF_GPIO_Type *) FL_NRF_GPIO_BASE)
|
||||
|
||||
template<uint8_t PIN, uint32_t _MASK> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { FL_NRF_GPIO->DIRSET = _MASK; }
|
||||
inline static void setInput() { FL_NRF_GPIO->DIRCLR = _MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTSET = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTCLR= _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT ^= _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &FL_NRF_GPIO->OUT; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
|
||||
inline static bool isset() __attribute__ ((always_inline)) { return (FL_NRF_GPIO->IN & _MASK) != 0; }
|
||||
};
|
||||
|
||||
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN> {};
|
||||
#endif
|
||||
|
||||
// Actual pin definitions
|
||||
#define MAX_PIN 31
|
||||
_FL_DEFPIN(0); _FL_DEFPIN(1); _FL_DEFPIN(2); _FL_DEFPIN(3);
|
||||
_FL_DEFPIN(4); _FL_DEFPIN(5); _FL_DEFPIN(6); _FL_DEFPIN(7);
|
||||
_FL_DEFPIN(8); _FL_DEFPIN(9); _FL_DEFPIN(10); _FL_DEFPIN(11);
|
||||
_FL_DEFPIN(12); _FL_DEFPIN(13); _FL_DEFPIN(14); _FL_DEFPIN(15);
|
||||
_FL_DEFPIN(16); _FL_DEFPIN(17); _FL_DEFPIN(18); _FL_DEFPIN(19);
|
||||
_FL_DEFPIN(20); _FL_DEFPIN(21); _FL_DEFPIN(22); _FL_DEFPIN(23);
|
||||
_FL_DEFPIN(24); _FL_DEFPIN(25); _FL_DEFPIN(26); _FL_DEFPIN(27);
|
||||
_FL_DEFPIN(28); _FL_DEFPIN(29); _FL_DEFPIN(30); _FL_DEFPIN(31);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
150
libraries/FastLED/platforms/arm/nrf51/fastspi_arm_nrf51.h
Normal file
150
libraries/FastLED/platforms/arm/nrf51/fastspi_arm_nrf51.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#ifndef __INC_FASTSPI_NRF_H
|
||||
#define __INC_FASTSPI_NRF_H
|
||||
|
||||
#ifdef NRF51
|
||||
|
||||
#ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
#define FASTLED_ALL_PINS_HARDWARE_SPI
|
||||
|
||||
// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
|
||||
// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
|
||||
// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class NRF51SPIOutput {
|
||||
|
||||
struct saveData {
|
||||
uint32_t sck;
|
||||
uint32_t mosi;
|
||||
uint32_t miso;
|
||||
uint32_t freq;
|
||||
uint32_t enable;
|
||||
} mSavedData;
|
||||
|
||||
void saveSPIData() {
|
||||
mSavedData.sck = NRF_SPI0->PSELSCK;
|
||||
mSavedData.mosi = NRF_SPI0->PSELMOSI;
|
||||
mSavedData.miso = NRF_SPI0->PSELMISO;
|
||||
mSavedData.freq = NRF_SPI0->FREQUENCY;
|
||||
mSavedData.enable = NRF_SPI0->ENABLE;
|
||||
}
|
||||
|
||||
void restoreSPIData() {
|
||||
NRF_SPI0->PSELSCK = mSavedData.sck;
|
||||
NRF_SPI0->PSELMOSI = mSavedData.mosi;
|
||||
NRF_SPI0->PSELMISO = mSavedData.miso;
|
||||
NRF_SPI0->FREQUENCY = mSavedData.freq;
|
||||
mSavedData.enable = NRF_SPI0->ENABLE;
|
||||
}
|
||||
|
||||
public:
|
||||
NRF51SPIOutput() { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
|
||||
NRF51SPIOutput(Selectable *pSelect) { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
|
||||
|
||||
// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { /* TODO */ }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() {
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
NRF_SPI0->PSELSCK = _CLOCK_PIN;
|
||||
NRF_SPI0->PSELMOSI = _DATA_PIN;
|
||||
NRF_SPI0->PSELMISO = 0xFFFFFFFF;
|
||||
NRF_SPI0->FREQUENCY = 0x80000000;
|
||||
NRF_SPI0->ENABLE = 1;
|
||||
NRF_SPI0->EVENTS_READY = 0;
|
||||
}
|
||||
|
||||
// latch the CS select
|
||||
void select() { saveSPIData(); init(); }
|
||||
|
||||
// release the CS select
|
||||
void release() { shouldWait(); restoreSPIData(); }
|
||||
|
||||
static bool shouldWait(bool wait = false) __attribute__((always_inline)) __attribute__((always_inline)) {
|
||||
// static bool sWait=false;
|
||||
// bool oldWait = sWait;
|
||||
// sWait = wait;
|
||||
// never going to bother with waiting since we're always running the spi clock at max speed on the rfduino
|
||||
// TODO: When we set clock rate, implement/fix waiting properly, otherwise the world hangs up
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait until all queued up data has been written
|
||||
static void waitFully() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
|
||||
static void wait() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); NRF_SPI0->TXD = b; NRF_SPI0->INTENCLR; shouldWait(true); }
|
||||
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)){ writeByte(w>>8); writeByte(w & 0xFF); }
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { writeByte(value); } }
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
template<class D> void writeBytes(uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(uint8_t *data, int len) {
|
||||
writeBytes<DATA_NOP>(data, len);
|
||||
}
|
||||
|
||||
// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
waitFully();
|
||||
NRF_SPI0->ENABLE = 0;
|
||||
if(b & 1<<BIT) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
NRF_SPI0->ENABLE = 1;
|
||||
}
|
||||
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef __LED_SYSDEFS_ARM_NRF51
|
||||
#define __LED_SYSDEFS_ARM_NRF51
|
||||
|
||||
#ifndef NRF51
|
||||
#define NRF51
|
||||
#endif
|
||||
|
||||
#define LED_TIMER NRF_TIMER1
|
||||
#define FASTLED_NO_PINMAP
|
||||
#define FASTLED_HAS_CLOCKLESS
|
||||
|
||||
#define FASTLED_SPI_BYTE_ONLY
|
||||
|
||||
#define FASTLED_ARM
|
||||
#define FASTLED_ARM_M0
|
||||
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 16000000
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <nrf51.h>
|
||||
#include <core_cm0.h>
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
typedef uint32_t prog_uint32_t;
|
||||
typedef uint8_t boolean;
|
||||
|
||||
#define PROGMEM
|
||||
#define NO_PROGMEM
|
||||
#define NEED_CXX_BITS
|
||||
|
||||
// Default to NOT using PROGMEM here
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
#endif
|
||||
115
libraries/FastLED/platforms/arm/nrf52/arbiter_nrf52.h
Normal file
115
libraries/FastLED/platforms/arm/nrf52/arbiter_nrf52.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef __INC_ARBITER_NRF52
|
||||
#define __INC_ARBITER_NRF52
|
||||
|
||||
#if defined(NRF52_SERIES)
|
||||
|
||||
#include "led_sysdefs_arm_nrf52.h"
|
||||
|
||||
//FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
typedef void (*FASTLED_NRF52_PWM_INTERRUPT_HANDLER)();
|
||||
|
||||
// a trick learned from other embedded projects ..
|
||||
// use the enum as an index to a statically-allocated array
|
||||
// to store unique information for that instance.
|
||||
// also provides a count of how many instances were enabled.
|
||||
//
|
||||
// See led_sysdefs_arm_nrf52.h for selection....
|
||||
//
|
||||
typedef enum _FASTLED_NRF52_ENABLED_PWM_INSTANCE {
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
|
||||
FASTLED_NRF52_PWM0_INSTANCE_IDX,
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
|
||||
FASTLED_NRF52_PWM1_INSTANCE_IDX,
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
|
||||
FASTLED_NRF52_PWM2_INSTANCE_IDX,
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
|
||||
FASTLED_NRF52_PWM3_INSTANCE_IDX,
|
||||
#endif
|
||||
FASTLED_NRF52_PWM_INSTANCE_COUNT
|
||||
} FASTLED_NRF52_ENABLED_PWM_INSTANCES;
|
||||
|
||||
static_assert(FASTLED_NRF52_PWM_INSTANCE_COUNT > 0, "Instance count must be greater than zero -- define FASTLED_NRF52_ENABLE_PWM_INSTNACE[n] (replace `[n]` with digit)");
|
||||
|
||||
template <uint32_t _PWM_ID>
|
||||
class PWM_Arbiter {
|
||||
|
||||
private:
|
||||
static_assert(_PWM_ID < 32, "PWM_ID over 31 breaks current arbitration bitmask");
|
||||
//const uint32_t _ACQUIRE_MASK = (1u << _PWM_ID) ;
|
||||
//const uint32_t _CLEAR_MASK = ~((uint32_t)(1u << _PWM_ID));
|
||||
static uint32_t s_PwmInUse;
|
||||
static NRF_PWM_Type * const s_PWM;
|
||||
static IRQn_Type const s_PWM_IRQ;
|
||||
static FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile s_Isr;
|
||||
|
||||
public:
|
||||
static void isr_handler() {
|
||||
return s_Isr();
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static bool isAcquired() {
|
||||
return (0u != (s_PwmInUse & 1u)); // _ACQUIRE_MASK
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void acquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) {
|
||||
while (!tryAcquire(isr));
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) {
|
||||
uint32_t oldValue = __sync_fetch_and_or(&s_PwmInUse, 1u); // _ACQUIRE_MASK
|
||||
if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
|
||||
s_Isr = isr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseFromIsr() {
|
||||
uint32_t oldValue = __sync_fetch_and_and(&s_PwmInUse, ~1u); // _CLEAR_MASK
|
||||
if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
|
||||
// TODO: This should never be true... indicates was not held.
|
||||
// Assert here?
|
||||
(void)oldValue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static NRF_PWM_Type * getPWM() {
|
||||
return s_PWM;
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static IRQn_Type getIRQn() { return s_PWM_IRQ; }
|
||||
};
|
||||
template <uint32_t _PWM_ID> NRF_PWM_Type * const PWM_Arbiter<_PWM_ID>::s_PWM =
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
|
||||
(_PWM_ID == 0 ? NRF_PWM0 :
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
|
||||
(_PWM_ID == 1 ? NRF_PWM1 :
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
|
||||
(_PWM_ID == 2 ? NRF_PWM2 :
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
|
||||
(_PWM_ID == 3 ? NRF_PWM3 :
|
||||
#endif
|
||||
(NRF_PWM_Type*)-1
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
|
||||
)
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
|
||||
)
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
|
||||
)
|
||||
#endif
|
||||
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
|
||||
)
|
||||
#endif
|
||||
;
|
||||
template <uint32_t _PWM_ID> IRQn_Type const PWM_Arbiter<_PWM_ID>::s_PWM_IRQ = ((IRQn_Type)((uint8_t)((uint32_t)(s_PWM) >> 12)));
|
||||
template <uint32_t _PWM_ID> uint32_t PWM_Arbiter<_PWM_ID>::s_PwmInUse = 0;
|
||||
template <uint32_t _PWM_ID> FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile PWM_Arbiter<_PWM_ID>::s_Isr = NULL;
|
||||
|
||||
//FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // NRF52_SERIES
|
||||
#endif // __INC_ARBITER_NRF52
|
||||
391
libraries/FastLED/platforms/arm/nrf52/clockless_arm_nrf52.h
Normal file
391
libraries/FastLED/platforms/arm/nrf52/clockless_arm_nrf52.h
Normal file
@@ -0,0 +1,391 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_NRF52
|
||||
#define __INC_CLOCKLESS_ARM_NRF52
|
||||
|
||||
#if defined(NRF52_SERIES)
|
||||
|
||||
|
||||
//FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
#define FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING 144 // TODO: Figure out how to safely let this be calller-defined....
|
||||
|
||||
// nRF52810 has a single PWM peripheral (PWM0)
|
||||
// nRF52832 has three PWM peripherals (PWM0, PWM1, PWM2)
|
||||
// nRF52840 has four PWM peripherals (PWM0, PWM1, PWM2, PWM3)
|
||||
// NOTE: Update platforms.cpp in root of FastLED library if this changes
|
||||
#define FASTLED_NRF52_PWM_ID 0
|
||||
|
||||
|
||||
extern uint32_t isrCount;
|
||||
|
||||
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER = RGB, int _XTRA0 = 0, bool _FLIP = false, int _WAIT_TIME_MICROSECONDS = 10>
|
||||
class ClocklessController : public CPixelLEDController<_RGB_ORDER> {
|
||||
static_assert(FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING > 0, "Maximum string length must be positive value (FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING)");
|
||||
static_assert(_T1 > 0 , "negative values are not allowed");
|
||||
static_assert(_T2 > 0 , "negative values are not allowed");
|
||||
static_assert(_T3 > 0 , "negative values are not allowed");
|
||||
static_assert(_T1 < (0x8000u-2u), "_T1 must fit in 15 bits");
|
||||
static_assert(_T2 < (0x8000u-2u), "_T2 must fit in 15 bits");
|
||||
static_assert(_T3 < (0x8000u-2u), "_T3 must fit in 15 bits");
|
||||
static_assert(_T1 < (0x8000u-2u), "_T0H must fit in 15 bits");
|
||||
static_assert(_T1+_T2 < (0x8000u-2u), "_T1H must fit in 15 bits");
|
||||
static_assert(_T1+_T2+_T3 < (0x8000u-2u), "_TOP must fit in 15 bits");
|
||||
static_assert(_T1+_T2+_T3 <= PWM_COUNTERTOP_COUNTERTOP_Msk, "_TOP too large for peripheral");
|
||||
|
||||
private:
|
||||
static const bool _INITIALIZE_PIN_HIGH = (_FLIP ? 1 : 0);
|
||||
static const uint16_t _POLARITY_BIT = (_FLIP ? 0 : 0x8000);
|
||||
|
||||
static const uint8_t _BITS_PER_PIXEL = (8 + _XTRA0) * 3; // NOTE: 3 means RGB only...
|
||||
static const uint16_t _PWM_BUFFER_COUNT = (_BITS_PER_PIXEL * FASTLED_NRF52_MAXIMUM_PIXELS_PER_STRING);
|
||||
static const uint8_t _T0H = ((uint16_t)(_T1 ));
|
||||
static const uint8_t _T1H = ((uint16_t)(_T1+_T2 ));
|
||||
static const uint8_t _TOP = ((uint16_t)(_T1+_T2+_T3));
|
||||
|
||||
// may as well be static, as can only attach one LED string per _DATA_PIN....
|
||||
static uint16_t s_SequenceBuffer[_PWM_BUFFER_COUNT];
|
||||
static uint16_t s_SequenceBufferValidElements;
|
||||
static volatile uint32_t s_SequenceBufferInUse;
|
||||
static CMinWait<_WAIT_TIME_MICROSECONDS> mWait; // ensure data has time to latch
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePinState() {
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
if (_INITIALIZE_PIN_HIGH) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_InitializePwmInstance(NRF_PWM_Type * pwm) {
|
||||
|
||||
// Pins must be set before enabling the peripheral
|
||||
pwm->PSEL.OUT[0] = FastPin<_DATA_PIN>::nrf_pin();
|
||||
pwm->PSEL.OUT[1] = NRF_PWM_PIN_NOT_CONNECTED;
|
||||
pwm->PSEL.OUT[2] = NRF_PWM_PIN_NOT_CONNECTED;
|
||||
pwm->PSEL.OUT[3] = NRF_PWM_PIN_NOT_CONNECTED;
|
||||
nrf_pwm_enable(pwm);
|
||||
nrf_pwm_configure(pwm, NRF_PWM_CLK_16MHz, NRF_PWM_MODE_UP, _TOP);
|
||||
nrf_pwm_decoder_set(pwm, NRF_PWM_LOAD_COMMON, NRF_PWM_STEP_AUTO);
|
||||
|
||||
// clear any prior shorts / interrupt enable bits
|
||||
nrf_pwm_shorts_set(pwm, 0);
|
||||
nrf_pwm_int_set(pwm, 0);
|
||||
// clear all prior events
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_STOPPED);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED0);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQSTARTED1);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND0);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_SEQEND1);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_PWMPERIODEND);
|
||||
nrf_pwm_event_clear(pwm, NRF_PWM_EVENT_LOOPSDONE);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_ConfigurePwmSequence(NRF_PWM_Type * pwm) {
|
||||
// config is easy, using SEQ0, no loops...
|
||||
nrf_pwm_sequence_t sequenceConfig;
|
||||
sequenceConfig.values.p_common = &(s_SequenceBuffer[0]);
|
||||
sequenceConfig.length = s_SequenceBufferValidElements;
|
||||
sequenceConfig.repeats = 0; // send the data once, and only once
|
||||
sequenceConfig.end_delay = 0; // no extra delay at the end of SEQ[0] / SEQ[1]
|
||||
nrf_pwm_sequence_set(pwm, 0, &sequenceConfig);
|
||||
nrf_pwm_sequence_set(pwm, 1, &sequenceConfig);
|
||||
nrf_pwm_loop_set(pwm, 0);
|
||||
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_EnableInterruptsAndShortcuts(NRF_PWM_Type * pwm) {
|
||||
IRQn_Type irqn = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getIRQn();
|
||||
// TODO: check API results...
|
||||
uint32_t result;
|
||||
|
||||
result = sd_nvic_SetPriority(irqn, configMAX_SYSCALL_INTERRUPT_PRIORITY);
|
||||
(void)result;
|
||||
result = sd_nvic_EnableIRQ(irqn);
|
||||
(void)result;
|
||||
|
||||
// shortcuts prevent (up to) 4-cycle delay from interrupt handler to next action
|
||||
uint32_t shortsToEnable = 0;
|
||||
shortsToEnable |= NRF_PWM_SHORT_SEQEND0_STOP_MASK; ///< SEQEND[0] --> STOP task.
|
||||
shortsToEnable |= NRF_PWM_SHORT_SEQEND1_STOP_MASK; ///< SEQEND[1] --> STOP task.
|
||||
//shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK; ///< LOOPSDONE --> SEQSTART[0] task.
|
||||
//shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_SEQSTART1_MASK; ///< LOOPSDONE --> SEQSTART[1] task.
|
||||
shortsToEnable |= NRF_PWM_SHORT_LOOPSDONE_STOP_MASK; ///< LOOPSDONE --> STOP task.
|
||||
nrf_pwm_shorts_set(pwm, shortsToEnable);
|
||||
|
||||
// mark which events should cause interrupts...
|
||||
uint32_t interruptsToEnable = 0;
|
||||
interruptsToEnable |= NRF_PWM_INT_SEQEND0_MASK;
|
||||
interruptsToEnable |= NRF_PWM_INT_SEQEND1_MASK;
|
||||
interruptsToEnable |= NRF_PWM_INT_LOOPSDONE_MASK;
|
||||
interruptsToEnable |= NRF_PWM_INT_STOPPED_MASK;
|
||||
nrf_pwm_int_set(pwm, interruptsToEnable);
|
||||
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback_StartTask(NRF_PWM_Type * pwm) {
|
||||
nrf_pwm_task_trigger(pwm, NRF_PWM_TASK_SEQSTART0);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void spinAcquireSequenceBuffer() {
|
||||
while (!tryAcquireSequenceBuffer());
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static bool tryAcquireSequenceBuffer() {
|
||||
return __sync_bool_compare_and_swap(&s_SequenceBufferInUse, 0, 1);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void releaseSequenceBuffer() {
|
||||
uint32_t tmp = __sync_val_compare_and_swap(&s_SequenceBufferInUse, 1, 0);
|
||||
if (tmp != 1) {
|
||||
// TODO: Error / Assert / log ?
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static void isr_handler() {
|
||||
NRF_PWM_Type * pwm = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getPWM();
|
||||
IRQn_Type irqn = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getIRQn();
|
||||
|
||||
// Currently, only use SEQUENCE 0, so only event
|
||||
// of consequence is LOOPSDONE ...
|
||||
if (nrf_pwm_event_check(pwm,NRF_PWM_EVENT_STOPPED)) {
|
||||
nrf_pwm_event_clear(pwm,NRF_PWM_EVENT_STOPPED);
|
||||
|
||||
// update the minimum time to next call
|
||||
mWait.mark();
|
||||
// mark the sequence as no longer in use -- pointer, comparator, exchange value
|
||||
releaseSequenceBuffer();
|
||||
// prevent further interrupts from PWM events
|
||||
nrf_pwm_int_set(pwm, 0);
|
||||
// disable PWM interrupts - None of the PWM IRQs are shared
|
||||
// with other peripherals, avoiding complexity of shared IRQs.
|
||||
sd_nvic_DisableIRQ(irqn);
|
||||
// disable the PWM instance
|
||||
nrf_pwm_disable(pwm);
|
||||
// may take up to 4 cycles for writes to propagate (APB bus @ 16MHz)
|
||||
asm __volatile__ ( "NOP; NOP; NOP; NOP;" );
|
||||
// release the PWM arbiter to be re-used by another LED string
|
||||
PWM_Arbiter<FASTLED_NRF52_PWM_ID>::releaseFromIsr();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void init() {
|
||||
FASTLED_NRF52_DEBUGPRINT("Clockless Timings:\n");
|
||||
FASTLED_NRF52_DEBUGPRINT(" T0H == %d", _T0H);
|
||||
FASTLED_NRF52_DEBUGPRINT(" T1H == %d", _T1H);
|
||||
FASTLED_NRF52_DEBUGPRINT(" TOP == %d\n", _TOP);
|
||||
// to avoid pin initialization from causing first LED to have invalid color,
|
||||
// call mWait.mark() to ensure data latches before color data gets sent.
|
||||
startPwmPlayback_InitializePinState();
|
||||
mWait.mark();
|
||||
|
||||
}
|
||||
virtual uint16_t getMaxRefreshRate() const { return 800; }
|
||||
|
||||
virtual void showPixels(PixelController<_RGB_ORDER> & pixels) {
|
||||
// wait for the only sequence buffer to become available
|
||||
spinAcquireSequenceBuffer();
|
||||
prepareSequenceBuffers(pixels);
|
||||
// ensure any prior data had time to latch
|
||||
mWait.wait();
|
||||
startPwmPlayback(s_SequenceBufferValidElements);
|
||||
return;
|
||||
}
|
||||
|
||||
template<uint8_t _BIT>
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void WriteBitToSequence(uint8_t byte, uint16_t * e) {
|
||||
*e = _POLARITY_BIT | (((byte & (1u << _BIT)) == 0) ? _T0H : _T1H);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void prepareSequenceBuffers(PixelController<_RGB_ORDER> & pixels) {
|
||||
s_SequenceBufferValidElements = 0;
|
||||
int32_t remainingSequenceElements = _PWM_BUFFER_COUNT;
|
||||
uint16_t * e = s_SequenceBuffer;
|
||||
uint32_t size_needed = pixels.size(); // count of pixels
|
||||
size_needed *= (8 + _XTRA0); // bits per pixel
|
||||
size_needed *= 2; // each bit takes two bytes
|
||||
|
||||
if (size_needed > _PWM_BUFFER_COUNT) {
|
||||
// TODO: assert()?
|
||||
return;
|
||||
}
|
||||
|
||||
while (pixels.has(1) && (remainingSequenceElements >= _BITS_PER_PIXEL)) {
|
||||
uint8_t b0 = pixels.loadAndScale0();
|
||||
WriteBitToSequence<7>(b0, e); e++;
|
||||
WriteBitToSequence<6>(b0, e); e++;
|
||||
WriteBitToSequence<5>(b0, e); e++;
|
||||
WriteBitToSequence<4>(b0, e); e++;
|
||||
WriteBitToSequence<3>(b0, e); e++;
|
||||
WriteBitToSequence<2>(b0, e); e++;
|
||||
WriteBitToSequence<1>(b0, e); e++;
|
||||
WriteBitToSequence<0>(b0, e); e++;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; i++) {
|
||||
WriteBitToSequence<0>(0,e); e++;
|
||||
}
|
||||
}
|
||||
uint8_t b1 = pixels.loadAndScale1();
|
||||
WriteBitToSequence<7>(b1, e); e++;
|
||||
WriteBitToSequence<6>(b1, e); e++;
|
||||
WriteBitToSequence<5>(b1, e); e++;
|
||||
WriteBitToSequence<4>(b1, e); e++;
|
||||
WriteBitToSequence<3>(b1, e); e++;
|
||||
WriteBitToSequence<2>(b1, e); e++;
|
||||
WriteBitToSequence<1>(b1, e); e++;
|
||||
WriteBitToSequence<0>(b1, e); e++;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; i++) {
|
||||
WriteBitToSequence<0>(0,e); e++;
|
||||
}
|
||||
}
|
||||
uint8_t b2 = pixels.loadAndScale2();
|
||||
WriteBitToSequence<7>(b2, e); e++;
|
||||
WriteBitToSequence<6>(b2, e); e++;
|
||||
WriteBitToSequence<5>(b2, e); e++;
|
||||
WriteBitToSequence<4>(b2, e); e++;
|
||||
WriteBitToSequence<3>(b2, e); e++;
|
||||
WriteBitToSequence<2>(b2, e); e++;
|
||||
WriteBitToSequence<1>(b2, e); e++;
|
||||
WriteBitToSequence<0>(b2, e); e++;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; i++) {
|
||||
WriteBitToSequence<0>(0,e); e++;
|
||||
}
|
||||
}
|
||||
|
||||
// advance pixel and sequence pointers
|
||||
s_SequenceBufferValidElements += _BITS_PER_PIXEL;
|
||||
remainingSequenceElements -= _BITS_PER_PIXEL;
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void startPwmPlayback(uint16_t bytesToSend) {
|
||||
PWM_Arbiter<FASTLED_NRF52_PWM_ID>::acquire(isr_handler);
|
||||
NRF_PWM_Type * pwm = PWM_Arbiter<FASTLED_NRF52_PWM_ID>::getPWM();
|
||||
|
||||
// mark the sequence as being in-use
|
||||
__sync_fetch_and_or(&s_SequenceBufferInUse, 1);
|
||||
|
||||
startPwmPlayback_InitializePinState();
|
||||
startPwmPlayback_InitializePwmInstance(pwm);
|
||||
startPwmPlayback_ConfigurePwmSequence(pwm);
|
||||
startPwmPlayback_EnableInterruptsAndShortcuts(pwm);
|
||||
startPwmPlayback_StartTask(pwm);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t* getRawSequenceBuffer() { return s_SequenceBuffer; }
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getRawSequenceBufferSize() { return _PWM_BUFFER_COUNT; }
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint16_t getSequenceBufferInUse() { return s_SequenceBufferInUse; }
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawSequenceBuffer(uint16_t bytesToSend) {
|
||||
mWait.wait(); // ensure min time between updates
|
||||
startPwmPlayback(bytesToSend);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void sendRawBytes(uint8_t * arrayOfBytes, uint16_t bytesToSend) {
|
||||
// wait for sequence buffer to be available
|
||||
while (s_SequenceBufferInUse != 0);
|
||||
|
||||
s_SequenceBufferValidElements = 0;
|
||||
int32_t remainingSequenceElements = _PWM_BUFFER_COUNT;
|
||||
uint16_t * e = s_SequenceBuffer;
|
||||
uint8_t * nextByte = arrayOfBytes;
|
||||
for (uint16_t bytesRemain = bytesToSend;
|
||||
(remainingSequenceElements >= 8) && (bytesRemain > 0);
|
||||
bytesRemain--,
|
||||
remainingSequenceElements -= 8,
|
||||
s_SequenceBufferValidElements += 8
|
||||
) {
|
||||
uint8_t b = *nextByte;
|
||||
WriteBitToSequence<7,false>(b, e); e++;
|
||||
WriteBitToSequence<6,false>(b, e); e++;
|
||||
WriteBitToSequence<5,false>(b, e); e++;
|
||||
WriteBitToSequence<4,false>(b, e); e++;
|
||||
WriteBitToSequence<3,false>(b, e); e++;
|
||||
WriteBitToSequence<2,false>(b, e); e++;
|
||||
WriteBitToSequence<1,false>(b, e); e++;
|
||||
WriteBitToSequence<0,false>(b, e); e++;
|
||||
if (_XTRA0 > 0) {
|
||||
for (int i = 0; i < _XTRA0; i++) {
|
||||
WriteBitToSequence<0,_FLIP>(0,e); e++;
|
||||
}
|
||||
}
|
||||
}
|
||||
mWait.wait(); // ensure min time between updates
|
||||
|
||||
startPwmPlayback(s_SequenceBufferValidElements);
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
};
|
||||
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferValidElements = 0;
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
uint32_t volatile ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBufferInUse = 0;
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
uint16_t ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::s_SequenceBuffer[_PWM_BUFFER_COUNT];
|
||||
template <uint8_t _DATA_PIN, int _T1, int _T2, int _T3, EOrder _RGB_ORDER, int _XTRA0, bool _FLIP, int _WAIT_TIME_MICROSECONDS>
|
||||
CMinWait<_WAIT_TIME_MICROSECONDS> ClocklessController<_DATA_PIN, _T1, _T2, _T3, _RGB_ORDER, _XTRA0, _FLIP, _WAIT_TIME_MICROSECONDS>::mWait;
|
||||
|
||||
/* nrf_pwm solution
|
||||
//
|
||||
// When the nRF52 softdevice (e.g., BLE) is enabled, the CPU can be pre-empted
|
||||
// at any time for radio interrupts. These interrupts cannot be disabled.
|
||||
// The problem is, even simple BLE advertising interrupts may take **`348μs`**
|
||||
// (per softdevice 1.40, see http://infocenter.nordicsemi.com/pdf/S140_SDS_v1.3.pdf)
|
||||
//
|
||||
// The nRF52 chips have a decent Easy-DMA-enabled PWM peripheral.
|
||||
//
|
||||
// The major downside:
|
||||
// [] The PWM peripheral has a fixed input buffer size at 16 bits per clock cycle.
|
||||
// (each clockless protocol bit == 2 bytes)
|
||||
//
|
||||
// The major upsides include:
|
||||
// [] Fully asynchronous, freeing CPU for other tasks
|
||||
// [] Softdevice interrupts do not affect PWM clocked output (reliable clocking)
|
||||
//
|
||||
// The initial solution generally does the following for showPixels():
|
||||
// [] wait for a sequence buffer to become available
|
||||
// [] prepare the entire LED string's sequence (see `prepareSequenceBuffers()`)
|
||||
// [] ensures minimum wait time from prior sequence's end
|
||||
//
|
||||
// Options after initial solution working:
|
||||
// []
|
||||
|
||||
// TODO: Double-buffers, so one can be doing DMA while the second
|
||||
// buffer is being prepared.
|
||||
// TODO: Pool of buffers, so can keep N-1 active in DMA, while
|
||||
// preparing data in the final buffer?
|
||||
// Write another class similar to PWM_Arbiter, only for
|
||||
// tracking use of sequence buffers?
|
||||
// TODO: Use volatile variable to track buffers that the
|
||||
// prior DMA operation is finished with, so can fill
|
||||
// in those buffers with newly-prepared data...
|
||||
// apis to send the pre-generated buffer. This would be essentially asynchronous,
|
||||
// and result in efficient run time if the pixels are either (a) static, or
|
||||
// (b) cycle through a limited number of options whose converted results can
|
||||
// be cached and re-used. While simple, this method takes lots of extra RAM...
|
||||
// 16 bits for every full clock (high/low) cycle.
|
||||
//
|
||||
// Clockless chips typically send 24 bits (3x 8-bit) per pixel.
|
||||
// One odd clockless chip sends 36 bits (3x 12-bit) per pixel.
|
||||
// Each bit requires a 16-bit sequence entry for the PWM peripheral.
|
||||
// This gives approximately:
|
||||
// 24 bpp 36 bpp
|
||||
// ==========================================
|
||||
// 1 pixel 48 bytes 72 bytes
|
||||
// 32 pixels 1,536 bytes 2,304 bytes
|
||||
// 64 pixels 3,072 bytes 4,608 bytes
|
||||
//
|
||||
//
|
||||
// UPDATE: this is the method I'm choosing, to get _SOMETHING_
|
||||
// clockless working... 3k RAM for 64 pixels is acceptable
|
||||
// for a first release, as it allows re-use of FASTLED
|
||||
// color correction, dithering, etc. ....
|
||||
*/
|
||||
|
||||
//FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // NRF52_SERIES
|
||||
#endif // __INC_CLOCKLESS_ARM_NRF52
|
||||
11
libraries/FastLED/platforms/arm/nrf52/fastled_arm_nrf52.h
Normal file
11
libraries/FastLED/platforms/arm/nrf52/fastled_arm_nrf52.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef __INC_FASTLED_ARM_NRF52_H
|
||||
#define __INC_FASTLED_ARM_NRF52_H
|
||||
|
||||
#include "led_sysdefs_arm_nrf52.h"
|
||||
#include "arbiter_nrf52.h"
|
||||
#include "fastpin_arm_nrf52.h"
|
||||
#include "fastspi_arm_nrf52.h"
|
||||
#include "clockless_arm_nrf52.h"
|
||||
|
||||
#endif // #ifndef __INC_FASTLED_ARM_NRF52_H
|
||||
|
||||
328
libraries/FastLED/platforms/arm/nrf52/fastpin_arm_nrf52.h
Normal file
328
libraries/FastLED/platforms/arm/nrf52/fastpin_arm_nrf52.h
Normal file
@@ -0,0 +1,328 @@
|
||||
#ifndef __FASTPIN_ARM_NRF52_H
|
||||
#define __FASTPIN_ARM_NRF52_H
|
||||
|
||||
|
||||
/*
|
||||
//
|
||||
// Background:
|
||||
// ===========
|
||||
// the nRF52 has more than 32 ports, and thus must support
|
||||
// two distinct GPIO port registers.
|
||||
//
|
||||
// For the nRF52 series, the structure to control the port is
|
||||
// `NRF_GPIO_Type`, with separate addresses mapped for set, clear, etc.
|
||||
// The two ports are defined as NRF_P0 and NRF_P1.
|
||||
// An example declaration for the ports is:
|
||||
// #define NRF_P0_BASE 0x50000000UL
|
||||
// #define NRF_P1_BASE 0x50000300UL
|
||||
// #define NRF_P0 ((NRF_GPIO_Type*)NRF_P0_BASE)
|
||||
// #define NRF_P1 ((NRF_GPIO_Type*)NRF_P1_BASE)
|
||||
//
|
||||
// Therefore, ideally, the _FL_DEFPIN() macro would simply
|
||||
// conditionally pass either NRF_P0 or NRF_P1 to the underlying
|
||||
// FastPin<> template class class.
|
||||
//
|
||||
// The "pin" provided to the FastLED<> template (and which
|
||||
// the _FL_DEFPIN() macro specializes for valid pins) is NOT
|
||||
// the microcontroller port.pin, but the Arduino digital pin.
|
||||
// Some boards have an identity mapping (e.g., nRF52832 Feather)
|
||||
// but most do not. Therefore, the _FL_DEFPIN() macro
|
||||
// must translate the Arduino pin to the mcu port.pin.
|
||||
//
|
||||
//
|
||||
// Difficulties:
|
||||
// =============
|
||||
// The goal is to avoid any such lookups, using compile-time
|
||||
// optimized functions for speed, in line with FastLED's
|
||||
// overall design goals. This means constexpr, compile-time
|
||||
// and aggressive inlining of functions....
|
||||
//
|
||||
// Right away, this precludes the use of g_ADigitalPinMap,
|
||||
// which is not constexpr, and thus not available for
|
||||
// preprocessor/compile-time optimizations. Therefore,
|
||||
// we have to specialize FastPin<uint8_t PIN>, given a
|
||||
// compile-time value for PIN, into at least a PORT and
|
||||
// a BITMASK for the port.
|
||||
//
|
||||
// Arduino compiles using C++11 for at least Feather nRF52840 Express.
|
||||
// C++11 is very restrictive about template parameters.
|
||||
// Template parameters can only be:
|
||||
// 1. a type (as most people expect)
|
||||
// 2. a template
|
||||
// 3. a constexpr native integer type
|
||||
//
|
||||
// Therefore, attempts to use `NRF_GPIO_Type *` as a
|
||||
// template parameter will fail....
|
||||
//
|
||||
// Solution:
|
||||
// =========
|
||||
// The solution chosen is to define a unique structure for each port,
|
||||
// whose SOLE purpose is to have a static inline function that
|
||||
// returns the `NRF_GPIO_Type *` that is needed.
|
||||
//
|
||||
// Thus, while it's illegal to pass `NRF_P0` as a template
|
||||
// parameter, it's perfectly legal to pass `__generated_struct_NRF_P0`,
|
||||
// and have the template call a well-known `static inline` function
|
||||
// that returns `NRF_P0` ... which is itself a compile-time constant.
|
||||
//
|
||||
// Note that additional magic can be applied that will automatically
|
||||
// generate the structures. If you want to add that to this platform,
|
||||
// check out the KL26 platform files for a starting point.
|
||||
//
|
||||
*/
|
||||
|
||||
// manually define two structures, to avoid fighting with preprocessor macros
|
||||
struct __generated_struct_NRF_P0 {
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() {
|
||||
return NRF_P0_BASE;
|
||||
}
|
||||
};
|
||||
struct __generated_struct_NRF_P1 {
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE constexpr static uintptr_t r() {
|
||||
return NRF_P1_BASE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// The actual class template can then use a typename, for what is essentially a constexpr NRF_GPIO_Type*
|
||||
template <uint32_t _MASK, typename _PORT, uint8_t _PORT_NUMBER, uint8_t _PIN_NUMBER> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void setOutput() {
|
||||
// OK for this to be more than one instruction, as unusual to quickly switch input/output modes
|
||||
nrf_gpio_cfg(
|
||||
nrf_pin(),
|
||||
NRF_GPIO_PIN_DIR_OUTPUT, // set pin as output
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering
|
||||
NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled
|
||||
NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds
|
||||
NRF_GPIO_PIN_NOSENSE // pin sense level disabled
|
||||
);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void setInput() {
|
||||
// OK for this to be more than one instruction, as unusual to quickly switch input/output modes
|
||||
nrf_gpio_cfg(
|
||||
nrf_pin(),
|
||||
NRF_GPIO_PIN_DIR_INPUT, // set pin as input
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT, // disconnect the input buffering
|
||||
NRF_GPIO_PIN_NOPULL, // neither pull-up nor pull-down resistors enabled
|
||||
NRF_GPIO_PIN_H0H1, // high drive mode required for faster speeds
|
||||
NRF_GPIO_PIN_NOSENSE // pin sense level disabled
|
||||
);
|
||||
}
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void hi() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTSET = _MASK; } // sets _MASK in the SET OUTPUT register (output set high)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void lo() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTCLR = _MASK; } // sets _MASK in the CLEAR OUTPUT register (output set low)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void toggle() { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT ^= _MASK; } // toggles _MASK bits in the OUTPUT GPIO port directly
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void strobe() { toggle(); toggle(); } // BUGBUG -- Is this used by FastLED? Without knowing (for example) SPI Speed?
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t hival() { return (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT | _MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 1
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t loval() { return (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT & ~_MASK; } // sets all _MASK bit(s) in the OUTPUT GPIO port to 0
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t port() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT); } // gets raw pointer to OUTPUT GPIO port
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t cport() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTCLR); } // gets raw pointer to SET DIRECTION GPIO port
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_ptr_t sport() { return &((reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUTSET); } // gets raw pointer to CLEAR DIRECTION GPIO port
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static port_t mask() { return _MASK; } // gets the value of _MASK
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void hi (register port_ptr_t port) { hi(); } // sets _MASK in the SET OUTPUT register (output set high)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void lo (register port_ptr_t port) { lo(); } // sets _MASK in the CLEAR OUTPUT register (output set low)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void set(register port_t val ) { (reinterpret_cast<NRF_GPIO_Type*>(_PORT::r()))->OUT = val; } // sets entire port's value (optimization used by FastLED)
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static void fastset(register port_ptr_t port, register port_t val) { *port = val; }
|
||||
constexpr static uint32_t nrf_pin2() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); }
|
||||
constexpr static bool LowSpeedOnlyRecommended() {
|
||||
// only allow one function body.
|
||||
#undef _FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT
|
||||
|
||||
// unique cases for each board / processor package / module?
|
||||
#if defined(NRF52810_XXAA) && defined(NRF52810_PACKAGE_QFN48)
|
||||
#if defined(_FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT)
|
||||
#error "Multiple board match"
|
||||
#endif
|
||||
#define _FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT 1
|
||||
static_assert(_PORT_NUMBER == 0, "nRF52810 only has one port");
|
||||
return (
|
||||
(_PIN_NUMBER == 25) ||
|
||||
(_PIN_NUMBER == 26) ||
|
||||
(_PIN_NUMBER == 27) ||
|
||||
(_PIN_NUMBER == 28) ||
|
||||
(_PIN_NUMBER == 29)
|
||||
);
|
||||
#endif
|
||||
#if defined(NRF52810_XXAA) && defined(NRF52810_PACKAGE_QFN32)
|
||||
#if defined(_FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT)
|
||||
#error "Multiple board match"
|
||||
#endif
|
||||
#define _FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT 1
|
||||
static_assert(_PORT_NUMBER == 0, "nRF52810 only has one port");
|
||||
if (_PORT_NUMBER == 0) {
|
||||
if (
|
||||
(_PIN_NUMBER == 26) ||
|
||||
(_PIN_NUMBER == 27)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
#if defined(NRF52832_XXAA) || defined(NRF52832_XXAB)
|
||||
#if defined(_FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT)
|
||||
#error "Multiple board match"
|
||||
#endif
|
||||
#define _FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT 1
|
||||
static_assert(_PORT_NUMBER == 0, "nRF52832 only has one port");
|
||||
// data sheets shows the same pins in both QFN48 and WLCSP package
|
||||
// are recommended as low-speed only:
|
||||
return (
|
||||
(_PIN_NUMBER == 22) ||
|
||||
(_PIN_NUMBER == 23) ||
|
||||
(_PIN_NUMBER == 24) ||
|
||||
(_PIN_NUMBER == 25) ||
|
||||
(_PIN_NUMBER == 26) ||
|
||||
(_PIN_NUMBER == 27) ||
|
||||
(_PIN_NUMBER == 28) ||
|
||||
(_PIN_NUMBER == 29) ||
|
||||
(_PIN_NUMBER == 30) ||
|
||||
(_PIN_NUMBER == 31)
|
||||
);
|
||||
#endif
|
||||
#if defined(NRF52840_XXAA) && defined(NRF52840_PACKAGE_aQFN73)
|
||||
#if defined(_FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT)
|
||||
#error "Multiple board match"
|
||||
#endif
|
||||
#define _FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT 1
|
||||
static_assert(_PORT_NUMBER == 0 || _PORT_NUMBER == 1, "nRF52840 only has two ports");
|
||||
return
|
||||
(
|
||||
(
|
||||
(_PORT_NUMBER == 0) &&
|
||||
(
|
||||
(_PIN_NUMBER == 2) ||
|
||||
(_PIN_NUMBER == 3) ||
|
||||
(_PIN_NUMBER == 9) ||
|
||||
(_PIN_NUMBER == 10) ||
|
||||
(_PIN_NUMBER == 11) ||
|
||||
(_PIN_NUMBER == 12) ||
|
||||
(_PIN_NUMBER == 14) ||
|
||||
(_PIN_NUMBER == 28) ||
|
||||
(_PIN_NUMBER == 29) ||
|
||||
(_PIN_NUMBER == 30) ||
|
||||
(_PIN_NUMBER == 31)
|
||||
)
|
||||
)
|
||||
||
|
||||
(
|
||||
(_PORT_NUMBER == 1) &&
|
||||
(
|
||||
(_PIN_NUMBER == 2) ||
|
||||
(_PIN_NUMBER == 3) ||
|
||||
(_PIN_NUMBER == 4) ||
|
||||
(_PIN_NUMBER == 5) ||
|
||||
(_PIN_NUMBER == 6) ||
|
||||
(_PIN_NUMBER == 7) ||
|
||||
(_PIN_NUMBER == 10) ||
|
||||
(_PIN_NUMBER == 13) ||
|
||||
(_PIN_NUMBER == 15)
|
||||
)
|
||||
)
|
||||
);
|
||||
#endif
|
||||
#if false && defined(NRF52840_XXAA) && (defined(NRF52840_PACKAGE_aQFN73) || defined(ARDUINO_NRF52840_FEATHER))
|
||||
// Adafruit nRF52840 feather uses RAYTAC MDBT50Q module, which is aQFN73
|
||||
// See https://cdn-learn.adafruit.com/assets/assets/000/068/544/original/Raytac_MDBT50Q.pdf
|
||||
#if defined(_FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT)
|
||||
#error "Multiple board match"
|
||||
#endif
|
||||
#define _FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT 1
|
||||
static_assert(_PORT_NUMBER == 0 || _PORT_NUMBER == 1, "nRF52840 only has two ports");
|
||||
return
|
||||
(
|
||||
(
|
||||
(_PORT_NUMBER == 0) &&
|
||||
(
|
||||
(_PIN_NUMBER == 2) ||
|
||||
(_PIN_NUMBER == 3) ||
|
||||
(_PIN_NUMBER == 9) ||
|
||||
(_PIN_NUMBER == 10) ||
|
||||
(_PIN_NUMBER == 28) ||
|
||||
(_PIN_NUMBER == 29) ||
|
||||
(_PIN_NUMBER == 30) ||
|
||||
(_PIN_NUMBER == 31)
|
||||
)
|
||||
)
|
||||
||
|
||||
(
|
||||
(_PORT_NUMBER == 1) &&
|
||||
(
|
||||
(_PIN_NUMBER == 1) ||
|
||||
(_PIN_NUMBER == 2) ||
|
||||
(_PIN_NUMBER == 3) ||
|
||||
(_PIN_NUMBER == 4) ||
|
||||
(_PIN_NUMBER == 5) ||
|
||||
(_PIN_NUMBER == 6) ||
|
||||
(_PIN_NUMBER == 7) ||
|
||||
(_PIN_NUMBER == 10) ||
|
||||
(_PIN_NUMBER == 11) ||
|
||||
(_PIN_NUMBER == 12) ||
|
||||
(_PIN_NUMBER == 13) ||
|
||||
(_PIN_NUMBER == 14) ||
|
||||
(_PIN_NUMBER == 15)
|
||||
)
|
||||
)
|
||||
);
|
||||
#endif
|
||||
#if !defined(_FASTLED_NRF52_LOW_SPEED_ONLY_BOARD_DETECT)
|
||||
#warning "Unknown board / package, ... caller must determine pins that support high-speed"
|
||||
return false; // choosing default to be FALSE, to allow users to ATTEMPT to use high-speed on pins where support is not known
|
||||
#endif
|
||||
}
|
||||
// Expose the nrf pin (port/pin combined), port, and pin as properties (e.g., for setting up SPI)
|
||||
|
||||
FASTLED_NRF52_INLINE_ATTRIBUTE static uint32_t nrf_pin() { return NRF_GPIO_PIN_MAP(_PORT_NUMBER, _PIN_NUMBER); }
|
||||
};
|
||||
|
||||
//
|
||||
// BOARD_PIN can be either the pin portion of a port.pin, or the combined NRF_GPIO_PIN_MAP() number.
|
||||
// For example both the following two defines refer to P1.15 (pin 47) as Arduino pin 3:
|
||||
// _FL_DEFPIN(3, 15, 1);
|
||||
// _FL_DEFPIN(3, 47, 1);
|
||||
//
|
||||
// Similarly, the following defines are all equivalent:
|
||||
// _DEFPIN_ARM_IDENTITY_P1(47);
|
||||
// _FL_DEFPIN(47, 15, 1);
|
||||
// _FL_DEFPIN(47, 47, 1);
|
||||
//
|
||||
|
||||
#define FL_DEFPIN(ARDUINO_PIN, BOARD_PIN, BOARD_PORT) \
|
||||
template<> class FastPin<ARDUINO_PIN> : \
|
||||
public _ARMPIN< \
|
||||
1u << (BOARD_PIN & 31u), \
|
||||
__generated_struct_NRF_P ## BOARD_PORT, \
|
||||
(BOARD_PIN / 32), \
|
||||
BOARD_PIN & 31u \
|
||||
> \
|
||||
{}
|
||||
|
||||
#define _DEFPIN_ARM_IDENTITY_P0(ARDUINO_PIN) \
|
||||
template<> class FastPin<ARDUINO_PIN> : \
|
||||
public _ARMPIN< \
|
||||
1u << (ARDUINO_PIN & 31u), \
|
||||
__generated_struct_NRF_P0, \
|
||||
0, \
|
||||
(ARDUINO_PIN & 31u) + 0 \
|
||||
> \
|
||||
{}
|
||||
|
||||
#define _DEFPIN_ARM_IDENTITY_P1(ARDUINO_PIN) \
|
||||
template<> class FastPin<ARDUINO_PIN> : \
|
||||
public _ARMPIN< \
|
||||
1u << (ARDUINO_PIN & 31u), \
|
||||
__generated_struct_NRF_P1, \
|
||||
1, \
|
||||
(ARDUINO_PIN & 31u) + 32 \
|
||||
> \
|
||||
{}
|
||||
|
||||
// The actual pin definitions are in a separate header file...
|
||||
#include "fastpin_arm_nrf52_variants.h"
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif // #ifndef __FASTPIN_ARM_NRF52_H
|
||||
@@ -0,0 +1,579 @@
|
||||
#ifndef __FASTPIN_ARM_NRF52_VARIANTS_H
|
||||
#define __FASTPIN_ARM_NRF52_VARIANTS_H
|
||||
|
||||
// use this to determine if found variant or not (avoid multiple boards at once)
|
||||
#undef __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
|
||||
// Adafruit Bluefruit nRF52832 Feather
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52832_FEATHER)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Adafruit Bluefruit nRF52832 Feather is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // xtal 1
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // xtal 2
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // a0
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // a1
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // a2
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // a3
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // TXD
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // GPIO #7
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // RXD
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // NFC1
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // NFC2
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // GPIO #11
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // SCK
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // MOSI
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // MISO
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // GPIO #15
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // GPIO #16
|
||||
_DEFPIN_ARM_IDENTITY_P0(17); // LED #1 (red)
|
||||
_DEFPIN_ARM_IDENTITY_P0(18); // SWO
|
||||
_DEFPIN_ARM_IDENTITY_P0(19); // LED #2 (blue)
|
||||
_DEFPIN_ARM_IDENTITY_P0(20); // DFU
|
||||
// _DEFPIN_ARM_IDENTITY_P0(21); // Reset -- not valid to use for FastLED?
|
||||
// _DEFPIN_ARM_IDENTITY_P0(22); // Factory Reset -- not vaild to use for FastLED?
|
||||
// _DEFPIN_ARM_IDENTITY_P0(23); // N/A
|
||||
// _DEFPIN_ARM_IDENTITY_P0(24); // N/A
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // SDA
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // SCL
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // GPIO #27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // A4
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // A5
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // A6
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // A7
|
||||
#endif // defined (ARDUINO_NRF52832_FEATHER)
|
||||
|
||||
// Adafruit Bluefruit nRF52840 Feather Express
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_FEATHER)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
|
||||
#define MAX_PIN (33u) // 34 if wanting to use NFC1 test point
|
||||
|
||||
// Arduino pins 0..7
|
||||
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 -- UART TX
|
||||
//_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 -- UART RX
|
||||
_FL_DEFPIN( 2, 10, 0); // D2 is P0.10 -- NFC2
|
||||
_FL_DEFPIN( 3, 47, 1); // D3 is P1.15 -- PIN_LED1 (red)
|
||||
_FL_DEFPIN( 4, 42, 1); // D4 is P1.10 -- PIN_LED2 (blue)
|
||||
_FL_DEFPIN( 5, 40, 1); // D5 is P1.08 -- SPI/SS
|
||||
_FL_DEFPIN( 6, 7, 0); // D6 is P0.07
|
||||
_FL_DEFPIN( 7, 34, 1); // D7 is P1.02 -- PIN_DFU (Button)
|
||||
|
||||
// Arduino pins 8..15
|
||||
_FL_DEFPIN( 8, 16, 0); // D8 is P0.16 -- PIN_NEOPIXEL
|
||||
_FL_DEFPIN( 9, 26, 0); // D9 is P0.26
|
||||
_FL_DEFPIN(10, 27, 0); // D10 is P0.27
|
||||
_FL_DEFPIN(11, 6, 0); // D11 is P0.06
|
||||
_FL_DEFPIN(12, 8, 0); // D12 is P0.08
|
||||
_FL_DEFPIN(13, 41, 1); // D13 is P1.09
|
||||
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 -- A0
|
||||
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 -- A1
|
||||
|
||||
// Arduino pins 16..23
|
||||
_FL_DEFPIN(16, 30, 0); // D16 is P0.30 -- A2
|
||||
_FL_DEFPIN(17, 28, 0); // D17 is P0.28 -- A3
|
||||
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 -- A4
|
||||
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 -- A5
|
||||
//_FL_DEFPIN(20, 29, 0); // D20 is P0.29 -- A6 -- Connected to battery!
|
||||
//_FL_DEFPIN(21, 31, 0); // D21 is P0.31 -- A7 -- AREF
|
||||
_FL_DEFPIN(22, 12, 0); // D22 is P0.12 -- SDA
|
||||
_FL_DEFPIN(23, 11, 0); // D23 is P0.11 -- SCL
|
||||
|
||||
// Arduino pins 24..31
|
||||
_FL_DEFPIN(24, 15, 0); // D24 is P0.15 -- PIN_SPI_MISO
|
||||
_FL_DEFPIN(25, 13, 0); // D25 is P0.13 -- PIN_SPI_MOSI
|
||||
_FL_DEFPIN(26, 14, 0); // D26 is P0.14 -- PIN_SPI_SCK
|
||||
//_FL_DEFPIN(27, 19, 0); // D27 is P0.19 -- PIN_QSPI_SCK
|
||||
//_FL_DEFPIN(28, 20, 0); // D28 is P0.20 -- PIN_QSPI_CS
|
||||
//_FL_DEFPIN(29, 17, 0); // D29 is P0.17 -- PIN_QSPI_DATA0
|
||||
//_FL_DEFPIN(30, 22, 0); // D30 is P0.22 -- PIN_QSPI_DATA1
|
||||
//_FL_DEFPIN(31, 23, 0); // D31 is P0.23 -- PIN_QSPI_DATA2
|
||||
|
||||
// Arduino pins 32..34
|
||||
//_FL_DEFPIN(32, 21, 0); // D32 is P0.21 -- PIN_QSPI_DATA3
|
||||
//_FL_DEFPIN(33, 9, 0); // D33 is NFC1, only accessible via test point
|
||||
#endif // defined (ARDUINO_NRF52840_FEATHER)
|
||||
|
||||
// Adafruit Bluefruit nRF52840 Metro Express
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_METRO)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Adafruit Bluefruit nRF52840 Metro Express is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
|
||||
_FL_DEFPIN( 0, 25, 0); // D0 is P0.25 (UART TX)
|
||||
_FL_DEFPIN( 1, 24, 0); // D1 is P0.24 (UART RX)
|
||||
_FL_DEFPIN( 2, 10, 1); // D2 is P1.10
|
||||
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
|
||||
_FL_DEFPIN( 4, 11, 1); // D4 is P1.11
|
||||
_FL_DEFPIN( 5, 12, 1); // D5 is P1.12
|
||||
_FL_DEFPIN( 6, 14, 1); // D6 is P1.14
|
||||
_FL_DEFPIN( 7, 26, 0); // D7 is P0.26
|
||||
_FL_DEFPIN( 8, 27, 0); // D8 is P0.27
|
||||
_FL_DEFPIN( 9, 12, 0); // D9 is P0.12
|
||||
_FL_DEFPIN(10, 6, 0); // D10 is P0.06
|
||||
_FL_DEFPIN(11, 8, 0); // D11 is P0.08
|
||||
_FL_DEFPIN(12, 9, 1); // D12 is P1.09
|
||||
_FL_DEFPIN(13, 14, 0); // D13 is P0.14
|
||||
_FL_DEFPIN(14, 4, 0); // D14 is P0.04 (A0)
|
||||
_FL_DEFPIN(15, 5, 0); // D15 is P0.05 (A1)
|
||||
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
|
||||
_FL_DEFPIN(17, 30, 0); // D17 is P0.30 (A3)
|
||||
_FL_DEFPIN(18, 2, 0); // D18 is P0.02 (A4)
|
||||
_FL_DEFPIN(19, 3, 0); // D19 is P0.03 (A5)
|
||||
_FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A6, battery)
|
||||
_FL_DEFPIN(21, 31, 0); // D21 is P0.31 (A7, ARef)
|
||||
_FL_DEFPIN(22, 15, 0); // D22 is P0.15 (SDA)
|
||||
_FL_DEFPIN(23, 16, 0); // D23 is P0.16 (SCL)
|
||||
_FL_DEFPIN(24, 11, 0); // D24 is P0.11 (SPI MISO)
|
||||
_FL_DEFPIN(25, 8, 1); // D25 is P1.08 (SPI MOSI)
|
||||
_FL_DEFPIN(26, 7, 0); // D26 is P0.07 (SPI SCK )
|
||||
//_FL_DEFPIN(27, 19, 0); // D27 is P0.19 (QSPI CLK )
|
||||
//_FL_DEFPIN(28, 20, 0); // D28 is P0.20 (QSPI CS )
|
||||
//_FL_DEFPIN(29, 17, 0); // D29 is P0.17 (QSPI Data 0)
|
||||
//_FL_DEFPIN(30, 23, 0); // D30 is P0.23 (QSPI Data 1)
|
||||
//_FL_DEFPIN(31, 22, 0); // D31 is P0.22 (QSPI Data 2)
|
||||
//_FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI Data 3)
|
||||
_FL_DEFPIN(33, 13, 1); // D33 is P1.13 LED1
|
||||
_FL_DEFPIN(34, 15, 1); // D34 is P1.15 LED2
|
||||
_FL_DEFPIN(35, 13, 0); // D35 is P0.13 NeoPixel
|
||||
_FL_DEFPIN(36, 0, 1); // D36 is P1.02 Switch
|
||||
_FL_DEFPIN(37, 0, 1); // D37 is P1.00 SWO/DFU
|
||||
_FL_DEFPIN(38, 9, 0); // D38 is P0.09 NFC1
|
||||
_FL_DEFPIN(39, 10, 0); // D39 is P0.10 NFC2
|
||||
#endif // defined (ARDUINO_NRF52840_METRO)
|
||||
|
||||
// Adafruit Bluefruit on nRF52840DK PCA10056
|
||||
// From https://www.adafruit.com/package_adafruit_index.json
|
||||
#if defined (ARDUINO_NRF52840_PCA10056)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Adafruit Bluefruit on nRF52840DK PCA10056 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
|
||||
#if defined(USE_ARDUINO_PIN_NUMBERING)
|
||||
/* pca10056_schematic_and_pcb.pdf
|
||||
Page 3 shows the Arduino Pin to GPIO Px.xx mapping
|
||||
*/
|
||||
_FL_DEFPIN( 0, 1, 1); // D0 is P1.01
|
||||
_FL_DEFPIN( 1, 2, 1); // D1 is P1.02
|
||||
_FL_DEFPIN( 2, 3, 1); // D2 is P1.03
|
||||
_FL_DEFPIN( 3, 4, 1); // D3 is P1.04
|
||||
_FL_DEFPIN( 4, 5, 1); // D4 is P1.05
|
||||
_FL_DEFPIN( 5, 6, 1); // D5 is P1.06
|
||||
_FL_DEFPIN( 6, 7, 1); // D6 is P1.07 (BUTTON1 option)
|
||||
_FL_DEFPIN( 7, 8, 1); // D7 is P1.08 (BUTTON2 option)
|
||||
_FL_DEFPIN( 8, 10, 1); // D8 is P1.10
|
||||
_FL_DEFPIN( 9, 11, 1); // D9 is P1.11
|
||||
_FL_DEFPIN(10, 12, 1); // D10 is P1.12
|
||||
_FL_DEFPIN(11, 13, 1); // D11 is P1.13
|
||||
_FL_DEFPIN(12, 14, 1); // D12 is P1.14
|
||||
_FL_DEFPIN(13, 15, 1); // D13 is P1.15
|
||||
_FL_DEFPIN(14, 0, 0); // D14 is P0.00 (if SB4 bridged)
|
||||
_FL_DEFPIN(15, 1, 0); // D15 is P0.01 (if SB3 bridged)
|
||||
_FL_DEFPIN(16, 5, 0); // D16 is P0.05 (aka AIN3, aka UART RTS)
|
||||
_FL_DEFPIN(17, 6, 0); // D17 is P0.06 (UART TxD)
|
||||
_FL_DEFPIN(18, 7, 0); // D18 is P0.07 (UART CTS default)
|
||||
_FL_DEFPIN(19, 8, 0); // D19 is P0.08 (UART RxD)
|
||||
_FL_DEFPIN(20, 9, 0); // D20 is P0.09 (NFC1)
|
||||
_FL_DEFPIN(21, 10, 0); // D21 is P0.10 (NFC2)
|
||||
_FL_DEFPIN(22, 11, 0); // D22 is P0.11 (TRACEDATA2 / BUTTON1 default)
|
||||
_FL_DEFPIN(23, 12, 0); // D23 is P0.12 (TRACEDATA1 / BUTTON2 default)
|
||||
_FL_DEFPIN(24, 13, 0); // D24 is P0.13 (LED1)
|
||||
_FL_DEFPIN(25, 14, 0); // D25 is P0.14 (LED2)
|
||||
_FL_DEFPIN(26, 15, 0); // D26 is P0.15 (LED3)
|
||||
_FL_DEFPIN(27, 16, 0); // D27 is P0.16 (LED4)
|
||||
_FL_DEFPIN(28, 17, 0); // D28 is P0.17 (QSPI !CS , unless SB13 cut)
|
||||
// _FL_DEFPIN(29, 18, 0); // D29 is P0.18 (RESET)
|
||||
_FL_DEFPIN(30, 19, 0); // D30 is P0.19 (QSPI CLK , unless SB11 cut)
|
||||
_FL_DEFPIN(31, 20, 0); // D31 is P0.20 (QSPI DIO0, unless SB12 cut)
|
||||
_FL_DEFPIN(32, 21, 0); // D32 is P0.21 (QSPI DIO1, unless SB14 cut)
|
||||
_FL_DEFPIN(33, 22, 0); // D33 is P0.22 (QSPI DIO2, unless SB15 cut)
|
||||
_FL_DEFPIN(34, 23, 0); // D34 is P0.23 (QSPI DIO3, unless SB10 cut)
|
||||
_FL_DEFPIN(35, 24, 0); // D35 is P0.24 (BUTTON3)
|
||||
_FL_DEFPIN(36, 25, 0); // D36 is P0.25 (BUTTON4)
|
||||
_FL_DEFPIN(37, 00, 1); // D37 is P1.00 (TRACEDATA0 / SWO)
|
||||
_FL_DEFPIN(38, 09, 1); // D38 is P1.09 (TRACEDATA3)
|
||||
//_FL_DEFPIN(??, 2, 0); // D?? is P0.02 (AREF, aka AIN0)
|
||||
//_FL_DEFPIN(??, 3, 0); // D?? is P0.03 (A0, aka AIN1)
|
||||
//_FL_DEFPIN(??, 4, 0); // D?? is P0.04 (A1, aka AIN2, aka UART CTS option)
|
||||
//_FL_DEFPIN(??, 28, 0); // D?? is P0.28 (A2, aka AIN4)
|
||||
//_FL_DEFPIN(??, 29, 0); // D?? is P0.29 (A3, aka AIN5)
|
||||
//_FL_DEFPIN(??, 30, 0); // D?? is P0.30 (A4, aka AIN6)
|
||||
//_FL_DEFPIN(??, 31, 0); // D?? is P0.31 (A5, aka AIN7)
|
||||
|
||||
#else
|
||||
/* 48 pins, defined using natural mapping in Adafruit's variant.cpp (!) */
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 (XL1 .. ensure SB4 bridged, SB2 cut)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (XL2 .. ensure SB3 bridged, SB1 cut)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (AIN0)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (AIN1)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (AIN2 / UART CTS option)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (AIN3 / UART RTS)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (UART TxD)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (TRACECLK / UART CTS default)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (UART RxD)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09 (NFC1)
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10 (NFC2)
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (TRACEDATA2 / BUTTON1 default)
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (TRACEDATA1 / BUTTON2 default)
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED1)
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (LED2)
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15 (LED3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16 (LED4)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (QSPI !CS )
|
||||
//_DEFPIN_ARM_IDENTITY_P0(18); // P0.18 (RESET)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (QSPI CLK )
|
||||
//_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (QSPI DIO0)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (QSPI DIO1)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (QSPI DIO2)
|
||||
//_DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (QSPI DIO3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (BUTTON3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (BUTTON4)
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (AIN4)
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (AIN5)
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (AIN6)
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (AIN7)
|
||||
_DEFPIN_ARM_IDENTITY_P0(32); // P1.00 (SWO / TRACEDATA0)
|
||||
_DEFPIN_ARM_IDENTITY_P0(33); // P1.01
|
||||
_DEFPIN_ARM_IDENTITY_P0(34); // P1.02
|
||||
_DEFPIN_ARM_IDENTITY_P0(35); // P1.03
|
||||
_DEFPIN_ARM_IDENTITY_P0(36); // P1.04
|
||||
_DEFPIN_ARM_IDENTITY_P0(37); // P1.05
|
||||
_DEFPIN_ARM_IDENTITY_P0(38); // P1.06
|
||||
_DEFPIN_ARM_IDENTITY_P0(39); // P1.07 (BUTTON1 option)
|
||||
_DEFPIN_ARM_IDENTITY_P0(40); // P1.08 (BUTTON2 option)
|
||||
_DEFPIN_ARM_IDENTITY_P0(41); // P1.09 (TRACEDATA3)
|
||||
_DEFPIN_ARM_IDENTITY_P0(42); // P1.10
|
||||
_DEFPIN_ARM_IDENTITY_P0(43); // P1.11
|
||||
_DEFPIN_ARM_IDENTITY_P0(44); // P1.12
|
||||
_DEFPIN_ARM_IDENTITY_P0(45); // P1.13
|
||||
_DEFPIN_ARM_IDENTITY_P0(46); // P1.14
|
||||
_DEFPIN_ARM_IDENTITY_P0(47); // P1.15
|
||||
#endif
|
||||
#endif // defined (ARDUINO_NRF52840_PCA10056)
|
||||
|
||||
// Electronut labs bluey
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/bluey/variant.cpp
|
||||
#if defined(ARDUINO_ELECTRONUT_BLUEY)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Electronut labs bluey is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
|
||||
_FL_DEFPIN( 0, 26, 0); // D0 is P0.26
|
||||
_FL_DEFPIN( 1, 27, 0); // D1 is P0.27
|
||||
_FL_DEFPIN( 2, 22, 0); // D2 is P0.22 (SPI SS )
|
||||
_FL_DEFPIN( 3, 23, 0); // D3 is P0.23 (SPI MOSI)
|
||||
_FL_DEFPIN( 4, 24, 0); // D4 is P0.24 (SPI MISO, also A3)
|
||||
_FL_DEFPIN( 5, 25, 0); // D5 is P0.25 (SPI SCK )
|
||||
_FL_DEFPIN( 6, 16, 0); // D6 is P0.16 (Button)
|
||||
_FL_DEFPIN( 7, 19, 0); // D7 is P0.19 (R)
|
||||
_FL_DEFPIN( 8, 18, 0); // D8 is P0.18 (G)
|
||||
_FL_DEFPIN( 9, 17, 0); // D9 is P0.17 (B)
|
||||
_FL_DEFPIN(10, 11, 0); // D10 is P0.11 (SCL)
|
||||
_FL_DEFPIN(11, 12, 0); // D11 is P0.12 (DRDYn)
|
||||
_FL_DEFPIN(12, 13, 0); // D12 is P0.13 (SDA)
|
||||
_FL_DEFPIN(13, 14, 0); // D13 is P0.17 (INT)
|
||||
_FL_DEFPIN(14, 15, 0); // D14 is P0.15 (INT1)
|
||||
_FL_DEFPIN(15, 20, 0); // D15 is P0.20 (INT2)
|
||||
_FL_DEFPIN(16, 2, 0); // D16 is P0.02 (A0)
|
||||
_FL_DEFPIN(17, 3, 0); // D17 is P0.03 (A1)
|
||||
_FL_DEFPIN(18, 4, 0); // D18 is P0.04 (A2)
|
||||
_FL_DEFPIN(19, 24, 0); // D19 is P0.24 (A3, also D4/SPI MISO) -- is this right?
|
||||
_FL_DEFPIN(20, 29, 0); // D20 is P0.29 (A4)
|
||||
_FL_DEFPIN(21, 30, 0); // D21 is P0.30 (A5)
|
||||
_FL_DEFPIN(22, 31, 0); // D22 is P0.31 (A6)
|
||||
_FL_DEFPIN(23, 8, 0); // D23 is P0.08 (RX)
|
||||
_FL_DEFPIN(24, 6, 0); // D24 is P0.06 (TX)
|
||||
_FL_DEFPIN(25, 5, 0); // D25 is P0.05 (RTS)
|
||||
_FL_DEFPIN(26, 7, 0); // D26 is P0.07 (CTS)
|
||||
#endif // defined(ARDUINO_ELECTRONUT_BLUEY)
|
||||
|
||||
// Electronut labs hackaBLE
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE/variant.cpp
|
||||
#if defined(ARDUINO_ELECTRONUT_HACKABLE)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Electronut labs hackaBLE is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
_FL_DEFPIN( 0, 14, 0); // D0 is P0.14 (RX)
|
||||
_FL_DEFPIN( 1, 13, 0); // D1 is P0.13 (TX)
|
||||
_FL_DEFPIN( 2, 12, 0); // D2 is P0.12
|
||||
_FL_DEFPIN( 3, 11, 0); // D3 is P0.11 (SPI MOSI)
|
||||
_FL_DEFPIN( 4, 8, 0); // D4 is P0.08 (SPI MISO)
|
||||
_FL_DEFPIN( 5, 7, 0); // D5 is P0.07 (SPI SCK )
|
||||
_FL_DEFPIN( 6, 6, 0); // D6 is P0.06
|
||||
_FL_DEFPIN( 7, 27, 0); // D7 is P0.27
|
||||
_FL_DEFPIN( 8, 26, 0); // D8 is P0.26
|
||||
_FL_DEFPIN( 9, 25, 0); // D9 is P0.25
|
||||
_FL_DEFPIN(10, 5, 0); // D10 is P0.05 (A3)
|
||||
_FL_DEFPIN(11, 4, 0); // D11 is P0.04 (A2)
|
||||
_FL_DEFPIN(12, 3, 0); // D12 is P0.03 (A1)
|
||||
_FL_DEFPIN(13, 2, 0); // D13 is P0.02 (A0 / AREF)
|
||||
_FL_DEFPIN(14, 23, 0); // D14 is P0.23
|
||||
_FL_DEFPIN(15, 22, 0); // D15 is P0.22
|
||||
_FL_DEFPIN(16, 18, 0); // D16 is P0.18
|
||||
_FL_DEFPIN(17, 16, 0); // D17 is P0.16
|
||||
_FL_DEFPIN(18, 15, 0); // D18 is P0.15
|
||||
_FL_DEFPIN(19, 24, 0); // D19 is P0.24
|
||||
_FL_DEFPIN(20, 28, 0); // D20 is P0.28 (A4)
|
||||
_FL_DEFPIN(21, 29, 0); // D21 is P0.29 (A5)
|
||||
_FL_DEFPIN(22, 30, 0); // D22 is P0.30 (A6)
|
||||
_FL_DEFPIN(23, 31, 0); // D23 is P0.31 (A7)
|
||||
_FL_DEFPIN(24, 19, 0); // D24 is P0.19 (RED LED)
|
||||
_FL_DEFPIN(25, 20, 0); // D25 is P0.20 (GREEN LED)
|
||||
_FL_DEFPIN(26, 17, 0); // D26 is P0.17 (BLUE LED)
|
||||
#endif // defined(ARDUINO_ELECTRONUT_HACKABLE)
|
||||
|
||||
// Electronut labs hackaBLE_v2
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/hackaBLE_v2/variant.cpp
|
||||
// (32 pins, natural mapping)
|
||||
#if defined(ARDUINO_ELECTRONUT_hackaBLE_v2)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Electronut labs hackaBLE_v2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A0 / SDA / AREF)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A1 / SCL )
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A2)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A3)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07 (RX)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08 (TX)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11 (SPI MISO)
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12 (SPI MOSI)
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (SPI SCK )
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14 (SPI SS )
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16
|
||||
_DEFPIN_ARM_IDENTITY_P0(17); // P0.17 (BLUE LED)
|
||||
_DEFPIN_ARM_IDENTITY_P0(18); // P0.18
|
||||
_DEFPIN_ARM_IDENTITY_P0(19); // P0.19 (RED LED)
|
||||
_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (GREEN LED)
|
||||
// _DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (RESET)
|
||||
_DEFPIN_ARM_IDENTITY_P0(22); // P0.22
|
||||
_DEFPIN_ARM_IDENTITY_P0(23); // P0.23
|
||||
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28 (A4)
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29 (A5)
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30 (A6)
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31 (A7)
|
||||
#endif // defined(ARDUINO_ELECTRONUT_hackaBLE_v2)
|
||||
|
||||
// RedBear Blend 2
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_Blend2/variant.cpp
|
||||
#if defined(ARDUINO_RB_BLEND_2)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "RedBear Blend 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
_FL_DEFPIN( 0, 11, 0); // D0 is P0.11
|
||||
_FL_DEFPIN( 1, 12, 0); // D1 is P0.12
|
||||
_FL_DEFPIN( 2, 13, 0); // D2 is P0.13
|
||||
_FL_DEFPIN( 3, 14, 0); // D3 is P0.14
|
||||
_FL_DEFPIN( 4, 15, 0); // D4 is P0.15
|
||||
_FL_DEFPIN( 5, 16, 0); // D5 is P0.16
|
||||
_FL_DEFPIN( 6, 17, 0); // D6 is P0.17
|
||||
_FL_DEFPIN( 7, 18, 0); // D7 is P0.18
|
||||
_FL_DEFPIN( 8, 19, 0); // D8 is P0.19
|
||||
_FL_DEFPIN( 9, 20, 0); // D9 is P0.20
|
||||
_FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS )
|
||||
_FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI)
|
||||
_FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO)
|
||||
_FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED)
|
||||
_FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0)
|
||||
_FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1)
|
||||
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
|
||||
_FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3)
|
||||
_FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4)
|
||||
_FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5)
|
||||
_FL_DEFPIN(20, 26, 0); // D20 is P0.26 (SDA)
|
||||
_FL_DEFPIN(21, 27, 0); // D21 is P0.27 (SCL)
|
||||
_FL_DEFPIN(22, 8, 0); // D22 is P0.08 (RX)
|
||||
_FL_DEFPIN(23, 6, 0); // D23 is P0.06 (TX)
|
||||
_FL_DEFPIN(24, 2, 0); // D24 is P0.02 (AREF)
|
||||
#endif // defined(ARDUINO_RB_BLEND_2)
|
||||
|
||||
// RedBear BLE Nano 2
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/RedBear_BLENano2/variant.cpp
|
||||
#if defined(ARDUINO_RB_BLE_NANO_2)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "RedBear BLE Nano 2 is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
_FL_DEFPIN( 0, 30, 0); // D0 is P0.30 (A0 / RX)
|
||||
_FL_DEFPIN( 1, 29, 0); // D1 is P0.29 (A1 / TX)
|
||||
_FL_DEFPIN( 2, 28, 0); // D2 is P0.28 (A2 / SDA)
|
||||
_FL_DEFPIN( 3, 2, 0); // D3 is P0.02 (A3 / SCL)
|
||||
_FL_DEFPIN( 4, 5, 0); // D4 is P0.05 (A4)
|
||||
_FL_DEFPIN( 5, 4, 0); // D5 is P0.04 (A5)
|
||||
_FL_DEFPIN( 6, 3, 0); // D6 is P0.03 (SPI SS )
|
||||
_FL_DEFPIN( 7, 6, 0); // D7 is P0.06 (SPI MOSI)
|
||||
_FL_DEFPIN( 8, 7, 0); // D8 is P0.07 (SPI MISO)
|
||||
_FL_DEFPIN( 9, 8, 0); // D9 is P0.08 (SPI SCK )
|
||||
// _FL_DEFPIN(10, 21, 0); // D10 is P0.21 (RESET)
|
||||
_FL_DEFPIN(13, 11, 0); // D11 is P0.11 (LED)
|
||||
#endif // defined(ARDUINO_RB_BLE_NANO_2)
|
||||
|
||||
// Nordic Semiconductor nRF52 DK
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/nRF52DK/variant.cpp
|
||||
#if defined(ARDUINO_NRF52_DK)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Nordic Semiconductor nRF52 DK is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
_FL_DEFPIN( 0, 11, 0); // D0 is P0.11
|
||||
_FL_DEFPIN( 1, 12, 0); // D1 is P0.12
|
||||
_FL_DEFPIN( 2, 13, 0); // D2 is P0.13 (BUTTON1)
|
||||
_FL_DEFPIN( 3, 14, 0); // D3 is P0.14 (BUTTON2)
|
||||
_FL_DEFPIN( 4, 15, 0); // D4 is P0.15 (BUTTON3)
|
||||
_FL_DEFPIN( 5, 16, 0); // D5 is P0.16 (BUTTON4)
|
||||
_FL_DEFPIN( 6, 17, 0); // D6 is P0.17 (LED1)
|
||||
_FL_DEFPIN( 7, 18, 0); // D7 is P0.18 (LED2)
|
||||
_FL_DEFPIN( 8, 19, 0); // D8 is P0.19 (LED3)
|
||||
_FL_DEFPIN( 9, 20, 0); // D9 is P0.20 (LED4)
|
||||
_FL_DEFPIN(10, 22, 0); // D10 is P0.22 (SPI SS )
|
||||
_FL_DEFPIN(11, 23, 0); // D11 is P0.23 (SPI MOSI)
|
||||
_FL_DEFPIN(12, 24, 0); // D12 is P0.24 (SPI MISO)
|
||||
_FL_DEFPIN(13, 25, 0); // D13 is P0.25 (SPI SCK / LED)
|
||||
_FL_DEFPIN(14, 3, 0); // D14 is P0.03 (A0)
|
||||
_FL_DEFPIN(15, 4, 0); // D15 is P0.04 (A1)
|
||||
_FL_DEFPIN(16, 28, 0); // D16 is P0.28 (A2)
|
||||
_FL_DEFPIN(17, 29, 0); // D17 is P0.29 (A3)
|
||||
_FL_DEFPIN(18, 30, 0); // D18 is P0.30 (A4)
|
||||
_FL_DEFPIN(19, 31, 0); // D19 is P0.31 (A5)
|
||||
_FL_DEFPIN(20, 5, 0); // D20 is P0.05 (A6)
|
||||
_FL_DEFPIN(21, 2, 0); // D21 is P0.02 (A7 / AREF)
|
||||
_FL_DEFPIN(22, 26, 0); // D22 is P0.26 (SDA)
|
||||
_FL_DEFPIN(23, 27, 0); // D23 is P0.27 (SCL)
|
||||
_FL_DEFPIN(24, 8, 0); // D24 is P0.08 (RX)
|
||||
_FL_DEFPIN(25, 6, 0); // D25 is P0.06 (TX)
|
||||
#endif // defined(ARDUINO_NRF52_DK)
|
||||
|
||||
// Taida Century nRF52 mini board
|
||||
// https://github.com/sandeepmistry/arduino-nRF5/blob/master/variants/Taida_Century_nRF52_minidev/variant.cpp
|
||||
#if defined(ARDUINO_STCT_NRF52_minidev)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Taida Century nRF52 mini board is an untested board -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
//_FL_DEFPIN( 0, 25, 0); // D0 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 1, 26, 0); // D1 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 2, 27, 0); // D2 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 3, 28, 0); // D3 is P0.xx (near radio!)
|
||||
//_FL_DEFPIN( 4, 29, 0); // D4 is P0.xx (Not connected, near radio!)
|
||||
//_FL_DEFPIN( 5, 30, 0); // D5 is P0.xx (LED1, near radio!)
|
||||
//_FL_DEFPIN( 6, 31, 0); // D6 is P0.xx (LED2, near radio!)
|
||||
_FL_DEFPIN( 7, 2, 0); // D7 is P0.xx (SDA)
|
||||
_FL_DEFPIN( 8, 3, 0); // D8 is P0.xx (SCL)
|
||||
_FL_DEFPIN( 9, 4, 0); // D9 is P0.xx (BUTTON1 / NFC1)
|
||||
_FL_DEFPIN(10, 5, 0); // D10 is P0.xx
|
||||
//_FL_DEFPIN(11, 0, 0); // D11 is P0.xx (Not connected)
|
||||
//_FL_DEFPIN(12, 1, 0); // D12 is P0.xx (Not connected)
|
||||
_FL_DEFPIN(13, 6, 0); // D13 is P0.xx
|
||||
_FL_DEFPIN(14, 7, 0); // D14 is P0.xx
|
||||
_FL_DEFPIN(15, 8, 0); // D15 is P0.xx
|
||||
//_FL_DEFPIN(16, 9, 0); // D16 is P0.xx (Not connected)
|
||||
//_FL_DEFPIN(17, 10, 0); // D17 is P0.xx (NFC2, Not connected)
|
||||
_FL_DEFPIN(18, 11, 0); // D18 is P0.xx (RXD)
|
||||
_FL_DEFPIN(19, 12, 0); // D19 is P0.xx (TXD)
|
||||
_FL_DEFPIN(20, 13, 0); // D20 is P0.xx (SPI SS )
|
||||
_FL_DEFPIN(21, 14, 0); // D21 is P0.xx (SPI MISO)
|
||||
_FL_DEFPIN(22, 15, 0); // D22 is P0.xx (SPI MOSI)
|
||||
_FL_DEFPIN(23, 16, 0); // D23 is P0.xx (SPI SCK )
|
||||
_FL_DEFPIN(24, 17, 0); // D24 is P0.xx (A0)
|
||||
_FL_DEFPIN(25, 18, 0); // D25 is P0.xx (A1)
|
||||
_FL_DEFPIN(26, 19, 0); // D26 is P0.xx (A2)
|
||||
_FL_DEFPIN(27, 20, 0); // D27 is P0.xx (A3)
|
||||
//_FL_DEFPIN(28, 22, 0); // D28 is P0.xx (A4, near radio!)
|
||||
//_FL_DEFPIN(29, 23, 0); // D29 is P0.xx (A5, near radio!)
|
||||
_FL_DEFPIN(30, 24, 0); // D30 is P0.xx
|
||||
// _FL_DEFPIN(31, 21, 0); // D31 is P0.21 (RESET)
|
||||
#endif // defined(ARDUINO_STCT_NRF52_minidev)
|
||||
|
||||
// Generic nRF52832
|
||||
// See https://github.com/sandeepmistry/arduino-nRF5/blob/master/boards.txt
|
||||
#if defined(ARDUINO_GENERIC) && (\
|
||||
defined(NRF52832_XXAA) || defined(NRF52832_XXAB)\
|
||||
)
|
||||
#if defined(__FASTPIN_ARM_NRF52_VARIANT_FOUND)
|
||||
#error "Cannot define more than one board at a time"
|
||||
#else
|
||||
#define __FASTPIN_ARM_NRF52_VARIANT_FOUND
|
||||
#endif
|
||||
#warning "Using `generic` NRF52832 board is an untested configuration -- test and let use know your results via https://github.com/FastLED/FastLED/issues"
|
||||
|
||||
_DEFPIN_ARM_IDENTITY_P0( 0); // P0.00 ( UART RX
|
||||
_DEFPIN_ARM_IDENTITY_P0( 1); // P0.01 (A0, UART TX)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 2); // P0.02 (A1)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 3); // P0.03 (A2)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 4); // P0.04 (A3)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 5); // P0.05 (A4)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 6); // P0.06 (A5)
|
||||
_DEFPIN_ARM_IDENTITY_P0( 7); // P0.07
|
||||
_DEFPIN_ARM_IDENTITY_P0( 8); // P0.08
|
||||
_DEFPIN_ARM_IDENTITY_P0( 9); // P0.09
|
||||
_DEFPIN_ARM_IDENTITY_P0(10); // P0.10
|
||||
_DEFPIN_ARM_IDENTITY_P0(11); // P0.11
|
||||
_DEFPIN_ARM_IDENTITY_P0(12); // P0.12
|
||||
_DEFPIN_ARM_IDENTITY_P0(13); // P0.13 (LED)
|
||||
_DEFPIN_ARM_IDENTITY_P0(14); // P0.14
|
||||
_DEFPIN_ARM_IDENTITY_P0(15); // P0.15
|
||||
_DEFPIN_ARM_IDENTITY_P0(16); // P0.16
|
||||
_DEFPIN_ARM_IDENTITY_P0(17); // P0.17
|
||||
_DEFPIN_ARM_IDENTITY_P0(18); // P0.18
|
||||
_DEFPIN_ARM_IDENTITY_P0(19); // P0.19
|
||||
_DEFPIN_ARM_IDENTITY_P0(20); // P0.20 (I2C SDA)
|
||||
_DEFPIN_ARM_IDENTITY_P0(21); // P0.21 (I2C SCL)
|
||||
_DEFPIN_ARM_IDENTITY_P0(22); // P0.22 (SPI MISO)
|
||||
_DEFPIN_ARM_IDENTITY_P0(23); // P0.23 (SPI MOSI)
|
||||
_DEFPIN_ARM_IDENTITY_P0(24); // P0.24 (SPI SCK )
|
||||
_DEFPIN_ARM_IDENTITY_P0(25); // P0.25 (SPI SS )
|
||||
_DEFPIN_ARM_IDENTITY_P0(26); // P0.26
|
||||
_DEFPIN_ARM_IDENTITY_P0(27); // P0.27
|
||||
_DEFPIN_ARM_IDENTITY_P0(28); // P0.28
|
||||
_DEFPIN_ARM_IDENTITY_P0(29); // P0.29
|
||||
_DEFPIN_ARM_IDENTITY_P0(30); // P0.30
|
||||
_DEFPIN_ARM_IDENTITY_P0(31); // P0.31
|
||||
#endif // defined(ARDUINO_GENERIC)
|
||||
|
||||
|
||||
#endif // __FASTPIN_ARM_NRF52_VARIANTS_H
|
||||
341
libraries/FastLED/platforms/arm/nrf52/fastspi_arm_nrf52.h
Normal file
341
libraries/FastLED/platforms/arm/nrf52/fastspi_arm_nrf52.h
Normal file
@@ -0,0 +1,341 @@
|
||||
#ifndef __FASTSPI_ARM_NRF52_H
|
||||
#define __FASTSPI_ARM_NRF52_H
|
||||
|
||||
|
||||
#ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
|
||||
#include <nrf_spim.h>
|
||||
|
||||
#define FASTLED_ALL_PINS_HARDWARE_SPI
|
||||
|
||||
|
||||
// NRF52810 has SPIM0: Frequencies from 125kbps to 8Mbps
|
||||
// NRF52832 adds SPIM1, SPIM2 (same frequencies)
|
||||
// NRF52840 adds SPIM3 (same frequencies), adds SPIM3 that can be @ up to 32Mbps frequency(!)
|
||||
#if !defined(FASTLED_NRF52_SPIM)
|
||||
#define FASTLED_NRF52_SPIM NRF_SPIM0
|
||||
#endif
|
||||
|
||||
/* This class is slightly simpler than fastpin, as it can rely on fastpin
|
||||
* to handle the mapping to the underlying PN.XX board-level pins...
|
||||
*/
|
||||
|
||||
/// SPI_CLOCK_DIVIDER is number of CPU clock cycles per SPI transmission bit?
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class NRF52SPIOutput {
|
||||
|
||||
private:
|
||||
// static variables -- always using same SPIM instance
|
||||
static bool s_InUse;
|
||||
static bool s_NeedToWait; // a data transfer was started, and completion event was not cleared.
|
||||
|
||||
/*
|
||||
// TODO -- Workaround nRF52840 errata #198, which relates to
|
||||
// contention between SPIM3 and CPU over AHB.
|
||||
// The workaround is to ensure the SPIM TX buffer
|
||||
// is on a different / dedicated RAM block.
|
||||
// This also avoids AHB contention generally, so
|
||||
// should be applied to all supported boards.
|
||||
//
|
||||
// But... how to allocate m_Buffer[] to be at a
|
||||
// specific memory range? Also, might need to
|
||||
// avoid use of single-transaction writeBytes()
|
||||
// as cannot control where that memory lies....
|
||||
*/
|
||||
static uint8_t s_BufferIndex;
|
||||
static uint8_t s_Buffer[2][2]; // 2x two-byte buffers, allows one buffer currently being sent, and a second one being prepped to send.
|
||||
|
||||
// This allows saving the configuration of the SPIM instance
|
||||
// upon select(), and restoring the configuration upon release().
|
||||
struct spim_config {
|
||||
uint32_t inten;
|
||||
uint32_t shorts;
|
||||
uint32_t sck_pin;
|
||||
uint32_t mosi_pin;
|
||||
uint32_t miso_pin;
|
||||
uint32_t frequency;
|
||||
// data pointers, RX/TX counts not saved as would only hide bugs
|
||||
uint32_t config; // mode & bit order
|
||||
uint32_t orc;
|
||||
|
||||
#if false // additional configuration to save/restore for SPIM3
|
||||
uint32_t csn_pin;
|
||||
uint32_t csn_polarity; // CSNPOL
|
||||
uint32_t csn_duration; // IFTIMING.CSNDUR
|
||||
uint32_t rx_delay; // IFTIMING.RXDELAY
|
||||
uint32_t dcx_pin; // PSELDCX
|
||||
uint32_t dcx_config; // DCXCNT
|
||||
#endif
|
||||
|
||||
} m_SpiSavedConfig;
|
||||
void saveSpimConfig() {
|
||||
m_SpiSavedConfig.inten = FASTLED_NRF52_SPIM->INTENSET;
|
||||
m_SpiSavedConfig.shorts = FASTLED_NRF52_SPIM->SHORTS;
|
||||
m_SpiSavedConfig.sck_pin = FASTLED_NRF52_SPIM->PSEL.SCK;
|
||||
m_SpiSavedConfig.mosi_pin = FASTLED_NRF52_SPIM->PSEL.MOSI;
|
||||
m_SpiSavedConfig.miso_pin = FASTLED_NRF52_SPIM->PSEL.MISO;
|
||||
m_SpiSavedConfig.frequency = FASTLED_NRF52_SPIM->FREQUENCY;
|
||||
m_SpiSavedConfig.config = FASTLED_NRF52_SPIM->CONFIG;
|
||||
m_SpiSavedConfig.orc = FASTLED_NRF52_SPIM->ORC;
|
||||
|
||||
#if false // additional configuration to save/restore for SPIM3
|
||||
m_SpiSavedConfig.csn_pin = FASTLED_NRF52_SPIM->PSEL.CSN;
|
||||
m_SpiSavedConfig.csn_polarity = FASTLED_NRF52_SPIM->CSNPOL;
|
||||
m_SpiSavedConfig.csn_duration = FASTLED_NRF52_SPIM->IFTIMING.CSNDUR;
|
||||
m_SpiSavedConfig.dcx_pin = FASTLED_NRF52_SPIM->PSELDCX;
|
||||
m_SpiSavedConfig.dcx_config = FASTLED_NRF52_SPIM->DCXCNT;
|
||||
#endif
|
||||
}
|
||||
void restoreSpimConfig() {
|
||||
// 0. ASSERT() the SPIM instance is not enabled
|
||||
|
||||
FASTLED_NRF52_SPIM->INTENCLR = 0xFFFFFFFF;
|
||||
FASTLED_NRF52_SPIM->INTENSET = m_SpiSavedConfig.inten;
|
||||
FASTLED_NRF52_SPIM->SHORTS = m_SpiSavedConfig.shorts;
|
||||
FASTLED_NRF52_SPIM->PSEL.SCK = m_SpiSavedConfig.sck_pin;
|
||||
FASTLED_NRF52_SPIM->PSEL.MOSI = m_SpiSavedConfig.mosi_pin;
|
||||
FASTLED_NRF52_SPIM->PSEL.MISO = m_SpiSavedConfig.miso_pin;
|
||||
FASTLED_NRF52_SPIM->FREQUENCY = m_SpiSavedConfig.frequency;
|
||||
FASTLED_NRF52_SPIM->CONFIG = m_SpiSavedConfig.config;
|
||||
FASTLED_NRF52_SPIM->ORC = m_SpiSavedConfig.orc;
|
||||
|
||||
#if false // additional configuration to save/restore for SPIM3
|
||||
FASTLED_NRF52_SPIM->PSEL.CSN = m_SpiSavedConfig.csn_pin;
|
||||
FASTLED_NRF52_SPIM->CSNPOL = m_SpiSavedConfig.csn_polarity;
|
||||
FASTLED_NRF52_SPIM->IFTIMING.CSNDUR = m_SpiSavedConfig.csn_duration;
|
||||
FASTLED_NRF52_SPIM->PSELDCX = m_SpiSavedConfig.dcx_pin;
|
||||
FASTLED_NRF52_SPIM->DCXCNT = m_SpiSavedConfig.dcx_config;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
NRF52SPIOutput() {}
|
||||
|
||||
// Low frequency GPIO is for signals with a frequency up to 10 kHz. Lowest speed SPIM is 125kbps.
|
||||
static_assert(!FastPin<_DATA_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified");
|
||||
static_assert(!FastPin<_CLOCK_PIN>::LowSpeedOnlyRecommended(), "Invalid (low-speed only) pin specified");
|
||||
|
||||
/// initialize the SPI subssytem
|
||||
void init() {
|
||||
// 0. ASSERT() the SPIM instance is not enabled / in use
|
||||
//ASSERT(m_SPIM->ENABLE != (SPIM_ENABLE_ENABLE_Enabled << SPIM_ENABLE_ENABLE_Pos));
|
||||
|
||||
// 1. set pins to output/H0H1 drive/etc.
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
|
||||
// 2. Configure SPIMx
|
||||
nrf_spim_configure(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_MODE_0,
|
||||
NRF_SPIM_BIT_ORDER_MSB_FIRST
|
||||
);
|
||||
nrf_spim_frequency_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_FREQ_4M // BUGBUG -- use _SPI_CLOCK_DIVIDER to determine frequency
|
||||
);
|
||||
nrf_spim_pins_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
FastPin<_CLOCK_PIN>::nrf_pin(),
|
||||
FastPin<_DATA_PIN>::nrf_pin(),
|
||||
NRF_SPIM_PIN_NOT_CONNECTED
|
||||
);
|
||||
|
||||
// 4. Ensure events are cleared
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END);
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED);
|
||||
|
||||
// 5. Enable the SPIM instance
|
||||
nrf_spim_enable(FASTLED_NRF52_SPIM);
|
||||
}
|
||||
|
||||
/// latch the CS select
|
||||
void select() {
|
||||
//ASSERT(!s_InUse);
|
||||
saveSpimConfig();
|
||||
s_InUse = true;
|
||||
init();
|
||||
}
|
||||
|
||||
/// release the CS select
|
||||
void release() {
|
||||
//ASSERT(s_InUse);
|
||||
waitFully();
|
||||
s_InUse = false;
|
||||
restoreSpimConfig();
|
||||
}
|
||||
|
||||
/// wait until all queued up data has been written
|
||||
static void waitFully() {
|
||||
if (!s_NeedToWait) return;
|
||||
// else, need to wait for END event
|
||||
while(!FASTLED_NRF52_SPIM->EVENTS_END) {};
|
||||
s_NeedToWait = 0;
|
||||
// only use two events in this code...
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_END);
|
||||
nrf_spim_event_clear(FASTLED_NRF52_SPIM, NRF_SPIM_EVENT_STARTED);
|
||||
return;
|
||||
}
|
||||
// wait only until we can add a new transaction into the registers
|
||||
// (caller must still waitFully() before actually starting this next transaction)
|
||||
static void wait() {
|
||||
if (!s_NeedToWait) return;
|
||||
while (!FASTLED_NRF52_SPIM->EVENTS_STARTED) {};
|
||||
// leave the event set here... caller must waitFully() and start next transaction
|
||||
return;
|
||||
}
|
||||
|
||||
/// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) {
|
||||
wait();
|
||||
// cannot use pointer to stack, so copy to m_buffer[]
|
||||
uint8_t i = (s_BufferIndex ? 1u : 0u);
|
||||
s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap
|
||||
|
||||
s_Buffer[i][0u] = b; // cannot use the stack location, so copy to a more permanent buffer...
|
||||
nrf_spim_tx_buffer_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
&(s_Buffer[i][0u]),
|
||||
1
|
||||
);
|
||||
|
||||
waitFully();
|
||||
nrf_spim_task_trigger(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_TASK_START
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) {
|
||||
wait();
|
||||
// cannot use pointer to stack, so copy to m_buffer[]
|
||||
uint8_t i = (s_BufferIndex ? 1u : 0u);
|
||||
s_BufferIndex = !s_BufferIndex; // 1 <==> 0 swap
|
||||
|
||||
s_Buffer[i][0u] = (w >> 8u); // cannot use the stack location, so copy to a more permanent buffer...
|
||||
s_Buffer[i][1u] = (w & 0xFFu); // cannot use the stack location, so copy to a more permanent buffer...
|
||||
nrf_spim_tx_buffer_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
&(s_Buffer[i][0u]),
|
||||
2
|
||||
);
|
||||
|
||||
waitFully();
|
||||
nrf_spim_task_trigger(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_TASK_START
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
/// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while (len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
/// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
writeBytesValueRaw(value, len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
/// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
void writeBytes(uint8_t *data, int len) {
|
||||
// This is a special-case, with no adjustment of the bytes... write them directly...
|
||||
select();
|
||||
wait();
|
||||
nrf_spim_tx_buffer_set(
|
||||
FASTLED_NRF52_SPIM,
|
||||
data,
|
||||
len
|
||||
);
|
||||
waitFully();
|
||||
nrf_spim_task_trigger(
|
||||
FASTLED_NRF52_SPIM,
|
||||
NRF_SPIM_TASK_START
|
||||
);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
/// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
template<class D> void writeBytes(uint8_t *data, int len) {
|
||||
uint8_t * end = data + len;
|
||||
select();
|
||||
wait();
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
/// specialization for DATA_NOP ...
|
||||
//template<DATA_NOP> void writeBytes(uint8_t * data, int len) {
|
||||
// writeBytes(data, len);
|
||||
//}
|
||||
|
||||
/// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
// SPIM instance must be finished transmitting and then disabled
|
||||
waitFully();
|
||||
nrf_spim_disable(FASTLED_NRF52_SPIM);
|
||||
// set the data pin to appropriate state
|
||||
if (b & (1 << BIT)) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
// delay 1/2 cycle per SPI bit
|
||||
delaycycles<_SPI_CLOCK_DIVIDER/2>();
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
delaycycles<_SPI_CLOCK_DIVIDER/2>();
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
// re-enable the SPIM instance
|
||||
nrf_spim_enable(FASTLED_NRF52_SPIM);
|
||||
}
|
||||
|
||||
/// write out pixel data from the given PixelController object, including select, release, and waiting
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
// TODO: If user indicates a pre-allocated double-buffer,
|
||||
// then process all the pixels at once into that buffer,
|
||||
// then use the non-templated WriteBytes(data, len) function
|
||||
// to write the entire buffer as a single SPI transaction.
|
||||
while (pixels.has(1)) {
|
||||
if (FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
// Static member definition and initialization using templates.
|
||||
// see https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template#answer-3229919
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_InUse = false;
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
bool NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_NeedToWait = false;
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_BufferIndex = 0;
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
uint8_t NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER>::s_Buffer[2][2] = {{0,0},{0,0}};
|
||||
|
||||
#endif // #ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
|
||||
|
||||
|
||||
#endif // #ifndef __FASTPIN_ARM_NRF52_H
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef __LED_SYSDEFS_ARM_NRF52
|
||||
#define __LED_SYSDEFS_ARM_NRF52
|
||||
|
||||
#define FASTLED_ARM
|
||||
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 64000000 // the NRF52 series has a 64MHz CPU
|
||||
#endif
|
||||
|
||||
// even though CPU is at 64MHz, use the 8MHz-defined timings because...
|
||||
// PWM module runs at 16MHz
|
||||
// SPI0..2 runs at 8MHz
|
||||
#define CLOCKLESS_FREQUENCY 16000000 // the NRF52 has EasyDMA for PWM module at 16MHz
|
||||
|
||||
#ifndef F_TIMER
|
||||
#define F_TIMER 16000000 // the NRF52 timer is 16MHz, even though CPU is 64MHz
|
||||
#endif
|
||||
|
||||
#if !defined(FASTLED_USE_PROGMEM)
|
||||
#define FASTLED_USE_PROGMEM 0 // nRF52 series have flat memory model
|
||||
#endif
|
||||
|
||||
#if !defined(FASTLED_ALLOW_INTERRUPTS)
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
// Use PWM instance 0
|
||||
// See clockless_arm_nrf52.h and (in root of library) platforms.cpp
|
||||
#define FASTLED_NRF52_ENABLE_PWM_INSTANCE0
|
||||
|
||||
#if defined(FASTLED_NRF52_NEVER_INLINE)
|
||||
#define FASTLED_NRF52_INLINE_ATTRIBUTE __attribute__((always_inline)) inline
|
||||
#else
|
||||
#define FASTLED_NRF52_INLINE_ATTRIBUTE __attribute__((always_inline)) inline
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include <nrf.h>
|
||||
#include <nrf_spim.h> // for FastSPI
|
||||
#include <nrf_pwm.h> // for Clockless
|
||||
#include <nrf_nvic.h> // for Clockless / anything else using interrupts
|
||||
typedef __I uint32_t RoReg;
|
||||
typedef __IO uint32_t RwReg;
|
||||
|
||||
#define cli() __disable_irq()
|
||||
#define sei() __enable_irq()
|
||||
|
||||
#define FASTLED_NRF52_DEBUGPRINT(format, ...)
|
||||
//#define FASTLED_NRF52_DEBUGPRINT(format, ...)\
|
||||
// do {\
|
||||
// FastLED_NRF52_DebugPrint(format, ##__VA_ARGS__);\
|
||||
// } while(0);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // __LED_SYSDEFS_ARM_NRF52
|
||||
120
libraries/FastLED/platforms/arm/sam/clockless_arm_sam.h
Normal file
120
libraries/FastLED/platforms/arm/sam/clockless_arm_sam.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_SAM_H
|
||||
#define __INC_CLOCKLESS_ARM_SAM_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
// Definition for a single channel clockless controller for the sam family of arm chips, like that used in the due and rfduino
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
|
||||
#if defined(__SAM3X8E__)
|
||||
|
||||
|
||||
#define TADJUST 0
|
||||
#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register uint8_t & b) {
|
||||
// Make sure we don't slot into a wrapping spot, this will delay up to 12.5µs for WS2812
|
||||
// bool bShift=0;
|
||||
// while(VAL < (TOTAL*10)) { bShift=true; }
|
||||
// if(bShift) { next_mark = (VAL-TOTAL); };
|
||||
|
||||
for(register uint32_t i = BITS; i > 0; i--) {
|
||||
// wait to start the bit, then set the pin high
|
||||
while(DUE_TIMER_VAL < next_mark);
|
||||
next_mark = (DUE_TIMER_VAL+TOTAL);
|
||||
*port = 1;
|
||||
|
||||
// how long we want to wait next depends on whether or not our bit is set to 1 or 0
|
||||
if(b&0x80) {
|
||||
// we're a 1, wait until there's less than T3 clocks left
|
||||
while((next_mark - DUE_TIMER_VAL) > (T3));
|
||||
} else {
|
||||
// we're a 0, wait until there's less than (T2+T3+slop) clocks left in this bit
|
||||
while((next_mark - DUE_TIMER_VAL) > (T2+T3+6+TADJUST+TADJUST));
|
||||
}
|
||||
*port=0;
|
||||
b <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Setup and start the clock
|
||||
TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1);
|
||||
pmc_enable_periph_clk(DUE_TIMER_ID);
|
||||
TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL);
|
||||
|
||||
register data_ptr_t port asm("r7") = FastPinBB<DATA_PIN>::port(); FORCE_REFERENCE(port);
|
||||
*port = 0;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL));
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
if(DUE_TIMER_VAL > next_mark) {
|
||||
if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return 0; }
|
||||
}
|
||||
#endif
|
||||
|
||||
writeBits<8+XTRA0>(next_mark, port, b);
|
||||
|
||||
b = pixels.loadAndScale1();
|
||||
writeBits<8+XTRA0>(next_mark, port,b);
|
||||
|
||||
b = pixels.loadAndScale2();
|
||||
writeBits<8+XTRA0>(next_mark, port,b);
|
||||
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL);
|
||||
return DUE_TIMER_VAL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
184
libraries/FastLED/platforms/arm/sam/clockless_block_arm_sam.h
Normal file
184
libraries/FastLED/platforms/arm/sam/clockless_block_arm_sam.h
Normal file
@@ -0,0 +1,184 @@
|
||||
#ifndef __INC_BLOCK_CLOCKLESS_H
|
||||
#define __INC_BLOCK_CLOCKLESS_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
|
||||
// is where the line is raised hi. The second pointsnt is where the line is dropped low for a zero. The third point is where the
|
||||
// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(__SAM3X8E__)
|
||||
#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFF))
|
||||
|
||||
#define FASTLED_HAS_BLOCKLESS 1
|
||||
|
||||
#define PORTD_FIRST_PIN 25
|
||||
#define PORTA_FIRST_PIN 69
|
||||
#define PORTB_FIRST_PIN 90
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[8];
|
||||
uint32_t raw[2];
|
||||
} Lines;
|
||||
|
||||
#define TADJUST 0
|
||||
#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
|
||||
#define T1_MARK (TOTAL - (T1+TADJUST))
|
||||
#define T2_MARK (T1_MARK - (T2+TADJUST))
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
|
||||
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * LANES; }
|
||||
virtual void init() {
|
||||
static_assert(LANES <= 8, "Maximum of 8 lanes for Due parallel controllers!");
|
||||
if(FIRST_PIN == PORTA_FIRST_PIN) {
|
||||
switch(LANES) {
|
||||
case 8: FastPin<31>::setOutput();
|
||||
case 7: FastPin<58>::setOutput();
|
||||
case 6: FastPin<100>::setOutput();
|
||||
case 5: FastPin<59>::setOutput();
|
||||
case 4: FastPin<60>::setOutput();
|
||||
case 3: FastPin<61>::setOutput();
|
||||
case 2: FastPin<68>::setOutput();
|
||||
case 1: FastPin<69>::setOutput();
|
||||
}
|
||||
} else if(FIRST_PIN == PORTD_FIRST_PIN) {
|
||||
switch(LANES) {
|
||||
case 8: FastPin<11>::setOutput();
|
||||
case 7: FastPin<29>::setOutput();
|
||||
case 6: FastPin<15>::setOutput();
|
||||
case 5: FastPin<14>::setOutput();
|
||||
case 4: FastPin<28>::setOutput();
|
||||
case 3: FastPin<27>::setOutput();
|
||||
case 2: FastPin<26>::setOutput();
|
||||
case 1: FastPin<25>::setOutput();
|
||||
}
|
||||
} else if(FIRST_PIN == PORTB_FIRST_PIN) {
|
||||
switch(LANES) {
|
||||
case 8: FastPin<97>::setOutput();
|
||||
case 7: FastPin<96>::setOutput();
|
||||
case 6: FastPin<95>::setOutput();
|
||||
case 5: FastPin<94>::setOutput();
|
||||
case 4: FastPin<93>::setOutput();
|
||||
case 3: FastPin<92>::setOutput();
|
||||
case 2: FastPin<91>::setOutput();
|
||||
case 1: FastPin<90>::setOutput();
|
||||
}
|
||||
}
|
||||
mPinMask = FastPin<FIRST_PIN>::mask();
|
||||
mPort = FastPin<FIRST_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) {
|
||||
mWait.wait();
|
||||
showRGBInternal(pixels);
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) {
|
||||
// Serial.println("Entering show");
|
||||
|
||||
int nLeds = allpixels.mLen;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
Lines b0,b1,b2;
|
||||
|
||||
allpixels.preStepFirstByteDithering();
|
||||
for(uint8_t i = 0; i < LANES; i++) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
|
||||
// Setup and start the clock
|
||||
TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1);
|
||||
pmc_enable_periph_clk(DUE_TIMER_ID);
|
||||
TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL);
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
#endif
|
||||
uint32_t next_mark = (DUE_TIMER_VAL + (TOTAL));
|
||||
while(nLeds--) {
|
||||
allpixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
if(DUE_TIMER_VAL > next_mark) {
|
||||
if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) {
|
||||
sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return DUE_TIMER_VAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(next_mark, b0, b1, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(next_mark, b1, b2, allpixels);
|
||||
|
||||
allpixels.advanceData();
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(next_mark, b2, b0, allpixels);
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
}
|
||||
|
||||
return DUE_TIMER_VAL;
|
||||
}
|
||||
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register Lines & b, Lines & b3, PixelController<RGB_ORDER,LANES, PORT_MASK> &pixels) { // , register uint32_t & b2) {
|
||||
Lines b2;
|
||||
transpose8x1(b.bytes,b2.bytes);
|
||||
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(uint32_t i = 0; (i < LANES) && (i<8); i++) {
|
||||
while(DUE_TIMER_VAL < next_mark);
|
||||
next_mark = (DUE_TIMER_VAL+TOTAL);
|
||||
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
|
||||
while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
|
||||
*FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;
|
||||
|
||||
while((next_mark - (DUE_TIMER_VAL)) > T3);
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
|
||||
b3.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
}
|
||||
|
||||
for(uint32_t i = LANES; i < 8; i++) {
|
||||
while(DUE_TIMER_VAL < next_mark);
|
||||
next_mark = (DUE_TIMER_VAL+TOTAL);
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK;
|
||||
|
||||
while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
|
||||
*FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;
|
||||
|
||||
while((next_mark - DUE_TIMER_VAL) > T3);
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
10
libraries/FastLED/platforms/arm/sam/fastled_arm_sam.h
Normal file
10
libraries/FastLED/platforms/arm/sam/fastled_arm_sam.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __INC_FASTLED_ARM_SAM_H
|
||||
#define __INC_FASTLED_ARM_SAM_H
|
||||
|
||||
// Include the sam headers
|
||||
#include "fastpin_arm_sam.h"
|
||||
#include "fastspi_arm_sam.h"
|
||||
#include "clockless_arm_sam.h"
|
||||
#include "clockless_block_arm_sam.h"
|
||||
|
||||
#endif
|
||||
138
libraries/FastLED/platforms/arm/sam/fastpin_arm_sam.h
Normal file
138
libraries/FastLED/platforms/arm/sam/fastpin_arm_sam.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef __INC_FASTPIN_ARM_SAM_H
|
||||
#define __INC_FASTPIN_ARM_SAM_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be sloightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/// Template definition for arduino due style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data register, set output register, clear output register, set data direction register
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _PDOR, typename _PSOR, typename _PCOR, typename _PDDR> class _DUEPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PSOR::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PCOR::r() = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _PDOR::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PDOR::r() ^= _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PDOR::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PDOR::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PDOR::r(); }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_PSOR::r(); }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_PCOR::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
|
||||
/// Template definition for DUE style ARM pins using bit banding, providing direct access to the various GPIO registers. GCC
|
||||
/// does a poor job of optimizing around these accesses so they are not being used just yet.
|
||||
template<uint8_t PIN, uint32_t _BIT, typename _PDOR, typename _PSOR, typename _PCOR, typename _PDDR> class _DUEPIN_BITBAND {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 1; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = 0; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { *_PDOR::template rx<_BIT>() ^= 1; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return 1; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return 0; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return _PDOR::template rx<_BIT>(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return 1; }
|
||||
};
|
||||
|
||||
#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)
|
||||
#define GPIO_BITBAND_PTR(reg, bit) ((uint32_t *)GPIO_BITBAND_ADDR((reg), (bit)))
|
||||
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; } \
|
||||
template<int BIT> static __attribute__((always_inline)) inline ptr_reg32_t rx() { return GPIO_BITBAND_PTR(T, BIT); } };
|
||||
#define _FL_IO(L,C) _RD32(REG_PIO ## L ## _ODSR); _RD32(REG_PIO ## L ## _SODR); _RD32(REG_PIO ## L ## _CODR); _RD32(REG_PIO ## L ## _OER); _FL_DEFINE_PORT3(L, C, _R(REG_PIO ## L ## _ODSR));
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _DUEPIN<PIN, 1 << BIT, _R(REG_PIO ## L ## _ODSR), _R(REG_PIO ## L ## _SODR), _R(REG_PIO ## L ## _CODR), \
|
||||
_R(GPIO ## L ## _OER)> {}; \
|
||||
template<> class FastPinBB<PIN> : public _DUEPIN_BITBAND<PIN, BIT, _R(REG_PIO ## L ## _ODSR), _R(REG_PIO ## L ## _SODR), _R(REG_PIO ## L ## _CODR), \
|
||||
_R(GPIO ## L ## _OER)> {};
|
||||
|
||||
_FL_IO(A,0);
|
||||
_FL_IO(B,1);
|
||||
_FL_IO(C,2);
|
||||
_FL_IO(D,3);
|
||||
|
||||
#if defined(__SAM3X8E__)
|
||||
|
||||
|
||||
#define MAX_PIN 78
|
||||
_FL_DEFPIN(0, 8, A); _FL_DEFPIN(1, 9, A); _FL_DEFPIN(2, 25, B); _FL_DEFPIN(3, 28, C);
|
||||
_FL_DEFPIN(4, 26, C); _FL_DEFPIN(5, 25, C); _FL_DEFPIN(6, 24, C); _FL_DEFPIN(7, 23, C);
|
||||
_FL_DEFPIN(8, 22, C); _FL_DEFPIN(9, 21, C); _FL_DEFPIN(10, 29, C); _FL_DEFPIN(11, 7, D);
|
||||
_FL_DEFPIN(12, 8, D); _FL_DEFPIN(13, 27, B); _FL_DEFPIN(14, 4, D); _FL_DEFPIN(15, 5, D);
|
||||
_FL_DEFPIN(16, 13, A); _FL_DEFPIN(17, 12, A); _FL_DEFPIN(18, 11, A); _FL_DEFPIN(19, 10, A);
|
||||
_FL_DEFPIN(20, 12, B); _FL_DEFPIN(21, 13, B); _FL_DEFPIN(22, 26, B); _FL_DEFPIN(23, 14, A);
|
||||
_FL_DEFPIN(24, 15, A); _FL_DEFPIN(25, 0, D); _FL_DEFPIN(26, 1, D); _FL_DEFPIN(27, 2, D);
|
||||
_FL_DEFPIN(28, 3, D); _FL_DEFPIN(29, 6, D); _FL_DEFPIN(30, 9, D); _FL_DEFPIN(31, 7, A);
|
||||
_FL_DEFPIN(32, 10, D); _FL_DEFPIN(33, 1, C); _FL_DEFPIN(34, 2, C); _FL_DEFPIN(35, 3, C);
|
||||
_FL_DEFPIN(36, 4, C); _FL_DEFPIN(37, 5, C); _FL_DEFPIN(38, 6, C); _FL_DEFPIN(39, 7, C);
|
||||
_FL_DEFPIN(40, 8, C); _FL_DEFPIN(41, 9, C); _FL_DEFPIN(42, 19, A); _FL_DEFPIN(43, 20, A);
|
||||
_FL_DEFPIN(44, 19, C); _FL_DEFPIN(45, 18, C); _FL_DEFPIN(46, 17, C); _FL_DEFPIN(47, 16, C);
|
||||
_FL_DEFPIN(48, 15, C); _FL_DEFPIN(49, 14, C); _FL_DEFPIN(50, 13, C); _FL_DEFPIN(51, 12, C);
|
||||
_FL_DEFPIN(52, 21, B); _FL_DEFPIN(53, 14, B); _FL_DEFPIN(54, 16, A); _FL_DEFPIN(55, 24, A);
|
||||
_FL_DEFPIN(56, 23, A); _FL_DEFPIN(57, 22, A); _FL_DEFPIN(58, 6, A); _FL_DEFPIN(59, 4, A);
|
||||
_FL_DEFPIN(60, 3, A); _FL_DEFPIN(61, 2, A); _FL_DEFPIN(62, 17, B); _FL_DEFPIN(63, 18, B);
|
||||
_FL_DEFPIN(64, 19, B); _FL_DEFPIN(65, 20, B); _FL_DEFPIN(66, 15, B); _FL_DEFPIN(67, 16, B);
|
||||
_FL_DEFPIN(68, 1, A); _FL_DEFPIN(69, 0, A); _FL_DEFPIN(70, 17, A); _FL_DEFPIN(71, 18, A);
|
||||
_FL_DEFPIN(72, 30, C); _FL_DEFPIN(73, 21, A); _FL_DEFPIN(74, 25, A); _FL_DEFPIN(75, 26, A);
|
||||
_FL_DEFPIN(76, 27, A); _FL_DEFPIN(77, 28, A); _FL_DEFPIN(78, 23, B);
|
||||
|
||||
// digix pins
|
||||
_FL_DEFPIN(90, 0, B); _FL_DEFPIN(91, 1, B); _FL_DEFPIN(92, 2, B); _FL_DEFPIN(93, 3, B);
|
||||
_FL_DEFPIN(94, 4, B); _FL_DEFPIN(95, 5, B); _FL_DEFPIN(96, 6, B); _FL_DEFPIN(97, 7, B);
|
||||
_FL_DEFPIN(98, 8, B); _FL_DEFPIN(99, 9, B); _FL_DEFPIN(100, 5, A); _FL_DEFPIN(101, 22, B);
|
||||
_FL_DEFPIN(102, 23, B); _FL_DEFPIN(103, 24, B); _FL_DEFPIN(104, 27, C); _FL_DEFPIN(105, 20, C);
|
||||
_FL_DEFPIN(106, 11, C); _FL_DEFPIN(107, 10, C); _FL_DEFPIN(108, 21, A); _FL_DEFPIN(109, 30, C);
|
||||
_FL_DEFPIN(110, 29, B); _FL_DEFPIN(111, 30, B); _FL_DEFPIN(112, 31, B); _FL_DEFPIN(113, 28, B);
|
||||
|
||||
#define SPI_DATA 75
|
||||
#define SPI_CLOCK 76
|
||||
#define ARM_HARDWARE_SPI
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_SAM_H
|
||||
163
libraries/FastLED/platforms/arm/sam/fastspi_arm_sam.h
Normal file
163
libraries/FastLED/platforms/arm/sam/fastspi_arm_sam.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#ifndef __INC_FASTSPI_ARM_SAM_H
|
||||
#define __INC_FASTSPI_ARM_SAM_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(__SAM3X8E__)
|
||||
#define m_SPI ((Spi*)SPI0)
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class SAMHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
|
||||
static inline void waitForEmpty() { while ((m_SPI->SPI_SR & SPI_SR_TDRE) == 0); }
|
||||
|
||||
void enableConfig() { m_SPI->SPI_WPMR &= ~SPI_WPMR_WPEN; }
|
||||
void disableConfig() { m_SPI->SPI_WPMR |= SPI_WPMR_WPEN; }
|
||||
|
||||
void enableSPI() { m_SPI->SPI_CR = SPI_CR_SPIEN; }
|
||||
void disableSPI() { m_SPI->SPI_CR = SPI_CR_SPIDIS; }
|
||||
void resetSPI() { m_SPI->SPI_CR = SPI_CR_SWRST; }
|
||||
|
||||
static inline void readyTransferBits(register uint32_t bits) {
|
||||
bits -= 8;
|
||||
// don't change the number of transfer bits while data is still being transferred from TDR to the shift register
|
||||
waitForEmpty();
|
||||
m_SPI->SPI_CSR[0] = SPI_CSR_NCPHA | SPI_CSR_CSAAT | (bits << SPI_CSR_BITS_Pos) | SPI_CSR_DLYBCT(1) | SPI_CSR_SCBR(_SPI_CLOCK_DIVIDER);
|
||||
}
|
||||
|
||||
template<int BITS> static inline void writeBits(uint16_t w) {
|
||||
waitForEmpty();
|
||||
m_SPI->SPI_TDR = (uint32_t)w | SPI_PCS(0);
|
||||
}
|
||||
|
||||
public:
|
||||
SAMHardwareSPIOutput() { m_pSelect = NULL; }
|
||||
SAMHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { /* TODO */ }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() {
|
||||
// m_SPI = SPI0;
|
||||
|
||||
// set the output pins master out, master in, clock. Note doing this here because I still don't
|
||||
// know how I want to expose this type of functionality in FastPin.
|
||||
PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN>::mask(), PIO_DEFAULT);
|
||||
PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_DATA_PIN-1>::mask(), PIO_DEFAULT);
|
||||
PIO_Configure(PIOA, PIO_PERIPH_A, FastPin<_CLOCK_PIN>::mask(), PIO_DEFAULT);
|
||||
|
||||
release();
|
||||
|
||||
// Configure the SPI clock, divider between 1-255
|
||||
// SCBR = _SPI_CLOCK_DIVIDER
|
||||
pmc_enable_periph_clk(ID_SPI0);
|
||||
disableSPI();
|
||||
|
||||
// reset twice (what the sam code does, not sure why?)
|
||||
resetSPI();
|
||||
resetSPI();
|
||||
|
||||
// Configure SPI as master, enable
|
||||
// Bits we want in MR: master, disable mode fault detection, variable peripheral select
|
||||
m_SPI->SPI_MR = SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PS;
|
||||
|
||||
enableSPI();
|
||||
|
||||
// Send everything out in 8 bit chunks, other sizes appear to work, poorly...
|
||||
readyTransferBits(8);
|
||||
}
|
||||
|
||||
// latch the CS select
|
||||
void inline select() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->select(); } }
|
||||
|
||||
// release the CS select
|
||||
void inline release() __attribute__((always_inline)) { if(m_pSelect != NULL) { m_pSelect->release(); } }
|
||||
|
||||
// wait until all queued up data has been written
|
||||
void waitFully() { while((m_SPI->SPI_SR & SPI_SR_TXEMPTY) == 0); }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) {
|
||||
writeBits<8>(b);
|
||||
}
|
||||
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) {
|
||||
writeBits<16>(w);
|
||||
}
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select(); writeBytesValueRaw(value, len); release();
|
||||
}
|
||||
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
// could be optimized to write 16bit words out instead of 8bit bytes
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
// not the most efficient mechanism in the world - but should be enough for sm16716 and friends
|
||||
template <uint8_t BIT> inline void writeBit(uint8_t b) {
|
||||
// need to wait for all exisiting data to go out the door, first
|
||||
waitFully();
|
||||
disableSPI();
|
||||
if(b & (1 << BIT)) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
|
||||
FastPin<_CLOCK_PIN>::hi();
|
||||
FastPin<_CLOCK_PIN>::lo();
|
||||
enableSPI();
|
||||
}
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
while(pixels.has(1)) {
|
||||
writeBits<9>((1<<8) | D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
} else {
|
||||
while(pixels.has(1)) {
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
}
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
#endif
|
||||
39
libraries/FastLED/platforms/arm/sam/led_sysdefs_arm_sam.h
Normal file
39
libraries/FastLED/platforms/arm/sam/led_sysdefs_arm_sam.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_SAM_H
|
||||
#define __INC_LED_SYSDEFS_ARM_SAM_H
|
||||
|
||||
|
||||
#define FASTLED_ARM
|
||||
|
||||
// Setup DUE timer defines/channels/etc...
|
||||
#ifndef DUE_TIMER_CHANNEL
|
||||
#define DUE_TIMER_GROUP 0
|
||||
#endif
|
||||
|
||||
#ifndef DUE_TIMER_CHANNEL
|
||||
#define DUE_TIMER_CHANNEL 0
|
||||
#endif
|
||||
|
||||
#define DUE_TIMER ((DUE_TIMER_GROUP==0) ? TC0 : ((DUE_TIMER_GROUP==1) ? TC1 : TC2))
|
||||
#define DUE_TIMER_ID (ID_TC0 + (DUE_TIMER_GROUP*3) + DUE_TIMER_CHANNEL)
|
||||
#define DUE_TIMER_VAL (DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_CV << 1)
|
||||
#define DUE_TIMER_RUNNING ((DUE_TIMER->TC_CHANNEL[DUE_TIMER_CHANNEL].TC_SR & TC_SR_CLKSTA) != 0)
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq(); __disable_fault_irq();
|
||||
#define sei() __enable_irq(); __enable_fault_irq();
|
||||
|
||||
|
||||
#endif
|
||||
126
libraries/FastLED/platforms/arm/stm32/clockless_arm_stm32.h
Normal file
126
libraries/FastLED/platforms/arm/stm32/clockless_arm_stm32.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_STM32_H
|
||||
#define __INC_CLOCKLESS_ARM_STM32_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
// Definition for a single channel clockless controller for the stm32 family of chips, like that used in the spark core
|
||||
// See clockless.h for detailed info on how the template parameters are used.
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
#define _CYCCNT (*(volatile uint32_t*)(0xE0001004UL))
|
||||
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register data_ptr_t port, register data_t hi, register data_t lo, register uint8_t & b) {
|
||||
for(register uint32_t i = BITS-1; i > 0; i--) {
|
||||
while(_CYCCNT < (T1+T2+T3-20));
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
_CYCCNT = 4;
|
||||
if(b&0x80) {
|
||||
while(_CYCCNT < (T1+T2-20));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while(_CYCCNT < (T1-10));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
b <<= 1;
|
||||
}
|
||||
|
||||
while(_CYCCNT < (T1+T2+T3-20));
|
||||
FastPin<DATA_PIN>::fastset(port, hi);
|
||||
_CYCCNT = 4;
|
||||
|
||||
if(b&0x80) {
|
||||
while(_CYCCNT < (T1+T2-20));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
} else {
|
||||
while(_CYCCNT < (T1-10));
|
||||
FastPin<DATA_PIN>::fastset(port, lo);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Get access to the clock
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
DWT->CYCCNT = 0;
|
||||
|
||||
register data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
register data_t hi = *port | FastPin<DATA_PIN>::mask();;
|
||||
register data_t lo = *port & ~FastPin<DATA_PIN>::mask();;
|
||||
*port = lo;
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
register uint8_t b = pixels.loadAndScale0();
|
||||
|
||||
cli();
|
||||
|
||||
uint32_t next_mark = (T1+T2+T3);
|
||||
|
||||
DWT->CYCCNT = 0;
|
||||
while(pixels.has(1)) {
|
||||
pixels.stepDithering();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
cli();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if(DWT->CYCCNT > next_mark) {
|
||||
if((DWT->CYCCNT-next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
|
||||
}
|
||||
|
||||
hi = *port | FastPin<DATA_PIN>::mask();
|
||||
lo = *port & ~FastPin<DATA_PIN>::mask();
|
||||
#endif
|
||||
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(next_mark, port, hi, lo, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
sei();
|
||||
#endif
|
||||
};
|
||||
|
||||
sei();
|
||||
return DWT->CYCCNT;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
63
libraries/FastLED/platforms/arm/stm32/cm3_regs.h
Normal file
63
libraries/FastLED/platforms/arm/stm32/cm3_regs.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef __CM3_REGS
|
||||
#define __CM3_REGS
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define __I volatile /*!< Defines 'read only' permissions */
|
||||
#else
|
||||
#define __I volatile const /*!< Defines 'read only' permissions */
|
||||
#endif
|
||||
#define __O volatile /*!< Defines 'write only' permissions */
|
||||
#define __IO volatile /*!< Defines 'read / write' permissions */
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
__IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */
|
||||
__O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */
|
||||
__IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */
|
||||
__IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */
|
||||
} CoreDebug_Type;
|
||||
|
||||
#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */
|
||||
#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */
|
||||
|
||||
#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */
|
||||
#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
__IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */
|
||||
__IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */
|
||||
__IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */
|
||||
__IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */
|
||||
__IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */
|
||||
__IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */
|
||||
__IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */
|
||||
__I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */
|
||||
__IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */
|
||||
__IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */
|
||||
__IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */
|
||||
uint32_t RESERVED0[1];
|
||||
__IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */
|
||||
__IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */
|
||||
__IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */
|
||||
uint32_t RESERVED1[1];
|
||||
__IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */
|
||||
__IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */
|
||||
__IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */
|
||||
uint32_t RESERVED2[1];
|
||||
__IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */
|
||||
__IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */
|
||||
__IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */
|
||||
} DWT_Type;
|
||||
|
||||
|
||||
#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */
|
||||
#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */
|
||||
|
||||
#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */
|
||||
#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */
|
||||
|
||||
#endif // __CM3_REGS
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef __INC_FASTLED_ARM_SAM_H
|
||||
#define __INC_FASTLED_ARM_SAM_H
|
||||
|
||||
// Include the sam headers
|
||||
#include "fastpin_arm_stm32.h"
|
||||
// #include "fastspi_arm_stm32.h"
|
||||
#include "clockless_arm_stm32.h"
|
||||
|
||||
#endif
|
||||
178
libraries/FastLED/platforms/arm/stm32/fastpin_arm_stm32.h
Normal file
178
libraries/FastLED/platforms/arm/stm32/fastpin_arm_stm32.h
Normal file
@@ -0,0 +1,178 @@
|
||||
#ifndef __FASTPIN_ARM_STM32_H
|
||||
#define __FASTPIN_ARM_STM32_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be sloightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
/// Template definition for STM32 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
|
||||
template<uint8_t PIN, uint8_t _BIT, uint32_t _MASK, typename _GPIO> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
#if 0
|
||||
inline static void setOutput() {
|
||||
if(_BIT<8) {
|
||||
_CRL::r() = (_CRL::r() & (0xF << (_BIT*4)) | (0x1 << (_BIT*4));
|
||||
} else {
|
||||
_CRH::r() = (_CRH::r() & (0xF << ((_BIT-8)*4))) | (0x1 << ((_BIT-8)*4));
|
||||
}
|
||||
}
|
||||
inline static void setInput() { /* TODO */ } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
#endif
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); } // TODO: perform MUX config { _PDDR::r() |= _MASK; }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); } // TODO: preform MUX config { _PDDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BRR = _MASK; }
|
||||
// inline static void lo() __attribute__ ((always_inline)) { _GPIO::r()->BSRR = (_MASK<<16); }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _GPIO::r()->ODR = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { if(_GPIO::r()->ODR & _MASK) { lo(); } else { hi(); } }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _GPIO::r()->ODR | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _GPIO::r()->ODR & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_GPIO::r()->ODR; }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPIO::r()->BSRR; }
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPIO::r()->BRR; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#if defined(STM32F10X_MD)
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline volatile GPIO_TypeDef * r() { return T; } };
|
||||
#define _FL_IO(L,C) _RD32(GPIO ## L); _FL_DEFINE_PORT3(L, C, _R(GPIO ## L));
|
||||
#elif defined(__STM32F1__)
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD32(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline gpio_reg_map* r() { return T->regs; } };
|
||||
#define _FL_IO(L,C) _RD32(GPIO ## L); _FL_DEFINE_PORT3(L, C, _R(GPIO ## L));
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
|
||||
#define _FL_DEFPIN(PIN, BIT, L) template<> class FastPin<PIN> : public _ARMPIN<PIN, BIT, 1 << BIT, _R(GPIO ## L)> {};
|
||||
|
||||
#ifdef GPIOA
|
||||
_FL_IO(A,0);
|
||||
#endif
|
||||
#ifdef GPIOB
|
||||
_FL_IO(B,1);
|
||||
#endif
|
||||
#ifdef GPIOC
|
||||
_FL_IO(C,2);
|
||||
#endif
|
||||
#ifdef GPIOD
|
||||
_FL_IO(D,3);
|
||||
#endif
|
||||
#ifdef GPIOE
|
||||
_FL_IO(E,4);
|
||||
#endif
|
||||
#ifdef GPIOF
|
||||
_FL_IO(F,5);
|
||||
#endif
|
||||
#ifdef GPIOG
|
||||
_FL_IO(G,6);
|
||||
#endif
|
||||
|
||||
// Actual pin definitions
|
||||
#if defined(SPARK) // Sparkfun STM32F103 based board
|
||||
|
||||
|
||||
|
||||
#define MAX_PIN 19
|
||||
_FL_DEFPIN(0, 7, B);
|
||||
_FL_DEFPIN(1, 6, B);
|
||||
_FL_DEFPIN(2, 5, B);
|
||||
_FL_DEFPIN(3, 4, B);
|
||||
_FL_DEFPIN(4, 3, B);
|
||||
_FL_DEFPIN(5, 15, A);
|
||||
_FL_DEFPIN(6, 14, A);
|
||||
_FL_DEFPIN(7, 13, A);
|
||||
_FL_DEFPIN(8, 8, A);
|
||||
_FL_DEFPIN(9, 9, A);
|
||||
_FL_DEFPIN(10, 0, A);
|
||||
_FL_DEFPIN(11, 1, A);
|
||||
_FL_DEFPIN(12, 4, A);
|
||||
_FL_DEFPIN(13, 5, A);
|
||||
_FL_DEFPIN(14, 6, A);
|
||||
_FL_DEFPIN(15, 7, A);
|
||||
_FL_DEFPIN(16, 0, B);
|
||||
_FL_DEFPIN(17, 1, B);
|
||||
_FL_DEFPIN(18, 3, A);
|
||||
_FL_DEFPIN(19, 2, A);
|
||||
|
||||
|
||||
#define SPI_DATA 15
|
||||
#define SPI_CLOCK 13
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif // SPARK
|
||||
|
||||
#if defined(__STM32F1__) // Generic STM32F103 aka "Blue Pill"
|
||||
|
||||
#define MAX_PIN 46
|
||||
|
||||
_FL_DEFPIN(10, 0, A); // PA0 - PA7
|
||||
_FL_DEFPIN(11, 1, A);
|
||||
_FL_DEFPIN(12, 2, A);
|
||||
_FL_DEFPIN(13, 3, A);
|
||||
_FL_DEFPIN(14, 4, A);
|
||||
_FL_DEFPIN(15, 5, A);
|
||||
_FL_DEFPIN(16, 6, A);
|
||||
_FL_DEFPIN(17, 7, A);
|
||||
_FL_DEFPIN(29, 8, A); // PA8 - PA15
|
||||
_FL_DEFPIN(30, 9, A);
|
||||
_FL_DEFPIN(31, 10, A);
|
||||
_FL_DEFPIN(32, 11, A);
|
||||
_FL_DEFPIN(33, 12, A);
|
||||
_FL_DEFPIN(34, 13, A);
|
||||
_FL_DEFPIN(37, 14, A);
|
||||
_FL_DEFPIN(38, 15, A);
|
||||
|
||||
_FL_DEFPIN(18, 0, B); // PB0 - PB11
|
||||
_FL_DEFPIN(19, 1, B);
|
||||
_FL_DEFPIN(20, 2, B);
|
||||
_FL_DEFPIN(39, 3, B);
|
||||
_FL_DEFPIN(40, 4, B);
|
||||
_FL_DEFPIN(41, 5, B);
|
||||
_FL_DEFPIN(42, 6, B);
|
||||
_FL_DEFPIN(43, 7, B);
|
||||
_FL_DEFPIN(45, 8, B);
|
||||
_FL_DEFPIN(46, 9, B);
|
||||
_FL_DEFPIN(21, 10, B);
|
||||
_FL_DEFPIN(22, 11, B);
|
||||
|
||||
_FL_DEFPIN(2, 13, C); // PC13 - PC15
|
||||
_FL_DEFPIN(3, 14, C);
|
||||
_FL_DEFPIN(4, 15, C);
|
||||
|
||||
#define SPI_DATA BOARD_SPI1_MOSI_PIN
|
||||
#define SPI_CLOCK BOARD_SPI1_SCK_PIN
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif // __STM32F1__
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_ARM_STM32
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef __INC_LED_SYSDEFS_ARM_SAM_H
|
||||
#define __INC_LED_SYSDEFS_ARM_SAM_H
|
||||
|
||||
#if defined(STM32F10X_MD)
|
||||
|
||||
#include <application.h>
|
||||
|
||||
#define FASTLED_NAMESPACE_BEGIN namespace NSFastLED {
|
||||
#define FASTLED_NAMESPACE_END }
|
||||
#define FASTLED_USING_NAMESPACE using namespace NSFastLED;
|
||||
|
||||
// reusing/abusing cli/sei defs for due
|
||||
#define cli() __disable_irq(); __disable_fault_irq();
|
||||
#define sei() __enable_irq(); __enable_fault_irq();
|
||||
|
||||
#elif defined (__STM32F1__)
|
||||
|
||||
#include "cm3_regs.h"
|
||||
|
||||
#define cli() nvic_globalirq_disable()
|
||||
#define sei() nvic_globalirq_enable()
|
||||
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
|
||||
#define FASTLED_ARM
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 1
|
||||
#endif
|
||||
|
||||
// Default to allowing interrupts
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 0
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
// pgmspace definitions
|
||||
#define PROGMEM
|
||||
#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
|
||||
#define pgm_read_dword_near(addr) pgm_read_dword(addr)
|
||||
|
||||
// Default to NOT using PROGMEM here
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
// data type defs
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
|
||||
#define FASTLED_NO_PINMAP
|
||||
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 72000000
|
||||
#endif
|
||||
#endif
|
||||
464
libraries/FastLED/platforms/avr/clockless_trinket.h
Normal file
464
libraries/FastLED/platforms/avr/clockless_trinket.h
Normal file
@@ -0,0 +1,464 @@
|
||||
#ifndef __INC_CLOCKLESS_TRINKET_H
|
||||
#define __INC_CLOCKLESS_TRINKET_H
|
||||
|
||||
#include "../../controller.h"
|
||||
#include "../../lib8tion.h"
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_AVR)
|
||||
|
||||
// Scaling macro choice
|
||||
#ifndef TRINKET_SCALE
|
||||
#define TRINKET_SCALE 1
|
||||
// whether or not to use dithering
|
||||
#define DITHER 1
|
||||
#endif
|
||||
|
||||
#if (F_CPU==8000000)
|
||||
#define FASTLED_SLOW_CLOCK_ADJUST // asm __volatile__ ("mov r0,r0\n\t");
|
||||
#else
|
||||
#define FASTLED_SLOW_CLOCK_ADJUST
|
||||
#endif
|
||||
|
||||
#define US_PER_TICK (64 / (F_CPU/1000000))
|
||||
|
||||
// Variations on the functions in delay.h - w/a loop var passed in to preserve registers across calls by the optimizer/compiler
|
||||
template<int CYCLES> inline void _dc(register uint8_t & loopvar);
|
||||
|
||||
template<int _LOOP, int PAD> __attribute__((always_inline)) inline void _dc_AVR(register uint8_t & loopvar) {
|
||||
_dc<PAD>(loopvar);
|
||||
// The convolution in here is to ensure that the state of the carry flag coming into the delay loop is preserved
|
||||
asm __volatile__ ( "BRCS L_PC%=\n\t"
|
||||
" LDI %[loopvar], %[_LOOP]\n\tL_%=: DEC %[loopvar]\n\t BRNE L_%=\n\tBREQ L_DONE%=\n\t"
|
||||
"L_PC%=: LDI %[loopvar], %[_LOOP]\n\tLL_%=: DEC %[loopvar]\n\t BRNE LL_%=\n\tBSET 0\n\t"
|
||||
"L_DONE%=:\n\t"
|
||||
:
|
||||
[loopvar] "+a" (loopvar) : [_LOOP] "M" (_LOOP) : );
|
||||
}
|
||||
|
||||
template<int CYCLES> __attribute__((always_inline)) inline void _dc(register uint8_t & loopvar) {
|
||||
_dc_AVR<CYCLES/6,CYCLES%6>(loopvar);
|
||||
}
|
||||
template<> __attribute__((always_inline)) inline void _dc<-6>(register uint8_t & ) {}
|
||||
template<> __attribute__((always_inline)) inline void _dc<-5>(register uint8_t & ) {}
|
||||
template<> __attribute__((always_inline)) inline void _dc<-4>(register uint8_t & ) {}
|
||||
template<> __attribute__((always_inline)) inline void _dc<-3>(register uint8_t & ) {}
|
||||
template<> __attribute__((always_inline)) inline void _dc<-2>(register uint8_t & ) {}
|
||||
template<> __attribute__((always_inline)) inline void _dc<-1>(register uint8_t & ) {}
|
||||
template<> __attribute__((always_inline)) inline void _dc< 0>(register uint8_t & ) {}
|
||||
template<> __attribute__((always_inline)) inline void _dc< 1>(register uint8_t & ) {asm __volatile__("mov r0,r0":::);}
|
||||
template<> __attribute__((always_inline)) inline void _dc< 2>(register uint8_t & ) {asm __volatile__("rjmp .+0":::);}
|
||||
template<> __attribute__((always_inline)) inline void _dc< 3>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<1>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc< 4>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc< 5>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<3>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc< 6>(register uint8_t & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); _dc<2>(loopvar);}
|
||||
template<> __attribute__((always_inline)) inline void _dc< 7>(register uint8_t & loopvar) { _dc<4>(loopvar); _dc<3>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc< 8>(register uint8_t & loopvar) { _dc<4>(loopvar); _dc<4>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc< 9>(register uint8_t & loopvar) { _dc<5>(loopvar); _dc<4>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<10>(register uint8_t & loopvar) { _dc<6>(loopvar); _dc<4>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<11>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<1>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<12>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<2>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<13>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<3>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<14>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<4>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<15>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<5>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<16>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<6>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<17>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<7>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<18>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<8>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<19>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<9>(loopvar); }
|
||||
template<> __attribute__((always_inline)) inline void _dc<20>(register uint8_t & loopvar) { _dc<10>(loopvar); _dc<10>(loopvar); }
|
||||
|
||||
#define DINTPIN(T,ADJ,PINADJ) (T-(PINADJ+ADJ)>0) ? _dc<T-(PINADJ+ADJ)>(loopvar) : _dc<0>(loopvar);
|
||||
#define DINT(T,ADJ) if(AVR_PIN_CYCLES(DATA_PIN)==1) { DINTPIN(T,ADJ,1) } else { DINTPIN(T,ADJ,2); }
|
||||
#define _D1(ADJ) DINT(T1,ADJ)
|
||||
#define _D2(ADJ) DINT(T2,ADJ)
|
||||
#define _D3(ADJ) DINT(T3,ADJ)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Base template for clockless controllers. These controllers have 3 control points in their cycle for each bit. The first point
|
||||
// is where the line is raised hi. The second point is where the line is dropped low for a zero. The third point is where the
|
||||
// line is dropped low for a one. T1, T2, and T3 correspond to the timings for those three in clock cycles.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if (!defined(NO_CORRECTION) || (NO_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0)
|
||||
static uint8_t gTimeErrorAccum256ths;
|
||||
#endif
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 10>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
static_assert(T1 >= 2 && T2 >= 2 && T3 >= 3, "Not enough cycles - use a higher clock speed");
|
||||
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
|
||||
mWait.wait();
|
||||
cli();
|
||||
|
||||
showRGBInternal(pixels);
|
||||
|
||||
// Adjust the timer
|
||||
#if (!defined(NO_CORRECTION) || (NO_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0)
|
||||
uint32_t microsTaken = (uint32_t)pixels.size() * (uint32_t)CLKS_TO_MICROS(24 * (T1 + T2 + T3));
|
||||
|
||||
// adust for approximate observed actal runtime (as of January 2015)
|
||||
// roughly 9.6 cycles per pixel, which is 0.6us/pixel at 16MHz
|
||||
// microsTaken += nLeds * 0.6 * CLKS_TO_MICROS(16);
|
||||
microsTaken += scale16by8(pixels.size(),(0.6 * 256) + 1) * CLKS_TO_MICROS(16);
|
||||
|
||||
// if less than 1000us, there is NO timer impact,
|
||||
// this is because the ONE interrupt that might come in while interrupts
|
||||
// are disabled is queued up, and it will be serviced as soon as
|
||||
// interrupts are re-enabled.
|
||||
// This actually should technically also account for the runtime of the
|
||||
// interrupt handler itself, but we're just not going to worry about that.
|
||||
if( microsTaken > 1000) {
|
||||
|
||||
// Since up to one timer tick will be queued, we don't need
|
||||
// to adjust the MS_COUNTER for that one.
|
||||
microsTaken -= 1000;
|
||||
|
||||
// Now convert microseconds to 256ths of a second, approximately like this:
|
||||
// 250ths = (us/4)
|
||||
// 256ths = 250ths * (263/256);
|
||||
uint16_t x256ths = microsTaken >> 2;
|
||||
x256ths += scale16by8(x256ths,7);
|
||||
|
||||
x256ths += gTimeErrorAccum256ths;
|
||||
MS_COUNTER += (x256ths >> 8);
|
||||
gTimeErrorAccum256ths = x256ths & 0xFF;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// For pixel counts of 30 and under at 16Mhz, no correction is necessary.
|
||||
// For pixel counts of 15 and under at 8Mhz, no correction is necessary.
|
||||
//
|
||||
// This code, below, is smaller, and quicker clock correction, which drifts much
|
||||
// more significantly, but is a few bytes smaller. Presented here for consideration
|
||||
// as an alternate on the ATtiny, which can't have more than about 150 pixels MAX
|
||||
// anyway, meaning that microsTaken will never be more than about 4,500, which fits in
|
||||
// a 16-bit variable. The difference between /1000 and /1024 only starts showing
|
||||
// up in the range of about 100 pixels, so many ATtiny projects won't even
|
||||
// see a clock difference due to the approximation there.
|
||||
uint16_t microsTaken = (uint32_t)nLeds * (uint32_t)CLKS_TO_MICROS((24) * (T1 + T2 + T3));
|
||||
MS_COUNTER += (microsTaken >> 10);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
#define USE_ASM_MACROS
|
||||
|
||||
// The variables that our various asm statemetns use. The same block of variables needs to be declared for
|
||||
// all the asm blocks because GCC is pretty stupid and it would clobber variables happily or optimize code away too aggressively
|
||||
#define ASM_VARS : /* write variables */ \
|
||||
[count] "+x" (count), \
|
||||
[data] "+z" (data), \
|
||||
[b1] "+a" (b1), \
|
||||
[d0] "+r" (d0), \
|
||||
[d1] "+r" (d1), \
|
||||
[d2] "+r" (d2), \
|
||||
[loopvar] "+a" (loopvar), \
|
||||
[scale_base] "+a" (scale_base) \
|
||||
: /* use variables */ \
|
||||
[ADV] "r" (advanceBy), \
|
||||
[b0] "a" (b0), \
|
||||
[hi] "r" (hi), \
|
||||
[lo] "r" (lo), \
|
||||
[s0] "r" (s0), \
|
||||
[s1] "r" (s1), \
|
||||
[s2] "r" (s2), \
|
||||
[e0] "r" (e0), \
|
||||
[e1] "r" (e1), \
|
||||
[e2] "r" (e2), \
|
||||
[PORT] "M" (FastPin<DATA_PIN>::port()-0x20), \
|
||||
[O0] "M" (RGB_BYTE0(RGB_ORDER)), \
|
||||
[O1] "M" (RGB_BYTE1(RGB_ORDER)), \
|
||||
[O2] "M" (RGB_BYTE2(RGB_ORDER)) \
|
||||
: "cc" /* clobber registers */
|
||||
|
||||
|
||||
// Note: the code in the else in HI1/LO1 will be turned into an sts (2 cycle, 2 word) opcode
|
||||
// 1 cycle, write hi to the port
|
||||
#define HI1 FASTLED_SLOW_CLOCK_ADJUST if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[hi]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=hi; }
|
||||
// 1 cycle, write lo to the port
|
||||
#define LO1 if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[lo]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=lo; }
|
||||
|
||||
// 2 cycles, sbrs on flipping the line to lo if we're pushing out a 0
|
||||
#define QLO2(B, N) asm __volatile__("sbrs %[" #B "], " #N ASM_VARS ); LO1;
|
||||
// load a byte from ram into the given var with the given offset
|
||||
#define LD2(B,O) asm __volatile__("ldd %[" #B "], Z + %[" #O "]\n\t" ASM_VARS );
|
||||
// 4 cycles - load a byte from ram into the scaling scratch space with the given offset, clear the target var, clear carry
|
||||
#define LDSCL4(B,O) asm __volatile__("ldd %[scale_base], Z + %[" #O "]\n\tclr %[" #B "]\n\tclc\n\t" ASM_VARS );
|
||||
|
||||
#if (DITHER==1)
|
||||
// apply dithering value before we do anything with scale_base
|
||||
#define PRESCALE4(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\tbrcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\t" ASM_VARS);
|
||||
|
||||
// Do the add for the prescale
|
||||
#define PRESCALEA2(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\t" ASM_VARS);
|
||||
|
||||
// Do the clamp for the prescale, clear carry when we're done - NOTE: Must ensure carry flag state is preserved!
|
||||
#define PRESCALEB4(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tneg %[" #D "]\n\tCLC" ASM_VARS);
|
||||
|
||||
// Clamp for prescale, increment data, since we won't ever wrap 65k, this also effectively clears carry for us
|
||||
#define PSBIDATA4(D) asm __volatile__("brcc L_%=\n\tldi %[scale_base], 0xFF\n\tL_%=:\n\tadd %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" ASM_VARS);
|
||||
|
||||
#else
|
||||
#define PRESCALE4(D) _dc<4>(loopvar);
|
||||
#define PRESCALEA2(D) _dc<2>(loopvar);
|
||||
#define PRESCALEB4(D) _dc<4>(loopvar);
|
||||
#define PSBIDATA4(D) asm __volatile__( "add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\trjmp .+0\n\t" ASM_VARS );
|
||||
#endif
|
||||
|
||||
// 2 cycles - perform one step of the scaling (if a given bit is set in scale, add scale-base to the scratch space)
|
||||
#define _SCALE02(B, N) "sbrc %[s0], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
|
||||
#define _SCALE12(B, N) "sbrc %[s1], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
|
||||
#define _SCALE22(B, N) "sbrc %[s2], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
|
||||
#define SCALE02(B,N) asm __volatile__( _SCALE02(B,N) ASM_VARS );
|
||||
#define SCALE12(B,N) asm __volatile__( _SCALE12(B,N) ASM_VARS );
|
||||
#define SCALE22(B,N) asm __volatile__( _SCALE22(B,N) ASM_VARS );
|
||||
|
||||
// 1 cycle - rotate right, pulling in from carry
|
||||
#define _ROR1(B) "ror %[" #B "]\n\t"
|
||||
#define ROR1(B) asm __volatile__( _ROR1(B) ASM_VARS);
|
||||
|
||||
// 1 cycle, clear the carry bit
|
||||
#define _CLC1 "clc\n\t"
|
||||
#define CLC1 asm __volatile__( _CLC1 ASM_VARS );
|
||||
|
||||
// 2 cycles, rortate right, pulling in from carry then clear the carry bit
|
||||
#define RORCLC2(B) asm __volatile__( _ROR1(B) _CLC1 ASM_VARS );
|
||||
|
||||
// 4 cycles, rotate, clear carry, scale next bit
|
||||
#define RORSC04(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE02(B, N) ASM_VARS );
|
||||
#define RORSC14(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE12(B, N) ASM_VARS );
|
||||
#define RORSC24(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE22(B, N) ASM_VARS );
|
||||
|
||||
// 4 cycles, scale bit, rotate, clear carry
|
||||
#define SCROR04(B, N) asm __volatile__( _SCALE02(B,N) _ROR1(B) _CLC1 ASM_VARS );
|
||||
#define SCROR14(B, N) asm __volatile__( _SCALE12(B,N) _ROR1(B) _CLC1 ASM_VARS );
|
||||
#define SCROR24(B, N) asm __volatile__( _SCALE22(B,N) _ROR1(B) _CLC1 ASM_VARS );
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// Loop life cycle
|
||||
|
||||
// dither adjustment macro - should be kept in sync w/what's in stepDithering
|
||||
// #define ADJDITHER2(D, E) D = E - D;
|
||||
#define _NEGD1(D) "neg %[" #D "]\n\t"
|
||||
#define _ADJD1(D,E) "add %[" #D "], %[" #E "]\n\t"
|
||||
#define ADJDITHER2(D, E) asm __volatile__ ( _NEGD1(D) _ADJD1(D, E) ASM_VARS);
|
||||
#define ADDDE1(D, E) asm __volatile__ ( _ADJD1(D, E) ASM_VARS );
|
||||
|
||||
// #define xstr(a) str(a)
|
||||
// #define str(a) #a
|
||||
// #define ADJDITHER2(D,E) asm __volatile__("subi %[" #D "], " xstr(DUSE) "\n\tand %[" #D "], %[" #E "]\n\t" ASM_VARS);
|
||||
|
||||
// define the beginning of the loop
|
||||
#define LOOP asm __volatile__("1:" ASM_VARS );
|
||||
// define the end of the loop
|
||||
#define DONE asm __volatile__("2:" ASM_VARS );
|
||||
|
||||
// 2 cycles - increment the data pointer
|
||||
#define IDATA2 asm __volatile__("add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" ASM_VARS );
|
||||
#define IDATACLC3 asm __volatile__("add %A[data], %[ADV]\n\tadc %B[data], __zero_reg__\n\t" _CLC1 ASM_VARS );
|
||||
|
||||
// 1 cycle mov
|
||||
#define _MOV1(B1, B2) "mov %[" #B1 "], %[" #B2 "]\n\t"
|
||||
|
||||
#define MOV1(B1, B2) asm __volatile__( _MOV1(B1,B2) ASM_VARS );
|
||||
|
||||
// 3 cycle mov - skip if scale fix is happening
|
||||
#if (FASTLED_SCALE8_FIXED == 1)
|
||||
#define _MOV_FIX03(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s0], __zero_reg__\n\t" _MOV1(B1, B2)
|
||||
#define _MOV_FIX13(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s1], __zero_reg__\n\t" _MOV1(B1, B2)
|
||||
#define _MOV_FIX23(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s2], __zero_reg__\n\t" _MOV1(B1, B2)
|
||||
#else
|
||||
// if we haven't fixed scale8, just do the move and nop the 2 cycles that would be used to
|
||||
// do the fixed adjustment
|
||||
#define _MOV_FIX03(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t"
|
||||
#define _MOV_FIX13(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t"
|
||||
#define _MOV_FIX23(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t"
|
||||
#endif
|
||||
|
||||
// 3 cycle mov + negate D for dither adjustment
|
||||
#define MOV_NEGD04(B1, B2, D) asm __volatile( _MOV_FIX03(B1, B2) _NEGD1(D) ASM_VARS );
|
||||
#define MOV_ADDDE04(B1, B2, D, E) asm __volatile( _MOV_FIX03(B1, B2) _ADJD1(D, E) ASM_VARS );
|
||||
#define MOV_NEGD14(B1, B2, D) asm __volatile( _MOV_FIX13(B1, B2) _NEGD1(D) ASM_VARS );
|
||||
#define MOV_ADDDE14(B1, B2, D, E) asm __volatile( _MOV_FIX13(B1, B2) _ADJD1(D, E) ASM_VARS );
|
||||
#define MOV_NEGD24(B1, B2, D) asm __volatile( _MOV_FIX23(B1, B2) _NEGD1(D) ASM_VARS );
|
||||
|
||||
// 2 cycles - decrement the counter
|
||||
#define DCOUNT2 asm __volatile__("sbiw %[count], 1" ASM_VARS );
|
||||
// 2 cycles - jump to the beginning of the loop
|
||||
#define JMPLOOP2 asm __volatile__("rjmp 1b" ASM_VARS );
|
||||
// 2 cycles - jump out of the loop
|
||||
#define BRLOOP1 asm __volatile__("brne 3\n\trjmp 2f\n\t3:" ASM_VARS );
|
||||
|
||||
// 5 cycles 2 sbiw, 3 for the breq/rjmp
|
||||
#define ENDLOOP5 asm __volatile__("sbiw %[count], 1\n\tbreq L_%=\n\trjmp 1b\n\tL_%=:\n\t" ASM_VARS);
|
||||
|
||||
// NOP using the variables, forcing a move
|
||||
#define DNOP asm __volatile__("mov r0,r0" ASM_VARS);
|
||||
|
||||
#define DADVANCE 3
|
||||
#define DUSE (0xFF - (DADVANCE-1))
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static void /*__attribute__((optimize("O0")))*/ /*__attribute__ ((always_inline))*/ showRGBInternal(PixelController<RGB_ORDER> & pixels) {
|
||||
uint8_t *data = (uint8_t*)pixels.mData;
|
||||
data_ptr_t port = FastPin<DATA_PIN>::port();
|
||||
data_t mask = FastPin<DATA_PIN>::mask();
|
||||
uint8_t scale_base = 0;
|
||||
|
||||
// register uint8_t *end = data + nLeds;
|
||||
data_t hi = *port | mask;
|
||||
data_t lo = *port & ~mask;
|
||||
*port = lo;
|
||||
|
||||
// the byte currently being written out
|
||||
uint8_t b0 = 0;
|
||||
// the byte currently being worked on to write the next out
|
||||
uint8_t b1 = 0;
|
||||
|
||||
// Setup the pixel controller
|
||||
pixels.preStepFirstByteDithering();
|
||||
|
||||
// pull the dithering/adjustment values out of the pixels object for direct asm access
|
||||
uint8_t advanceBy = pixels.advanceBy();
|
||||
uint16_t count = pixels.mLen;
|
||||
|
||||
uint8_t s0 = pixels.mScale.raw[RO(0)];
|
||||
uint8_t s1 = pixels.mScale.raw[RO(1)];
|
||||
uint8_t s2 = pixels.mScale.raw[RO(2)];
|
||||
#if (FASTLED_SCALE8_FIXED==1)
|
||||
s0++; s1++; s2++;
|
||||
#endif
|
||||
uint8_t d0 = pixels.d[RO(0)];
|
||||
uint8_t d1 = pixels.d[RO(1)];
|
||||
uint8_t d2 = pixels.d[RO(2)];
|
||||
uint8_t e0 = pixels.e[RO(0)];
|
||||
uint8_t e1 = pixels.e[RO(1)];
|
||||
uint8_t e2 = pixels.e[RO(2)];
|
||||
|
||||
uint8_t loopvar=0;
|
||||
|
||||
// This has to be done in asm to keep gcc from messing up the asm code further down
|
||||
b0 = data[RO(0)];
|
||||
{
|
||||
LDSCL4(b0,O0) PRESCALEA2(d0)
|
||||
PRESCALEB4(d0) SCALE02(b0,0)
|
||||
RORSC04(b0,1) ROR1(b0) CLC1
|
||||
SCROR04(b0,2) SCALE02(b0,3)
|
||||
RORSC04(b0,4) ROR1(b0) CLC1
|
||||
SCROR04(b0,5) SCALE02(b0,6)
|
||||
RORSC04(b0,7) ROR1(b0) CLC1
|
||||
MOV_ADDDE04(b1,b0,d0,e0)
|
||||
MOV1(b0,b1)
|
||||
}
|
||||
|
||||
{
|
||||
// while(--count)
|
||||
{
|
||||
// Loop beginning
|
||||
DNOP;
|
||||
LOOP;
|
||||
|
||||
// Sum of the clock counts across each row should be 10 for 8Mhz, WS2811
|
||||
// The values in the D1/D2/D3 indicate how many cycles the previous column takes
|
||||
// to allow things to line back up.
|
||||
//
|
||||
// While writing out byte 0, we're loading up byte 1, applying the dithering adjustment,
|
||||
// then scaling it using 8 cycles of shift/add interleaved in between writing the bits
|
||||
// out. When doing byte 1, we're doing the above for byte 2. When we're doing byte 2,
|
||||
// we're cycling back around and doing the above for byte 0.
|
||||
|
||||
// Inline scaling - RGB ordering
|
||||
// DNOP
|
||||
HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O1) _D2(4) LO1 PRESCALEA2(d1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 6) PRESCALEB4(d1) _D2(4) LO1 SCALE12(b1,0) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 5) RORSC14(b1,1) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 4) SCROR14(b1,2) _D2(4) LO1 SCALE12(b1,3) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 3) RORSC14(b1,4) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 2) SCROR14(b1,5) _D2(4) LO1 SCALE12(b1,6) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 1) RORSC14(b1,7) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 0)
|
||||
switch(XTRA0) {
|
||||
case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
}
|
||||
MOV_ADDDE14(b0,b1,d1,e1) _D2(4) LO1 _D3(0)
|
||||
|
||||
HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O2) _D2(4) LO1 PRESCALEA2(d2) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 6) PSBIDATA4(d2) _D2(4) LO1 SCALE22(b1,0) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 5) RORSC24(b1,1) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 4) SCROR24(b1,2) _D2(4) LO1 SCALE22(b1,3) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 3) RORSC24(b1,4) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 2) SCROR24(b1,5) _D2(4) LO1 SCALE22(b1,6) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 1) RORSC24(b1,7) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 0)
|
||||
switch(XTRA0) {
|
||||
case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
}
|
||||
|
||||
// Because Prescale on the middle byte also increments the data counter,
|
||||
// we have to do both halves of updating d2 here - negating it (in the
|
||||
// MOV_NEGD24 macro) and then adding E back into it
|
||||
MOV_NEGD24(b0,b1,d2) _D2(4) LO1 ADDDE1(d2,e2) _D3(1)
|
||||
HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O0) _D2(4) LO1 PRESCALEA2(d0) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 6) PRESCALEB4(d0) _D2(4) LO1 SCALE02(b1,0) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 5) RORSC04(b1,1) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 4) SCROR04(b1,2) _D2(4) LO1 SCALE02(b1,3) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 3) RORSC04(b1,4) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 2) SCROR04(b1,5) _D2(4) LO1 SCALE02(b1,6) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 1) RORSC04(b1,7) _D2(4) LO1 RORCLC2(b1) _D3(2)
|
||||
HI1 _D1(1) QLO2(b0, 0)
|
||||
switch(XTRA0) {
|
||||
case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
|
||||
}
|
||||
MOV_ADDDE04(b0,b1,d0,e0) _D2(4) LO1 _D3(5)
|
||||
ENDLOOP5
|
||||
}
|
||||
DONE;
|
||||
}
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
// stop using the clock juggler
|
||||
TCCR0A &= ~0x30;
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
13
libraries/FastLED/platforms/avr/fastled_avr.h
Normal file
13
libraries/FastLED/platforms/avr/fastled_avr.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef __INC_FASTLED_AVR_H
|
||||
#define __INC_FASTLED_AVR_H
|
||||
|
||||
#include "fastpin_avr.h"
|
||||
#include "fastspi_avr.h"
|
||||
#include "clockless_trinket.h"
|
||||
|
||||
// Default to using PROGMEM
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
361
libraries/FastLED/platforms/avr/fastpin_avr.h
Normal file
361
libraries/FastLED/platforms/avr/fastpin_avr.h
Normal file
@@ -0,0 +1,361 @@
|
||||
#ifndef __INC_FASTPIN_AVR_H
|
||||
#define __INC_FASTPIN_AVR_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#if defined(FASTLED_FORCE_SOFTWARE_PINS)
|
||||
#warning "Software pin support forced, pin access will be slightly slower."
|
||||
#define NO_HARDWARE_PIN_SUPPORT
|
||||
#undef HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#else
|
||||
|
||||
#define AVR_PIN_CYCLES(_PIN) ((((int)FastPin<_PIN>::port())-0x20 < 64) ? 1 : 2)
|
||||
|
||||
/// Class definition for a Pin where we know the port registers at compile time for said pin. This allows us to make
|
||||
/// a lot of optimizations, as the inlined hi/lo methods will devolve to a single io register write/bitset.
|
||||
template<uint8_t PIN, uint8_t _MASK, typename _PORT, typename _DDR, typename _PIN> class _AVRPIN {
|
||||
public:
|
||||
typedef volatile uint8_t * port_ptr_t;
|
||||
typedef uint8_t port_t;
|
||||
|
||||
inline static void setOutput() { _DDR::r() |= _MASK; }
|
||||
inline static void setInput() { _DDR::r() &= ~_MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _PORT::r() |= _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _PORT::r() &= ~_MASK; }
|
||||
inline static void set(register uint8_t val) __attribute__ ((always_inline)) { _PORT::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _PIN::r() = _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t /*port*/) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t /*port*/, register uint8_t val) __attribute__ ((always_inline)) { set(val); }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _PORT::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _PORT::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_PORT::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// AVR definitions for pins. Getting around the fact that I can't pass GPIO register addresses in as template arguments by instead creating
|
||||
/// a custom type for each GPIO register with a single, static, aggressively inlined function that returns that specific GPIO register. A similar
|
||||
/// trick is used a bit further below for the ARM GPIO registers (of which there are far more than on AVR!)
|
||||
typedef volatile uint8_t & reg8_t;
|
||||
#define _R(T) struct __gen_struct_ ## T
|
||||
#define _RD8(T) struct __gen_struct_ ## T { static inline reg8_t r() { return T; }};
|
||||
#define _FL_IO(L,C) _RD8(DDR ## L); _RD8(PORT ## L); _RD8(PIN ## L); _FL_DEFINE_PORT3(L, C, _R(PORT ## L));
|
||||
#define _FL_DEFPIN(_PIN, BIT, L) template<> class FastPin<_PIN> : public _AVRPIN<_PIN, 1<<BIT, _R(PORT ## L), _R(DDR ## L), _R(PIN ## L)> {};
|
||||
|
||||
// Pre-do all the port definitions
|
||||
#ifdef PORTA
|
||||
_FL_IO(A,0)
|
||||
#endif
|
||||
#ifdef PORTB
|
||||
_FL_IO(B,1)
|
||||
#endif
|
||||
#ifdef PORTC
|
||||
_FL_IO(C,2)
|
||||
#endif
|
||||
#ifdef PORTD
|
||||
_FL_IO(D,3)
|
||||
#endif
|
||||
#ifdef PORTE
|
||||
_FL_IO(E,4)
|
||||
#endif
|
||||
#ifdef PORTF
|
||||
_FL_IO(F,5)
|
||||
#endif
|
||||
#ifdef PORTG
|
||||
_FL_IO(G,6)
|
||||
#endif
|
||||
#ifdef PORTH
|
||||
_FL_IO(H,7)
|
||||
#endif
|
||||
#ifdef PORTI
|
||||
_FL_IO(I,8)
|
||||
#endif
|
||||
#ifdef PORTJ
|
||||
_FL_IO(J,9)
|
||||
#endif
|
||||
#ifdef PORTK
|
||||
_FL_IO(K,10)
|
||||
#endif
|
||||
#ifdef PORTL
|
||||
_FL_IO(L,11)
|
||||
#endif
|
||||
#ifdef PORTM
|
||||
_FL_IO(M,12)
|
||||
#endif
|
||||
#ifdef PORTN
|
||||
_FL_IO(N,13)
|
||||
#endif
|
||||
|
||||
#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__)
|
||||
|
||||
#if defined(__AVR_ATtiny25__)
|
||||
#pragma message "ATtiny25 has very limited storage. This library could use up to more than 100% of its flash size"
|
||||
#endif
|
||||
|
||||
#define MAX_PIN 5
|
||||
|
||||
_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B);
|
||||
_FL_DEFPIN(4, 4, B); _FL_DEFPIN(5, 5, B);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny441__)
|
||||
#define MAX_PIN 11
|
||||
|
||||
_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B);
|
||||
_FL_DEFPIN(3, 7, A); _FL_DEFPIN(4, 6, A); _FL_DEFPIN(5, 5, A);
|
||||
_FL_DEFPIN(6, 4, A); _FL_DEFPIN(7, 3, A); _FL_DEFPIN(8, 2, A);
|
||||
_FL_DEFPIN(9, 1, A); _FL_DEFPIN(10, 0, A); _FL_DEFPIN(11, 3, B);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_AVR_DIGISPARK) // digispark pin layout
|
||||
#define MAX_PIN 5
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B);
|
||||
_FL_DEFPIN(3, 7, A); _FL_DEFPIN(4, 6, A); _FL_DEFPIN(5, 5, A);
|
||||
|
||||
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
|
||||
|
||||
#define MAX_PIN 10
|
||||
|
||||
_FL_DEFPIN(0, 0, A); _FL_DEFPIN(1, 1, A); _FL_DEFPIN(2, 2, A); _FL_DEFPIN(3, 3, A);
|
||||
_FL_DEFPIN(4, 4, A); _FL_DEFPIN(5, 5, A); _FL_DEFPIN(6, 6, A); _FL_DEFPIN(7, 7, A);
|
||||
_FL_DEFPIN(8, 2, B); _FL_DEFPIN(9, 1, B); _FL_DEFPIN(10, 0, B);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(ARDUINO_AVR_DIGISPARKPRO)
|
||||
|
||||
#define MAX_PIN 12
|
||||
|
||||
_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 5, B);
|
||||
_FL_DEFPIN(4, 3, B); _FL_DEFPIN(5, 7, A); _FL_DEFPIN(6, 0, A); _FL_DEFPIN(7, 1, A);
|
||||
_FL_DEFPIN(8, 2, A); _FL_DEFPIN(9, 3, A); _FL_DEFPIN(10, 4, A); _FL_DEFPIN(11, 5, A);
|
||||
_FL_DEFPIN(12, 6, A);
|
||||
|
||||
#elif defined(__AVR_ATtiny167__) || defined(__AVR_ATtiny87__)
|
||||
|
||||
#define MAX_PIN 15
|
||||
|
||||
_FL_DEFPIN(0, 0, A); _FL_DEFPIN(1, 1, A); _FL_DEFPIN(2, 2, A); _FL_DEFPIN(3, 3, A);
|
||||
_FL_DEFPIN(4, 4, A); _FL_DEFPIN(5, 5, A); _FL_DEFPIN(6, 6, A); _FL_DEFPIN(7, 7, A);
|
||||
_FL_DEFPIN(8, 0, B); _FL_DEFPIN(9, 1, B); _FL_DEFPIN(10, 2, B); _FL_DEFPIN(11, 3, B);
|
||||
_FL_DEFPIN(12, 4, B); _FL_DEFPIN(13, 5, B); _FL_DEFPIN(14, 6, B); _FL_DEFPIN(15, 7, B);
|
||||
|
||||
#define SPI_DATA 4
|
||||
#define SPI_CLOCK 5
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
#elif defined(ARDUINO_HOODLOADER2) && (defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__)) || defined(__AVR_AT90USB82__) || defined(__AVR_AT90USB162__)
|
||||
|
||||
#define MAX_PIN 20
|
||||
|
||||
_FL_DEFPIN( 0, 0, B); _FL_DEFPIN( 1, 1, B); _FL_DEFPIN( 2, 2, B); _FL_DEFPIN( 3, 3, B);
|
||||
_FL_DEFPIN( 4, 4, B); _FL_DEFPIN( 5, 5, B); _FL_DEFPIN( 6, 6, B); _FL_DEFPIN( 7, 7, B);
|
||||
|
||||
_FL_DEFPIN( 8, 7, C); _FL_DEFPIN( 9, 6, C); _FL_DEFPIN( 10, 5,C); _FL_DEFPIN( 11, 4, C);
|
||||
_FL_DEFPIN( 12, 2, C); _FL_DEFPIN( 13, 0, D); _FL_DEFPIN( 14, 1, D); _FL_DEFPIN(15, 2, D);
|
||||
_FL_DEFPIN( 16, 3, D); _FL_DEFPIN( 17, 4, D); _FL_DEFPIN( 18, 5, D); _FL_DEFPIN( 19, 6, D);
|
||||
_FL_DEFPIN( 20, 7, D);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
// #define SPI_DATA 2
|
||||
// #define SPI_CLOCK 1
|
||||
// #define AVR_HARDWARE_SPI 1
|
||||
|
||||
#elif defined(IS_BEAN)
|
||||
|
||||
#define MAX_PIN 19
|
||||
_FL_DEFPIN( 0, 6, D); _FL_DEFPIN( 1, 1, B); _FL_DEFPIN( 2, 2, B); _FL_DEFPIN( 3, 3, B);
|
||||
_FL_DEFPIN( 4, 4, B); _FL_DEFPIN( 5, 5, B); _FL_DEFPIN( 6, 0, D); _FL_DEFPIN( 7, 7, D);
|
||||
_FL_DEFPIN( 8, 0, B); _FL_DEFPIN( 9, 1, D); _FL_DEFPIN(10, 2, D); _FL_DEFPIN(11, 3, D);
|
||||
_FL_DEFPIN(12, 4, D); _FL_DEFPIN(13, 5, D); _FL_DEFPIN(14, 0, C); _FL_DEFPIN(15, 1, C);
|
||||
_FL_DEFPIN(16, 2, C); _FL_DEFPIN(17, 3, C); _FL_DEFPIN(18, 4, C); _FL_DEFPIN(19, 5, C);
|
||||
|
||||
#define SPI_DATA 3
|
||||
#define SPI_CLOCK 5
|
||||
#define SPI_SELECT 2
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#ifndef __AVR_ATmega8__
|
||||
#define SPI_UART0_DATA 9
|
||||
#define SPI_UART0_CLOCK 12
|
||||
#endif
|
||||
|
||||
#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega8__)
|
||||
|
||||
#define MAX_PIN 19
|
||||
_FL_DEFPIN( 0, 0, D); _FL_DEFPIN( 1, 1, D); _FL_DEFPIN( 2, 2, D); _FL_DEFPIN( 3, 3, D);
|
||||
_FL_DEFPIN( 4, 4, D); _FL_DEFPIN( 5, 5, D); _FL_DEFPIN( 6, 6, D); _FL_DEFPIN( 7, 7, D);
|
||||
_FL_DEFPIN( 8, 0, B); _FL_DEFPIN( 9, 1, B); _FL_DEFPIN(10, 2, B); _FL_DEFPIN(11, 3, B);
|
||||
_FL_DEFPIN(12, 4, B); _FL_DEFPIN(13, 5, B); _FL_DEFPIN(14, 0, C); _FL_DEFPIN(15, 1, C);
|
||||
_FL_DEFPIN(16, 2, C); _FL_DEFPIN(17, 3, C); _FL_DEFPIN(18, 4, C); _FL_DEFPIN(19, 5, C);
|
||||
|
||||
#define SPI_DATA 11
|
||||
#define SPI_CLOCK 13
|
||||
#define SPI_SELECT 10
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#ifndef __AVR_ATmega8__
|
||||
#define SPI_UART0_DATA 1
|
||||
#define SPI_UART0_CLOCK 4
|
||||
#endif
|
||||
|
||||
#elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega32__)
|
||||
|
||||
#define MAX_PIN 31
|
||||
_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B);
|
||||
_FL_DEFPIN(4, 4, B); _FL_DEFPIN(5, 5, B); _FL_DEFPIN(6, 6, B); _FL_DEFPIN(7, 7, B);
|
||||
_FL_DEFPIN(8, 0, D); _FL_DEFPIN(9, 1, D); _FL_DEFPIN(10, 2, D); _FL_DEFPIN(11, 3, D);
|
||||
_FL_DEFPIN(12, 4, D); _FL_DEFPIN(13, 5, D); _FL_DEFPIN(14, 6, D); _FL_DEFPIN(15, 7, D);
|
||||
_FL_DEFPIN(16, 0, C); _FL_DEFPIN(17, 1, C); _FL_DEFPIN(18, 2, C); _FL_DEFPIN(19, 3, C);
|
||||
_FL_DEFPIN(20, 4, C); _FL_DEFPIN(21, 5, C); _FL_DEFPIN(22, 6, C); _FL_DEFPIN(23, 7, C);
|
||||
_FL_DEFPIN(24, 0, A); _FL_DEFPIN(25, 1, A); _FL_DEFPIN(26, 2, A); _FL_DEFPIN(27, 3, A);
|
||||
_FL_DEFPIN(28, 4, A); _FL_DEFPIN(29, 5, A); _FL_DEFPIN(30, 6, A); _FL_DEFPIN(31, 7, A);
|
||||
|
||||
#define SPI_DATA 5
|
||||
#define SPI_CLOCK 7
|
||||
#define SPI_SELECT 4
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega256RFR2__)
|
||||
|
||||
// AKA the Pinoccio
|
||||
_FL_DEFPIN( 0, 0, E); _FL_DEFPIN( 1, 1, E); _FL_DEFPIN( 2, 7, B); _FL_DEFPIN( 3, 3, E);
|
||||
_FL_DEFPIN( 4, 4, E); _FL_DEFPIN( 5, 5, E); _FL_DEFPIN( 6, 2, E); _FL_DEFPIN( 7, 6, E);
|
||||
_FL_DEFPIN( 8, 5, D); _FL_DEFPIN( 9, 0, B); _FL_DEFPIN(10, 2, B); _FL_DEFPIN(11, 3, B);
|
||||
_FL_DEFPIN(12, 1, B); _FL_DEFPIN(13, 2, D); _FL_DEFPIN(14, 3, D); _FL_DEFPIN(15, 0, D);
|
||||
_FL_DEFPIN(16, 1, D); _FL_DEFPIN(17, 4, D); _FL_DEFPIN(18, 7, E); _FL_DEFPIN(19, 6, D);
|
||||
_FL_DEFPIN(20, 7, D); _FL_DEFPIN(21, 4, B); _FL_DEFPIN(22, 5, B); _FL_DEFPIN(23, 6, B);
|
||||
_FL_DEFPIN(24, 0, F); _FL_DEFPIN(25, 1, F); _FL_DEFPIN(26, 2, F); _FL_DEFPIN(27, 3, F);
|
||||
_FL_DEFPIN(28, 4, F); _FL_DEFPIN(29, 5, F); _FL_DEFPIN(30, 6, F); _FL_DEFPIN(31, 7, F);
|
||||
|
||||
#define SPI_DATA 10
|
||||
#define SPI_CLOCK 12
|
||||
#define SPI_SELECT 9
|
||||
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
// megas
|
||||
#define MAX_PIN 69
|
||||
_FL_DEFPIN(0, 0, E); _FL_DEFPIN(1, 1, E); _FL_DEFPIN(2, 4, E); _FL_DEFPIN(3, 5, E);
|
||||
_FL_DEFPIN(4, 5, G); _FL_DEFPIN(5, 3, E); _FL_DEFPIN(6, 3, H); _FL_DEFPIN(7, 4, H);
|
||||
_FL_DEFPIN(8, 5, H); _FL_DEFPIN(9, 6, H); _FL_DEFPIN(10, 4, B); _FL_DEFPIN(11, 5, B);
|
||||
_FL_DEFPIN(12, 6, B); _FL_DEFPIN(13, 7, B); _FL_DEFPIN(14, 1, J); _FL_DEFPIN(15, 0, J);
|
||||
_FL_DEFPIN(16, 1, H); _FL_DEFPIN(17, 0, H); _FL_DEFPIN(18, 3, D); _FL_DEFPIN(19, 2, D);
|
||||
_FL_DEFPIN(20, 1, D); _FL_DEFPIN(21, 0, D); _FL_DEFPIN(22, 0, A); _FL_DEFPIN(23, 1, A);
|
||||
_FL_DEFPIN(24, 2, A); _FL_DEFPIN(25, 3, A); _FL_DEFPIN(26, 4, A); _FL_DEFPIN(27, 5, A);
|
||||
_FL_DEFPIN(28, 6, A); _FL_DEFPIN(29, 7, A); _FL_DEFPIN(30, 7, C); _FL_DEFPIN(31, 6, C);
|
||||
_FL_DEFPIN(32, 5, C); _FL_DEFPIN(33, 4, C); _FL_DEFPIN(34, 3, C); _FL_DEFPIN(35, 2, C);
|
||||
_FL_DEFPIN(36, 1, C); _FL_DEFPIN(37, 0, C); _FL_DEFPIN(38, 7, D); _FL_DEFPIN(39, 2, G);
|
||||
_FL_DEFPIN(40, 1, G); _FL_DEFPIN(41, 0, G); _FL_DEFPIN(42, 7, L); _FL_DEFPIN(43, 6, L);
|
||||
_FL_DEFPIN(44, 5, L); _FL_DEFPIN(45, 4, L); _FL_DEFPIN(46, 3, L); _FL_DEFPIN(47, 2, L);
|
||||
_FL_DEFPIN(48, 1, L); _FL_DEFPIN(49, 0, L); _FL_DEFPIN(50, 3, B); _FL_DEFPIN(51, 2, B);
|
||||
_FL_DEFPIN(52, 1, B); _FL_DEFPIN(53, 0, B); _FL_DEFPIN(54, 0, F); _FL_DEFPIN(55, 1, F);
|
||||
_FL_DEFPIN(56, 2, F); _FL_DEFPIN(57, 3, F); _FL_DEFPIN(58, 4, F); _FL_DEFPIN(59, 5, F);
|
||||
_FL_DEFPIN(60, 6, F); _FL_DEFPIN(61, 7, F); _FL_DEFPIN(62, 0, K); _FL_DEFPIN(63, 1, K);
|
||||
_FL_DEFPIN(64, 2, K); _FL_DEFPIN(65, 3, K); _FL_DEFPIN(66, 4, K); _FL_DEFPIN(67, 5, K);
|
||||
_FL_DEFPIN(68, 6, K); _FL_DEFPIN(69, 7, K);
|
||||
|
||||
#define SPI_DATA 51
|
||||
#define SPI_CLOCK 52
|
||||
#define SPI_SELECT 53
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
// Leonardo, teensy, blinkm
|
||||
#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
|
||||
|
||||
// teensy defs
|
||||
#define MAX_PIN 23
|
||||
_FL_DEFPIN(0, 0, B); _FL_DEFPIN(1, 1, B); _FL_DEFPIN(2, 2, B); _FL_DEFPIN(3, 3, B);
|
||||
_FL_DEFPIN(4, 7, B); _FL_DEFPIN(5, 0, D); _FL_DEFPIN(6, 1, D); _FL_DEFPIN(7, 2, D);
|
||||
_FL_DEFPIN(8, 3, D); _FL_DEFPIN(9, 6, C); _FL_DEFPIN(10, 7, C); _FL_DEFPIN(11, 6, D);
|
||||
_FL_DEFPIN(12, 7, D); _FL_DEFPIN(13, 4, B); _FL_DEFPIN(14, 5, B); _FL_DEFPIN(15, 6, B);
|
||||
_FL_DEFPIN(16, 7, F); _FL_DEFPIN(17, 6, F); _FL_DEFPIN(18, 5, F); _FL_DEFPIN(19, 4, F);
|
||||
_FL_DEFPIN(20, 1, F); _FL_DEFPIN(21, 0, F); _FL_DEFPIN(22, 4, D); _FL_DEFPIN(23, 5, D);
|
||||
|
||||
#define SPI_DATA 2
|
||||
#define SPI_CLOCK 1
|
||||
#define SPI_SELECT 0
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
// PD3/PD5
|
||||
#define SPI_UART1_DATA 8
|
||||
#define SPI_UART1_CLOCK 23
|
||||
|
||||
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
|
||||
// teensy++ 2 defs
|
||||
#define MAX_PIN 45
|
||||
_FL_DEFPIN(0, 0, D); _FL_DEFPIN(1, 1, D); _FL_DEFPIN(2, 2, D); _FL_DEFPIN(3, 3, D);
|
||||
_FL_DEFPIN(4, 4, D); _FL_DEFPIN(5, 5, D); _FL_DEFPIN(6, 6, D); _FL_DEFPIN(7, 7, D);
|
||||
_FL_DEFPIN(8, 0, E); _FL_DEFPIN(9, 1, E); _FL_DEFPIN(10, 0, C); _FL_DEFPIN(11, 1, C);
|
||||
_FL_DEFPIN(12, 2, C); _FL_DEFPIN(13, 3, C); _FL_DEFPIN(14, 4, C); _FL_DEFPIN(15, 5, C);
|
||||
_FL_DEFPIN(16, 6, C); _FL_DEFPIN(17, 7, C); _FL_DEFPIN(18, 6, E); _FL_DEFPIN(19, 7, E);
|
||||
_FL_DEFPIN(20, 0, B); _FL_DEFPIN(21, 1, B); _FL_DEFPIN(22, 2, B); _FL_DEFPIN(23, 3, B);
|
||||
_FL_DEFPIN(24, 4, B); _FL_DEFPIN(25, 5, B); _FL_DEFPIN(26, 6, B); _FL_DEFPIN(27, 7, B);
|
||||
_FL_DEFPIN(28, 0, A); _FL_DEFPIN(29, 1, A); _FL_DEFPIN(30, 2, A); _FL_DEFPIN(31, 3, A);
|
||||
_FL_DEFPIN(32, 4, A); _FL_DEFPIN(33, 5, A); _FL_DEFPIN(34, 6, A); _FL_DEFPIN(35, 7, A);
|
||||
_FL_DEFPIN(36, 4, E); _FL_DEFPIN(37, 5, E); _FL_DEFPIN(38, 0, F); _FL_DEFPIN(39, 1, F);
|
||||
_FL_DEFPIN(40, 2, F); _FL_DEFPIN(41, 3, F); _FL_DEFPIN(42, 4, F); _FL_DEFPIN(43, 5, F);
|
||||
_FL_DEFPIN(44, 6, F); _FL_DEFPIN(45, 7, F);
|
||||
|
||||
#define SPI_DATA 22
|
||||
#define SPI_CLOCK 21
|
||||
#define SPI_SELECT 20
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
// PD3/PD5
|
||||
#define SPI_UART1_DATA 3
|
||||
#define SPI_UART1_CLOCK 5
|
||||
|
||||
|
||||
#elif defined(__AVR_ATmega32U4__)
|
||||
|
||||
// leonard defs
|
||||
#define MAX_PIN 30
|
||||
_FL_DEFPIN(0, 2, D); _FL_DEFPIN(1, 3, D); _FL_DEFPIN(2, 1, D); _FL_DEFPIN(3, 0, D);
|
||||
_FL_DEFPIN(4, 4, D); _FL_DEFPIN(5, 6, C); _FL_DEFPIN(6, 7, D); _FL_DEFPIN(7, 6, E);
|
||||
_FL_DEFPIN(8, 4, B); _FL_DEFPIN(9, 5, B); _FL_DEFPIN(10, 6, B); _FL_DEFPIN(11, 7, B);
|
||||
_FL_DEFPIN(12, 6, D); _FL_DEFPIN(13, 7, C); _FL_DEFPIN(14, 3, B); _FL_DEFPIN(15, 1, B);
|
||||
_FL_DEFPIN(16, 2, B); _FL_DEFPIN(17, 0, B); _FL_DEFPIN(18, 7, F); _FL_DEFPIN(19, 6, F);
|
||||
_FL_DEFPIN(20, 5, F); _FL_DEFPIN(21, 4, F); _FL_DEFPIN(22, 1, F); _FL_DEFPIN(23, 0, F);
|
||||
_FL_DEFPIN(24, 4, D); _FL_DEFPIN(25, 7, D); _FL_DEFPIN(26, 4, B); _FL_DEFPIN(27, 5, B);
|
||||
_FL_DEFPIN(28, 6, B); _FL_DEFPIN(29, 6, D); _FL_DEFPIN(30, 5, D);
|
||||
|
||||
#define SPI_DATA 16
|
||||
#define SPI_CLOCK 15
|
||||
#define AVR_HARDWARE_SPI 1
|
||||
#define HAS_HARDWARE_PIN_SUPPORT 1
|
||||
|
||||
// PD3/PD5
|
||||
#define SPI_UART1_DATA 1
|
||||
#define SPI_UART1_CLOCK 30
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FASTLED_FORCE_SOFTWARE_PINS
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
|
||||
#endif // __INC_FASTPIN_AVR_H
|
||||
682
libraries/FastLED/platforms/avr/fastspi_avr.h
Normal file
682
libraries/FastLED/platforms/avr/fastspi_avr.h
Normal file
@@ -0,0 +1,682 @@
|
||||
#ifndef __INC_FASTSPI_AVR_H
|
||||
#define __INC_FASTSPI_AVR_H
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Hardware SPI support using USART registers and friends
|
||||
//
|
||||
// TODO: Complete/test implementation - right now this doesn't work
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// uno/mini/duemilanove
|
||||
#if defined(AVR_HARDWARE_SPI)
|
||||
|
||||
#if defined(UBRR1)
|
||||
|
||||
#ifndef UCPHA1
|
||||
#define UCPHA1 1
|
||||
#endif
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class AVRUSART1SPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
|
||||
public:
|
||||
AVRUSART1SPIOutput() { m_pSelect = NULL; }
|
||||
AVRUSART1SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
void init() {
|
||||
UBRR1 = 0;
|
||||
|
||||
/* Set MSPI mode of operation and SPI data mode 0. */
|
||||
UCSR1C = (1<<UMSEL11)|(1<<UMSEL10)|(0<<UCPHA1)|(0<<UCPOL1);
|
||||
/* Enable receiver and transmitter. */
|
||||
UCSR1B = (1<<RXEN1)|(1<<TXEN1);
|
||||
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
|
||||
// must be done last, see page 206
|
||||
setSPIRate();
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
if(_SPI_CLOCK_DIVIDER > 2) {
|
||||
UBRR1 = (_SPI_CLOCK_DIVIDER/2)-1;
|
||||
} else {
|
||||
UBRR1 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void stop() {
|
||||
// TODO: stop the uart spi output
|
||||
}
|
||||
|
||||
static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
|
||||
static bool sWait=false;
|
||||
if(sWait) {
|
||||
sWait = wait; return true;
|
||||
} else {
|
||||
sWait = wait; return false;
|
||||
}
|
||||
// return true;
|
||||
}
|
||||
static void wait() __attribute__((always_inline)) {
|
||||
if(shouldWait()) {
|
||||
while(!(UCSR1A & (1<<UDRE1)));
|
||||
}
|
||||
}
|
||||
static void waitFully() __attribute__((always_inline)) { wait(); }
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR1=b; shouldWait(true); }
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR1=b; shouldWait(true); wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR1=b; shouldWait(true); }
|
||||
|
||||
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
if(b && (1 << BIT)) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
|
||||
FastPin<_CLOCK_PIN>::hi();
|
||||
FastPin<_CLOCK_PIN>::lo();
|
||||
}
|
||||
|
||||
void enable_pins() { }
|
||||
void disable_pins() { }
|
||||
|
||||
void select() {
|
||||
if(m_pSelect != NULL) {
|
||||
m_pSelect->select();
|
||||
}
|
||||
enable_pins();
|
||||
setSPIRate();
|
||||
}
|
||||
|
||||
void release() {
|
||||
if(m_pSelect != NULL) {
|
||||
m_pSelect->release();
|
||||
}
|
||||
disable_pins();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
//setSPIRate();
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
//setSPIRate();
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
while(data != end) {
|
||||
// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
|
||||
writeByte(D::adjust(*data++)); delaycycles<3>();
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
//setSPIRate();
|
||||
int len = pixels.mLen;
|
||||
|
||||
select();
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale0()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale1()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale2()));
|
||||
} else {
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
release();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(UBRR0)
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class AVRUSART0SPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
|
||||
public:
|
||||
AVRUSART0SPIOutput() { m_pSelect = NULL; }
|
||||
AVRUSART0SPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
void init() {
|
||||
UBRR0 = 0;
|
||||
|
||||
/* Set MSPI mode of operation and SPI data mode 0. */
|
||||
UCSR0C = (1<<UMSEL01)|(1<<UMSEL00)/*|(0<<UCPHA0)*/|(0<<UCPOL0);
|
||||
/* Enable receiver and transmitter. */
|
||||
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
|
||||
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
|
||||
|
||||
// must be done last, see page 206
|
||||
setSPIRate();
|
||||
}
|
||||
|
||||
void setSPIRate() {
|
||||
if(_SPI_CLOCK_DIVIDER > 2) {
|
||||
UBRR0 = (_SPI_CLOCK_DIVIDER/2)-1;
|
||||
} else {
|
||||
UBRR0 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void stop() {
|
||||
// TODO: stop the uart spi output
|
||||
}
|
||||
|
||||
static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
|
||||
static bool sWait=false;
|
||||
if(sWait) {
|
||||
sWait = wait; return true;
|
||||
} else {
|
||||
sWait = wait; return false;
|
||||
}
|
||||
// return true;
|
||||
}
|
||||
static void wait() __attribute__((always_inline)) {
|
||||
if(shouldWait()) {
|
||||
while(!(UCSR0A & (1<<UDRE0)));
|
||||
}
|
||||
}
|
||||
static void waitFully() __attribute__((always_inline)) { wait(); }
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); UDR0=b; shouldWait(true); }
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { UDR0=b; shouldWait(true); wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { UDR0=b; shouldWait(true); }
|
||||
|
||||
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
if(b && (1 << BIT)) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
|
||||
FastPin<_CLOCK_PIN>::hi();
|
||||
FastPin<_CLOCK_PIN>::lo();
|
||||
}
|
||||
|
||||
void enable_pins() { }
|
||||
void disable_pins() { }
|
||||
|
||||
void select() {
|
||||
if(m_pSelect != NULL) {
|
||||
m_pSelect->select();
|
||||
}
|
||||
enable_pins();
|
||||
setSPIRate();
|
||||
}
|
||||
|
||||
void release() {
|
||||
if(m_pSelect != NULL) {
|
||||
m_pSelect->release();
|
||||
}
|
||||
disable_pins();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
//setSPIRate();
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
//setSPIRate();
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
while(data != end) {
|
||||
// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
|
||||
writeByte(D::adjust(*data++)); delaycycles<3>();
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
//setSPIRate();
|
||||
int len = pixels.mLen;
|
||||
|
||||
select();
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale0()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale1()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale2()));
|
||||
} else {
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(SPSR)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Hardware SPI support using SPDR registers and friends
|
||||
//
|
||||
// Technically speaking, this uses the AVR SPI registers. This will work on the Teensy 3.0 because Paul made a set of compatability
|
||||
// classes that map the AVR SPI registers to ARM's, however this caps the performance of output.
|
||||
//
|
||||
// TODO: implement ARMHardwareSPIOutput
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class AVRHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
bool mWait;
|
||||
public:
|
||||
AVRHardwareSPIOutput() { m_pSelect = NULL; mWait = false;}
|
||||
AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
void setSPIRate() {
|
||||
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear out the prescalar bits
|
||||
|
||||
bool b2x = false;
|
||||
|
||||
if(_SPI_CLOCK_DIVIDER >= 128) { SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR |= (1<<SPR1);}
|
||||
else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR |= (1<<SPR1); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR |= (1<<SPR0); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
|
||||
else { b2x = true; }
|
||||
|
||||
if(b2x) { SPSR |= (1<<SPI2X); }
|
||||
else { SPSR &= ~ (1<<SPI2X); }
|
||||
}
|
||||
|
||||
void init() {
|
||||
volatile uint8_t clr;
|
||||
|
||||
// set the pins to output
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
#ifdef SPI_SELECT
|
||||
// Make sure the slave select line is set to output, or arduino will block us
|
||||
FastPin<SPI_SELECT>::setOutput();
|
||||
FastPin<SPI_SELECT>::lo();
|
||||
#endif
|
||||
|
||||
SPCR |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
|
||||
SPCR &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear out the prescalar bits
|
||||
|
||||
clr = SPSR; // clear SPI status register
|
||||
clr = SPDR; // clear SPI data register
|
||||
clr;
|
||||
|
||||
bool b2x = false;
|
||||
|
||||
if(_SPI_CLOCK_DIVIDER >= 128) { SPCR |= (1<<SPR1); SPCR |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR |= (1<<SPR1);}
|
||||
else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR |= (1<<SPR1); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR |= (1<<SPR0); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
|
||||
else { b2x = true; }
|
||||
|
||||
if(b2x) { SPSR |= (1<<SPI2X); }
|
||||
else { SPSR &= ~ (1<<SPI2X); }
|
||||
|
||||
SPDR=0;
|
||||
shouldWait(false);
|
||||
release();
|
||||
}
|
||||
|
||||
static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
|
||||
static bool sWait=false;
|
||||
if(sWait) { sWait = wait; return true; } else { sWait = wait; return false; }
|
||||
// return true;
|
||||
}
|
||||
static void wait() __attribute__((always_inline)) { if(shouldWait()) { while(!(SPSR & (1<<SPIF))); } }
|
||||
static void waitFully() __attribute__((always_inline)) { wait(); }
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPDR=b; shouldWait(true); }
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPDR=b; shouldWait(true); }
|
||||
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
SPCR &= ~(1 << SPE);
|
||||
if(b & (1 << BIT)) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
|
||||
FastPin<_CLOCK_PIN>::hi();
|
||||
FastPin<_CLOCK_PIN>::lo();
|
||||
SPCR |= 1 << SPE;
|
||||
shouldWait(false);
|
||||
}
|
||||
|
||||
void enable_pins() {
|
||||
SPCR |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
|
||||
}
|
||||
|
||||
void disable_pins() {
|
||||
SPCR &= ~(((1<<SPE) | (1<<MSTR) )); // disable SPI
|
||||
}
|
||||
|
||||
void select() {
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
enable_pins();
|
||||
setSPIRate();
|
||||
}
|
||||
|
||||
void release() {
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
disable_pins();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
//setSPIRate();
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
//setSPIRate();
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
while(data != end) {
|
||||
// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
|
||||
writeByte(D::adjust(*data++)); delaycycles<3>();
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
//setSPIRate();
|
||||
int len = pixels.mLen;
|
||||
|
||||
select();
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale0()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale1()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale2()));
|
||||
} else {
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
};
|
||||
#elif defined(SPSR0)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Hardware SPI support using SPDR0 registers and friends
|
||||
//
|
||||
// Technically speaking, this uses the AVR SPI registers. This will work on the Teensy 3.0 because Paul made a set of compatability
|
||||
// classes that map the AVR SPI registers to ARM's, however this caps the performance of output.
|
||||
//
|
||||
// TODO: implement ARMHardwareSPIOutput
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class AVRHardwareSPIOutput {
|
||||
Selectable *m_pSelect;
|
||||
bool mWait;
|
||||
public:
|
||||
AVRHardwareSPIOutput() { m_pSelect = NULL; mWait = false;}
|
||||
AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
void setSelect(Selectable *pSelect) { m_pSelect = pSelect; }
|
||||
|
||||
void setSPIRate() {
|
||||
SPCR0 &= ~ ( (1<<SPR10) | (1<<SPR0) ); // clear out the prescalar bits
|
||||
|
||||
bool b2x = false;
|
||||
|
||||
if(_SPI_CLOCK_DIVIDER >= 128) { SPCR0 |= (1<<SPR1); SPCR0 |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR0 |= (1<<SPR1);}
|
||||
else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR0 |= (1<<SPR1); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR0 |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR0 |= (1<<SPR0); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
|
||||
else { b2x = true; }
|
||||
|
||||
if(b2x) { SPSR0 |= (1<<SPI2X); }
|
||||
else { SPSR0 &= ~ (1<<SPI2X); }
|
||||
}
|
||||
|
||||
void init() {
|
||||
volatile uint8_t clr;
|
||||
|
||||
// set the pins to output
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
#ifdef SPI_SELECT
|
||||
// Make sure the slave select line is set to output, or arduino will block us
|
||||
FastPin<SPI_SELECT>::setOutput();
|
||||
FastPin<SPI_SELECT>::lo();
|
||||
#endif
|
||||
|
||||
SPCR0 |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
|
||||
SPCR0 &= ~ ( (1<<SPR1) | (1<<SPR0) ); // clear out the prescalar bits
|
||||
|
||||
clr = SPSR0; // clear SPI status register
|
||||
clr = SPDR0; // clear SPI data register
|
||||
clr;
|
||||
|
||||
bool b2x = false;
|
||||
|
||||
if(_SPI_CLOCK_DIVIDER >= 128) { SPCR0 |= (1<<SPR1); SPCR0 |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 64) { SPCR0 |= (1<<SPR1);}
|
||||
else if(_SPI_CLOCK_DIVIDER >= 32) { SPCR0 |= (1<<SPR1); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 16) { SPCR0 |= (1<<SPR0); }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 8) { SPCR0 |= (1<<SPR0); b2x = true; }
|
||||
else if(_SPI_CLOCK_DIVIDER >= 4) { /* do nothing - default rate */ }
|
||||
else { b2x = true; }
|
||||
|
||||
if(b2x) { SPSR0 |= (1<<SPI2X); }
|
||||
else { SPSR0 &= ~ (1<<SPI2X); }
|
||||
|
||||
SPDR0=0;
|
||||
shouldWait(false);
|
||||
release();
|
||||
}
|
||||
|
||||
static bool shouldWait(bool wait = false) __attribute__((always_inline)) {
|
||||
static bool sWait=false;
|
||||
if(sWait) { sWait = wait; return true; } else { sWait = wait; return false; }
|
||||
// return true;
|
||||
}
|
||||
static void wait() __attribute__((always_inline)) { if(shouldWait()) { while(!(SPSR0 & (1<<SPIF))); } }
|
||||
static void waitFully() __attribute__((always_inline)) { wait(); }
|
||||
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)) { writeByte(w>>8); writeByte(w&0xFF); }
|
||||
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); SPDR0=b; shouldWait(true); }
|
||||
static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPDR0=b; shouldWait(true); wait(); }
|
||||
static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPDR0=b; shouldWait(true); }
|
||||
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
SPCR0 &= ~(1 << SPE);
|
||||
if(b & (1 << BIT)) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
|
||||
FastPin<_CLOCK_PIN>::hi();
|
||||
FastPin<_CLOCK_PIN>::lo();
|
||||
SPCR0 |= 1 << SPE;
|
||||
shouldWait(false);
|
||||
}
|
||||
|
||||
void enable_pins() {
|
||||
SPCR0 |= ((1<<SPE) | (1<<MSTR) ); // enable SPI as master
|
||||
}
|
||||
|
||||
void disable_pins() {
|
||||
SPCR0 &= ~(((1<<SPE) | (1<<MSTR) )); // disable SPI
|
||||
}
|
||||
|
||||
void select() {
|
||||
if(m_pSelect != NULL) { m_pSelect->select(); }
|
||||
enable_pins();
|
||||
setSPIRate();
|
||||
}
|
||||
|
||||
void release() {
|
||||
if(m_pSelect != NULL) { m_pSelect->release(); }
|
||||
disable_pins();
|
||||
}
|
||||
|
||||
static void writeBytesValueRaw(uint8_t value, int len) {
|
||||
while(len--) { writeByte(value); }
|
||||
}
|
||||
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
//setSPIRate();
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
// Write a block of n uint8_ts out
|
||||
template <class D> void writeBytes(register uint8_t *data, int len) {
|
||||
//setSPIRate();
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
while(data != end) {
|
||||
// a slight touch of delay here helps optimize the timing of the status register check loop (not used on ARM)
|
||||
writeByte(D::adjust(*data++)); delaycycles<3>();
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(register uint8_t *data, int len) { writeBytes<DATA_NOP>(data, len); }
|
||||
|
||||
// write a block of uint8_ts out in groups of three. len is the total number of uint8_ts to write out. The template
|
||||
// parameters indicate how many uint8_ts to skip at the beginning and/or end of each grouping
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
//setSPIRate();
|
||||
int len = pixels.mLen;
|
||||
|
||||
select();
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale0()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale1()));
|
||||
writeBytePostWait(D::adjust(pixels.loadAndScale2()));
|
||||
} else {
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
}
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#else
|
||||
// #define FASTLED_FORCE_SOFTWARE_SPI
|
||||
#endif
|
||||
|
||||
FASTLED_NAMESPACE_END;
|
||||
|
||||
|
||||
#endif
|
||||
67
libraries/FastLED/platforms/avr/led_sysdefs_avr.h
Normal file
67
libraries/FastLED/platforms/avr/led_sysdefs_avr.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef __INC_LED_SYSDEFS_AVR_H
|
||||
#define __INC_LED_SYSDEFS_AVR_H
|
||||
|
||||
#define FASTLED_AVR
|
||||
|
||||
#ifndef INTERRUPT_THRESHOLD
|
||||
#define INTERRUPT_THRESHOLD 2
|
||||
#endif
|
||||
|
||||
#define FASTLED_SPI_BYTE_ONLY
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h> // for cli/se definitions
|
||||
|
||||
// Define the register types
|
||||
typedef volatile uint8_t RoReg; /**< Read only 8-bit register (volatile const unsigned int) */
|
||||
typedef volatile uint8_t RwReg; /**< Read-Write 8-bit register (volatile unsigned int) */
|
||||
|
||||
|
||||
// Default to disallowing interrupts (may want to gate this on teensy2 vs. other arm platforms, since the
|
||||
// teensy2 has a good, fast millis interrupt implementation)
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 0
|
||||
#endif
|
||||
|
||||
#if FASTLED_ALLOW_INTERRUPTS == 1
|
||||
#define FASTLED_ACCURATE_CLOCK
|
||||
#endif
|
||||
|
||||
|
||||
// Default to using PROGMEM here
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 1
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_AVR_DIGISPARK) || defined(ARDUINO_AVR_DIGISPARKPRO)
|
||||
#ifndef NO_CORRECTION
|
||||
#define NO_CORRECTION 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
# if defined(CORE_TEENSY) || defined(TEENSYDUINO)
|
||||
extern volatile unsigned long timer0_millis_count;
|
||||
# define MS_COUNTER timer0_millis_count
|
||||
# elif defined(ATTINY_CORE)
|
||||
extern volatile unsigned long millis_timer_millis;
|
||||
# define MS_COUNTER millis_timer_millis
|
||||
# else
|
||||
extern volatile unsigned long timer0_millis;
|
||||
# define MS_COUNTER timer0_millis
|
||||
# endif
|
||||
};
|
||||
|
||||
// special defs for the tiny environments
|
||||
#if defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega8U2__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny167__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtinyX41__) || defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny441__)
|
||||
#define LIB8_ATTINY 1
|
||||
#define FASTLED_NEEDS_YIELD
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO) && (ARDUINO > 150) && !defined(IS_BEAN) && !defined (ARDUINO_AVR_DIGISPARK) && !defined (LIB8_TINY) && !defined (ARDUINO_AVR_LARDU_328E)
|
||||
// don't need YIELD defined by the library
|
||||
#else
|
||||
#define FASTLED_NEEDS_YIELD
|
||||
extern "C" void yield();
|
||||
#endif
|
||||
#endif
|
||||
168
libraries/FastLED/platforms/esp/32/clockless_block_esp32.h
Normal file
168
libraries/FastLED/platforms/esp/32/clockless_block_esp32.h
Normal file
@@ -0,0 +1,168 @@
|
||||
#ifndef __INC_CLOCKLESS_BLOCK_ESP8266_H
|
||||
#define __INC_CLOCKLESS_BLOCK_ESP8266_H
|
||||
|
||||
#define FASTLED_HAS_BLOCKLESS 1
|
||||
|
||||
#define PORT_MASK (((1<<LANES)-1) & 0x0000FFFFL)
|
||||
#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
|
||||
#define USED_LANES (MIN(LANES,4))
|
||||
#define REAL_FIRST_PIN 12
|
||||
#define LAST_PIN (12 + USED_LANES - 1)
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
extern uint32_t _frame_cnt;
|
||||
extern uint32_t _retry_cnt;
|
||||
#endif
|
||||
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 5>
|
||||
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
|
||||
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<FIRST_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * LANES; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) {
|
||||
// mWait.wait();
|
||||
/*uint32_t clocks = */
|
||||
int cnt=FASTLED_INTERRUPT_RETRY_COUNT;
|
||||
while(!showRGBInternal(pixels) && cnt--) {
|
||||
ets_intr_unlock();
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
_retry_cnt++;
|
||||
#endif
|
||||
delayMicroseconds(WAIT_TIME * 10);
|
||||
ets_intr_lock();
|
||||
}
|
||||
// #if FASTLED_ALLOW_INTTERUPTS == 0
|
||||
// Adjust the timer
|
||||
// long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
// MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
// #endif
|
||||
|
||||
// mWait.mark();
|
||||
}
|
||||
|
||||
template<int PIN> static void initPin() {
|
||||
if(PIN >= REAL_FIRST_PIN && PIN <= LAST_PIN) {
|
||||
_ESPPIN<PIN, 1<<(PIN & 0xFF)>::setOutput();
|
||||
// FastPin<PIN>::setOutput();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
// Only supportd on pins 12-15
|
||||
// SZG: This probably won't work (check pins definitions in fastpin_esp32)
|
||||
initPin<12>();
|
||||
initPin<13>();
|
||||
initPin<14>();
|
||||
initPin<15>();
|
||||
mPinMask = FastPin<FIRST_PIN>::mask();
|
||||
mPort = FastPin<FIRST_PIN>::port();
|
||||
|
||||
// Serial.print("Mask is "); Serial.println(PORT_MASK);
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[8];
|
||||
uint16_t shorts[4];
|
||||
uint32_t raw[2];
|
||||
} Lines;
|
||||
|
||||
#define ESP_ADJUST 0 // (2*(F_CPU/24000000))
|
||||
#define ESP_ADJUST2 0
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register Lines & b, PixelController<RGB_ORDER, LANES, PORT_MASK> &pixels) { // , register uint32_t & b2) {
|
||||
Lines b2 = b;
|
||||
transpose8x1_noinline(b.bytes,b2.bytes);
|
||||
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(register uint32_t i = 0; i < USED_LANES; i++) {
|
||||
while((__clock_cycles() - last_mark) < (T1+T2+T3));
|
||||
last_mark = __clock_cycles();
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK << REAL_FIRST_PIN;
|
||||
|
||||
uint32_t nword = ((uint32_t)(~b2.bytes[7-i]) & PORT_MASK) << REAL_FIRST_PIN;
|
||||
while((__clock_cycles() - last_mark) < (T1-6));
|
||||
*FastPin<FIRST_PIN>::cport() = nword;
|
||||
|
||||
while((__clock_cycles() - last_mark) < (T1+T2));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK << REAL_FIRST_PIN;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
}
|
||||
|
||||
for(register uint32_t i = USED_LANES; i < 8; i++) {
|
||||
while((__clock_cycles() - last_mark) < (T1+T2+T3));
|
||||
last_mark = __clock_cycles();
|
||||
*FastPin<FIRST_PIN>::sport() = PORT_MASK << REAL_FIRST_PIN;
|
||||
|
||||
uint32_t nword = ((uint32_t)(~b2.bytes[7-i]) & PORT_MASK) << REAL_FIRST_PIN;
|
||||
while((__clock_cycles() - last_mark) < (T1-6));
|
||||
*FastPin<FIRST_PIN>::cport() = nword;
|
||||
|
||||
while((__clock_cycles() - last_mark) < (T1+T2));
|
||||
*FastPin<FIRST_PIN>::cport() = PORT_MASK << REAL_FIRST_PIN;
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) {
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
Lines b0;
|
||||
|
||||
for(int i = 0; i < USED_LANES; i++) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
allpixels.preStepFirstByteDithering();
|
||||
|
||||
ets_intr_lock();
|
||||
uint32_t _start = __clock_cycles();
|
||||
uint32_t last_mark = _start;
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(last_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(last_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(last_mark, b0, allpixels);
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
ets_intr_unlock();
|
||||
#endif
|
||||
|
||||
allpixels.stepDithering();
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
ets_intr_lock();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if((int32_t)(__clock_cycles()-last_mark) > 0) {
|
||||
if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { ets_intr_unlock(); return 0; }
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
ets_intr_unlock();
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
_frame_cnt++;
|
||||
#endif
|
||||
return __clock_cycles() - _start;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
#endif
|
||||
786
libraries/FastLED/platforms/esp/32/clockless_esp32.h.orig
Normal file
786
libraries/FastLED/platforms/esp/32/clockless_esp32.h.orig
Normal file
@@ -0,0 +1,786 @@
|
||||
/*
|
||||
* Integration into FastLED ClocklessController 2017 Thomas Basler
|
||||
*
|
||||
* Modifications Copyright (c) 2017 Martin F. Falatic
|
||||
*
|
||||
* Modifications Copyright (c) 2018 Samuel Z. Guyer
|
||||
*
|
||||
* ESP32 support is provided using the RMT peripheral device -- a unit
|
||||
* on the chip designed specifically for generating (and receiving)
|
||||
* precisely-timed digital signals. Nominally for use in infrared
|
||||
* remote controls, we use it to generate the signals for clockless
|
||||
* LED strips. The main advantage of using the RMT device is that,
|
||||
* once programmed, it generates the signal asynchronously, allowing
|
||||
* the CPU to continue executing other code. It is also not vulnerable
|
||||
* to interrupts or other timing problems that could disrupt the signal.
|
||||
*
|
||||
* The implementation strategy is borrowed from previous work and from
|
||||
* the RMT support built into the ESP32 IDF. The RMT device has 8
|
||||
* channels, which can be programmed independently to send sequences
|
||||
* of high/low bits. Memory for each channel is limited, however, so
|
||||
* in order to send a long sequence of bits, we need to continuously
|
||||
* refill the buffer until all the data is sent. To do this, we fill
|
||||
* half the buffer and then set an interrupt to go off when that half
|
||||
* is sent. Then we refill that half while the second half is being
|
||||
* sent. This strategy effectively overlaps computation (by the CPU)
|
||||
* and communication (by the RMT).
|
||||
*
|
||||
* Since the RMT device only has 8 channels, we need a strategy to
|
||||
* allow more than 8 LED controllers. Our driver assigns controllers
|
||||
* to channels on the fly, queuing up controllers as necessary until a
|
||||
* channel is free. The main showPixels routine just fires off the
|
||||
* first 8 controllers; the interrupt handler starts new controllers
|
||||
* asynchronously as previous ones finish. So, for example, it can
|
||||
* send the data for 8 controllers simultaneously, but 16 controllers
|
||||
* would take approximately twice as much time.
|
||||
*
|
||||
* There is a #define that allows a program to control the total
|
||||
* number of channels that the driver is allowed to use. It defaults
|
||||
* to 8 -- use all the channels. Setting it to 1, for example, results
|
||||
* in fully serial output:
|
||||
*
|
||||
* #define FASTLED_RMT_MAX_CHANNELS 1
|
||||
*
|
||||
* OTHER RMT APPLICATIONS
|
||||
*
|
||||
* The default FastLED driver takes over control of the RMT interrupt
|
||||
* handler, making it hard to use the RMT device for other
|
||||
* (non-FastLED) purposes. You can change it's behavior to use the ESP
|
||||
* core driver instead, allowing other RMT applications to
|
||||
* co-exist. To switch to this mode, add the following directive
|
||||
* before you include FastLED.h:
|
||||
*
|
||||
* #define FASTLED_RMT_BUILTIN_DRIVER
|
||||
*
|
||||
* There may be a performance penalty for using this mode. We need to
|
||||
* compute the RMT signal for the entire LED strip ahead of time,
|
||||
* rather than overlapping it with communication. We also need a large
|
||||
* buffer to hold the signal specification. Each bit of pixel data is
|
||||
* represented by a 32-bit pulse specification, so it is a 32X blow-up
|
||||
* in memory use.
|
||||
*
|
||||
*
|
||||
* Based on public domain code created 19 Nov 2016 by Chris Osborn <fozztexx@fozztexx.com>
|
||||
* http://insentricity.com *
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "esp_intr.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rmt.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/rmt_struct.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
|
||||
uint32_t cyc;
|
||||
__asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc));
|
||||
return cyc;
|
||||
}
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
// -- Configuration constants
|
||||
#define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */
|
||||
#define MAX_PULSES 32 /* A channel has a 64 "pulse" buffer - we use half per pass */
|
||||
|
||||
// -- Convert ESP32 cycles back into nanoseconds
|
||||
#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ)
|
||||
|
||||
// -- Convert nanoseconds into RMT cycles
|
||||
#define F_CPU_RMT ( 80000000L)
|
||||
#define NS_PER_SEC (1000000000L)
|
||||
#define CYCLES_PER_SEC (F_CPU_RMT/DIVIDER)
|
||||
#define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC )
|
||||
#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE )
|
||||
|
||||
// -- Convert ESP32 cycles to RMT cycles
|
||||
#define TO_RMT_CYCLES(_CLKS) NS_TO_CYCLES(ESPCLKS_TO_NS(_CLKS))
|
||||
|
||||
// -- Number of cycles to signal the strip to latch
|
||||
#define RMT_RESET_DURATION NS_TO_CYCLES(50000)
|
||||
|
||||
// -- Core or custom driver
|
||||
#ifndef FASTLED_RMT_BUILTIN_DRIVER
|
||||
#define FASTLED_RMT_BUILTIN_DRIVER false
|
||||
#endif
|
||||
|
||||
// -- Max number of controllers we can support
|
||||
#ifndef FASTLED_RMT_MAX_CONTROLLERS
|
||||
#define FASTLED_RMT_MAX_CONTROLLERS 32
|
||||
#endif
|
||||
|
||||
// -- Number of RMT channels to use (up to 8)
|
||||
// Redefine this value to 1 to force serial output
|
||||
#ifndef FASTLED_RMT_MAX_CHANNELS
|
||||
#define FASTLED_RMT_MAX_CHANNELS 8
|
||||
#endif
|
||||
|
||||
// -- Array of all controllers
|
||||
static CLEDController * gControllers[FASTLED_RMT_MAX_CONTROLLERS];
|
||||
|
||||
// -- Current set of active controllers, indexed by the RMT
|
||||
// channel assigned to them.
|
||||
static CLEDController * gOnChannel[FASTLED_RMT_MAX_CHANNELS];
|
||||
|
||||
static int gNumControllers = 0;
|
||||
static int gNumStarted = 0;
|
||||
static int gNumDone = 0;
|
||||
static int gNext = 0;
|
||||
|
||||
static intr_handle_t gRMT_intr_handle = NULL;
|
||||
|
||||
// -- Global semaphore for the whole show process
|
||||
// Semaphore is not given until all data has been sent
|
||||
static xSemaphoreHandle gTX_sem = NULL;
|
||||
|
||||
static bool gInitialized = false;
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 5>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER>
|
||||
{
|
||||
// -- RMT has 8 channels, numbered 0 to 7
|
||||
rmt_channel_t mRMT_channel;
|
||||
|
||||
// -- Store the GPIO pin
|
||||
gpio_num_t mPin;
|
||||
<<<<<<< HEAD
|
||||
|
||||
// -- This instantiation forces a check on the pin choice
|
||||
FastPin<DATA_PIN> mFastPin;
|
||||
|
||||
// -- Timing values for zero and one bits, derived from T1, T2, and T3
|
||||
rmt_item32_t mZero;
|
||||
rmt_item32_t mOne;
|
||||
|
||||
=======
|
||||
|
||||
// -- Timing values for zero and one bits, derived from T1, T2, and T3
|
||||
rmt_item32_t mZero;
|
||||
rmt_item32_t mOne;
|
||||
|
||||
>>>>>>> upstream/master
|
||||
// -- State information for keeping track of where we are in the pixel data
|
||||
PixelController<RGB_ORDER> * mPixels = NULL;
|
||||
void * mPixelSpace = NULL;
|
||||
uint8_t mRGB_channel;
|
||||
uint16_t mCurPulse;
|
||||
|
||||
// -- Buffer to hold all of the pulses. For the version that uses
|
||||
// the RMT driver built into the ESP core.
|
||||
rmt_item32_t * mBuffer;
|
||||
uint16_t mBufferSize;
|
||||
|
||||
public:
|
||||
|
||||
virtual void init()
|
||||
{
|
||||
// -- Precompute rmt items corresponding to a zero bit and a one bit
|
||||
// according to the timing values given in the template instantiation
|
||||
// T1H
|
||||
mOne.level0 = 1;
|
||||
mOne.duration0 = TO_RMT_CYCLES(T1+T2);
|
||||
// T1L
|
||||
mOne.level1 = 0;
|
||||
mOne.duration1 = TO_RMT_CYCLES(T3);
|
||||
|
||||
// T0H
|
||||
mZero.level0 = 1;
|
||||
mZero.duration0 = TO_RMT_CYCLES(T1);
|
||||
// T0L
|
||||
mZero.level1 = 0;
|
||||
mZero.duration1 = TO_RMT_CYCLES(T2 + T3);
|
||||
|
||||
<<<<<<< HEAD
|
||||
gControllers[gNumControllers] = this;
|
||||
gNumControllers++;
|
||||
|
||||
mPin = gpio_num_t(DATA_PIN);
|
||||
=======
|
||||
gControllers[gNumControllers] = this;
|
||||
gNumControllers++;
|
||||
|
||||
mPin = gpio_num_t(DATA_PIN);
|
||||
>>>>>>> upstream/master
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
void initRMT()
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
// -- Only need to do this once
|
||||
if (gInitialized) return;
|
||||
|
||||
for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) {
|
||||
gOnChannel[i] = NULL;
|
||||
|
||||
// -- RMT configuration for transmission
|
||||
rmt_config_t rmt_tx;
|
||||
rmt_tx.channel = rmt_channel_t(i);
|
||||
rmt_tx.rmt_mode = RMT_MODE_TX;
|
||||
rmt_tx.gpio_num = mPin; // The particular pin will be assigned later
|
||||
rmt_tx.mem_block_num = 1;
|
||||
rmt_tx.clk_div = DIVIDER;
|
||||
rmt_tx.tx_config.loop_en = false;
|
||||
rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
|
||||
rmt_tx.tx_config.carrier_en = false;
|
||||
rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
|
||||
rmt_tx.tx_config.idle_output_en = true;
|
||||
|
||||
// -- Apply the configuration
|
||||
rmt_config(&rmt_tx);
|
||||
|
||||
if (FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
rmt_driver_install(rmt_channel_t(i), 0, 0);
|
||||
} else {
|
||||
// -- Set up the RMT to send 1/2 of the pulse buffer and then
|
||||
// generate an interrupt. When we get this interrupt we
|
||||
// fill the other half in preparation (kind of like double-buffering)
|
||||
rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, MAX_PULSES);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Create a semaphore to block execution until all the controllers are done
|
||||
if (gTX_sem == NULL) {
|
||||
gTX_sem = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(gTX_sem);
|
||||
}
|
||||
|
||||
if ( ! FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
// -- Allocate the interrupt if we have not done so yet. This
|
||||
// interrupt handler must work for all different kinds of
|
||||
// strips, so it delegates to the refill function for each
|
||||
// specific instantiation of ClocklessController.
|
||||
if (gRMT_intr_handle == NULL)
|
||||
esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, interruptHandler, 0, &gRMT_intr_handle);
|
||||
}
|
||||
|
||||
gInitialized = true;
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels)
|
||||
{
|
||||
if (gNumStarted == 0) {
|
||||
// -- First controller: make sure everything is set up
|
||||
initRMT();
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
}
|
||||
|
||||
// -- Initialize the local state, save a pointer to the pixel
|
||||
// data. We need to make a copy because pixels is a local
|
||||
// variable in the calling function, and this data structure
|
||||
// needs to outlive this call to showPixels.
|
||||
|
||||
if (mPixels != NULL) delete mPixels;
|
||||
mPixels = new PixelController<RGB_ORDER>(pixels);
|
||||
|
||||
// -- Keep track of the number of strips we've seen
|
||||
gNumStarted++;
|
||||
|
||||
// -- The last call to showPixels is the one responsible for doing
|
||||
// all of the actual worl
|
||||
if (gNumStarted == gNumControllers) {
|
||||
gNext = 0;
|
||||
|
||||
// -- First, fill all the available channels
|
||||
int channel = 0;
|
||||
while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) {
|
||||
startNext(channel);
|
||||
channel++;
|
||||
}
|
||||
|
||||
// -- Wait here while the rest of the data is sent. The interrupt handler
|
||||
// will keep refilling the RMT buffers until it is all sent; then it
|
||||
// gives the semaphore back.
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
xSemaphoreGive(gTX_sem);
|
||||
|
||||
// -- Reset the counters
|
||||
gNumStarted = 0;
|
||||
gNumDone = 0;
|
||||
gNext = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Start up the next controller
|
||||
// This method is static so that it can dispatch to the appropriate
|
||||
// startOnChannel method of the given controller.
|
||||
static void startNext(int channel)
|
||||
{
|
||||
if (gNext < gNumControllers) {
|
||||
ClocklessController * pController = static_cast<ClocklessController*>(gControllers[gNext]);
|
||||
pController->startOnChannel(channel);
|
||||
gNext++;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void startOnChannel(int channel)
|
||||
{
|
||||
// -- Assign this channel and configure the RMT
|
||||
mRMT_channel = rmt_channel_t(channel);
|
||||
|
||||
// -- Store a reference to this controller, so we can get it
|
||||
// inside the interrupt handler
|
||||
gOnChannel[channel] = this;
|
||||
|
||||
// -- Assign the pin to this channel
|
||||
rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin);
|
||||
|
||||
if (FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
// -- Use the built-in RMT driver to send all the data in one shot
|
||||
rmt_register_tx_end_callback(doneOnChannel, 0);
|
||||
writeAllRMTItems();
|
||||
} else {
|
||||
// -- Use our custom driver to send the data incrementally
|
||||
|
||||
// -- Turn on the interrupts
|
||||
rmt_set_tx_intr_en(mRMT_channel, true);
|
||||
|
||||
// -- Initialize the counters that keep track of where we are in
|
||||
// the pixel data.
|
||||
mCurPulse = 0;
|
||||
mRGB_channel = 0;
|
||||
|
||||
// -- Fill both halves of the buffer
|
||||
fillHalfRMTBuffer();
|
||||
fillHalfRMTBuffer();
|
||||
|
||||
// -- Turn on the interrupts
|
||||
rmt_set_tx_intr_en(mRMT_channel, true);
|
||||
|
||||
// -- Start the RMT TX operation
|
||||
rmt_tx_start(mRMT_channel, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void doneOnChannel(rmt_channel_t channel, void * arg)
|
||||
{
|
||||
ClocklessController * controller = static_cast<ClocklessController*>(gOnChannel[channel]);
|
||||
portBASE_TYPE HPTaskAwoken = 0;
|
||||
|
||||
// -- Turn off output on the pin
|
||||
gpio_matrix_out(controller->mPin, 0x100, 0, 0);
|
||||
|
||||
gOnChannel[channel] = NULL;
|
||||
gNumDone++;
|
||||
|
||||
if (gNumDone == gNumControllers) {
|
||||
// -- If this is the last controller, signal that we are all done
|
||||
xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken);
|
||||
if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR();
|
||||
} else {
|
||||
// -- Otherwise, if there are still controllers waiting, then
|
||||
// start the next one on this channel
|
||||
if (gNext < gNumControllers)
|
||||
startNext(channel);
|
||||
}
|
||||
=======
|
||||
// -- Only need to do this once
|
||||
if (gInitialized) return;
|
||||
|
||||
for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) {
|
||||
gOnChannel[i] = NULL;
|
||||
|
||||
// -- RMT configuration for transmission
|
||||
rmt_config_t rmt_tx;
|
||||
rmt_tx.channel = rmt_channel_t(i);
|
||||
rmt_tx.rmt_mode = RMT_MODE_TX;
|
||||
rmt_tx.gpio_num = mPin; // The particular pin will be assigned later
|
||||
rmt_tx.mem_block_num = 1;
|
||||
rmt_tx.clk_div = DIVIDER;
|
||||
rmt_tx.tx_config.loop_en = false;
|
||||
rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
|
||||
rmt_tx.tx_config.carrier_en = false;
|
||||
rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
|
||||
rmt_tx.tx_config.idle_output_en = true;
|
||||
|
||||
// -- Apply the configuration
|
||||
rmt_config(&rmt_tx);
|
||||
|
||||
if (FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
rmt_driver_install(rmt_channel_t(i), 0, 0);
|
||||
} else {
|
||||
// -- Set up the RMT to send 1/2 of the pulse buffer and then
|
||||
// generate an interrupt. When we get this interrupt we
|
||||
// fill the other half in preparation (kind of like double-buffering)
|
||||
rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, MAX_PULSES);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Create a semaphore to block execution until all the controllers are done
|
||||
if (gTX_sem == NULL) {
|
||||
gTX_sem = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(gTX_sem);
|
||||
}
|
||||
|
||||
if ( ! FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
// -- Allocate the interrupt if we have not done so yet. This
|
||||
// interrupt handler must work for all different kinds of
|
||||
// strips, so it delegates to the refill function for each
|
||||
// specific instantiation of ClocklessController.
|
||||
if (gRMT_intr_handle == NULL)
|
||||
esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, interruptHandler, 0, &gRMT_intr_handle);
|
||||
}
|
||||
|
||||
gInitialized = true;
|
||||
}
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels)
|
||||
{
|
||||
if (gNumStarted == 0) {
|
||||
// -- First controller: make sure everything is set up
|
||||
initRMT();
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
}
|
||||
|
||||
// -- Initialize the local state, save a pointer to the pixel
|
||||
// data. We need to make a copy because pixels is a local
|
||||
// variable in the calling function, and this data structure
|
||||
// needs to outlive this call to showPixels.
|
||||
|
||||
if (mPixels != NULL) delete mPixels;
|
||||
mPixels = new PixelController<RGB_ORDER>(pixels);
|
||||
|
||||
// -- Keep track of the number of strips we've seen
|
||||
gNumStarted++;
|
||||
|
||||
// -- The last call to showPixels is the one responsible for doing
|
||||
// all of the actual worl
|
||||
if (gNumStarted == gNumControllers) {
|
||||
gNext = 0;
|
||||
|
||||
// -- First, fill all the available channels
|
||||
int channel = 0;
|
||||
while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) {
|
||||
startNext(channel);
|
||||
channel++;
|
||||
}
|
||||
|
||||
// -- Wait here while the rest of the data is sent. The interrupt handler
|
||||
// will keep refilling the RMT buffers until it is all sent; then it
|
||||
// gives the semaphore back.
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
xSemaphoreGive(gTX_sem);
|
||||
|
||||
// -- Reset the counters
|
||||
gNumStarted = 0;
|
||||
gNumDone = 0;
|
||||
gNext = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Start up the next controller
|
||||
// This method is static so that it can dispatch to the appropriate
|
||||
// startOnChannel method of the given controller.
|
||||
static void startNext(int channel)
|
||||
{
|
||||
if (gNext < gNumControllers) {
|
||||
ClocklessController * pController = static_cast<ClocklessController*>(gControllers[gNext]);
|
||||
pController->startOnChannel(channel);
|
||||
gNext++;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void startOnChannel(int channel)
|
||||
{
|
||||
// -- Assign this channel and configure the RMT
|
||||
mRMT_channel = rmt_channel_t(channel);
|
||||
|
||||
// -- Store a reference to this controller, so we can get it
|
||||
// inside the interrupt handler
|
||||
gOnChannel[channel] = this;
|
||||
|
||||
// -- Assign the pin to this channel
|
||||
rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin);
|
||||
|
||||
if (FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
// -- Use the built-in RMT driver to send all the data in one shot
|
||||
rmt_register_tx_end_callback(doneOnChannel, 0);
|
||||
writeAllRMTItems();
|
||||
} else {
|
||||
// -- Use our custom driver to send the data incrementally
|
||||
|
||||
// -- Turn on the interrupts
|
||||
rmt_set_tx_intr_en(mRMT_channel, true);
|
||||
|
||||
// -- Initialize the counters that keep track of where we are in
|
||||
// the pixel data.
|
||||
mCurPulse = 0;
|
||||
mRGB_channel = 0;
|
||||
|
||||
// -- Fill both halves of the buffer
|
||||
fillHalfRMTBuffer();
|
||||
fillHalfRMTBuffer();
|
||||
|
||||
// -- Turn on the interrupts
|
||||
rmt_set_tx_intr_en(mRMT_channel, true);
|
||||
|
||||
// -- Start the RMT TX operation
|
||||
rmt_tx_start(mRMT_channel, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void doneOnChannel(rmt_channel_t channel, void * arg)
|
||||
{
|
||||
ClocklessController * controller = static_cast<ClocklessController*>(gOnChannel[channel]);
|
||||
portBASE_TYPE HPTaskAwoken = 0;
|
||||
|
||||
// -- Turn off output on the pin
|
||||
gpio_matrix_out(controller->mPin, 0x100, 0, 0);
|
||||
|
||||
gOnChannel[channel] = NULL;
|
||||
gNumDone++;
|
||||
|
||||
if (gNumDone == gNumControllers) {
|
||||
// -- If this is the last controller, signal that we are all done
|
||||
xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken);
|
||||
if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR();
|
||||
} else {
|
||||
// -- Otherwise, if there are still controllers waiting, then
|
||||
// start the next one on this channel
|
||||
if (gNext < gNumControllers)
|
||||
startNext(channel);
|
||||
}
|
||||
>>>>>>> upstream/master
|
||||
}
|
||||
|
||||
static IRAM_ATTR void interruptHandler(void *arg)
|
||||
{
|
||||
// -- The basic structure of this code is borrowed from the
|
||||
// interrupt handler in esp-idf/components/driver/rmt.c
|
||||
uint32_t intr_st = RMT.int_st.val;
|
||||
uint8_t channel;
|
||||
|
||||
for (channel = 0; channel < FASTLED_RMT_MAX_CHANNELS; channel++) {
|
||||
int tx_done_bit = channel * 3;
|
||||
int tx_next_bit = channel + 24;
|
||||
|
||||
if (gOnChannel[channel] != NULL) {
|
||||
|
||||
<<<<<<< HEAD
|
||||
ClocklessController * controller = static_cast<ClocklessController*>(gOnChannel[channel]);
|
||||
|
||||
// -- More to send on this channel
|
||||
if (intr_st & BIT(tx_next_bit)) {
|
||||
RMT.int_clr.val |= BIT(tx_next_bit);
|
||||
|
||||
// -- Refill the half of the buffer that we just finished,
|
||||
// allowing the other half to proceed.
|
||||
controller->fillHalfRMTBuffer();
|
||||
}
|
||||
|
||||
// -- Transmission is complete on this channel
|
||||
if (intr_st & BIT(tx_done_bit)) {
|
||||
RMT.int_clr.val |= BIT(tx_done_bit);
|
||||
doneOnChannel(rmt_channel_t(channel), 0);
|
||||
=======
|
||||
ClocklessController * controller = static_cast<ClocklessController*>(gOnChannel[channel]);
|
||||
|
||||
// -- More to send on this channel
|
||||
if (intr_st & BIT(tx_next_bit)) {
|
||||
RMT.int_clr.val |= BIT(tx_next_bit);
|
||||
|
||||
// -- Refill the half of the buffer that we just finished,
|
||||
// allowing the other half to proceed.
|
||||
controller->fillHalfRMTBuffer();
|
||||
}
|
||||
|
||||
// -- Transmission is complete on this channel
|
||||
if (intr_st & BIT(tx_done_bit)) {
|
||||
RMT.int_clr.val |= BIT(tx_done_bit);
|
||||
doneOnChannel(rmt_channel_t(channel), 0);
|
||||
>>>>>>> upstream/master
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void fillHalfRMTBuffer()
|
||||
{
|
||||
// -- Fill half of the RMT pulse buffer
|
||||
|
||||
// The buffer holds 64 total pulse items, so this loop converts
|
||||
// as many pixels as can fit in half of the buffer (MAX_PULSES =
|
||||
// 32 items). In our case, each pixel consists of three bytes,
|
||||
// each bit turns into one pulse item -- 24 items per pixel. So,
|
||||
// each half of the buffer can hold 1 and 1/3 of a pixel.
|
||||
|
||||
// The member variable mCurPulse keeps track of which of the 64
|
||||
// items we are writing. During the first call to this method it
|
||||
// fills 0-31; in the second call it fills 32-63, and then wraps
|
||||
// back around to zero.
|
||||
|
||||
// When we run out of pixel data, just fill the remaining items
|
||||
// with zero pulses.
|
||||
|
||||
uint16_t pulse_count = 0; // Ranges from 0-31 (half a buffer)
|
||||
uint32_t byteval = 0;
|
||||
uint32_t one_val = mOne.val;
|
||||
uint32_t zero_val = mZero.val;
|
||||
bool done_strip = false;
|
||||
|
||||
while (pulse_count < MAX_PULSES) {
|
||||
if (! mPixels->has(1)) {
|
||||
<<<<<<< HEAD
|
||||
if (mCurPulse > 0) {
|
||||
// -- Extend the last pulse to force the strip to latch. Honestly, I'm not
|
||||
// sure if this is really necessary.
|
||||
// RMTMEM.chan[mRMT_channel].data32[mCurPulse-1].duration1 = RMT_RESET_DURATION;
|
||||
}
|
||||
=======
|
||||
>>>>>>> upstream/master
|
||||
done_strip = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// -- Cycle through the R,G, and B values in the right order
|
||||
switch (mRGB_channel) {
|
||||
case 0:
|
||||
byteval = mPixels->loadAndScale0();
|
||||
mRGB_channel = 1;
|
||||
break;
|
||||
case 1:
|
||||
byteval = mPixels->loadAndScale1();
|
||||
mRGB_channel = 2;
|
||||
break;
|
||||
case 2:
|
||||
byteval = mPixels->loadAndScale2();
|
||||
mPixels->advanceData();
|
||||
mPixels->stepDithering();
|
||||
mRGB_channel = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
byteval <<= 24;
|
||||
// Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the
|
||||
// rmt_item32_t value corresponding to the buffered bit value
|
||||
for (register uint32_t j = 0; j < 8; j++) {
|
||||
uint32_t val = (byteval & 0x80000000L) ? one_val : zero_val;
|
||||
RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = val;
|
||||
byteval <<= 1;
|
||||
mCurPulse++;
|
||||
pulse_count++;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
if (done_strip)
|
||||
RMTMEM.chan[mRMT_channel].data32[mCurPulse-1].duration1 = RMT_RESET_DURATION;
|
||||
>>>>>>> upstream/master
|
||||
}
|
||||
|
||||
if (done_strip) {
|
||||
// -- And fill the remaining items with zero pulses. The zero values triggers
|
||||
// the tx_done interrupt.
|
||||
while (pulse_count < MAX_PULSES) {
|
||||
RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = 0;
|
||||
mCurPulse++;
|
||||
pulse_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// -- When we have filled the back half the buffer, reset the position to the first half
|
||||
if (mCurPulse >= MAX_PULSES*2)
|
||||
mCurPulse = 0;
|
||||
}
|
||||
|
||||
virtual void writeAllRMTItems()
|
||||
{
|
||||
// -- Compute the pulse values for the whole strip at once.
|
||||
// Requires a large buffer
|
||||
<<<<<<< HEAD
|
||||
mBufferSize = mPixels->size() * 3 * 8;
|
||||
=======
|
||||
mBufferSize = mPixels->size() * 3 * 8;
|
||||
>>>>>>> upstream/master
|
||||
|
||||
// TODO: need a specific number here
|
||||
if (mBuffer == NULL) {
|
||||
mBuffer = (rmt_item32_t *) calloc( mBufferSize, sizeof(rmt_item32_t));
|
||||
}
|
||||
|
||||
mCurPulse = 0;
|
||||
mRGB_channel = 0;
|
||||
uint32_t byteval = 0;
|
||||
while (mPixels->has(1)) {
|
||||
// -- Cycle through the R,G, and B values in the right order
|
||||
switch (mRGB_channel) {
|
||||
case 0:
|
||||
byteval = mPixels->loadAndScale0();
|
||||
mRGB_channel = 1;
|
||||
break;
|
||||
case 1:
|
||||
byteval = mPixels->loadAndScale1();
|
||||
mRGB_channel = 2;
|
||||
break;
|
||||
case 2:
|
||||
byteval = mPixels->loadAndScale2();
|
||||
mPixels->advanceData();
|
||||
mPixels->stepDithering();
|
||||
mRGB_channel = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
byteval <<= 24;
|
||||
// Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the
|
||||
// rmt_item32_t value corresponding to the buffered bit value
|
||||
for (register uint32_t j = 0; j < 8; j++) {
|
||||
mBuffer[mCurPulse] = (byteval & 0x80000000L) ? mOne : mZero;
|
||||
byteval <<= 1;
|
||||
mCurPulse++;
|
||||
}
|
||||
}
|
||||
|
||||
mBuffer[mCurPulse-1].duration1 = RMT_RESET_DURATION;
|
||||
assert(mCurPulse == mBufferSize);
|
||||
|
||||
<<<<<<< HEAD
|
||||
rmt_write_items(mRMT_channel, mBuffer, mBufferSize, false);
|
||||
=======
|
||||
rmt_write_items(mRMT_channel, mBuffer, mBufferSize, false);
|
||||
>>>>>>> upstream/master
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
767
libraries/FastLED/platforms/esp/32/clockless_i2s_esp32.h
Normal file
767
libraries/FastLED/platforms/esp/32/clockless_i2s_esp32.h
Normal file
@@ -0,0 +1,767 @@
|
||||
/*
|
||||
* I2S Driver
|
||||
*
|
||||
* Copyright (c) 2019 Yves Bazin
|
||||
* Copyright (c) 2019 Samuel Z. Guyer
|
||||
* Derived from lots of code examples from other people.
|
||||
*
|
||||
* The I2S implementation can drive up to 24 strips in parallel, but
|
||||
* with the following limitation: all the strips must have the same
|
||||
* timing (i.e., they must all use the same chip).
|
||||
*
|
||||
* To enable the I2S driver, add the following line *before* including
|
||||
* FastLED.h (no other changes are necessary):
|
||||
*
|
||||
* #define FASTLED_ESP32_I2S true
|
||||
*
|
||||
* The overall strategy is to use the parallel mode of the I2S "audio"
|
||||
* peripheral to send up to 24 bits in parallel to 24 different pins.
|
||||
* Unlike the RMT peripheral the I2S system cannot send bits of
|
||||
* different lengths. Instead, we set the I2S data clock fairly high
|
||||
* and then encode a signal as a series of bits.
|
||||
*
|
||||
* For example, with a clock divider of 10 the data clock will be
|
||||
* 8MHz, so each bit is 125ns. The WS2812 expects a "1" bit to be
|
||||
* encoded as a HIGH signal for around 875ns, followed by LOW for
|
||||
* 375ns. Sending the following pattern results in the right shape
|
||||
* signal:
|
||||
*
|
||||
* 1111111000 WS2812 "1" bit encoded as 10 125ns pulses
|
||||
*
|
||||
* The I2S peripheral expects the bits for all 24 outputs to be packed
|
||||
* into a single 32-bit word. The complete signal is a series of these
|
||||
* 32-bit values -- one for each bit for each strip. The pixel data,
|
||||
* however, is stored "serially" as a series of RGB values separately
|
||||
* for each strip. To prepare the data we need to do three things: (1)
|
||||
* take 1 pixel from each strip, and (2) tranpose the bits so that
|
||||
* they are in the parallel form, (3) translate each data bit into the
|
||||
* bit pattern that encodes the signal for that bit. This code is in
|
||||
* the fillBuffer() method:
|
||||
*
|
||||
* 1. Read 1 pixel from each strip into an array; store this data by
|
||||
* color channel (e.g., all the red bytes, then all the green
|
||||
* bytes, then all the blue bytes). For three color channels, the
|
||||
* array is 3 X 24 X 8 bits.
|
||||
*
|
||||
* 2. Tranpose the array so that it is 3 X 8 X 24 bits. The hardware
|
||||
* wants the data in 32-bit chunks, so the actual form is 3 X 8 X
|
||||
* 32, with the low 8 bits unused.
|
||||
*
|
||||
* 3. Take each group of 24 parallel bits and "expand" them into a
|
||||
* pattern according to the encoding. For example, with a 8MHz
|
||||
* data clock, each data bit turns into 10 I2s pulses, so 24
|
||||
* parallel data bits turn into 10 X 24 pulses.
|
||||
*
|
||||
* We send data to the I2S peripheral using the DMA interface. We use
|
||||
* two DMA buffers, so that we can fill one buffer while the other
|
||||
* buffer is being sent. Each DMA buffer holds the fully-expanded
|
||||
* pulse pattern for one pixel on up to 24 strips. The exact amount of
|
||||
* memory required depends on the number of color channels and the
|
||||
* number of pulses used to encode each bit.
|
||||
*
|
||||
* We get an interrupt each time a buffer is sent; we then fill that
|
||||
* buffer while the next one is being sent. The DMA interface allows
|
||||
* us to configure the buffers as a circularly linked list, so that it
|
||||
* can automatically start on the next buffer.
|
||||
*/
|
||||
/*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma message "NOTE: ESP32 support using I2S parallel driver. All strips must use the same chipset"
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/i2s_reg.h"
|
||||
#include "soc/i2s_struct.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "rom/lldesc.h"
|
||||
#include "esp_intr.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
|
||||
uint32_t cyc;
|
||||
__asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc));
|
||||
return cyc;
|
||||
}
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
#define NUM_COLOR_CHANNELS 3
|
||||
|
||||
// -- Choose which I2S device to use
|
||||
#ifndef I2S_DEVICE
|
||||
#define I2S_DEVICE 0
|
||||
#endif
|
||||
|
||||
// -- Max number of controllers we can support
|
||||
#ifndef FASTLED_I2S_MAX_CONTROLLERS
|
||||
#define FASTLED_I2S_MAX_CONTROLLERS 24
|
||||
#endif
|
||||
|
||||
// -- I2S clock
|
||||
#define I2S_BASE_CLK (80000000L)
|
||||
#define I2S_MAX_CLK (20000000L) //more tha a certain speed and the I2s looses some bits
|
||||
#define I2S_MAX_PULSE_PER_BIT 20 //put it higher to get more accuracy but it could decrease the refresh rate without real improvement
|
||||
// -- Convert ESP32 cycles back into nanoseconds
|
||||
#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ)
|
||||
|
||||
// -- Array of all controllers
|
||||
static CLEDController * gControllers[FASTLED_I2S_MAX_CONTROLLERS];
|
||||
static int gNumControllers = 0;
|
||||
static int gNumStarted = 0;
|
||||
|
||||
// -- Global semaphore for the whole show process
|
||||
// Semaphore is not given until all data has been sent
|
||||
static xSemaphoreHandle gTX_sem = NULL;
|
||||
|
||||
// -- One-time I2S initialization
|
||||
static bool gInitialized = false;
|
||||
|
||||
// -- Interrupt handler
|
||||
static intr_handle_t gI2S_intr_handle = NULL;
|
||||
|
||||
// -- A pointer to the memory-mapped structure: I2S0 or I2S1
|
||||
static i2s_dev_t * i2s;
|
||||
|
||||
// -- I2S goes to these pins until we remap them using the GPIO matrix
|
||||
static int i2s_base_pin_index;
|
||||
|
||||
// --- I2S DMA buffers
|
||||
struct DMABuffer {
|
||||
lldesc_t descriptor;
|
||||
uint8_t * buffer;
|
||||
};
|
||||
|
||||
#define NUM_DMA_BUFFERS 2
|
||||
static DMABuffer * dmaBuffers[NUM_DMA_BUFFERS];
|
||||
|
||||
// -- Bit patterns
|
||||
// For now, we require all strips to be the same chipset, so these
|
||||
// are global variables.
|
||||
|
||||
static int gPulsesPerBit = 0;
|
||||
static uint32_t gOneBit[40] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
static uint32_t gZeroBit[40] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
|
||||
// -- Counters to track progress
|
||||
static int gCurBuffer = 0;
|
||||
static bool gDoneFilling = false;
|
||||
static int ones_for_one;
|
||||
static int ones_for_zero;
|
||||
|
||||
// -- Temp buffers for pixels and bits being formatted for DMA
|
||||
static uint8_t gPixelRow[NUM_COLOR_CHANNELS][32];
|
||||
static uint8_t gPixelBits[NUM_COLOR_CHANNELS][8][4];
|
||||
static int CLOCK_DIVIDER_N;
|
||||
static int CLOCK_DIVIDER_A;
|
||||
static int CLOCK_DIVIDER_B;
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 5>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER>
|
||||
{
|
||||
// -- Store the GPIO pin
|
||||
gpio_num_t mPin;
|
||||
|
||||
// -- This instantiation forces a check on the pin choice
|
||||
FastPin<DATA_PIN> mFastPin;
|
||||
|
||||
// -- Save the pixel controller
|
||||
PixelController<RGB_ORDER> * mPixels;
|
||||
|
||||
public:
|
||||
|
||||
void init()
|
||||
{
|
||||
i2sInit();
|
||||
|
||||
// -- Allocate space to save the pixel controller
|
||||
// during parallel output
|
||||
mPixels = (PixelController<RGB_ORDER> *) malloc(sizeof(PixelController<RGB_ORDER>));
|
||||
|
||||
gControllers[gNumControllers] = this;
|
||||
int my_index = gNumControllers;
|
||||
gNumControllers++;
|
||||
|
||||
// -- Set up the pin We have to do two things: configure the
|
||||
// actual GPIO pin, and route the output from the default
|
||||
// pin (determined by the I2S device) to the pin we
|
||||
// want. We compute the default pin using the index of this
|
||||
// controller in the array. This order is crucial because
|
||||
// the bits must go into the DMA buffer in the same order.
|
||||
mPin = gpio_num_t(DATA_PIN);
|
||||
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DATA_PIN], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(mPin, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
|
||||
pinMode(mPin,OUTPUT);
|
||||
gpio_matrix_out(mPin, i2s_base_pin_index + my_index, false, false);
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
static int pgcd(int smallest,int precision,int a,int b,int c)
|
||||
{
|
||||
int pgc_=1;
|
||||
for( int i=smallest;i>0;i--)
|
||||
{
|
||||
|
||||
if( a%i<=precision && b%i<=precision && c%i<=precision)
|
||||
{
|
||||
pgc_=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pgc_;
|
||||
}
|
||||
|
||||
/** Compute pules/bit patterns
|
||||
*
|
||||
* This is Yves Bazin's mad code for computing the pulse pattern
|
||||
* and clock timing given the target signal given by T1, T2, and
|
||||
* T3. In general, these parameters are interpreted as follows:
|
||||
*
|
||||
* a "1" bit is encoded by setting the pin HIGH to T1+T2 ns, then LOW for T3 ns
|
||||
* a "0" bit is encoded by setting the pin HIGH to T1 ns, then LOW for T2+T3 ns
|
||||
*
|
||||
*/
|
||||
static void initBitPatterns()
|
||||
{
|
||||
// Precompute the bit patterns based on the I2S sample rate
|
||||
// Serial.println("Setting up fastled using I2S");
|
||||
|
||||
// -- First, convert back to ns from CPU clocks
|
||||
uint32_t T1ns = ESPCLKS_TO_NS(T1);
|
||||
uint32_t T2ns = ESPCLKS_TO_NS(T2);
|
||||
uint32_t T3ns = ESPCLKS_TO_NS(T3);
|
||||
|
||||
// Serial.print("T1 = "); Serial.print(T1); Serial.print(" ns "); Serial.println(T1ns);
|
||||
// Serial.print("T2 = "); Serial.print(T2); Serial.print(" ns "); Serial.println(T2ns);
|
||||
// Serial.print("T3 = "); Serial.print(T3); Serial.print(" ns "); Serial.println(T3ns);
|
||||
|
||||
/*
|
||||
We calculate the best pcgd to the timing
|
||||
ie
|
||||
WS2811 77 77 154 => 1 1 2 => nb pulses= 4
|
||||
WS2812 60 150 90 => 2 5 3 => nb pulses=10
|
||||
*/
|
||||
int smallest=0;
|
||||
if (T1>T2)
|
||||
smallest=T2;
|
||||
else
|
||||
smallest=T1;
|
||||
if(smallest>T3)
|
||||
smallest=T3;
|
||||
double freq=(double)1/(double)(T1ns + T2ns + T3ns);
|
||||
// Serial.printf("chipset frequency:%f Khz\n", 1000000L*freq);
|
||||
// Serial.printf("smallest %d\n",smallest);
|
||||
int pgc_=1;
|
||||
int precision=0;
|
||||
pgc_=pgcd(smallest,precision,T1,T2,T3);
|
||||
//Serial.printf("%f\n",I2S_MAX_CLK/(1000000000L*freq));
|
||||
while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_PULSE_PER_BIT) //while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_CLK/(1000000000L*freq))
|
||||
{
|
||||
precision++;
|
||||
pgc_=pgcd(smallest,precision,T1,T2,T3);
|
||||
//Serial.printf("%d %d\n",pgc_,(a+b+c)/pgc_);
|
||||
}
|
||||
pgc_=pgcd(smallest,precision,T1,T2,T3);
|
||||
// Serial.printf("pgcd %d precision:%d\n",pgc_,precision);
|
||||
// Serial.printf("nb pulse per bit:%d\n",T1/pgc_ +T2/pgc_ +T3/pgc_);
|
||||
gPulsesPerBit=(int)T1/pgc_ +(int)T2/pgc_ +(int)T3/pgc_;
|
||||
/*
|
||||
we calculate the duration of one pulse nd htre base frequency of the led
|
||||
ie WS2812B F=1/(250+625+375)=800kHz or 1250ns
|
||||
as we need 10 pulses each pulse is 125ns => frequency 800Khz*10=8MHz
|
||||
WS2811 T=320+320+641=1281ns qnd we need 4 pulses => pulse duration 320.25ns =>frequency 3.1225605Mhz
|
||||
|
||||
*/
|
||||
|
||||
freq=1000000000L*freq*gPulsesPerBit;
|
||||
// Serial.printf("needed frequency (nbpiulse per bit)*(chispset frequency):%f Mhz\n",freq/1000000);
|
||||
|
||||
/*
|
||||
we do calculate the needed N a and b
|
||||
as f=basefred/(N+b/a);
|
||||
as a is max 63 the precision for the decimal is 1/63
|
||||
|
||||
*/
|
||||
|
||||
CLOCK_DIVIDER_N=(int)((double)I2S_BASE_CLK/freq);
|
||||
double v=I2S_BASE_CLK/freq-CLOCK_DIVIDER_N;
|
||||
|
||||
double prec=(double)1/63;
|
||||
int a=1;
|
||||
int b=0;
|
||||
CLOCK_DIVIDER_A=1;
|
||||
CLOCK_DIVIDER_B=0;
|
||||
for(a=1;a<64;a++)
|
||||
{
|
||||
for(b=0;b<a;b++)
|
||||
{
|
||||
//printf("%d %d %f %f %f\n",b,a,v,(double)v*(double)a,fabsf(v-(double)b/a));
|
||||
if(fabsf(v-(double)b/a) <= prec/2)
|
||||
break;
|
||||
}
|
||||
if(fabsf(v-(double)b/a) ==0)
|
||||
{
|
||||
CLOCK_DIVIDER_A=a;
|
||||
CLOCK_DIVIDER_B=b;
|
||||
break;
|
||||
}
|
||||
if(fabsf(v-(double)b/a) < prec/2)
|
||||
{
|
||||
if (fabsf(v-(double)b/a) <fabsf(v-(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A))
|
||||
{
|
||||
CLOCK_DIVIDER_A=a;
|
||||
CLOCK_DIVIDER_B=b;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//top take care of an issue with double 0.9999999999
|
||||
if(CLOCK_DIVIDER_A==CLOCK_DIVIDER_B)
|
||||
{
|
||||
CLOCK_DIVIDER_A=1;
|
||||
CLOCK_DIVIDER_B=0;
|
||||
CLOCK_DIVIDER_N++;
|
||||
}
|
||||
|
||||
//printf("%d %d %f %f %d\n",CLOCK_DIVIDER_B,CLOCK_DIVIDER_A,(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A,v,CLOCK_DIVIDER_N);
|
||||
//Serial.printf("freq %f %f\n",freq,I2S_BASE_CLK/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A));
|
||||
freq=1/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A);
|
||||
freq=freq*I2S_BASE_CLK;
|
||||
// Serial.printf("calculted for i2s frequency:%f Mhz N:%d B:%d A:%d\n",freq/1000000,CLOCK_DIVIDER_N,CLOCK_DIVIDER_B,CLOCK_DIVIDER_A);
|
||||
double pulseduration=1000000000/freq;
|
||||
// Serial.printf("Pulse duration: %f ns\n",pulseduration);
|
||||
// gPulsesPerBit = (T1ns + T2ns + T3ns)/FASTLED_I2S_NS_PER_PULSE;
|
||||
|
||||
//Serial.print("Pulses per bit: "); Serial.println(gPulsesPerBit);
|
||||
|
||||
//int ones_for_one = ((T1ns + T2ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1;
|
||||
ones_for_one = T1/pgc_ +T2/pgc_;
|
||||
//Serial.print("One bit: target ");
|
||||
//Serial.print(T1ns+T2ns); Serial.print("ns --- ");
|
||||
//Serial.print(ones_for_one); Serial.print(" 1 bits");
|
||||
//Serial.print(" = "); Serial.print(ones_for_one * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns");
|
||||
// Serial.printf("one bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns+T2ns,ones_for_one ,ones_for_one*pulseduration);
|
||||
|
||||
|
||||
int i = 0;
|
||||
while ( i < ones_for_one ) {
|
||||
gOneBit[i] = 0xFFFFFF00;
|
||||
i++;
|
||||
}
|
||||
while ( i < gPulsesPerBit ) {
|
||||
gOneBit[i] = 0x00000000;
|
||||
i++;
|
||||
}
|
||||
|
||||
//int ones_for_zero = ((T1ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1;
|
||||
ones_for_zero =T1/pgc_ ;
|
||||
// Serial.print("Zero bit: target ");
|
||||
// Serial.print(T1ns); Serial.print("ns --- ");
|
||||
//Serial.print(ones_for_zero); Serial.print(" 1 bits");
|
||||
//Serial.print(" = "); Serial.print(ones_for_zero * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns");
|
||||
// Serial.printf("Zero bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns,ones_for_zero ,ones_for_zero*pulseduration);
|
||||
i = 0;
|
||||
while ( i < ones_for_zero ) {
|
||||
gZeroBit[i] = 0xFFFFFF00;
|
||||
i++;
|
||||
}
|
||||
while ( i < gPulsesPerBit ) {
|
||||
gZeroBit[i] = 0x00000000;
|
||||
i++;
|
||||
}
|
||||
|
||||
memset(gPixelRow, 0, NUM_COLOR_CHANNELS * 32);
|
||||
memset(gPixelBits, 0, NUM_COLOR_CHANNELS * 32);
|
||||
}
|
||||
|
||||
static DMABuffer * allocateDMABuffer(int bytes)
|
||||
{
|
||||
DMABuffer * b = (DMABuffer *)heap_caps_malloc(sizeof(DMABuffer), MALLOC_CAP_DMA);
|
||||
|
||||
b->buffer = (uint8_t *)heap_caps_malloc(bytes, MALLOC_CAP_DMA);
|
||||
memset(b->buffer, 0, bytes);
|
||||
|
||||
b->descriptor.length = bytes;
|
||||
b->descriptor.size = bytes;
|
||||
b->descriptor.owner = 1;
|
||||
b->descriptor.sosf = 1;
|
||||
b->descriptor.buf = b->buffer;
|
||||
b->descriptor.offset = 0;
|
||||
b->descriptor.empty = 0;
|
||||
b->descriptor.eof = 1;
|
||||
b->descriptor.qe.stqe_next = 0;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static void i2sInit()
|
||||
{
|
||||
// -- Only need to do this once
|
||||
if (gInitialized) return;
|
||||
|
||||
// -- Construct the bit patterns for ones and zeros
|
||||
initBitPatterns();
|
||||
|
||||
// -- Choose whether to use I2S device 0 or device 1
|
||||
// Set up the various device-specific parameters
|
||||
int interruptSource;
|
||||
if (I2S_DEVICE == 0) {
|
||||
i2s = &I2S0;
|
||||
periph_module_enable(PERIPH_I2S0_MODULE);
|
||||
interruptSource = ETS_I2S0_INTR_SOURCE;
|
||||
i2s_base_pin_index = I2S0O_DATA_OUT0_IDX;
|
||||
} else {
|
||||
i2s = &I2S1;
|
||||
periph_module_enable(PERIPH_I2S1_MODULE);
|
||||
interruptSource = ETS_I2S1_INTR_SOURCE;
|
||||
i2s_base_pin_index = I2S1O_DATA_OUT0_IDX;
|
||||
}
|
||||
|
||||
// -- Reset everything
|
||||
i2sReset();
|
||||
i2sReset_DMA();
|
||||
i2sReset_FIFO();
|
||||
|
||||
// -- Main configuration
|
||||
i2s->conf.tx_msb_right = 1;
|
||||
i2s->conf.tx_mono = 0;
|
||||
i2s->conf.tx_short_sync = 0;
|
||||
i2s->conf.tx_msb_shift = 0;
|
||||
i2s->conf.tx_right_first = 1; // 0;//1;
|
||||
i2s->conf.tx_slave_mod = 0;
|
||||
|
||||
// -- Set parallel mode
|
||||
i2s->conf2.val = 0;
|
||||
i2s->conf2.lcd_en = 1;
|
||||
i2s->conf2.lcd_tx_wrx2_en = 0; // 0 for 16 or 32 parallel output
|
||||
i2s->conf2.lcd_tx_sdx2_en = 0; // HN
|
||||
|
||||
// -- Set up the clock rate and sampling
|
||||
i2s->sample_rate_conf.val = 0;
|
||||
i2s->sample_rate_conf.tx_bits_mod = 32; // Number of parallel bits/pins
|
||||
i2s->sample_rate_conf.tx_bck_div_num = 1;
|
||||
i2s->clkm_conf.val = 0;
|
||||
i2s->clkm_conf.clka_en = 0;
|
||||
|
||||
// -- Data clock is computed as Base/(div_num + (div_b/div_a))
|
||||
// Base is 80Mhz, so 80/(10 + 0/1) = 8Mhz
|
||||
// One cycle is 125ns
|
||||
i2s->clkm_conf.clkm_div_a = CLOCK_DIVIDER_A;
|
||||
i2s->clkm_conf.clkm_div_b = CLOCK_DIVIDER_B;
|
||||
i2s->clkm_conf.clkm_div_num = CLOCK_DIVIDER_N;
|
||||
|
||||
i2s->fifo_conf.val = 0;
|
||||
i2s->fifo_conf.tx_fifo_mod_force_en = 1;
|
||||
i2s->fifo_conf.tx_fifo_mod = 3; // 32-bit single channel data
|
||||
i2s->fifo_conf.tx_data_num = 32; // fifo length
|
||||
i2s->fifo_conf.dscr_en = 1; // fifo will use dma
|
||||
|
||||
i2s->conf1.val = 0;
|
||||
i2s->conf1.tx_stop_en = 0;
|
||||
i2s->conf1.tx_pcm_bypass = 1;
|
||||
|
||||
i2s->conf_chan.val = 0;
|
||||
i2s->conf_chan.tx_chan_mod = 1; // Mono mode, with tx_msb_right = 1, everything goes to right-channel
|
||||
|
||||
i2s->timing.val = 0;
|
||||
|
||||
// -- Allocate two DMA buffers
|
||||
dmaBuffers[0] = allocateDMABuffer(32 * NUM_COLOR_CHANNELS * gPulsesPerBit);
|
||||
dmaBuffers[1] = allocateDMABuffer(32 * NUM_COLOR_CHANNELS * gPulsesPerBit);
|
||||
|
||||
// -- Arrange them as a circularly linked list
|
||||
dmaBuffers[0]->descriptor.qe.stqe_next = &(dmaBuffers[1]->descriptor);
|
||||
dmaBuffers[1]->descriptor.qe.stqe_next = &(dmaBuffers[0]->descriptor);
|
||||
|
||||
// -- Allocate i2s interrupt
|
||||
SET_PERI_REG_BITS(I2S_INT_ENA_REG(I2S_DEVICE), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S);
|
||||
esp_err_t e = esp_intr_alloc(interruptSource, 0, // ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3,
|
||||
&interruptHandler, 0, &gI2S_intr_handle);
|
||||
|
||||
// -- Create a semaphore to block execution until all the controllers are done
|
||||
if (gTX_sem == NULL) {
|
||||
gTX_sem = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(gTX_sem);
|
||||
}
|
||||
|
||||
// Serial.println("Init I2S");
|
||||
gInitialized = true;
|
||||
}
|
||||
|
||||
/** Clear DMA buffer
|
||||
*
|
||||
* Yves' clever trick: initialize the bits that we know must be 0
|
||||
* or 1 regardless of what bit they encode.
|
||||
*/
|
||||
static void empty( uint32_t *buf)
|
||||
{
|
||||
for(int i=0;i<8*NUM_COLOR_CHANNELS;i++)
|
||||
{
|
||||
int offset=gPulsesPerBit*i;
|
||||
for(int j=0;j<ones_for_zero;j++)
|
||||
buf[offset+j]=0xffffffff;
|
||||
|
||||
for(int j=ones_for_one;j<gPulsesPerBit;j++)
|
||||
buf[offset+j]=0;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Show pixels
|
||||
// This is the main entry point for the controller.
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels)
|
||||
{
|
||||
if (gNumStarted == 0) {
|
||||
// -- First controller: make sure everything is set up
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
}
|
||||
|
||||
// -- Initialize the local state, save a pointer to the pixel
|
||||
// data. We need to make a copy because pixels is a local
|
||||
// variable in the calling function, and this data structure
|
||||
// needs to outlive this call to showPixels.
|
||||
(*mPixels) = pixels;
|
||||
|
||||
// -- Keep track of the number of strips we've seen
|
||||
gNumStarted++;
|
||||
|
||||
// Serial.print("Show pixels ");
|
||||
// Serial.println(gNumStarted);
|
||||
|
||||
// -- The last call to showPixels is the one responsible for doing
|
||||
// all of the actual work
|
||||
if (gNumStarted == gNumControllers) {
|
||||
empty((uint32_t*)dmaBuffers[0]->buffer);
|
||||
empty((uint32_t*)dmaBuffers[1]->buffer);
|
||||
gCurBuffer = 0;
|
||||
gDoneFilling = false;
|
||||
|
||||
// -- Prefill both buffers
|
||||
fillBuffer();
|
||||
fillBuffer();
|
||||
|
||||
i2sStart();
|
||||
|
||||
// -- Wait here while the rest of the data is sent. The interrupt handler
|
||||
// will keep refilling the DMA buffers until it is all sent; then it
|
||||
// gives the semaphore back.
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
xSemaphoreGive(gTX_sem);
|
||||
|
||||
i2sStop();
|
||||
|
||||
// -- Reset the counters
|
||||
gNumStarted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Custom interrupt handler
|
||||
static IRAM_ATTR void interruptHandler(void *arg)
|
||||
{
|
||||
if (i2s->int_st.out_eof) {
|
||||
i2s->int_clr.val = i2s->int_raw.val;
|
||||
|
||||
if ( ! gDoneFilling) {
|
||||
fillBuffer();
|
||||
} else {
|
||||
portBASE_TYPE HPTaskAwoken = 0;
|
||||
xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken);
|
||||
if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Fill DMA buffer
|
||||
*
|
||||
* This is where the real work happens: take a row of pixels (one
|
||||
* from each strip), transpose and encode the bits, and store
|
||||
* them in the DMA buffer for the I2S peripheral to read.
|
||||
*/
|
||||
static void fillBuffer()
|
||||
{
|
||||
// -- Alternate between buffers
|
||||
volatile uint32_t * buf = (uint32_t *) dmaBuffers[gCurBuffer]->buffer;
|
||||
gCurBuffer = (gCurBuffer + 1) % NUM_DMA_BUFFERS;
|
||||
|
||||
// -- Get the requested pixel from each controller. Store the
|
||||
// data for each color channel in a separate array.
|
||||
uint32_t has_data_mask = 0;
|
||||
for (int i = 0; i < gNumControllers; i++) {
|
||||
// -- Store the pixels in reverse controller order starting at index 23
|
||||
// This causes the bits to come out in the right position after we
|
||||
// transpose them.
|
||||
int bit_index = 23-i;
|
||||
ClocklessController * pController = static_cast<ClocklessController*>(gControllers[i]);
|
||||
if (pController->mPixels->has(1)) {
|
||||
gPixelRow[0][bit_index] = pController->mPixels->loadAndScale0();
|
||||
gPixelRow[1][bit_index] = pController->mPixels->loadAndScale1();
|
||||
gPixelRow[2][bit_index] = pController->mPixels->loadAndScale2();
|
||||
pController->mPixels->advanceData();
|
||||
pController->mPixels->stepDithering();
|
||||
|
||||
// -- Record that this controller still has data to send
|
||||
has_data_mask |= (1 << (i+8));
|
||||
}
|
||||
}
|
||||
|
||||
// -- None of the strips has data? We are done.
|
||||
if (has_data_mask == 0) {
|
||||
gDoneFilling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// -- Transpose and encode the pixel data for the DMA buffer
|
||||
int buf_index = 0;
|
||||
for (int channel = 0; channel < NUM_COLOR_CHANNELS; channel++) {
|
||||
|
||||
// -- Tranpose each array: all the bit 7's, then all the bit 6's, ...
|
||||
transpose32(gPixelRow[channel], gPixelBits[channel][0] );
|
||||
|
||||
//Serial.print("Channel: "); Serial.print(channel); Serial.print(" ");
|
||||
for (int bitnum = 0; bitnum < 8; bitnum++) {
|
||||
uint8_t * row = (uint8_t *) (gPixelBits[channel][bitnum]);
|
||||
uint32_t bit = (row[0] << 24) | (row[1] << 16) | (row[2] << 8) | row[3];
|
||||
|
||||
/* SZG: More general, but too slow:
|
||||
for (int pulse_num = 0; pulse_num < gPulsesPerBit; pulse_num++) {
|
||||
buf[buf_index++] = has_data_mask & ( (bit & gOneBit[pulse_num]) | (~bit & gZeroBit[pulse_num]) );
|
||||
}
|
||||
*/
|
||||
|
||||
// -- Only fill in the pulses that are different between the "0" and "1" encodings
|
||||
for(int pulse_num = ones_for_zero; pulse_num < ones_for_one; pulse_num++) {
|
||||
buf[bitnum*gPulsesPerBit+channel*8*gPulsesPerBit+pulse_num] = has_data_mask & bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void transpose32(uint8_t * pixels, uint8_t * bits)
|
||||
{
|
||||
transpose8rS32(& pixels[0], 1, 4, & bits[0]);
|
||||
transpose8rS32(& pixels[8], 1, 4, & bits[1]);
|
||||
transpose8rS32(& pixels[16], 1, 4, & bits[2]);
|
||||
//transpose8rS32(& pixels[24], 1, 4, & bits[3]); Can only use 24 bits
|
||||
}
|
||||
|
||||
/** Transpose 8x8 bit matrix
|
||||
* From Hacker's Delight
|
||||
*/
|
||||
static void transpose8rS32(uint8_t * A, int m, int n, uint8_t * B)
|
||||
{
|
||||
uint32_t x, y, t;
|
||||
|
||||
// Load the array and pack it into x and y.
|
||||
|
||||
x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
|
||||
y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
|
||||
|
||||
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
|
||||
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
|
||||
|
||||
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
|
||||
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
|
||||
|
||||
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
|
||||
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
|
||||
x = t;
|
||||
|
||||
B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x;
|
||||
B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y;
|
||||
}
|
||||
|
||||
/** Start I2S transmission
|
||||
*/
|
||||
static void i2sStart()
|
||||
{
|
||||
// esp_intr_disable(gI2S_intr_handle);
|
||||
// Serial.println("I2S start");
|
||||
i2sReset();
|
||||
//Serial.println(dmaBuffers[0]->sampleCount());
|
||||
i2s->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN;
|
||||
i2s->out_link.addr = (uint32_t) & (dmaBuffers[0]->descriptor);
|
||||
i2s->out_link.start = 1;
|
||||
////vTaskDelay(5);
|
||||
i2s->int_clr.val = i2s->int_raw.val;
|
||||
// //vTaskDelay(5);
|
||||
i2s->int_ena.out_dscr_err = 1;
|
||||
//enable interrupt
|
||||
////vTaskDelay(5);
|
||||
esp_intr_enable(gI2S_intr_handle);
|
||||
// //vTaskDelay(5);
|
||||
i2s->int_ena.val = 0;
|
||||
i2s->int_ena.out_eof = 1;
|
||||
|
||||
//start transmission
|
||||
i2s->conf.tx_start = 1;
|
||||
}
|
||||
|
||||
static void i2sReset()
|
||||
{
|
||||
// Serial.println("I2S reset");
|
||||
const unsigned long lc_conf_reset_flags = I2S_IN_RST_M | I2S_OUT_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M;
|
||||
i2s->lc_conf.val |= lc_conf_reset_flags;
|
||||
i2s->lc_conf.val &= ~lc_conf_reset_flags;
|
||||
|
||||
const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M;
|
||||
i2s->conf.val |= conf_reset_flags;
|
||||
i2s->conf.val &= ~conf_reset_flags;
|
||||
}
|
||||
|
||||
static void i2sReset_DMA()
|
||||
{
|
||||
i2s->lc_conf.in_rst=1; i2s->lc_conf.in_rst=0;
|
||||
i2s->lc_conf.out_rst=1; i2s->lc_conf.out_rst=0;
|
||||
}
|
||||
|
||||
static void i2sReset_FIFO()
|
||||
{
|
||||
i2s->conf.rx_fifo_reset=1; i2s->conf.rx_fifo_reset=0;
|
||||
i2s->conf.tx_fifo_reset=1; i2s->conf.tx_fifo_reset=0;
|
||||
}
|
||||
|
||||
static void i2sStop()
|
||||
{
|
||||
// Serial.println("I2S stop");
|
||||
esp_intr_disable(gI2S_intr_handle);
|
||||
i2sReset();
|
||||
i2s->conf.rx_start = 0;
|
||||
i2s->conf.tx_start = 0;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
570
libraries/FastLED/platforms/esp/32/clockless_rmt_esp32.h
Normal file
570
libraries/FastLED/platforms/esp/32/clockless_rmt_esp32.h
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* Integration into FastLED ClocklessController
|
||||
* Copyright (c) 2018 Samuel Z. Guyer
|
||||
* Copyright (c) 2017 Thomas Basler
|
||||
* Copyright (c) 2017 Martin F. Falatic
|
||||
*
|
||||
* ESP32 support is provided using the RMT peripheral device -- a unit
|
||||
* on the chip designed specifically for generating (and receiving)
|
||||
* precisely-timed digital signals. Nominally for use in infrared
|
||||
* remote controls, we use it to generate the signals for clockless
|
||||
* LED strips. The main advantage of using the RMT device is that,
|
||||
* once programmed, it generates the signal asynchronously, allowing
|
||||
* the CPU to continue executing other code. It is also not vulnerable
|
||||
* to interrupts or other timing problems that could disrupt the signal.
|
||||
*
|
||||
* The implementation strategy is borrowed from previous work and from
|
||||
* the RMT support built into the ESP32 IDF. The RMT device has 8
|
||||
* channels, which can be programmed independently to send sequences
|
||||
* of high/low bits. Memory for each channel is limited, however, so
|
||||
* in order to send a long sequence of bits, we need to continuously
|
||||
* refill the buffer until all the data is sent. To do this, we fill
|
||||
* half the buffer and then set an interrupt to go off when that half
|
||||
* is sent. Then we refill that half while the second half is being
|
||||
* sent. This strategy effectively overlaps computation (by the CPU)
|
||||
* and communication (by the RMT).
|
||||
*
|
||||
* Since the RMT device only has 8 channels, we need a strategy to
|
||||
* allow more than 8 LED controllers. Our driver assigns controllers
|
||||
* to channels on the fly, queuing up controllers as necessary until a
|
||||
* channel is free. The main showPixels routine just fires off the
|
||||
* first 8 controllers; the interrupt handler starts new controllers
|
||||
* asynchronously as previous ones finish. So, for example, it can
|
||||
* send the data for 8 controllers simultaneously, but 16 controllers
|
||||
* would take approximately twice as much time.
|
||||
*
|
||||
* There is a #define that allows a program to control the total
|
||||
* number of channels that the driver is allowed to use. It defaults
|
||||
* to 8 -- use all the channels. Setting it to 1, for example, results
|
||||
* in fully serial output:
|
||||
*
|
||||
* #define FASTLED_RMT_MAX_CHANNELS 1
|
||||
*
|
||||
* OTHER RMT APPLICATIONS
|
||||
*
|
||||
* The default FastLED driver takes over control of the RMT interrupt
|
||||
* handler, making it hard to use the RMT device for other
|
||||
* (non-FastLED) purposes. You can change it's behavior to use the ESP
|
||||
* core driver instead, allowing other RMT applications to
|
||||
* co-exist. To switch to this mode, add the following directive
|
||||
* before you include FastLED.h:
|
||||
*
|
||||
* #define FASTLED_RMT_BUILTIN_DRIVER
|
||||
*
|
||||
* There may be a performance penalty for using this mode. We need to
|
||||
* compute the RMT signal for the entire LED strip ahead of time,
|
||||
* rather than overlapping it with communication. We also need a large
|
||||
* buffer to hold the signal specification. Each bit of pixel data is
|
||||
* represented by a 32-bit pulse specification, so it is a 32X blow-up
|
||||
* in memory use.
|
||||
*
|
||||
*
|
||||
* Based on public domain code created 19 Nov 2016 by Chris Osborn <fozztexx@fozztexx.com>
|
||||
* http://insentricity.com *
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "esp_intr.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rmt.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/rmt_struct.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
|
||||
uint32_t cyc;
|
||||
__asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc));
|
||||
return cyc;
|
||||
}
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
#define NUM_COLOR_CHANNELS 3
|
||||
|
||||
// -- Configuration constants
|
||||
#define DIVIDER 2 /* 4, 8 still seem to work, but timings become marginal */
|
||||
#define MAX_PULSES 32 /* A channel has a 64 "pulse" buffer - we use half per pass */
|
||||
|
||||
// -- Convert ESP32 CPU cycles to RMT device cycles, taking into account the divider
|
||||
#define F_CPU_RMT ( 80000000L)
|
||||
#define RMT_CYCLES_PER_SEC (F_CPU_RMT/DIVIDER)
|
||||
#define RMT_CYCLES_PER_ESP_CYCLE (F_CPU / RMT_CYCLES_PER_SEC)
|
||||
#define ESP_TO_RMT_CYCLES(n) ((n) / (RMT_CYCLES_PER_ESP_CYCLE))
|
||||
|
||||
// -- Number of cycles to signal the strip to latch
|
||||
#define NS_PER_CYCLE ( 1000000000L / RMT_CYCLES_PER_SEC )
|
||||
#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE )
|
||||
#define RMT_RESET_DURATION NS_TO_CYCLES(50000)
|
||||
|
||||
// -- Core or custom driver
|
||||
#ifndef FASTLED_RMT_BUILTIN_DRIVER
|
||||
#define FASTLED_RMT_BUILTIN_DRIVER false
|
||||
#endif
|
||||
|
||||
// -- Max number of controllers we can support
|
||||
#ifndef FASTLED_RMT_MAX_CONTROLLERS
|
||||
#define FASTLED_RMT_MAX_CONTROLLERS 32
|
||||
#endif
|
||||
|
||||
// -- Number of RMT channels to use (up to 8)
|
||||
// Redefine this value to 1 to force serial output
|
||||
#ifndef FASTLED_RMT_MAX_CHANNELS
|
||||
#define FASTLED_RMT_MAX_CHANNELS 8
|
||||
#endif
|
||||
|
||||
// -- Array of all controllers
|
||||
static CLEDController * gControllers[FASTLED_RMT_MAX_CONTROLLERS];
|
||||
|
||||
// -- Current set of active controllers, indexed by the RMT
|
||||
// channel assigned to them.
|
||||
static CLEDController * gOnChannel[FASTLED_RMT_MAX_CHANNELS];
|
||||
|
||||
static int gNumControllers = 0;
|
||||
static int gNumStarted = 0;
|
||||
static int gNumDone = 0;
|
||||
static int gNext = 0;
|
||||
|
||||
static intr_handle_t gRMT_intr_handle = NULL;
|
||||
|
||||
// -- Global semaphore for the whole show process
|
||||
// Semaphore is not given until all data has been sent
|
||||
static xSemaphoreHandle gTX_sem = NULL;
|
||||
|
||||
static bool gInitialized = false;
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 5>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER>
|
||||
{
|
||||
// -- RMT has 8 channels, numbered 0 to 7
|
||||
rmt_channel_t mRMT_channel;
|
||||
|
||||
// -- Store the GPIO pin
|
||||
gpio_num_t mPin;
|
||||
|
||||
// -- This instantiation forces a check on the pin choice
|
||||
FastPin<DATA_PIN> mFastPin;
|
||||
|
||||
// -- Timing values for zero and one bits, derived from T1, T2, and T3
|
||||
rmt_item32_t mZero;
|
||||
rmt_item32_t mOne;
|
||||
|
||||
// -- Save the pixel controller
|
||||
PixelController<RGB_ORDER> * mPixels;
|
||||
int mCurColor;
|
||||
uint16_t mCurPulse;
|
||||
volatile uint32_t * mRMT_mem_ptr;
|
||||
|
||||
// -- Buffer to hold all of the pulses. For the version that uses
|
||||
// the RMT driver built into the ESP core.
|
||||
rmt_item32_t * mBuffer;
|
||||
uint16_t mBufferSize;
|
||||
|
||||
public:
|
||||
|
||||
void init()
|
||||
{
|
||||
// -- Allocate space to save the pixel controller
|
||||
// during parallel output
|
||||
mPixels = (PixelController<RGB_ORDER> *) malloc(sizeof(PixelController<RGB_ORDER>));
|
||||
|
||||
// -- Precompute rmt items corresponding to a zero bit and a one bit
|
||||
// according to the timing values given in the template instantiation
|
||||
// T1H
|
||||
mOne.level0 = 1;
|
||||
mOne.duration0 = ESP_TO_RMT_CYCLES(T1+T2); // TO_RMT_CYCLES(T1+T2);
|
||||
// T1L
|
||||
mOne.level1 = 0;
|
||||
mOne.duration1 = ESP_TO_RMT_CYCLES(T3); // TO_RMT_CYCLES(T3);
|
||||
|
||||
// T0H
|
||||
mZero.level0 = 1;
|
||||
mZero.duration0 = ESP_TO_RMT_CYCLES(T1); // TO_RMT_CYCLES(T1);
|
||||
// T0L
|
||||
mZero.level1 = 0;
|
||||
mZero.duration1 = ESP_TO_RMT_CYCLES(T2+T3); // TO_RMT_CYCLES(T2 + T3);
|
||||
|
||||
gControllers[gNumControllers] = this;
|
||||
gNumControllers++;
|
||||
|
||||
mPin = gpio_num_t(DATA_PIN);
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
void initRMT()
|
||||
{
|
||||
// -- Only need to do this once
|
||||
if (gInitialized) return;
|
||||
|
||||
for (int i = 0; i < FASTLED_RMT_MAX_CHANNELS; i++) {
|
||||
gOnChannel[i] = NULL;
|
||||
|
||||
// -- RMT configuration for transmission
|
||||
rmt_config_t rmt_tx;
|
||||
rmt_tx.channel = rmt_channel_t(i);
|
||||
rmt_tx.rmt_mode = RMT_MODE_TX;
|
||||
rmt_tx.gpio_num = mPin; // The particular pin will be assigned later
|
||||
rmt_tx.mem_block_num = 1;
|
||||
rmt_tx.clk_div = DIVIDER;
|
||||
rmt_tx.tx_config.loop_en = false;
|
||||
rmt_tx.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
|
||||
rmt_tx.tx_config.carrier_en = false;
|
||||
rmt_tx.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
|
||||
rmt_tx.tx_config.idle_output_en = true;
|
||||
|
||||
// -- Apply the configuration
|
||||
rmt_config(&rmt_tx);
|
||||
|
||||
if (FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
rmt_driver_install(rmt_channel_t(i), 0, 0);
|
||||
} else {
|
||||
// -- Set up the RMT to send 1/2 of the pulse buffer and then
|
||||
// generate an interrupt. When we get this interrupt we
|
||||
// fill the other half in preparation (kind of like double-buffering)
|
||||
rmt_set_tx_thr_intr_en(rmt_channel_t(i), true, MAX_PULSES);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Create a semaphore to block execution until all the controllers are done
|
||||
if (gTX_sem == NULL) {
|
||||
gTX_sem = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(gTX_sem);
|
||||
}
|
||||
|
||||
if ( ! FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
// -- Allocate the interrupt if we have not done so yet. This
|
||||
// interrupt handler must work for all different kinds of
|
||||
// strips, so it delegates to the refill function for each
|
||||
// specific instantiation of ClocklessController.
|
||||
if (gRMT_intr_handle == NULL)
|
||||
esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, interruptHandler, 0, &gRMT_intr_handle);
|
||||
}
|
||||
|
||||
gInitialized = true;
|
||||
}
|
||||
|
||||
// -- Show pixels
|
||||
// This is the main entry point for the controller.
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels)
|
||||
{
|
||||
if (gNumStarted == 0) {
|
||||
// -- First controller: make sure everything is set up
|
||||
initRMT();
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
}
|
||||
|
||||
if (FASTLED_RMT_BUILTIN_DRIVER)
|
||||
convertAllPixelData(pixels);
|
||||
else {
|
||||
// -- Initialize the local state, save a pointer to the pixel
|
||||
// data. We need to make a copy because pixels is a local
|
||||
// variable in the calling function, and this data structure
|
||||
// needs to outlive this call to showPixels.
|
||||
(*mPixels) = pixels;
|
||||
}
|
||||
|
||||
// -- Keep track of the number of strips we've seen
|
||||
gNumStarted++;
|
||||
|
||||
// -- The last call to showPixels is the one responsible for doing
|
||||
// all of the actual worl
|
||||
if (gNumStarted == gNumControllers) {
|
||||
gNext = 0;
|
||||
|
||||
// -- First, fill all the available channels
|
||||
int channel = 0;
|
||||
while (channel < FASTLED_RMT_MAX_CHANNELS && gNext < gNumControllers) {
|
||||
startNext(channel);
|
||||
channel++;
|
||||
}
|
||||
|
||||
// -- Wait here while the rest of the data is sent. The interrupt handler
|
||||
// will keep refilling the RMT buffers until it is all sent; then it
|
||||
// gives the semaphore back.
|
||||
xSemaphoreTake(gTX_sem, portMAX_DELAY);
|
||||
xSemaphoreGive(gTX_sem);
|
||||
|
||||
// -- Reset the counters
|
||||
gNumStarted = 0;
|
||||
gNumDone = 0;
|
||||
gNext = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Convert all pixels to RMT pulses
|
||||
// This function is only used when the user chooses to use the
|
||||
// built-in RMT driver, which needs all of the RMT pulses
|
||||
// up-front.
|
||||
virtual void convertAllPixelData(PixelController<RGB_ORDER> & pixels)
|
||||
{
|
||||
// -- Compute the pulse values for the whole strip at once.
|
||||
// Requires a large buffer
|
||||
mBufferSize = pixels.size() * 3 * 8;
|
||||
|
||||
if (mBuffer == NULL) {
|
||||
mBuffer = (rmt_item32_t *) calloc( mBufferSize, sizeof(rmt_item32_t));
|
||||
}
|
||||
|
||||
// -- Cycle through the R,G, and B values in the right order,
|
||||
// storing the pulses in the big buffer
|
||||
mCurPulse = 0;
|
||||
|
||||
uint32_t byteval;
|
||||
while (pixels.has(1)) {
|
||||
byteval = pixels.loadAndScale0();
|
||||
convertByte(byteval);
|
||||
byteval = pixels.loadAndScale1();
|
||||
convertByte(byteval);
|
||||
byteval = pixels.loadAndScale2();
|
||||
convertByte(byteval);
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
|
||||
mBuffer[mCurPulse-1].duration1 = RMT_RESET_DURATION;
|
||||
assert(mCurPulse == mBufferSize);
|
||||
}
|
||||
|
||||
void convertByte(uint32_t byteval)
|
||||
{
|
||||
// -- Write one byte's worth of RMT pulses to the big buffer
|
||||
byteval <<= 24;
|
||||
for (register uint32_t j = 0; j < 8; j++) {
|
||||
mBuffer[mCurPulse] = (byteval & 0x80000000L) ? mOne : mZero;
|
||||
byteval <<= 1;
|
||||
mCurPulse++;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Start up the next controller
|
||||
// This method is static so that it can dispatch to the
|
||||
// appropriate startOnChannel method of the given controller.
|
||||
static void IRAM_ATTR startNext(int channel)
|
||||
{
|
||||
if (gNext < gNumControllers) {
|
||||
ClocklessController * pController = static_cast<ClocklessController*>(gControllers[gNext]);
|
||||
pController->startOnChannel(channel);
|
||||
gNext++;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Start this controller on the given channel
|
||||
// This function just initiates the RMT write; it does not wait
|
||||
// for it to finish.
|
||||
void IRAM_ATTR startOnChannel(int channel)
|
||||
{
|
||||
// -- Assign this channel and configure the RMT
|
||||
mRMT_channel = rmt_channel_t(channel);
|
||||
|
||||
// -- Store a reference to this controller, so we can get it
|
||||
// inside the interrupt handler
|
||||
gOnChannel[channel] = this;
|
||||
|
||||
// -- Assign the pin to this channel
|
||||
rmt_set_pin(mRMT_channel, RMT_MODE_TX, mPin);
|
||||
|
||||
if (FASTLED_RMT_BUILTIN_DRIVER) {
|
||||
// -- Use the built-in RMT driver to send all the data in one shot
|
||||
rmt_register_tx_end_callback(doneOnChannel, 0);
|
||||
rmt_write_items(mRMT_channel, mBuffer, mBufferSize, false);
|
||||
} else {
|
||||
// -- Use our custom driver to send the data incrementally
|
||||
|
||||
// -- Turn on the interrupts
|
||||
rmt_set_tx_intr_en(mRMT_channel, true);
|
||||
|
||||
// -- Initialize the counters that keep track of where we are in
|
||||
// the pixel data.
|
||||
mRMT_mem_ptr = & (RMTMEM.chan[mRMT_channel].data32[0].val);
|
||||
mCurPulse = 0;
|
||||
mCurColor = 0;
|
||||
|
||||
// -- Fill both halves of the buffer
|
||||
fillHalfRMTBuffer();
|
||||
fillHalfRMTBuffer();
|
||||
|
||||
// -- Turn on the interrupts
|
||||
rmt_set_tx_intr_en(mRMT_channel, true);
|
||||
|
||||
// -- Start the RMT TX operation
|
||||
rmt_tx_start(mRMT_channel, true);
|
||||
}
|
||||
}
|
||||
|
||||
// -- A controller is done
|
||||
// This function is called when a controller finishes writing
|
||||
// its data. It is called either by the custom interrupt
|
||||
// handler (below), or as a callback from the built-in
|
||||
// interrupt handler. It is static because we don't know which
|
||||
// controller is done until we look it up.
|
||||
static void doneOnChannel(rmt_channel_t channel, void * arg)
|
||||
{
|
||||
ClocklessController * controller = static_cast<ClocklessController*>(gOnChannel[channel]);
|
||||
portBASE_TYPE HPTaskAwoken = 0;
|
||||
|
||||
// -- Turn off output on the pin
|
||||
gpio_matrix_out(controller->mPin, 0x100, 0, 0);
|
||||
|
||||
gOnChannel[channel] = NULL;
|
||||
gNumDone++;
|
||||
|
||||
if (gNumDone == gNumControllers) {
|
||||
// -- If this is the last controller, signal that we are all done
|
||||
xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken);
|
||||
if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR();
|
||||
} else {
|
||||
// -- Otherwise, if there are still controllers waiting, then
|
||||
// start the next one on this channel
|
||||
if (gNext < gNumControllers)
|
||||
startNext(channel);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Custom interrupt handler
|
||||
// This interrupt handler handles two cases: a controller is
|
||||
// done writing its data, or a controller needs to fill the
|
||||
// next half of the RMT buffer with data.
|
||||
static void IRAM_ATTR interruptHandler(void *arg)
|
||||
{
|
||||
// -- The basic structure of this code is borrowed from the
|
||||
// interrupt handler in esp-idf/components/driver/rmt.c
|
||||
uint32_t intr_st = RMT.int_st.val;
|
||||
uint8_t channel;
|
||||
|
||||
for (channel = 0; channel < FASTLED_RMT_MAX_CHANNELS; channel++) {
|
||||
int tx_done_bit = channel * 3;
|
||||
int tx_next_bit = channel + 24;
|
||||
|
||||
if (gOnChannel[channel] != NULL) {
|
||||
|
||||
// -- More to send on this channel
|
||||
if (intr_st & BIT(tx_next_bit)) {
|
||||
RMT.int_clr.val |= BIT(tx_next_bit);
|
||||
|
||||
// -- Refill the half of the buffer that we just finished,
|
||||
// allowing the other half to proceed.
|
||||
ClocklessController * controller = static_cast<ClocklessController*>(gOnChannel[channel]);
|
||||
controller->fillHalfRMTBuffer();
|
||||
} else {
|
||||
// -- Transmission is complete on this channel
|
||||
if (intr_st & BIT(tx_done_bit)) {
|
||||
RMT.int_clr.val |= BIT(tx_done_bit);
|
||||
doneOnChannel(rmt_channel_t(channel), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR getNextByte() __attribute__ ((always_inline))
|
||||
{
|
||||
uint8_t byte;
|
||||
|
||||
// -- Cycle through the color channels
|
||||
switch (mCurColor) {
|
||||
case 0:
|
||||
byte = mPixels->loadAndScale0();
|
||||
break;
|
||||
case 1:
|
||||
byte = mPixels->loadAndScale1();
|
||||
break;
|
||||
case 2:
|
||||
byte = mPixels->loadAndScale2();
|
||||
mPixels->advanceData();
|
||||
mPixels->stepDithering();
|
||||
break;
|
||||
default:
|
||||
// -- This is bad!
|
||||
byte = 0;
|
||||
}
|
||||
|
||||
mCurColor = (mCurColor + 1) % NUM_COLOR_CHANNELS;
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
// -- Fill the RMT buffer
|
||||
// This function fills the next 32 slots in the RMT write
|
||||
// buffer with pixel data. It also handles the case where the
|
||||
// pixel data is exhausted, so we need to fill the RMT buffer
|
||||
// with zeros to signal that it's done.
|
||||
virtual void IRAM_ATTR fillHalfRMTBuffer()
|
||||
{
|
||||
uint32_t one_val = mOne.val;
|
||||
uint32_t zero_val = mZero.val;
|
||||
|
||||
// -- Convert (up to) 32 bits of the raw pixel data into
|
||||
// into RMT pulses that encode the zeros and ones.
|
||||
int pulses = 0;
|
||||
uint32_t byteval;
|
||||
while (pulses < 32 && mPixels->has(1)) {
|
||||
// -- Get one byte
|
||||
byteval = getNextByte();
|
||||
byteval <<= 24;
|
||||
// Shift bits out, MSB first, setting RMTMEM.chan[n].data32[x] to the
|
||||
// rmt_item32_t value corresponding to the buffered bit value
|
||||
for (register uint32_t j = 0; j < 8; j++) {
|
||||
uint32_t val = (byteval & 0x80000000L) ? one_val : zero_val;
|
||||
* mRMT_mem_ptr++ = val;
|
||||
// Replaces: RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = val;
|
||||
byteval <<= 1;
|
||||
mCurPulse++;
|
||||
}
|
||||
pulses += 8;
|
||||
}
|
||||
|
||||
// -- When we reach the end of the pixel data, fill the rest of the
|
||||
// RMT buffer with 0's, which signals to the device that we're done.
|
||||
if ( ! mPixels->has(1) ) {
|
||||
while (pulses < 32) {
|
||||
* mRMT_mem_ptr++ = 0;
|
||||
// Replaces: RMTMEM.chan[mRMT_channel].data32[mCurPulse].val = 0;
|
||||
mCurPulse++;
|
||||
pulses++;
|
||||
}
|
||||
}
|
||||
|
||||
// -- When we have filled the back half the buffer, reset the position to the first half
|
||||
if (mCurPulse >= MAX_PULSES*2) {
|
||||
mRMT_mem_ptr = & (RMTMEM.chan[mRMT_channel].data32[0].val);
|
||||
mCurPulse = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
11
libraries/FastLED/platforms/esp/32/fastled_esp32.h
Normal file
11
libraries/FastLED/platforms/esp/32/fastled_esp32.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastpin_esp32.h"
|
||||
|
||||
#ifdef FASTLED_ESP32_I2S
|
||||
#include "clockless_i2s_esp32.h"
|
||||
#else
|
||||
#include "clockless_rmt_esp32.h"
|
||||
#endif
|
||||
|
||||
// #include "clockless_block_esp32.h"
|
||||
115
libraries/FastLED/platforms/esp/32/fastpin_esp32.h
Normal file
115
libraries/FastLED/platforms/esp/32/fastpin_esp32.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
template<uint8_t PIN, uint32_t MASK> class _ESPPIN {
|
||||
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) GPIO.out_w1ts = MASK;
|
||||
else GPIO.out1_w1ts.val = MASK;
|
||||
}
|
||||
|
||||
inline static void lo() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) GPIO.out_w1tc = MASK;
|
||||
else GPIO.out1_w1tc.val = MASK;
|
||||
}
|
||||
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) GPIO.out = val;
|
||||
else GPIO.out1.val = val;
|
||||
}
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) {
|
||||
if(PIN < 32) { GPIO.out ^= MASK; }
|
||||
else { GPIO.out1.val ^=MASK; }
|
||||
}
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) return GPIO.out | MASK;
|
||||
else return GPIO.out1.val | MASK;
|
||||
}
|
||||
|
||||
inline static port_t loval() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) return GPIO.out & ~MASK;
|
||||
else return GPIO.out1.val & ~MASK;
|
||||
}
|
||||
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) return &GPIO.out;
|
||||
else return &GPIO.out1.val;
|
||||
}
|
||||
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) return &GPIO.out_w1ts;
|
||||
else return &GPIO.out1_w1ts.val;
|
||||
}
|
||||
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) return &GPIO.out_w1tc;
|
||||
else return &GPIO.out1_w1tc.val;
|
||||
}
|
||||
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return MASK; }
|
||||
|
||||
inline static bool isset() __attribute__ ((always_inline)) {
|
||||
if (PIN < 32) return GPIO.out & MASK;
|
||||
else return GPIO.out1.val & MASK;
|
||||
}
|
||||
};
|
||||
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ESPPIN<PIN, ((PIN<32)?((uint32_t)1 << PIN):((uint32_t)1 << (PIN-32)))> {};
|
||||
|
||||
_FL_DEFPIN(0);
|
||||
_FL_DEFPIN(1); // WARNING: Using TX causes flashiness when uploading
|
||||
_FL_DEFPIN(2);
|
||||
_FL_DEFPIN(3); // WARNING: Using RX causes flashiness when uploading
|
||||
_FL_DEFPIN(4);
|
||||
_FL_DEFPIN(5);
|
||||
|
||||
// -- These pins are not safe to use:
|
||||
// _FL_DEFPIN(6,6); _FL_DEFPIN(7,7); _FL_DEFPIN(8,8);
|
||||
// _FL_DEFPIN(9,9); _FL_DEFPIN(10,10); _FL_DEFPIN(11,11);
|
||||
|
||||
_FL_DEFPIN(12);
|
||||
_FL_DEFPIN(13);
|
||||
_FL_DEFPIN(14);
|
||||
_FL_DEFPIN(15);
|
||||
_FL_DEFPIN(16);
|
||||
_FL_DEFPIN(17);
|
||||
_FL_DEFPIN(18);
|
||||
_FL_DEFPIN(19);
|
||||
|
||||
// No pin 20 : _FL_DEFPIN(20,20);
|
||||
|
||||
_FL_DEFPIN(21); // Works, but note that GPIO21 is I2C SDA
|
||||
_FL_DEFPIN(22); // Works, but note that GPIO22 is I2C SCL
|
||||
_FL_DEFPIN(23);
|
||||
|
||||
// No pin 24 : _FL_DEFPIN(24,24);
|
||||
|
||||
_FL_DEFPIN(25);
|
||||
_FL_DEFPIN(26);
|
||||
_FL_DEFPIN(27);
|
||||
|
||||
// No pin 28-31: _FL_DEFPIN(28,28); _FL_DEFPIN(29,29); _FL_DEFPIN(30,30); _FL_DEFPIN(31,31);
|
||||
|
||||
// Need special handling for pins > 31
|
||||
_FL_DEFPIN(32);
|
||||
_FL_DEFPIN(33);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
33
libraries/FastLED/platforms/esp/32/led_sysdefs_esp32.h
Normal file
33
libraries/FastLED/platforms/esp/32/led_sysdefs_esp32.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef ESP32
|
||||
#define ESP32
|
||||
#endif
|
||||
|
||||
#define FASTLED_ESP32
|
||||
|
||||
// Use system millis timer
|
||||
#define FASTLED_HAS_MILLIS
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
typedef unsigned long prog_uint32_t;
|
||||
|
||||
|
||||
// Default to NOT using PROGMEM here
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
# define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
# define FASTLED_ALLOW_INTERRUPTS 1
|
||||
# define INTERRUPT_THRESHOLD 0
|
||||
#endif
|
||||
|
||||
#define NEED_CXX_BITS
|
||||
|
||||
// These can be overridden
|
||||
# define FASTLED_ESP32_RAW_PIN_ORDER
|
||||
|
||||
// #define cli() os_intr_lock();
|
||||
// #define sei() os_intr_lock();
|
||||
159
libraries/FastLED/platforms/esp/8266/clockless_block_esp8266.h
Normal file
159
libraries/FastLED/platforms/esp/8266/clockless_block_esp8266.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#ifndef __INC_CLOCKLESS_BLOCK_ESP8266_H
|
||||
#define __INC_CLOCKLESS_BLOCK_ESP8266_H
|
||||
|
||||
#define FASTLED_HAS_BLOCKLESS 1
|
||||
|
||||
#define FIX_BITS(bits) (((bits & 0x0fL) << 12) | (bits & 0x30))
|
||||
|
||||
#define MIN(X,Y) (((X)<(Y)) ? (X):(Y))
|
||||
#define USED_LANES (MIN(LANES, 6))
|
||||
#define PORT_MASK (((1 << USED_LANES)-1) & 0x0000FFFFL)
|
||||
#define PIN_MASK FIX_BITS(PORT_MASK)
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
extern uint32_t _frame_cnt;
|
||||
extern uint32_t _retry_cnt;
|
||||
#endif
|
||||
|
||||
template <uint8_t LANES, int FIRST_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = GRB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
|
||||
typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<FIRST_PIN>::port_t data_t;
|
||||
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual int size() { return CLEDController::size() * LANES; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) {
|
||||
// mWait.wait();
|
||||
/*uint32_t clocks = */
|
||||
int cnt=FASTLED_INTERRUPT_RETRY_COUNT;
|
||||
while(!showRGBInternal(pixels) && cnt--) {
|
||||
os_intr_unlock();
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
_retry_cnt++;
|
||||
#endif
|
||||
delayMicroseconds(WAIT_TIME * 10);
|
||||
os_intr_lock();
|
||||
}
|
||||
// #if FASTLED_ALLOW_INTTERUPTS == 0
|
||||
// Adjust the timer
|
||||
// long microsTaken = CLKS_TO_MICROS(clocks);
|
||||
// MS_COUNTER += (1 + (microsTaken / 1000));
|
||||
// #endif
|
||||
|
||||
// mWait.mark();
|
||||
}
|
||||
|
||||
template<int PIN> static void initPin() {
|
||||
_ESPPIN<PIN, 1<<(PIN & 0xFF)>::setOutput();
|
||||
}
|
||||
|
||||
virtual void init() {
|
||||
void (* funcs[])() ={initPin<12>, initPin<13>, initPin<14>, initPin<15>, initPin<4>, initPin<5>};
|
||||
|
||||
for (uint8_t i = 0; i < USED_LANES; ++i) {
|
||||
funcs[i]();
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[8];
|
||||
uint16_t shorts[4];
|
||||
uint32_t raw[2];
|
||||
} Lines;
|
||||
|
||||
#define ESP_ADJUST 0 // (2*(F_CPU/24000000))
|
||||
#define ESP_ADJUST2 0
|
||||
template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register Lines & b, PixelController<RGB_ORDER, LANES, PORT_MASK> &pixels) { // , register uint32_t & b2) {
|
||||
Lines b2 = b;
|
||||
transpose8x1_noinline(b.bytes,b2.bytes);
|
||||
|
||||
register uint8_t d = pixels.template getd<PX>(pixels);
|
||||
register uint8_t scale = pixels.template getscale<PX>(pixels);
|
||||
|
||||
for(register uint32_t i = 0; i < USED_LANES; i++) {
|
||||
while((__clock_cycles() - last_mark) < (T1+T2+T3));
|
||||
last_mark = __clock_cycles();
|
||||
*FastPin<FIRST_PIN>::sport() = PIN_MASK;
|
||||
|
||||
uint32_t nword = (uint32_t)(~b2.bytes[7-i]);
|
||||
while((__clock_cycles() - last_mark) < (T1-6));
|
||||
*FastPin<FIRST_PIN>::cport() = FIX_BITS(nword);
|
||||
|
||||
while((__clock_cycles() - last_mark) < (T1+T2));
|
||||
*FastPin<FIRST_PIN>::cport() = PIN_MASK;
|
||||
|
||||
b.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
|
||||
}
|
||||
|
||||
for(register uint32_t i = USED_LANES; i < 8; i++) {
|
||||
while((__clock_cycles() - last_mark) < (T1+T2+T3));
|
||||
last_mark = __clock_cycles();
|
||||
*FastPin<FIRST_PIN>::sport() = PIN_MASK;
|
||||
|
||||
uint32_t nword = (uint32_t)(~b2.bytes[7-i]);
|
||||
while((__clock_cycles() - last_mark) < (T1-6));
|
||||
*FastPin<FIRST_PIN>::cport() = FIX_BITS(nword);
|
||||
|
||||
while((__clock_cycles() - last_mark) < (T1+T2));
|
||||
*FastPin<FIRST_PIN>::cport() = PIN_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t ICACHE_RAM_ATTR showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) {
|
||||
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
Lines b0;
|
||||
|
||||
for(int i = 0; i < USED_LANES; i++) {
|
||||
b0.bytes[i] = allpixels.loadAndScale0(i);
|
||||
}
|
||||
allpixels.preStepFirstByteDithering();
|
||||
|
||||
os_intr_lock();
|
||||
uint32_t _start = __clock_cycles();
|
||||
uint32_t last_mark = _start;
|
||||
|
||||
while(allpixels.has(1)) {
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0,1>(last_mark, b0, allpixels);
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0,2>(last_mark, b0, allpixels);
|
||||
allpixels.advanceData();
|
||||
|
||||
// Write third byte
|
||||
writeBits<8+XTRA0,0>(last_mark, b0, allpixels);
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
os_intr_unlock();
|
||||
#endif
|
||||
|
||||
allpixels.stepDithering();
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
os_intr_lock();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if((int32_t)(__clock_cycles()-last_mark) > 0) {
|
||||
if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { os_intr_unlock(); return 0; }
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
os_intr_unlock();
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
_frame_cnt++;
|
||||
#endif
|
||||
return __clock_cycles() - _start;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
#endif
|
||||
117
libraries/FastLED/platforms/esp/8266/clockless_esp8266.h
Normal file
117
libraries/FastLED/platforms/esp/8266/clockless_esp8266.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
extern uint32_t _frame_cnt;
|
||||
extern uint32_t _retry_cnt;
|
||||
#endif
|
||||
|
||||
// Info on reading cycle counter from https://github.com/kbeckmann/nodemcu-firmware/blob/ws2812-dual/app/modules/ws2812.c
|
||||
__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
|
||||
uint32_t cyc;
|
||||
__asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc));
|
||||
return cyc;
|
||||
}
|
||||
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPin<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPin<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPin<DATA_PIN>::mask();
|
||||
mPort = FastPin<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
// mWait.wait();
|
||||
int cnt = FASTLED_INTERRUPT_RETRY_COUNT;
|
||||
while((showRGBInternal(pixels)==0) && cnt--) {
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
_retry_cnt++;
|
||||
#endif
|
||||
os_intr_unlock();
|
||||
delayMicroseconds(WAIT_TIME);
|
||||
os_intr_lock();
|
||||
}
|
||||
// mWait.mark();
|
||||
}
|
||||
|
||||
#define _ESP_ADJ (0)
|
||||
#define _ESP_ADJ2 (0)
|
||||
|
||||
template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & last_mark, register uint32_t b) {
|
||||
b <<= 24; b = ~b;
|
||||
for(register uint32_t i = BITS; i > 0; i--) {
|
||||
while((__clock_cycles() - last_mark) < (T1+T2+T3));
|
||||
last_mark = __clock_cycles();
|
||||
FastPin<DATA_PIN>::hi();
|
||||
|
||||
while((__clock_cycles() - last_mark) < T1);
|
||||
if(b & 0x80000000L) { FastPin<DATA_PIN>::lo(); }
|
||||
b <<= 1;
|
||||
|
||||
while((__clock_cycles() - last_mark) < (T1+T2));
|
||||
FastPin<DATA_PIN>::lo();
|
||||
}
|
||||
}
|
||||
|
||||
// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
|
||||
// gcc will use register Y for the this pointer.
|
||||
static uint32_t ICACHE_RAM_ATTR showRGBInternal(PixelController<RGB_ORDER> pixels) {
|
||||
// Setup the pixel controller and load/scale the first byte
|
||||
pixels.preStepFirstByteDithering();
|
||||
register uint32_t b = pixels.loadAndScale0();
|
||||
pixels.preStepFirstByteDithering();
|
||||
os_intr_lock();
|
||||
uint32_t start = __clock_cycles();
|
||||
uint32_t last_mark = start;
|
||||
while(pixels.has(1)) {
|
||||
// Write first byte, read next byte
|
||||
writeBits<8+XTRA0>(last_mark, b);
|
||||
b = pixels.loadAndScale1();
|
||||
|
||||
// Write second byte, read 3rd byte
|
||||
writeBits<8+XTRA0>(last_mark, b);
|
||||
b = pixels.loadAndScale2();
|
||||
|
||||
// Write third byte, read 1st byte of next pixel
|
||||
writeBits<8+XTRA0>(last_mark, b);
|
||||
b = pixels.advanceAndLoadAndScale0();
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
os_intr_unlock();
|
||||
#endif
|
||||
|
||||
pixels.stepDithering();
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS == 1)
|
||||
os_intr_lock();
|
||||
// if interrupts took longer than 45µs, punt on the current frame
|
||||
if((int32_t)(__clock_cycles()-last_mark) > 0) {
|
||||
if((int32_t)(__clock_cycles()-last_mark) > (T1+T2+T3+((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US))) { sei(); return 0; }
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
os_intr_unlock();
|
||||
#ifdef FASTLED_DEBUG_COUNT_FRAME_RETRIES
|
||||
_frame_cnt++;
|
||||
#endif
|
||||
return __clock_cycles() - start;
|
||||
}
|
||||
};
|
||||
|
||||
FASTLED_NAMESPACE_END
|
||||
5
libraries/FastLED/platforms/esp/8266/fastled_esp8266.h
Normal file
5
libraries/FastLED/platforms/esp/8266/fastled_esp8266.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastpin_esp8266.h"
|
||||
#include "clockless_esp8266.h"
|
||||
#include "clockless_block_esp8266.h"
|
||||
101
libraries/FastLED/platforms/esp/8266/fastpin_esp8266.h
Normal file
101
libraries/FastLED/platforms/esp/8266/fastpin_esp8266.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
FASTLED_NAMESPACE_BEGIN
|
||||
|
||||
struct FASTLED_ESP_IO {
|
||||
volatile uint32_t _GPO;
|
||||
volatile uint32_t _GPOS;
|
||||
volatile uint32_t _GPOC;
|
||||
};
|
||||
|
||||
#define _GPB (*(FASTLED_ESP_IO*)(0x60000000+(0x300)))
|
||||
|
||||
|
||||
template<uint8_t PIN, uint32_t MASK> class _ESPPIN {
|
||||
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { pinMode(PIN, OUTPUT); }
|
||||
inline static void setInput() { pinMode(PIN, INPUT); }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPOS = MASK; } else { GP16O |= MASK; } }
|
||||
inline static void lo() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPOC = MASK; } else { GP16O &= ~MASK; } }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPO = val; } else { GP16O = val; }}
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { if(PIN < 16) { _GPB._GPO ^= MASK; } else { GP16O ^= MASK; } }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { if (PIN<16) { return GPO | MASK; } else { return GP16O | MASK; } }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { if (PIN<16) { return GPO & ~MASK; } else { return GP16O & ~MASK; } }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { if(PIN<16) { return &_GPB._GPO; } else { return &GP16O; } }
|
||||
inline static port_ptr_t sport() __attribute__ ((always_inline)) { return &_GPB._GPOS; } // there is no GP160 support for this
|
||||
inline static port_ptr_t cport() __attribute__ ((always_inline)) { return &_GPB._GPOC; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return MASK; }
|
||||
|
||||
inline static bool isset() __attribute__ ((always_inline)) { return (PIN < 16) ? (GPO & MASK) : (GP16O & MASK); }
|
||||
};
|
||||
|
||||
#define _FL_DEFPIN(PIN, REAL_PIN) template<> class FastPin<PIN> : public _ESPPIN<REAL_PIN, (1<<(REAL_PIN & 0xFF))> {};
|
||||
|
||||
|
||||
#ifdef FASTLED_ESP8266_RAW_PIN_ORDER
|
||||
#define MAX_PIN 16
|
||||
_FL_DEFPIN(0,0); _FL_DEFPIN(1,1); _FL_DEFPIN(2,2); _FL_DEFPIN(3,3);
|
||||
_FL_DEFPIN(4,4); _FL_DEFPIN(5,5);
|
||||
|
||||
// These pins should be disabled, as they always cause WDT resets
|
||||
// _FL_DEFPIN(6,6); _FL_DEFPIN(7,7);
|
||||
// _FL_DEFPIN(8,8); _FL_DEFPIN(9,9); _FL_DEFPIN(10,10); _FL_DEFPIN(11,11);
|
||||
|
||||
_FL_DEFPIN(12,12); _FL_DEFPIN(13,13); _FL_DEFPIN(14,14); _FL_DEFPIN(15,15);
|
||||
_FL_DEFPIN(16,16);
|
||||
|
||||
#define PORTA_FIRST_PIN 12
|
||||
#elif defined(FASTLED_ESP8266_D1_PIN_ORDER)
|
||||
#define MAX_PIN 15
|
||||
_FL_DEFPIN(0,3);
|
||||
_FL_DEFPIN(1,1);
|
||||
_FL_DEFPIN(2,16);
|
||||
_FL_DEFPIN(3,5);
|
||||
_FL_DEFPIN(4,4);
|
||||
_FL_DEFPIN(5,14);
|
||||
_FL_DEFPIN(6,12);
|
||||
_FL_DEFPIN(7,13);
|
||||
_FL_DEFPIN(8,0);
|
||||
_FL_DEFPIN(9,2);
|
||||
_FL_DEFPIN(10,15);
|
||||
_FL_DEFPIN(11,13);
|
||||
_FL_DEFPIN(12,12);
|
||||
_FL_DEFPIN(13,14);
|
||||
_FL_DEFPIN(14,4);
|
||||
_FL_DEFPIN(15,5);
|
||||
|
||||
#define PORTA_FIRST_PIN 12
|
||||
|
||||
#else // if defined(FASTLED_ESP8266_NODEMCU_PIN_ORDER)
|
||||
#define MAX_PIN 10
|
||||
|
||||
// This seems to be the standard Dxx pin mapping on most of the esp boards that i've found
|
||||
_FL_DEFPIN(0,16); _FL_DEFPIN(1,5); _FL_DEFPIN(2,4); _FL_DEFPIN(3,0);
|
||||
_FL_DEFPIN(4,2); _FL_DEFPIN(5,14); _FL_DEFPIN(6,12); _FL_DEFPIN(7,13);
|
||||
_FL_DEFPIN(8,15); _FL_DEFPIN(9,3); _FL_DEFPIN(10,1);
|
||||
|
||||
#define PORTA_FIRST_PIN 6
|
||||
|
||||
// The rest of the pins - these are generally not available
|
||||
// _FL_DEFPIN(11,6);
|
||||
// _FL_DEFPIN(12,7); _FL_DEFPIN(13,8); _FL_DEFPIN(14,9); _FL_DEFPIN(15,10);
|
||||
// _FL_DEFPIN(16,11);
|
||||
|
||||
#endif
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#define FASTLED_NAMESPACE_END
|
||||
39
libraries/FastLED/platforms/esp/8266/led_sysdefs_esp8266.h
Normal file
39
libraries/FastLED/platforms/esp/8266/led_sysdefs_esp8266.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef ESP8266
|
||||
#define ESP8266
|
||||
#endif
|
||||
|
||||
#define FASTLED_ESP8266
|
||||
|
||||
// Use system millis timer
|
||||
#define FASTLED_HAS_MILLIS
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
typedef uint32_t prog_uint32_t;
|
||||
|
||||
|
||||
// Default to NOT using PROGMEM here
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
# define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
# define FASTLED_ALLOW_INTERRUPTS 1
|
||||
# define INTERRUPT_THRESHOLD 0
|
||||
#endif
|
||||
|
||||
#define NEED_CXX_BITS
|
||||
|
||||
// These can be overridden
|
||||
#if !defined(FASTLED_ESP8266_RAW_PIN_ORDER) && !defined(FASTLED_ESP8266_NODEMCU_PIN_ORDER) && !defined(FASTLED_ESP8266_D1_PIN_ORDER)
|
||||
# ifdef ARDUINO_ESP8266_NODEMCU
|
||||
# define FASTLED_ESP8266_NODEMCU_PIN_ORDER
|
||||
# else
|
||||
# define FASTLED_ESP8266_RAW_PIN_ORDER
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// #define cli() os_intr_lock();
|
||||
// #define sei() os_intr_lock();
|
||||
Reference in New Issue
Block a user