forked from Makuna/NeoPixelBus
Avr APA106 (#721)
* Apa106 timing support for 600Kbps * interpixel time by speed object
This commit is contained in:
@@ -39,6 +39,7 @@ extern "C"
|
|||||||
void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||||
void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||||
void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||||
|
void send_data_16mhz_600(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
|
||||||
void send_data_32mhz(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask, const uint8_t cycleTiming);
|
void send_data_32mhz(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask, const uint8_t cycleTiming);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +75,38 @@ public:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NeoAvrSpeed600KbpsBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
||||||
|
{
|
||||||
|
#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
|
||||||
|
#ifdef PORTD // PORTD isn't present on ATtiny85, etc.
|
||||||
|
if (port == &PORTD)
|
||||||
|
send_data_8mhz_800_PortD(data, sizeData, pinMask);
|
||||||
|
else if (port == &PORTB)
|
||||||
|
#endif // PORTD
|
||||||
|
send_data_8mhz_800_PortB(data, sizeData, pinMask);
|
||||||
|
|
||||||
|
#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
|
||||||
|
#ifdef PORTD // PORTD
|
||||||
|
if (port == &PORTD)
|
||||||
|
send_data_12mhz_800_PortD(data, sizeData, pinMask);
|
||||||
|
else if (port == &PORTB)
|
||||||
|
#endif // PORTD
|
||||||
|
send_data_12mhz_800_PortB(data, sizeData, pinMask);
|
||||||
|
|
||||||
|
#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU
|
||||||
|
send_data_16mhz_600(data, sizeData, port, pinMask);
|
||||||
|
#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
|
||||||
|
send_data_32mhz(data, sizeData, port, pinMask, 3);
|
||||||
|
#else
|
||||||
|
#error "CPU SPEED NOT SUPPORTED"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class NeoAvrSpeedWs2812x : public NeoAvrSpeed800KbpsBase
|
class NeoAvrSpeedWs2812x : public NeoAvrSpeed800KbpsBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -86,6 +119,19 @@ public:
|
|||||||
static const uint32_t ResetTimeUs = 80;
|
static const uint32_t ResetTimeUs = 80;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NeoAvrSpeedApa106 : public NeoAvrSpeed600KbpsBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const uint32_t ResetTimeUs = 100;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NeoAvrSpeed600KbpsIps : public NeoAvrSpeed600KbpsBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const uint32_t ResetTimeUs = 300;
|
||||||
|
static const uint16_t InterpixelTimeUs = 8; // 12.4, with loop overhead of about 5us for loop
|
||||||
|
};
|
||||||
|
|
||||||
class NeoAvrSpeedTm1814 : public NeoAvrSpeed800KbpsBase
|
class NeoAvrSpeedTm1814 : public NeoAvrSpeed800KbpsBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -212,7 +258,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
const size_t _sizeData; // size of _data below
|
const size_t _sizeData; // size of _data below
|
||||||
const uint8_t _pin; // output pin number
|
const uint8_t _pin; // output pin number
|
||||||
|
|
||||||
@@ -223,9 +269,64 @@ private:
|
|||||||
uint8_t _pinMask; // Output PORT bitmask
|
uint8_t _pinMask; // Output PORT bitmask
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T_SPEED> class NeoAvrIpsMethodBase : public NeoAvrMethodBase<T_SPEED>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NeoAvrIpsMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
|
||||||
|
NeoAvrMethodBase<T_SPEED>(pin, pixelCount, elementSize, settingsSize),
|
||||||
|
_elementSize(elementSize)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~NeoAvrIpsMethodBase()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(bool)
|
||||||
|
{
|
||||||
|
// Data latch = 50+ microsecond pause in the output stream. Rather than
|
||||||
|
// put a delay at the end of the function, the ending time is noted and
|
||||||
|
// the function will simply hold off (if needed) on issuing the
|
||||||
|
// subsequent round of data until the latch time has elapsed. This
|
||||||
|
// allows the mainline code to start generating the next frame of data
|
||||||
|
// rather than stalling for the latch.
|
||||||
|
while (!NeoAvrMethodBase<T_SPEED>::IsReadyToUpdate())
|
||||||
|
{
|
||||||
|
#if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA)
|
||||||
|
yield(); // allows for system yield if needed
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
noInterrupts(); // Need 100% focus on instruction timing
|
||||||
|
|
||||||
|
uint8_t* dataPixel = NeoAvrMethodBase<T_SPEED>::_data;
|
||||||
|
const uint8_t* dataEnd = dataPixel + NeoAvrMethodBase<T_SPEED>::_sizeData;
|
||||||
|
|
||||||
|
while (dataPixel < dataEnd)
|
||||||
|
{
|
||||||
|
T_SPEED::send_data(dataPixel,
|
||||||
|
_elementSize,
|
||||||
|
NeoAvrMethodBase<T_SPEED>::_port,
|
||||||
|
NeoAvrMethodBase<T_SPEED>::_pinMask);
|
||||||
|
dataPixel += _elementSize;
|
||||||
|
delayMicroseconds(T_SPEED::InterpixelTimeUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
interrupts();
|
||||||
|
|
||||||
|
// save EOD time for latch on next call
|
||||||
|
NeoAvrMethodBase<T_SPEED>::_endTime = micros();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const size_t _elementSize; // size of a single pixel
|
||||||
|
};
|
||||||
|
|
||||||
typedef NeoAvrMethodBase<NeoAvrSpeedWs2812x> NeoAvrWs2812xMethod;
|
typedef NeoAvrMethodBase<NeoAvrSpeedWs2812x> NeoAvrWs2812xMethod;
|
||||||
typedef NeoAvrMethodBase<NeoAvrSpeedSk6812> NeoAvrSk6812Method;
|
typedef NeoAvrMethodBase<NeoAvrSpeedSk6812> NeoAvrSk6812Method;
|
||||||
|
typedef NeoAvrMethodBase<NeoAvrSpeedApa106> NeoAvrApa106Method;
|
||||||
|
typedef NeoAvrIpsMethodBase<NeoAvrSpeed600KbpsIps> NeoAvr600KbpsIpsMethod;
|
||||||
|
|
||||||
typedef NeoAvrMethodBase<NeoAvrSpeedTm1814> NeoAvrTm1814InvertedMethod;
|
typedef NeoAvrMethodBase<NeoAvrSpeedTm1814> NeoAvrTm1814InvertedMethod;
|
||||||
typedef NeoAvrMethodBase<NeoAvrSpeedTm1829> NeoAvrTm1829InvertedMethod;
|
typedef NeoAvrMethodBase<NeoAvrSpeedTm1829> NeoAvrTm1829InvertedMethod;
|
||||||
typedef NeoAvrMethodBase<NeoAvrSpeed800Kbps> NeoAvr800KbpsMethod;
|
typedef NeoAvrMethodBase<NeoAvrSpeed800Kbps> NeoAvr800KbpsMethod;
|
||||||
@@ -240,7 +341,7 @@ typedef NeoAvrWs2812xMethod NeoWs2811Method;
|
|||||||
typedef NeoAvrWs2812xMethod NeoWs2816Method;
|
typedef NeoAvrWs2812xMethod NeoWs2816Method;
|
||||||
typedef NeoAvrSk6812Method NeoSk6812Method;
|
typedef NeoAvrSk6812Method NeoSk6812Method;
|
||||||
typedef NeoAvrSk6812Method NeoLc8812Method;
|
typedef NeoAvrSk6812Method NeoLc8812Method;
|
||||||
typedef NeoAvr400KbpsMethod NeoApa106Method;
|
typedef NeoAvrApa106Method NeoApa106Method;
|
||||||
typedef NeoAvrWs2812xMethod Neo800KbpsMethod;
|
typedef NeoAvrWs2812xMethod Neo800KbpsMethod;
|
||||||
typedef NeoAvr400KbpsMethod Neo400KbpsMethod;
|
typedef NeoAvr400KbpsMethod Neo400KbpsMethod;
|
||||||
|
|
||||||
|
@@ -466,7 +466,10 @@ void send_data_12mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask)
|
|||||||
[lo] "r" (lo));
|
[lo] "r" (lo));
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
void send_data_12mhz_400(uint8_t* data,
|
||||||
|
size_t sizeData,
|
||||||
|
volatile uint8_t* port,
|
||||||
|
uint8_t pinMask)
|
||||||
{
|
{
|
||||||
volatile uint16_t i = (uint16_t)sizeData; // Loop counter
|
volatile uint16_t i = (uint16_t)sizeData; // Loop counter
|
||||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||||
@@ -477,7 +480,8 @@ void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port,
|
|||||||
// 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL
|
// 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL
|
||||||
// ST instructions: ^ ^ ^ (T=0,6,15)
|
// ST instructions: ^ ^ ^ (T=0,6,15)
|
||||||
|
|
||||||
volatile uint8_t next, bit;
|
volatile uint8_t next;
|
||||||
|
volatile uint8_t bit;
|
||||||
|
|
||||||
hi = *port | pinMask;
|
hi = *port | pinMask;
|
||||||
lo = *port & ~pinMask;
|
lo = *port & ~pinMask;
|
||||||
@@ -577,7 +581,10 @@ void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port,
|
|||||||
[lo] "r" (lo));
|
[lo] "r" (lo));
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
|
void send_data_16mhz_400(uint8_t* data,
|
||||||
|
size_t sizeData,
|
||||||
|
volatile uint8_t* port,
|
||||||
|
uint8_t pinMask)
|
||||||
{
|
{
|
||||||
volatile size_t i = sizeData; // Loop counter
|
volatile size_t i = sizeData; // Loop counter
|
||||||
volatile uint8_t* ptr = data; // Pointer to next byte
|
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||||
@@ -590,7 +597,8 @@ void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port,
|
|||||||
// 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL
|
// 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL
|
||||||
// ST instructions: ^ ^ ^ (T=0,8,20)
|
// ST instructions: ^ ^ ^ (T=0,8,20)
|
||||||
|
|
||||||
volatile uint8_t next, bit;
|
volatile uint8_t next;
|
||||||
|
volatile uint8_t bit;
|
||||||
|
|
||||||
hi = *port | pinMask;
|
hi = *port | pinMask;
|
||||||
lo = *port & ~pinMask;
|
lo = *port & ~pinMask;
|
||||||
@@ -641,6 +649,68 @@ void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port,
|
|||||||
[lo] "r" (lo));
|
[lo] "r" (lo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 400us (320-480)
|
||||||
|
// 1 1100us (960-1200)
|
||||||
|
// w 1600us
|
||||||
|
void send_data_16mhz_600(uint8_t* data,
|
||||||
|
size_t sizeData,
|
||||||
|
volatile uint8_t* port,
|
||||||
|
uint8_t pinMask)
|
||||||
|
{
|
||||||
|
volatile size_t i = sizeData; // Loop counter
|
||||||
|
volatile uint8_t* ptr = data; // Pointer to next byte
|
||||||
|
volatile uint8_t b = *ptr++; // Current byte value
|
||||||
|
volatile uint8_t hi; // PORT w/output bit set high
|
||||||
|
volatile uint8_t lo; // PORT w/output bit set low
|
||||||
|
|
||||||
|
// The 633 KHz clock on 16 MHz MCU.
|
||||||
|
//
|
||||||
|
// 25 inst. clocks per bit: HHHHHHHHxxxxxxxxxxLLLLLLLL
|
||||||
|
// ST instructions: ^ ^ ^ (T=0,8,18)
|
||||||
|
|
||||||
|
|
||||||
|
volatile uint8_t next;
|
||||||
|
volatile uint8_t bit;
|
||||||
|
|
||||||
|
hi = *port | pinMask;
|
||||||
|
lo = *port & ~pinMask;
|
||||||
|
next = lo;
|
||||||
|
bit = 8;
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
"head40:" "\n\t" // Clk Pseudocode (T = 0)
|
||||||
|
"st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2)
|
||||||
|
"sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0b10000000)
|
||||||
|
"mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4)
|
||||||
|
"rjmp .+0" "\n\t" // 2 nop nop (T = 6)
|
||||||
|
"st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8)
|
||||||
|
"mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 9)
|
||||||
|
"rjmp .+0" "\n\t" // 2 nop nop (T = 11)
|
||||||
|
"rjmp .+0" "\n\t" // 2 nop nop (T = 13)
|
||||||
|
"rjmp .+0" "\n\t" // 2 nop nop (T = 15)
|
||||||
|
"dec %[bit]" "\n\t" // 1 bit-- (T = 16)
|
||||||
|
"breq nextbyte40" "\n\t" // 1-2 if(bit == 0)
|
||||||
|
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 18) duplicate here improves high length for non byte boundary
|
||||||
|
"rol %[byte]" "\n\t" // 1 b <<= 1 (T = 21)
|
||||||
|
"nop" "\n\t" // 1 nop (T = 22)
|
||||||
|
"rjmp .+0" "\n\t" // 2 nop nop (T = 24)
|
||||||
|
"rjmp head40" "\n\t" // 2 -> head40 (next bit out)
|
||||||
|
"nextbyte40:" "\n\t" // (T = 18)
|
||||||
|
"st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 20) duplicate here improves high length while reducing interbyte
|
||||||
|
"ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 21)
|
||||||
|
"ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 23)
|
||||||
|
"sbiw %[count], 1" "\n\t" // 2 i-- (T = 25)
|
||||||
|
"brne head40" "\n" // 1-2 if(i != 0) -> (next byte)
|
||||||
|
: [port] "+e" (port),
|
||||||
|
[byte] "+r" (b),
|
||||||
|
[bit] "+r" (bit),
|
||||||
|
[next] "+r" (next),
|
||||||
|
[count] "+w" (i)
|
||||||
|
: [ptr] "e" (ptr),
|
||||||
|
[hi] "r" (hi),
|
||||||
|
[lo] "r" (lo));
|
||||||
|
}
|
||||||
|
|
||||||
#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
|
#elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
|
||||||
|
|
||||||
void send_data_32mhz(uint8_t* data,
|
void send_data_32mhz(uint8_t* data,
|
||||||
|
Reference in New Issue
Block a user