diff options
author | Mark Kriegsman <1334634+kriegsman@users.noreply.github.com> | 2021-01-04 19:23:09 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-04 19:23:09 +0300 |
commit | ffee7627d0c5d70303df337a285d030fca2cbe77 (patch) | |
tree | f608940615e8a9e2d070ff0127a1a3f327d3169b | |
parent | 41652f2045ec9df7d5faf5b35efac280e13837f5 (diff) | |
parent | d544953523a79c51a1084bb6e91ee39b55c8486a (diff) |
Merge pull request #1166 from tom-miller1/master
Add hardware SPI support for Arduino Nano Every
-rw-r--r-- | src/platforms/avr/fastpin_avr.h | 11 | ||||
-rw-r--r-- | src/platforms/avr/fastspi_avr.h | 159 |
2 files changed, 163 insertions, 7 deletions
diff --git a/src/platforms/avr/fastpin_avr.h b/src/platforms/avr/fastpin_avr.h index db2beeaf..99a78958 100644 --- a/src/platforms/avr/fastpin_avr.h +++ b/src/platforms/avr/fastpin_avr.h @@ -215,15 +215,12 @@ _FL_DEFPIN(16, 1, D); _FL_DEFPIN(17, 0, D); _FL_DEFPIN(18, 2, A); _FL_DEFPIN(19, _FL_DEFPIN(20, 4, D); _FL_DEFPIN(21, 5, D); _FL_DEFPIN(22, 2, A); // To confirm for the SPI interfaces -//#define SPI_DATA 18 -//#define SPI_CLOCK 13 -//#define SPI_SELECT 19 -//#define AVR_HARDWARE_SPI 1 +#define SPI_DATA 11 +#define SPI_CLOCK 13 +#define SPI_SELECT 8 +#define AVR_HARDWARE_SPI 1 #define HAS_HARDWARE_PIN_SUPPORT 1 -//#define SPI_UART0_DATA 1 -//#define SPI_UART0_CLOCK 4 - #elif defined(__AVR_ATmega4809__) #define MAX_PIN 21 diff --git a/src/platforms/avr/fastspi_avr.h b/src/platforms/avr/fastspi_avr.h index 245e4065..325a24b8 100644 --- a/src/platforms/avr/fastspi_avr.h +++ b/src/platforms/avr/fastspi_avr.h @@ -314,6 +314,165 @@ public: #endif +#if defined(SPI0_CTRLA) + +template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER> +class AVRHardwareSPIOutput { + Selectable *m_pSelect; + +public: + AVRHardwareSPIOutput() { m_pSelect = NULL; } + AVRHardwareSPIOutput(Selectable *pSelect) { m_pSelect = pSelect; } + void setSelect(Selectable *pSelect) { m_pSelect = pSelect; } + + void init() { + FastPin<_CLOCK_PIN>::setOutput(); + FastPin<_DATA_PIN>::setOutput(); + + // Arduino Nano Every documentation lists SPI pins in ALT2 portmux position + PORTMUX_TWISPIROUTEA = PORTMUX_SPI01_bm; + + // Set SPI master mode and clock scaler. + SPI0_CTRLA = SPI_MASTER_bm; + if(_SPI_CLOCK_DIVIDER >= 128) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 64) { SPI0_CTRLA |= SPI_PRESC1_bm; } + else if (_SPI_CLOCK_DIVIDER >= 32) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 16) { SPI0_CTRLA |= SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 8) { SPI0_CTRLA |= SPI_PRESC0_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 4) { /* default rate */ } + else { SPI0_CTRLA |= SPI_CLK2X_bm; } + + // Set mode 0 and disable slave select. + SPI0_CTRLB = SPI_SSD_bm; + + // Enable SPI. + SPI0_CTRLA |= SPI_ENABLE_bm; + } + + void setSPIRate() { + SPI0_CTRLA &= ~ ( (1<<SPI_PRESC1_bp)|(1<<SPI_PRESC0_bp)|(1<<SPI_CLK2X_bp) ); // clear pre-scaler and clock multiplier bits + + if(_SPI_CLOCK_DIVIDER >= 128) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 64) { SPI0_CTRLA |= SPI_PRESC1_bm; } + else if (_SPI_CLOCK_DIVIDER >= 32) { SPI0_CTRLA |= SPI_PRESC1_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 16) { SPI0_CTRLA |= SPI_PRESC0_bm; } + else if (_SPI_CLOCK_DIVIDER >= 8) { SPI0_CTRLA |= SPI_PRESC0_bm|SPI_CLK2X_bm; } + else if (_SPI_CLOCK_DIVIDER >= 4) { /* default rate */ } + else { SPI0_CTRLA |= SPI_CLK2X_bm; } + } + + static void stop() { + SPI0_CTRLA &= ~(SPI_ENABLE_bm); + } + + static bool shouldWait(bool wait = false) __attribute__((always_inline)) { + static bool sWait=false; + if(sWait) { + sWait = wait; return true; + } else { + sWait = wait; return false; + } + } + static void wait() __attribute__((always_inline)) { + if(shouldWait()) { + while(!(SPI0_INTFLAGS & SPI_IF_bm)); + } + } + 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(); SPI0_DATA=b; shouldWait(true); } + static void writeBytePostWait(uint8_t b) __attribute__((always_inline)) { SPI0_DATA=b; shouldWait(true); wait(); } + static void writeByteNoWait(uint8_t b) __attribute__((always_inline)) { SPI0_DATA=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(SPSR) |