683 lines
19 KiB
C
683 lines
19 KiB
C
|
#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, uint8_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, uint8_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, uint8_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, uint8_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
|