/* * Copyright (c) 2010 by Cristian Maglie * Copyright (c) 2014 by Paul Stoffregen (Transaction API) * Copyright (c) 2014 by Matthijs Kooijman (SPISettings AVR) * SPI Master library for arduino. * * This file is free software; you can redistribute it and/or modify * it under the terms of either the GNU General Public License version 2 * or the GNU Lesser General Public License version 2.1, both as * published by the Free Software Foundation. */ #ifndef _SPI_H_INCLUDED #define _SPI_H_INCLUDED #include // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(), // usingInterrupt(), and SPISetting(clock, bitOrder, dataMode) #define SPI_HAS_TRANSACTION 1 // Uncomment this line to add detection of mismatched begin/end transactions. // A mismatch occurs if other libraries fail to use SPI.endTransaction() for // each SPI.beginTransaction(). Connect a LED to this pin. The LED will turn // on if any mismatch is ever detected. //#define SPI_TRANSACTION_MISMATCH_LED 5 #ifndef __SAM3X8E__ #ifndef LSBFIRST #define LSBFIRST 0 #endif #ifndef MSBFIRST #define MSBFIRST 1 #endif #endif #define SPI_MODE0 0x00 #define SPI_MODE1 0x04 #define SPI_MODE2 0x08 #define SPI_MODE3 0x0C #define SPI_CLOCK_DIV4 0x00 #define SPI_CLOCK_DIV16 0x01 #define SPI_CLOCK_DIV64 0x02 #define SPI_CLOCK_DIV128 0x03 #define SPI_CLOCK_DIV2 0x04 #define SPI_CLOCK_DIV8 0x05 #define SPI_CLOCK_DIV32 0x06 #define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR #define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR #define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR /**********************************************************/ /* 8 bit AVR-based boards */ /**********************************************************/ #if defined(__AVR__) // define SPI_AVR_EIMSK for AVR boards with external interrupt pins #if defined(EIMSK) #define SPI_AVR_EIMSK EIMSK #elif defined(GICR) #define SPI_AVR_EIMSK GICR #elif defined(GIMSK) #define SPI_AVR_EIMSK GIMSK #endif class SPISettings { public: SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { if (__builtin_constant_p(clock)) { init_AlwaysInline(clock, bitOrder, dataMode); } else { init_MightInline(clock, bitOrder, dataMode); } } SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } private: void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { init_AlwaysInline(clock, bitOrder, dataMode); } void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) { // Clock settings are defined as follows. Note that this shows SPI2X // inverted, so the bits form increasing numbers. Also note that // fosc/64 appears twice // SPR1 SPR0 ~SPI2X Freq // 0 0 0 fosc/2 // 0 0 1 fosc/4 // 0 1 0 fosc/8 // 0 1 1 fosc/16 // 1 0 0 fosc/32 // 1 0 1 fosc/64 // 1 1 0 fosc/64 // 1 1 1 fosc/128 // We find the fastest clock that is less than or equal to the // given clock rate. The clock divider that results in clock_setting // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the // slowest (128 == 2 ^^ 7, so clock_div = 6). uint8_t clockDiv; // When the clock is known at compiletime, use this if-then-else // cascade, which the compiler knows how to completely optimize // away. When clock is not known, use a loop instead, which generates // shorter code. if (__builtin_constant_p(clock)) { if (clock >= F_CPU / 2) { clockDiv = 0; } else if (clock >= F_CPU / 4) { clockDiv = 1; } else if (clock >= F_CPU / 8) { clockDiv = 2; } else if (clock >= F_CPU / 16) { clockDiv = 3; } else if (clock >= F_CPU / 32) { clockDiv = 4; } else if (clock >= F_CPU / 64) { clockDiv = 5; } else { clockDiv = 6; } } else { uint32_t clockSetting = F_CPU / 2; clockDiv = 0; while (clockDiv < 6 && clock < clockSetting) { clockSetting /= 2; clockDiv++; } } // Compensate for the duplicate fosc/64 if (clockDiv == 6) clockDiv = 7; // Invert the SPI2X bit clockDiv ^= 0x1; // Pack into the SPISettings class spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) | (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK); spsr = clockDiv & SPI_2XCLOCK_MASK; } uint8_t spcr; uint8_t spsr; friend class SPIClass; }; class SPIClass { public: // Initialize the SPI library static void begin(); // If SPI is used from within an interrupt, this function registers // that interrupt with the SPI library, so beginTransaction() can // prevent conflicts. The input interruptNumber is the number used // with attachInterrupt. If SPI is used from a different interrupt // (eg, a timer), interruptNumber should be 255. static void usingInterrupt(uint8_t interruptNumber); // Before using SPI.transfer() or asserting chip select pins, // this function is used to gain exclusive access to the SPI bus // and configure the correct settings. inline static void beginTransaction(SPISettings settings) { if (interruptMode > 0) { #ifdef SPI_AVR_EIMSK if (interruptMode == 1) { interruptSave = SPI_AVR_EIMSK; SPI_AVR_EIMSK &= ~interruptMask; } else #endif { uint8_t tmp = SREG; cli(); interruptSave = tmp; } } #ifdef SPI_TRANSACTION_MISMATCH_LED if (inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 1; #endif SPCR = settings.spcr; SPSR = settings.spsr; } // Write to the SPI bus (MOSI pin) and also receive (MISO pin) inline static uint8_t transfer(uint8_t data) { SPDR = data; asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; // wait return SPDR; } inline static uint16_t transfer16(uint16_t data) { union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out; in.val = data; if ((SPCR & _BV(DORD))) { SPDR = in.lsb; asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; out.lsb = SPDR; SPDR = in.msb; asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; out.msb = SPDR; } else { SPDR = in.msb; asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; out.msb = SPDR; SPDR = in.lsb; asm volatile("nop"); while (!(SPSR & _BV(SPIF))) ; out.lsb = SPDR; } return out.val; } inline static void transfer(void *buf, size_t count) { if (count == 0) return; uint8_t *p = (uint8_t *)buf; SPDR = *p; while (--count > 0) { uint8_t out = *(p + 1); while (!(SPSR & _BV(SPIF))) ; uint8_t in = SPDR; SPDR = out; *p++ = in; } while (!(SPSR & _BV(SPIF))) ; *p = SPDR; } // After performing a group of transfers and releasing the chip select // signal, this function allows others to access the SPI bus inline static void endTransaction(void) { #ifdef SPI_TRANSACTION_MISMATCH_LED if (!inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 0; #endif if (interruptMode > 0) { #ifdef SPI_AVR_EIMSK if (interruptMode == 1) { SPI_AVR_EIMSK = interruptSave; } else #endif { SREG = interruptSave; } } } // Disable the SPI bus static void end(); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setBitOrder(uint8_t bitOrder) { if (bitOrder == LSBFIRST) SPCR |= _BV(DORD); else SPCR &= ~(_BV(DORD)); } // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setDataMode(uint8_t dataMode) { SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode; } // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setClockDivider(uint8_t clockDiv) { SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK); SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK); } // These undocumented functions should not be used. SPI.transfer() // polls the hardware flag which is automatically cleared as the // AVR responds to SPI's interrupt inline static void attachInterrupt() { SPCR |= _BV(SPIE); } inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); } private: static uint8_t interruptMode; // 0=none, 1=mask, 2=global static uint8_t interruptMask; // which interrupts to mask static uint8_t interruptSave; // temp storage, to restore state #ifdef SPI_TRANSACTION_MISMATCH_LED static uint8_t inTransactionFlag; #endif }; /**********************************************************/ /* 32 bit Teensy 3.0 and 3.1 */ /**********************************************************/ #elif defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISK) #define SPI_HAS_NOTUSINGINTERRUPT 1 class SPISettings { public: SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { if (__builtin_constant_p(clock)) { init_AlwaysInline(clock, bitOrder, dataMode); } else { init_MightInline(clock, bitOrder, dataMode); } } SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } private: void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { init_AlwaysInline(clock, bitOrder, dataMode); } void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) { uint32_t t, c = SPI_CTAR_FMSZ(7); if (bitOrder == LSBFIRST) c |= SPI_CTAR_LSBFE; if (__builtin_constant_p(clock)) { if (clock >= F_BUS / 2) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0); } else if (clock >= F_BUS / 3) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0); } else if (clock >= F_BUS / 4) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); } else if (clock >= F_BUS / 5) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0); } else if (clock >= F_BUS / 6) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); } else if (clock >= F_BUS / 8) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); } else if (clock >= F_BUS / 10) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); } else if (clock >= F_BUS / 12) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); } else if (clock >= F_BUS / 16) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); } else if (clock >= F_BUS / 20) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(0); } else if (clock >= F_BUS / 24) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); } else if (clock >= F_BUS / 32) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3); } else if (clock >= F_BUS / 40) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); } else if (clock >= F_BUS / 56) { t = SPI_CTAR_PBR(3) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); } else if (clock >= F_BUS / 64) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4); } else if (clock >= F_BUS / 96) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4); } else if (clock >= F_BUS / 128) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5); } else if (clock >= F_BUS / 192) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5); } else if (clock >= F_BUS / 256) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6); } else if (clock >= F_BUS / 384) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6); } else if (clock >= F_BUS / 512) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7); } else if (clock >= F_BUS / 640) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6); } else { /* F_BUS / 768 */ t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7); } } else { for (uint32_t i=0; i<23; i++) { t = ctar_clock_table[i]; if (clock >= F_BUS / ctar_div_table[i]) break; } } if (dataMode & 0x08) { c |= SPI_CTAR_CPOL; } if (dataMode & 0x04) { c |= SPI_CTAR_CPHA; t = (t & 0xFFFF0FFF) | ((t & 0xF000) >> 4); } ctar = c | t; } static const uint16_t ctar_div_table[23]; static const uint32_t ctar_clock_table[23]; uint32_t ctar; friend class SPIClass; #if defined(__MK64FX512__) || defined(__MK66FX1M0__) friend class SPI1Class; friend class SPI2Class; #endif }; class SPIClass { public: // Initialize the SPI library static void begin(); // If SPI is to used from within an interrupt, this function registers // that interrupt with the SPI library, so beginTransaction() can // prevent conflicts. The input interruptNumber is the number used // with attachInterrupt. If SPI is used from a different interrupt // (eg, a timer), interruptNumber should be 255. static void usingInterrupt(uint8_t n) { if (n == 3 || n == 4 || n == 24 || n == 33) { usingInterrupt(IRQ_PORTA); } else if (n == 0 || n == 1 || (n >= 16 && n <= 19) || n == 25 || n == 32) { usingInterrupt(IRQ_PORTB); } else if ((n >= 9 && n <= 13) || n == 15 || n == 22 || n == 23 || (n >= 27 && n <= 30)) { usingInterrupt(IRQ_PORTC); } else if (n == 2 || (n >= 5 && n <= 8) || n == 14 || n == 20 || n == 21) { usingInterrupt(IRQ_PORTD); } else if (n == 26 || n == 31) { usingInterrupt(IRQ_PORTE); } } static void usingInterrupt(IRQ_NUMBER_t interruptName); static void notUsingInterrupt(IRQ_NUMBER_t interruptName); // Before using SPI.transfer() or asserting chip select pins, // this function is used to gain exclusive access to the SPI bus // and configure the correct settings. inline static void beginTransaction(SPISettings settings) { if (interruptMasksUsed) { __disable_irq(); if (interruptMasksUsed & 0x01) { interruptSave[0] = NVIC_ICER0 & interruptMask[0]; NVIC_ICER0 = interruptSave[0]; } #if NVIC_NUM_INTERRUPTS > 32 if (interruptMasksUsed & 0x02) { interruptSave[1] = NVIC_ICER1 & interruptMask[1]; NVIC_ICER1 = interruptSave[1]; } #endif #if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2) if (interruptMasksUsed & 0x04) { interruptSave[2] = NVIC_ICER2 & interruptMask[2]; NVIC_ICER2 = interruptSave[2]; } #endif #if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3) if (interruptMasksUsed & 0x08) { interruptSave[3] = NVIC_ICER3 & interruptMask[3]; NVIC_ICER3 = interruptSave[3]; } #endif __enable_irq(); } #ifdef SPI_TRANSACTION_MISMATCH_LED if (inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 1; #endif if (SPI0_CTAR0 != settings.ctar) { SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); SPI0_CTAR0 = settings.ctar; SPI0_CTAR1 = settings.ctar| SPI_CTAR_FMSZ(8); SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); } } // Write to the SPI bus (MOSI pin) and also receive (MISO pin) inline static uint8_t transfer(uint8_t data) { SPI0_SR = SPI_SR_TCF; SPI0_PUSHR = data; while (!(SPI0_SR & SPI_SR_TCF)) ; // wait return SPI0_POPR; } inline static uint16_t transfer16(uint16_t data) { SPI0_SR = SPI_SR_TCF; SPI0_PUSHR = data | SPI_PUSHR_CTAS(1); while (!(SPI0_SR & SPI_SR_TCF)) ; // wait return SPI0_POPR; } inline static void transfer(void *buf, size_t count) { if (count == 0) return; uint8_t *p = (uint8_t *)buf; SPDR = *p; while (--count > 0) { uint8_t out = *(p + 1); while (!(SPSR & _BV(SPIF))) ; uint8_t in = SPDR; SPDR = out; *p++ = in; } while (!(SPSR & _BV(SPIF))) ; *p = SPDR; } // After performing a group of transfers and releasing the chip select // signal, this function allows others to access the SPI bus inline static void endTransaction(void) { #ifdef SPI_TRANSACTION_MISMATCH_LED if (!inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 0; #endif if (interruptMasksUsed) { if (interruptMasksUsed & 0x01) { NVIC_ISER0 = interruptSave[0]; } #if NVIC_NUM_INTERRUPTS > 32 if (interruptMasksUsed & 0x02) { NVIC_ISER1 = interruptSave[1]; } #endif #if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2) if (interruptMasksUsed & 0x04) { NVIC_ISER2 = interruptSave[2]; } #endif #if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3) if (interruptMasksUsed & 0x08) { NVIC_ISER3 = interruptSave[3]; } #endif } } // Disable the SPI bus static void end(); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setBitOrder(uint8_t bitOrder); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setDataMode(uint8_t dataMode); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setClockDivider(uint8_t clockDiv) { if (clockDiv == SPI_CLOCK_DIV2) { setClockDivider_noInline(SPISettings(12000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV4) { setClockDivider_noInline(SPISettings(4000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV8) { setClockDivider_noInline(SPISettings(2000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV16) { setClockDivider_noInline(SPISettings(1000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV32) { setClockDivider_noInline(SPISettings(500000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV64) { setClockDivider_noInline(SPISettings(250000, MSBFIRST, SPI_MODE0).ctar); } else { /* clockDiv == SPI_CLOCK_DIV128 */ setClockDivider_noInline(SPISettings(125000, MSBFIRST, SPI_MODE0).ctar); } } static void setClockDivider_noInline(uint32_t clk); // These undocumented functions should not be used. SPI.transfer() // polls the hardware flag which is automatically cleared as the // AVR responds to SPI's interrupt inline static void attachInterrupt() { } inline static void detachInterrupt() { } // Teensy 3.x can use alternate pins for these 3 SPI signals. inline static void setMOSI(uint8_t pin) __attribute__((always_inline)) { SPCR.setMOSI(pin); } inline static void setMISO(uint8_t pin) __attribute__((always_inline)) { SPCR.setMISO(pin); } inline static void setSCK(uint8_t pin) __attribute__((always_inline)) { SPCR.setSCK(pin); } // return true if "pin" has special chip select capability static uint8_t pinIsChipSelect(uint8_t pin); // return true if both pin1 and pin2 have independent chip select capability static bool pinIsChipSelect(uint8_t pin1, uint8_t pin2); // configure a pin for chip select and return its SPI_MCR_PCSIS bitmask static uint8_t setCS(uint8_t pin); private: static uint8_t interruptMasksUsed; static uint32_t interruptMask[(NVIC_NUM_INTERRUPTS+31)/32]; static uint32_t interruptSave[(NVIC_NUM_INTERRUPTS+31)/32]; #ifdef SPI_TRANSACTION_MISMATCH_LED static uint8_t inTransactionFlag; #endif }; /**********************************************************/ /* Teensy 3.5 and 3.6 have SPI1 as well */ /**********************************************************/ #if defined(__MK64FX512__) || defined(__MK66FX1M0__) class SPI1Class { public: // Initialize the SPI library static void begin(); // If SPI is to used from within an interrupt, this function registers // that interrupt with the SPI library, so beginTransaction() can // prevent conflicts. The input interruptNumber is the number used // with attachInterrupt. If SPI is used from a different interrupt // (eg, a timer), interruptNumber should be 255. static void usingInterrupt(uint8_t n) { if (n == 3 || n == 4 || n == 24 || n == 33) { usingInterrupt(IRQ_PORTA); } else if (n == 0 || n == 1 || (n >= 16 && n <= 19) || n == 25 || n == 32) { usingInterrupt(IRQ_PORTB); } else if ((n >= 9 && n <= 13) || n == 15 || n == 22 || n == 23 || (n >= 27 && n <= 30)) { usingInterrupt(IRQ_PORTC); } else if (n == 2 || (n >= 5 && n <= 8) || n == 14 || n == 20 || n == 21) { usingInterrupt(IRQ_PORTD); } else if (n == 26 || n == 31) { usingInterrupt(IRQ_PORTE); } } static void usingInterrupt(IRQ_NUMBER_t interruptName); static void notUsingInterrupt(IRQ_NUMBER_t interruptName); // Before using SPI.transfer() or asserting chip select pins, // this function is used to gain exclusive access to the SPI bus // and configure the correct settings. inline static void beginTransaction(SPISettings settings) { if (interruptMasksUsed) { __disable_irq(); if (interruptMasksUsed & 0x01) { interruptSave[0] = NVIC_ICER0 & interruptMask[0]; NVIC_ICER0 = interruptSave[0]; } #if NVIC_NUM_INTERRUPTS > 32 if (interruptMasksUsed & 0x02) { interruptSave[1] = NVIC_ICER1 & interruptMask[1]; NVIC_ICER1 = interruptSave[1]; } #endif #if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2) if (interruptMasksUsed & 0x04) { interruptSave[2] = NVIC_ICER2 & interruptMask[2]; NVIC_ICER2 = interruptSave[2]; } #endif #if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3) if (interruptMasksUsed & 0x08) { interruptSave[3] = NVIC_ICER3 & interruptMask[3]; NVIC_ICER3 = interruptSave[3]; } #endif __enable_irq(); } #ifdef SPI_TRANSACTION_MISMATCH_LED if (inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 1; #endif if (SPI1_CTAR0 != settings.ctar) { SPI1_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); SPI1_CTAR0 = settings.ctar; SPI1_CTAR1 = settings.ctar| SPI_CTAR_FMSZ(8); SPI1_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); } } // Write to the SPI bus (MOSI pin) and also receive (MISO pin) inline static uint8_t transfer(uint8_t data) { SPI1_SR = SPI_SR_TCF; SPI1_PUSHR = data; while (!(SPI1_SR & SPI_SR_TCF)) ; // wait return SPI1_POPR; } inline static uint16_t transfer16(uint16_t data) { SPI1_SR = SPI_SR_TCF; SPI1_PUSHR = data | SPI_PUSHR_CTAS(1); while (!(SPI1_SR & SPI_SR_TCF)) ; // wait return SPI1_POPR; } inline static void transfer(void *buf, size_t count) { uint8_t *p = (uint8_t *)buf; while (count--) { *p = transfer(*p); p++; } } // After performing a group of transfers and releasing the chip select // signal, this function allows others to access the SPI bus inline static void endTransaction(void) { #ifdef SPI_TRANSACTION_MISMATCH_LED if (!inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 0; #endif if (interruptMasksUsed) { if (interruptMasksUsed & 0x01) { NVIC_ISER0 = interruptSave[0]; } #if NVIC_NUM_INTERRUPTS > 32 if (interruptMasksUsed & 0x02) { NVIC_ISER1 = interruptSave[1]; } #endif #if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2) if (interruptMasksUsed & 0x04) { NVIC_ISER2 = interruptSave[2]; } #endif #if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3) if (interruptMasksUsed & 0x08) { NVIC_ISER3 = interruptSave[3]; } #endif } } // Disable the SPI bus static void end(); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setBitOrder(uint8_t bitOrder); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setDataMode(uint8_t dataMode); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setClockDivider(uint8_t clockDiv) { if (clockDiv == SPI_CLOCK_DIV2) { setClockDivider_noInline(SPISettings(12000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV4) { setClockDivider_noInline(SPISettings(4000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV8) { setClockDivider_noInline(SPISettings(2000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV16) { setClockDivider_noInline(SPISettings(1000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV32) { setClockDivider_noInline(SPISettings(500000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV64) { setClockDivider_noInline(SPISettings(250000, MSBFIRST, SPI_MODE0).ctar); } else { /* clockDiv == SPI_CLOCK_DIV128 */ setClockDivider_noInline(SPISettings(125000, MSBFIRST, SPI_MODE0).ctar); } } static void setClockDivider_noInline(uint32_t clk); // These undocumented functions should not be used. SPI.transfer() // polls the hardware flag which is automatically cleared as the // AVR responds to SPI's interrupt inline static void attachInterrupt() { } inline static void detachInterrupt() { } // Teensy 3.x can use alternate pins for these 3 SPI signals. inline static void setMOSI(uint8_t pin) __attribute__((always_inline)) { SPCR1.setMOSI(pin); } inline static void setMISO(uint8_t pin) __attribute__((always_inline)) { SPCR1.setMISO(pin); } inline static void setSCK(uint8_t pin) __attribute__((always_inline)) { SPCR1.setSCK(pin); } // return true if "pin" has special chip select capability static bool pinIsChipSelect(uint8_t pin); // return true if both pin1 and pin2 have independent chip select capability static bool pinIsChipSelect(uint8_t pin1, uint8_t pin2); // configure a pin for chip select and return its SPI_MCR_PCSIS bitmask static uint8_t setCS(uint8_t pin); private: static uint8_t interruptMasksUsed; static uint32_t interruptMask[(NVIC_NUM_INTERRUPTS+31)/32]; static uint32_t interruptSave[(NVIC_NUM_INTERRUPTS+31)/32]; #ifdef SPI_TRANSACTION_MISMATCH_LED static uint8_t inTransactionFlag; #endif }; class SPI2Class { public: // Initialize the SPI library static void begin(); // If SPI is to used from within an interrupt, this function registers // that interrupt with the SPI library, so beginTransaction() can // prevent conflicts. The input interruptNumber is the number used // with attachInterrupt. If SPI is used from a different interrupt // (eg, a timer), interruptNumber should be 255. static void usingInterrupt(uint8_t n) { if (n == 3 || n == 4 || n == 24 || n == 33) { usingInterrupt(IRQ_PORTA); } else if (n == 0 || n == 1 || (n >= 16 && n <= 19) || n == 25 || n == 32) { usingInterrupt(IRQ_PORTB); } else if ((n >= 9 && n <= 13) || n == 15 || n == 22 || n == 23 || (n >= 27 && n <= 30)) { usingInterrupt(IRQ_PORTC); } else if (n == 2 || (n >= 5 && n <= 8) || n == 14 || n == 20 || n == 21) { usingInterrupt(IRQ_PORTD); } else if (n == 26 || n == 31) { usingInterrupt(IRQ_PORTE); } } static void usingInterrupt(IRQ_NUMBER_t interruptName); static void notUsingInterrupt(IRQ_NUMBER_t interruptName); // Before using SPI.transfer() or asserting chip select pins, // this function is used to gain exclusive access to the SPI bus // and configure the correct settings. inline static void beginTransaction(SPISettings settings) { if (interruptMasksUsed) { __disable_irq(); if (interruptMasksUsed & 0x01) { interruptSave[0] = NVIC_ICER0 & interruptMask[0]; NVIC_ICER0 = interruptSave[0]; } #if NVIC_NUM_INTERRUPTS > 32 if (interruptMasksUsed & 0x02) { interruptSave[1] = NVIC_ICER1 & interruptMask[1]; NVIC_ICER1 = interruptSave[1]; } #endif #if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2) if (interruptMasksUsed & 0x04) { interruptSave[2] = NVIC_ICER2 & interruptMask[2]; NVIC_ICER2 = interruptSave[2]; } #endif #if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3) if (interruptMasksUsed & 0x08) { interruptSave[3] = NVIC_ICER3 & interruptMask[3]; NVIC_ICER3 = interruptSave[3]; } #endif __enable_irq(); } #ifdef SPI_TRANSACTION_MISMATCH_LED if (inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 1; #endif if (SPI2_CTAR0 != settings.ctar) { SPI2_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); SPI2_CTAR0 = settings.ctar; SPI2_CTAR1 = settings.ctar| SPI_CTAR_FMSZ(8); SPI2_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F); } } // Write to the SPI bus (MOSI pin) and also receive (MISO pin) inline static uint8_t transfer(uint8_t data) { SPI2_SR = SPI_SR_TCF; SPI2_PUSHR = data; while (!(SPI2_SR & SPI_SR_TCF)) ; // wait return SPI2_POPR; } inline static uint16_t transfer16(uint16_t data) { SPI2_SR = SPI_SR_TCF; SPI2_PUSHR = data | SPI_PUSHR_CTAS(1); while (!(SPI2_SR & SPI_SR_TCF)) ; // wait return SPI2_POPR; } inline static void transfer(void *buf, size_t count) { uint8_t *p = (uint8_t *)buf; while (count--) { *p = transfer(*p); p++; } } // After performing a group of transfers and releasing the chip select // signal, this function allows others to access the SPI bus inline static void endTransaction(void) { #ifdef SPI_TRANSACTION_MISMATCH_LED if (!inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 0; #endif if (interruptMasksUsed) { if (interruptMasksUsed & 0x01) { NVIC_ISER0 = interruptSave[0]; } #if NVIC_NUM_INTERRUPTS > 32 if (interruptMasksUsed & 0x02) { NVIC_ISER1 = interruptSave[1]; } #endif #if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2) if (interruptMasksUsed & 0x04) { NVIC_ISER2 = interruptSave[2]; } #endif #if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3) if (interruptMasksUsed & 0x08) { NVIC_ISER3 = interruptSave[3]; } #endif } } // Disable the SPI bus static void end(); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setBitOrder(uint8_t bitOrder); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setDataMode(uint8_t dataMode); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setClockDivider(uint8_t clockDiv) { if (clockDiv == SPI_CLOCK_DIV2) { setClockDivider_noInline(SPISettings(12000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV4) { setClockDivider_noInline(SPISettings(4000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV8) { setClockDivider_noInline(SPISettings(2000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV16) { setClockDivider_noInline(SPISettings(1000000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV32) { setClockDivider_noInline(SPISettings(500000, MSBFIRST, SPI_MODE0).ctar); } else if (clockDiv == SPI_CLOCK_DIV64) { setClockDivider_noInline(SPISettings(250000, MSBFIRST, SPI_MODE0).ctar); } else { /* clockDiv == SPI_CLOCK_DIV128 */ setClockDivider_noInline(SPISettings(125000, MSBFIRST, SPI_MODE0).ctar); } } static void setClockDivider_noInline(uint32_t clk); // These undocumented functions should not be used. SPI.transfer() // polls the hardware flag which is automatically cleared as the // AVR responds to SPI's interrupt inline static void attachInterrupt() { } inline static void detachInterrupt() { } // Teensy 3.x can use alternate pins for these 3 SPI signals. inline static void setMOSI(uint8_t pin) __attribute__((always_inline)) { SPCR2.setMOSI(pin); } inline static void setMISO(uint8_t pin) __attribute__((always_inline)) { SPCR2.setMISO(pin); } inline static void setSCK(uint8_t pin) __attribute__((always_inline)) { SPCR2.setSCK(pin); } // return true if "pin" has special chip select capability static uint8_t pinIsChipSelect(uint8_t pin); // return true if both pin1 and pin2 have independent chip select capability static bool pinIsChipSelect(uint8_t pin1, uint8_t pin2); // configure a pin for chip select and return its SPI_MCR_PCSIS bitmask static uint8_t setCS(uint8_t pin); private: static uint8_t interruptMasksUsed; static uint32_t interruptMask[(NVIC_NUM_INTERRUPTS+31)/32]; static uint32_t interruptSave[(NVIC_NUM_INTERRUPTS+31)/32]; #ifdef SPI_TRANSACTION_MISMATCH_LED static uint8_t inTransactionFlag; #endif }; #endif /**********************************************************/ /* 32 bit Teensy-LC */ /**********************************************************/ #elif defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISL) class SPISettings { public: SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { if (__builtin_constant_p(clock)) { init_AlwaysInline(clock, bitOrder, dataMode); } else { init_MightInline(clock, bitOrder, dataMode); } } SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } private: void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { init_AlwaysInline(clock, bitOrder, dataMode); } void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) { uint8_t c = SPI_C1_MSTR | SPI_C1_SPE; if (dataMode & 0x04) c |= SPI_C1_CPHA; if (dataMode & 0x08) c |= SPI_C1_CPOL; if (bitOrder == LSBFIRST) c |= SPI_C1_LSBFE; c1 = c; if (__builtin_constant_p(clock)) { if (clock >= F_BUS / 2) { c = SPI_BR_SPPR(0) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 4) { c = SPI_BR_SPPR(1) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 6) { c = SPI_BR_SPPR(2) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 8) { c = SPI_BR_SPPR(3) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 10) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 12) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 14) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 16) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(0); } else if (clock >= F_BUS / 20) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(1); } else if (clock >= F_BUS / 24) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(1); } else if (clock >= F_BUS / 28) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(1); } else if (clock >= F_BUS / 32) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(1); } else if (clock >= F_BUS / 40) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(2); } else if (clock >= F_BUS / 48) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(2); } else if (clock >= F_BUS / 56) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(2); } else if (clock >= F_BUS / 64) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(2); } else if (clock >= F_BUS / 80) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(3); } else if (clock >= F_BUS / 96) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(3); } else if (clock >= F_BUS / 112) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(3); } else if (clock >= F_BUS / 128) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(3); } else if (clock >= F_BUS / 160) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(4); } else if (clock >= F_BUS / 192) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(4); } else if (clock >= F_BUS / 224) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(4); } else if (clock >= F_BUS / 256) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(4); } else if (clock >= F_BUS / 320) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(5); } else if (clock >= F_BUS / 384) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(5); } else if (clock >= F_BUS / 448) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(5); } else if (clock >= F_BUS / 512) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(5); } else if (clock >= F_BUS / 640) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(6); } else /* F_BUS / 768 */ { c = SPI_BR_SPPR(5) | SPI_BR_SPR(6); } } else { for (uint32_t i=0; i<30; i++) { c = br_clock_table[i]; if (clock >= F_BUS / br_div_table[i]) break; } } br0 = c; if (__builtin_constant_p(clock)) { if (clock >= (F_PLL/2) / 2) { c = SPI_BR_SPPR(0) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 4) { c = SPI_BR_SPPR(1) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 6) { c = SPI_BR_SPPR(2) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 8) { c = SPI_BR_SPPR(3) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 10) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 12) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 14) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 16) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(0); } else if (clock >= (F_PLL/2) / 20) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(1); } else if (clock >= (F_PLL/2) / 24) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(1); } else if (clock >= (F_PLL/2) / 28) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(1); } else if (clock >= (F_PLL/2) / 32) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(1); } else if (clock >= (F_PLL/2) / 40) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(2); } else if (clock >= (F_PLL/2) / 48) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(2); } else if (clock >= (F_PLL/2) / 56) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(2); } else if (clock >= (F_PLL/2) / 64) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(2); } else if (clock >= (F_PLL/2) / 80) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(3); } else if (clock >= (F_PLL/2) / 96) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(3); } else if (clock >= (F_PLL/2) / 112) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(3); } else if (clock >= (F_PLL/2) / 128) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(3); } else if (clock >= (F_PLL/2) / 160) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(4); } else if (clock >= (F_PLL/2) / 192) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(4); } else if (clock >= (F_PLL/2) / 224) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(4); } else if (clock >= (F_PLL/2) / 256) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(4); } else if (clock >= (F_PLL/2) / 320) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(5); } else if (clock >= (F_PLL/2) / 384) { c = SPI_BR_SPPR(5) | SPI_BR_SPR(5); } else if (clock >= (F_PLL/2) / 448) { c = SPI_BR_SPPR(6) | SPI_BR_SPR(5); } else if (clock >= (F_PLL/2) / 512) { c = SPI_BR_SPPR(7) | SPI_BR_SPR(5); } else if (clock >= (F_PLL/2) / 640) { c = SPI_BR_SPPR(4) | SPI_BR_SPR(6); } else /* (F_PLL/2) / 768 */ { c = SPI_BR_SPPR(5) | SPI_BR_SPR(6); } } else { for (uint32_t i=0; i<30; i++) { c = br_clock_table[i]; if (clock >= (F_PLL/2) / br_div_table[i]) break; } } br1 = c; } static const uint8_t br_clock_table[30]; static const uint16_t br_div_table[30]; uint8_t c1, br0, br1; friend class SPIClass; friend class SPI1Class; }; class SPIClass { public: // Initialize the SPI library static void begin(); // If SPI is to used from within an interrupt, this function registers // that interrupt with the SPI library, so beginTransaction() can // prevent conflicts. The input interruptNumber is the number used // with attachInterrupt. If SPI is used from a different interrupt // (eg, a timer), interruptNumber should be 255. static void usingInterrupt(uint8_t n) { if (n == 3 || n == 4) { usingInterrupt(IRQ_PORTA); } else if ((n >= 2 && n <= 15) || (n >= 20 && n <= 23)) { usingInterrupt(IRQ_PORTCD); } } static void usingInterrupt(IRQ_NUMBER_t interruptName) { uint32_t n = (uint32_t)interruptName; if (n < NVIC_NUM_INTERRUPTS) interruptMask |= (1 << n); } static void notUsingInterrupt(IRQ_NUMBER_t interruptName) { uint32_t n = (uint32_t)interruptName; if (n < NVIC_NUM_INTERRUPTS) interruptMask &= ~(1 << n); } // Before using SPI.transfer() or asserting chip select pins, // this function is used to gain exclusive access to the SPI bus // and configure the correct settings. inline static void beginTransaction(SPISettings settings) { if (interruptMask) { __disable_irq(); interruptSave = NVIC_ICER0 & interruptMask; NVIC_ICER0 = interruptSave; __enable_irq(); } #ifdef SPI_TRANSACTION_MISMATCH_LED if (inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 1; #endif SPI0_C1 = settings.c1; SPI0_BR = settings.br0; } // Write to the SPI bus (MOSI pin) and also receive (MISO pin) inline static uint8_t transfer(uint8_t data) { SPI0_DL = data; while (!(SPI0_S & SPI_S_SPRF)) ; // wait return SPI0_DL; } inline static uint16_t transfer16(uint16_t data) { SPI0_C2 = SPI_C2_SPIMODE; SPI0_S; SPI0_DL = data; SPI0_DH = data >> 8; while (!(SPI0_S & SPI_S_SPRF)) ; // wait uint16_t r = SPI0_DL | (SPI0_DH << 8); SPI0_C2 = 0; SPI0_S; return r; } inline static void transfer(void *buf, size_t count) { if (count == 0) return; uint8_t *p = (uint8_t *)buf; while (!(SPI0_S & SPI_S_SPTEF)) ; // wait SPI0_DL = *p; while (--count > 0) { uint8_t out = *(p + 1); while (!(SPI0_S & SPI_S_SPTEF)) ; // wait __disable_irq(); SPI0_DL = out; while (!(SPI0_S & SPI_S_SPRF)) ; // wait uint8_t in = SPI0_DL; __enable_irq(); *p++ = in; } while (!(SPI0_S & SPI_S_SPRF)) ; // wait *p = SPDR; } // After performing a group of transfers and releasing the chip select // signal, this function allows others to access the SPI bus inline static void endTransaction(void) { #ifdef SPI_TRANSACTION_MISMATCH_LED if (!inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 0; #endif if (interruptMask) { NVIC_ISER0 = interruptSave; } } // Disable the SPI bus static void end(); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setBitOrder(uint8_t bitOrder) { uint8_t c = SPI0_C1 | SPI_C1_SPE; if (bitOrder == LSBFIRST) c |= SPI_C1_LSBFE; else c &= ~SPI_C1_LSBFE; SPI0_C1 = c; } // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setDataMode(uint8_t dataMode) { uint8_t c = SPI0_C1 | SPI_C1_SPE; if (dataMode & 0x04) c |= SPI_C1_CPHA; else c &= ~SPI_C1_CPHA; if (dataMode & 0x08) c |= SPI_C1_CPOL; else c &= ~SPI_C1_CPOL; SPI0_C1 = c; } // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setClockDivider(uint8_t clockDiv) { if (clockDiv == SPI_CLOCK_DIV2) { SPI0_BR = (SPISettings(12000000, MSBFIRST, SPI_MODE0).br0); } else if (clockDiv == SPI_CLOCK_DIV4) { SPI0_BR = (SPISettings(4000000, MSBFIRST, SPI_MODE0).br0); } else if (clockDiv == SPI_CLOCK_DIV8) { SPI0_BR = (SPISettings(2000000, MSBFIRST, SPI_MODE0).br0); } else if (clockDiv == SPI_CLOCK_DIV16) { SPI0_BR = (SPISettings(1000000, MSBFIRST, SPI_MODE0).br0); } else if (clockDiv == SPI_CLOCK_DIV32) { SPI0_BR = (SPISettings(500000, MSBFIRST, SPI_MODE0).br0); } else if (clockDiv == SPI_CLOCK_DIV64) { SPI0_BR = (SPISettings(250000, MSBFIRST, SPI_MODE0).br0); } else { /* clockDiv == SPI_CLOCK_DIV128 */ SPI0_BR = (SPISettings(125000, MSBFIRST, SPI_MODE0).br0); } } // These undocumented functions should not be used. SPI.transfer() // polls the hardware flag which is automatically cleared as the // AVR responds to SPI's interrupt inline static void attachInterrupt() { } inline static void detachInterrupt() { } // Teensy LC can use alternate pins for these 3 SPI signals. inline static void setMOSI(uint8_t pin) __attribute__((always_inline)) { SPCR.setMOSI(pin); } inline static void setMISO(uint8_t pin) __attribute__((always_inline)) { SPCR.setMISO(pin); } inline static void setSCK(uint8_t pin) __attribute__((always_inline)) { SPCR.setSCK(pin); } // return true if "pin" has special chip select capability static bool pinIsChipSelect(uint8_t pin) { return (pin == 10 || pin == 2); } // return true if both pin1 and pin2 have independent chip select capability static bool pinIsChipSelect(uint8_t pin1, uint8_t pin2) { return false; } // configure a pin for chip select and return its SPI_MCR_PCSIS bitmask static uint8_t setCS(uint8_t pin); private: static uint32_t interruptMask; static uint32_t interruptSave; #ifdef SPI_TRANSACTION_MISMATCH_LED static uint8_t inTransactionFlag; #endif }; class SPI1Class { public: // Initialize the SPI library static void begin(); // If SPI is to used from within an interrupt, this function registers // that interrupt with the SPI library, so beginTransaction() can // prevent conflicts. The input interruptNumber is the number used // with attachInterrupt. If SPI is used from a different interrupt // (eg, a timer), interruptNumber should be 255. static void usingInterrupt(uint8_t n) { if (n == 3 || n == 4) { usingInterrupt(IRQ_PORTA); } else if ((n >= 2 && n <= 15) || (n >= 20 && n <= 23)) { usingInterrupt(IRQ_PORTCD); } } static void usingInterrupt(IRQ_NUMBER_t interruptName) { uint32_t n = (uint32_t)interruptName; if (n < NVIC_NUM_INTERRUPTS) interruptMask |= (1 << n); } static void notUsingInterrupt(IRQ_NUMBER_t interruptName) { uint32_t n = (uint32_t)interruptName; if (n < NVIC_NUM_INTERRUPTS) interruptMask &= ~(1 << n); } // Before using SPI.transfer() or asserting chip select pins, // this function is used to gain exclusive access to the SPI bus // and configure the correct settings. inline static void beginTransaction(SPISettings settings) { if (interruptMask) { __disable_irq(); interruptSave = NVIC_ICER0 & interruptMask; NVIC_ICER0 = interruptSave; __enable_irq(); } #ifdef SPI_TRANSACTION_MISMATCH_LED if (inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 1; #endif SPI1_C1 = settings.c1; SPI1_BR = settings.br1; } // Write to the SPI bus (MOSI pin) and also receive (MISO pin) inline static uint8_t transfer(uint8_t data) { SPI1_DL = data; while (!(SPI1_S & SPI_S_SPRF)) ; // wait return SPI1_DL; } inline static uint16_t transfer16(uint16_t data) { SPI1_C2 = SPI_C2_SPIMODE; SPI1_S; SPI1_DL = data; SPI1_DH = data >> 8; while (!(SPI1_S & SPI_S_SPRF)) ; // wait uint16_t r = SPI1_DL | (SPI1_DH << 8); SPI1_C2 = 0; SPI1_S; return r; } inline static void transfer(void *buf, size_t count) { if (count == 0) return; uint8_t *p = (uint8_t *)buf; while (!(SPI1_S & SPI_S_SPTEF)) ; // wait SPI1_DL = *p; while (--count > 0) { uint8_t out = *(p + 1); while (!(SPI1_S & SPI_S_SPTEF)) ; // wait __disable_irq(); SPI1_DL = out; while (!(SPI1_S & SPI_S_SPRF)) ; // wait uint8_t in = SPI1_DL; __enable_irq(); *p++ = in; } while (!(SPI1_S & SPI_S_SPRF)) ; // wait *p = SPDR; } // After performing a group of transfers and releasing the chip select // signal, this function allows others to access the SPI bus inline static void endTransaction(void) { #ifdef SPI_TRANSACTION_MISMATCH_LED if (!inTransactionFlag) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } inTransactionFlag = 0; #endif if (interruptMask) { NVIC_ISER0 = interruptSave; } } // Disable the SPI bus static void end(); // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setBitOrder(uint8_t bitOrder) { uint8_t c = SPI1_C1 | SPI_C1_SPE; if (bitOrder == LSBFIRST) c |= SPI_C1_LSBFE; else c &= ~SPI_C1_LSBFE; SPI1_C1 = c; } // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. static void setDataMode(uint8_t dataMode) { uint8_t c = SPI1_C1 | SPI_C1_SPE; if (dataMode & 0x04) c |= SPI_C1_CPHA; else c &= ~SPI_C1_CPHA; if (dataMode & 0x08) c |= SPI_C1_CPOL; else c &= ~SPI_C1_CPOL; SPI1_C1 = c; } // This function is deprecated. New applications should use // beginTransaction() to configure SPI settings. inline static void setClockDivider(uint8_t clockDiv) { if (clockDiv == SPI_CLOCK_DIV2) { SPI1_BR = (SPISettings(12000000, MSBFIRST, SPI_MODE0).br1); } else if (clockDiv == SPI_CLOCK_DIV4) { SPI1_BR = (SPISettings(4000000, MSBFIRST, SPI_MODE0).br1); } else if (clockDiv == SPI_CLOCK_DIV8) { SPI1_BR = (SPISettings(2000000, MSBFIRST, SPI_MODE0).br1); } else if (clockDiv == SPI_CLOCK_DIV16) { SPI1_BR = (SPISettings(1000000, MSBFIRST, SPI_MODE0).br1); } else if (clockDiv == SPI_CLOCK_DIV32) { SPI1_BR = (SPISettings(500000, MSBFIRST, SPI_MODE0).br1); } else if (clockDiv == SPI_CLOCK_DIV64) { SPI1_BR = (SPISettings(250000, MSBFIRST, SPI_MODE0).br1); } else { /* clockDiv == SPI_CLOCK_DIV128 */ SPI1_BR = (SPISettings(125000, MSBFIRST, SPI_MODE0).br1); } } // These undocumented functions should not be used. SPI.transfer() // polls the hardware flag which is automatically cleared as the // AVR responds to SPI's interrupt inline static void attachInterrupt() { } inline static void detachInterrupt() { } // Teensy LC can use alternate pins for these 3 SPI signals. inline static void setMOSI(uint8_t pin) __attribute__((always_inline)) { SPCR1.setMOSI(pin); } inline static void setMISO(uint8_t pin) __attribute__((always_inline)) { SPCR1.setMISO(pin); } inline static void setSCK(uint8_t pin) __attribute__((always_inline)) { SPCR1.setSCK(pin); } // return true if "pin" has special chip select capability static bool pinIsChipSelect(uint8_t pin) { return (pin == 6); } // return true if both pin1 and pin2 have independent chip select capability static bool pinIsChipSelect(uint8_t pin1, uint8_t pin2) { return false; } // configure a pin for chip select and return its SPI_MCR_PCSIS bitmask static uint8_t setCS(uint8_t pin); private: static uint32_t interruptMask; static uint32_t interruptSave; #ifdef SPI_TRANSACTION_MISMATCH_LED static uint8_t inTransactionFlag; #endif }; /**********************************************************/ /* 32 bit Arduino Due */ /**********************************************************/ #elif defined(__arm__) && defined(__SAM3X8E__) #undef SPI_MODE0 #undef SPI_MODE1 #undef SPI_MODE2 #undef SPI_MODE3 #define SPI_MODE0 0x02 #define SPI_MODE1 0x00 #define SPI_MODE2 0x03 #define SPI_MODE3 0x01 #undef SPI_CLOCK_DIV2 #undef SPI_CLOCK_DIV4 #undef SPI_CLOCK_DIV8 #undef SPI_CLOCK_DIV16 #undef SPI_CLOCK_DIV32 #undef SPI_CLOCK_DIV64 #undef SPI_CLOCK_DIV128 #define SPI_CLOCK_DIV2 11 #define SPI_CLOCK_DIV4 21 #define SPI_CLOCK_DIV8 42 #define SPI_CLOCK_DIV16 84 #define SPI_CLOCK_DIV32 168 #define SPI_CLOCK_DIV64 255 #define SPI_CLOCK_DIV128 255 enum SPITransferMode { SPI_CONTINUE, SPI_LAST }; class SPISettings { public: SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) { if (__builtin_constant_p(clock)) { init_AlwaysInline(clock, bitOrder, dataMode); } else { init_MightInline(clock, bitOrder, dataMode); } } SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } private: void init_MightInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) { init_AlwaysInline(clock, bitOrder, dataMode); } void init_AlwaysInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) { uint8_t div; border = bitOrder; if (__builtin_constant_p(clock)) { if (clock >= F_CPU / 2) div = 2; else if (clock >= F_CPU / 3) div = 3; else if (clock >= F_CPU / 4) div = 4; else if (clock >= F_CPU / 5) div = 5; else if (clock >= F_CPU / 6) div = 6; else if (clock >= F_CPU / 7) div = 7; else if (clock >= F_CPU / 8) div = 8; else if (clock >= F_CPU / 9) div = 9; else if (clock >= F_CPU / 10) div = 10; else if (clock >= F_CPU / 11) div = 11; else if (clock >= F_CPU / 12) div = 12; else if (clock >= F_CPU / 13) div = 13; else if (clock >= F_CPU / 14) div = 14; else if (clock >= F_CPU / 15) div = 15; else if (clock >= F_CPU / 16) div = 16; else if (clock >= F_CPU / 17) div = 17; else if (clock >= F_CPU / 18) div = 18; else if (clock >= F_CPU / 19) div = 19; else if (clock >= F_CPU / 20) div = 20; else if (clock >= F_CPU / 21) div = 21; else if (clock >= F_CPU / 22) div = 22; else if (clock >= F_CPU / 23) div = 23; else if (clock >= F_CPU / 24) div = 24; else if (clock >= F_CPU / 25) div = 25; else if (clock >= F_CPU / 26) div = 26; else if (clock >= F_CPU / 27) div = 27; else if (clock >= F_CPU / 28) div = 28; else if (clock >= F_CPU / 29) div = 29; else if (clock >= F_CPU / 30) div = 30; else if (clock >= F_CPU / 31) div = 31; else if (clock >= F_CPU / 32) div = 32; else if (clock >= F_CPU / 33) div = 33; else if (clock >= F_CPU / 34) div = 34; else if (clock >= F_CPU / 35) div = 35; else if (clock >= F_CPU / 36) div = 36; else if (clock >= F_CPU / 37) div = 37; else if (clock >= F_CPU / 38) div = 38; else if (clock >= F_CPU / 39) div = 39; else if (clock >= F_CPU / 40) div = 40; else if (clock >= F_CPU / 41) div = 41; else if (clock >= F_CPU / 42) div = 42; else if (clock >= F_CPU / 43) div = 43; else if (clock >= F_CPU / 44) div = 44; else if (clock >= F_CPU / 45) div = 45; else if (clock >= F_CPU / 46) div = 46; else if (clock >= F_CPU / 47) div = 47; else if (clock >= F_CPU / 48) div = 48; else if (clock >= F_CPU / 49) div = 49; else if (clock >= F_CPU / 50) div = 50; else if (clock >= F_CPU / 51) div = 51; else if (clock >= F_CPU / 52) div = 52; else if (clock >= F_CPU / 53) div = 53; else if (clock >= F_CPU / 54) div = 54; else if (clock >= F_CPU / 55) div = 55; else if (clock >= F_CPU / 56) div = 56; else if (clock >= F_CPU / 57) div = 57; else if (clock >= F_CPU / 58) div = 58; else if (clock >= F_CPU / 59) div = 59; else if (clock >= F_CPU / 60) div = 60; else if (clock >= F_CPU / 61) div = 61; else if (clock >= F_CPU / 62) div = 62; else if (clock >= F_CPU / 63) div = 63; else if (clock >= F_CPU / 64) div = 64; else if (clock >= F_CPU / 65) div = 65; else if (clock >= F_CPU / 66) div = 66; else if (clock >= F_CPU / 67) div = 67; else if (clock >= F_CPU / 68) div = 68; else if (clock >= F_CPU / 69) div = 69; else if (clock >= F_CPU / 70) div = 70; else if (clock >= F_CPU / 71) div = 71; else if (clock >= F_CPU / 72) div = 72; else if (clock >= F_CPU / 73) div = 73; else if (clock >= F_CPU / 74) div = 74; else if (clock >= F_CPU / 75) div = 75; else if (clock >= F_CPU / 76) div = 76; else if (clock >= F_CPU / 77) div = 77; else if (clock >= F_CPU / 78) div = 78; else if (clock >= F_CPU / 79) div = 79; else if (clock >= F_CPU / 80) div = 80; else if (clock >= F_CPU / 81) div = 81; else if (clock >= F_CPU / 82) div = 82; else if (clock >= F_CPU / 83) div = 83; else if (clock >= F_CPU / 84) div = 84; else if (clock >= F_CPU / 85) div = 85; else if (clock >= F_CPU / 86) div = 86; else if (clock >= F_CPU / 87) div = 87; else if (clock >= F_CPU / 88) div = 88; else if (clock >= F_CPU / 89) div = 89; else if (clock >= F_CPU / 90) div = 90; else if (clock >= F_CPU / 91) div = 91; else if (clock >= F_CPU / 92) div = 92; else if (clock >= F_CPU / 93) div = 93; else if (clock >= F_CPU / 94) div = 94; else if (clock >= F_CPU / 95) div = 95; else if (clock >= F_CPU / 96) div = 96; else if (clock >= F_CPU / 97) div = 97; else if (clock >= F_CPU / 98) div = 98; else if (clock >= F_CPU / 99) div = 99; else if (clock >= F_CPU / 100) div = 100; else if (clock >= F_CPU / 101) div = 101; else if (clock >= F_CPU / 102) div = 102; else if (clock >= F_CPU / 103) div = 103; else if (clock >= F_CPU / 104) div = 104; else if (clock >= F_CPU / 105) div = 105; else if (clock >= F_CPU / 106) div = 106; else if (clock >= F_CPU / 107) div = 107; else if (clock >= F_CPU / 108) div = 108; else if (clock >= F_CPU / 109) div = 109; else if (clock >= F_CPU / 110) div = 110; else if (clock >= F_CPU / 111) div = 111; else if (clock >= F_CPU / 112) div = 112; else if (clock >= F_CPU / 113) div = 113; else if (clock >= F_CPU / 114) div = 114; else if (clock >= F_CPU / 115) div = 115; else if (clock >= F_CPU / 116) div = 116; else if (clock >= F_CPU / 117) div = 117; else if (clock >= F_CPU / 118) div = 118; else if (clock >= F_CPU / 119) div = 119; else if (clock >= F_CPU / 120) div = 120; else if (clock >= F_CPU / 121) div = 121; else if (clock >= F_CPU / 122) div = 122; else if (clock >= F_CPU / 123) div = 123; else if (clock >= F_CPU / 124) div = 124; else if (clock >= F_CPU / 125) div = 125; else if (clock >= F_CPU / 126) div = 126; else if (clock >= F_CPU / 127) div = 127; else if (clock >= F_CPU / 128) div = 128; else if (clock >= F_CPU / 129) div = 129; else if (clock >= F_CPU / 130) div = 130; else if (clock >= F_CPU / 131) div = 131; else if (clock >= F_CPU / 132) div = 132; else if (clock >= F_CPU / 133) div = 133; else if (clock >= F_CPU / 134) div = 134; else if (clock >= F_CPU / 135) div = 135; else if (clock >= F_CPU / 136) div = 136; else if (clock >= F_CPU / 137) div = 137; else if (clock >= F_CPU / 138) div = 138; else if (clock >= F_CPU / 139) div = 139; else if (clock >= F_CPU / 140) div = 140; else if (clock >= F_CPU / 141) div = 141; else if (clock >= F_CPU / 142) div = 142; else if (clock >= F_CPU / 143) div = 143; else if (clock >= F_CPU / 144) div = 144; else if (clock >= F_CPU / 145) div = 145; else if (clock >= F_CPU / 146) div = 146; else if (clock >= F_CPU / 147) div = 147; else if (clock >= F_CPU / 148) div = 148; else if (clock >= F_CPU / 149) div = 149; else if (clock >= F_CPU / 150) div = 150; else if (clock >= F_CPU / 151) div = 151; else if (clock >= F_CPU / 152) div = 152; else if (clock >= F_CPU / 153) div = 153; else if (clock >= F_CPU / 154) div = 154; else if (clock >= F_CPU / 155) div = 155; else if (clock >= F_CPU / 156) div = 156; else if (clock >= F_CPU / 157) div = 157; else if (clock >= F_CPU / 158) div = 158; else if (clock >= F_CPU / 159) div = 159; else if (clock >= F_CPU / 160) div = 160; else if (clock >= F_CPU / 161) div = 161; else if (clock >= F_CPU / 162) div = 162; else if (clock >= F_CPU / 163) div = 163; else if (clock >= F_CPU / 164) div = 164; else if (clock >= F_CPU / 165) div = 165; else if (clock >= F_CPU / 166) div = 166; else if (clock >= F_CPU / 167) div = 167; else if (clock >= F_CPU / 168) div = 168; else if (clock >= F_CPU / 169) div = 169; else if (clock >= F_CPU / 170) div = 170; else if (clock >= F_CPU / 171) div = 171; else if (clock >= F_CPU / 172) div = 172; else if (clock >= F_CPU / 173) div = 173; else if (clock >= F_CPU / 174) div = 174; else if (clock >= F_CPU / 175) div = 175; else if (clock >= F_CPU / 176) div = 176; else if (clock >= F_CPU / 177) div = 177; else if (clock >= F_CPU / 178) div = 178; else if (clock >= F_CPU / 179) div = 179; else if (clock >= F_CPU / 180) div = 180; else if (clock >= F_CPU / 181) div = 181; else if (clock >= F_CPU / 182) div = 182; else if (clock >= F_CPU / 183) div = 183; else if (clock >= F_CPU / 184) div = 184; else if (clock >= F_CPU / 185) div = 185; else if (clock >= F_CPU / 186) div = 186; else if (clock >= F_CPU / 187) div = 187; else if (clock >= F_CPU / 188) div = 188; else if (clock >= F_CPU / 189) div = 189; else if (clock >= F_CPU / 190) div = 190; else if (clock >= F_CPU / 191) div = 191; else if (clock >= F_CPU / 192) div = 192; else if (clock >= F_CPU / 193) div = 193; else if (clock >= F_CPU / 194) div = 194; else if (clock >= F_CPU / 195) div = 195; else if (clock >= F_CPU / 196) div = 196; else if (clock >= F_CPU / 197) div = 197; else if (clock >= F_CPU / 198) div = 198; else if (clock >= F_CPU / 199) div = 199; else if (clock >= F_CPU / 200) div = 200; else if (clock >= F_CPU / 201) div = 201; else if (clock >= F_CPU / 202) div = 202; else if (clock >= F_CPU / 203) div = 203; else if (clock >= F_CPU / 204) div = 204; else if (clock >= F_CPU / 205) div = 205; else if (clock >= F_CPU / 206) div = 206; else if (clock >= F_CPU / 207) div = 207; else if (clock >= F_CPU / 208) div = 208; else if (clock >= F_CPU / 209) div = 209; else if (clock >= F_CPU / 210) div = 210; else if (clock >= F_CPU / 211) div = 211; else if (clock >= F_CPU / 212) div = 212; else if (clock >= F_CPU / 213) div = 213; else if (clock >= F_CPU / 214) div = 214; else if (clock >= F_CPU / 215) div = 215; else if (clock >= F_CPU / 216) div = 216; else if (clock >= F_CPU / 217) div = 217; else if (clock >= F_CPU / 218) div = 218; else if (clock >= F_CPU / 219) div = 219; else if (clock >= F_CPU / 220) div = 220; else if (clock >= F_CPU / 221) div = 221; else if (clock >= F_CPU / 222) div = 222; else if (clock >= F_CPU / 223) div = 223; else if (clock >= F_CPU / 224) div = 224; else if (clock >= F_CPU / 225) div = 225; else if (clock >= F_CPU / 226) div = 226; else if (clock >= F_CPU / 227) div = 227; else if (clock >= F_CPU / 228) div = 228; else if (clock >= F_CPU / 229) div = 229; else if (clock >= F_CPU / 230) div = 230; else if (clock >= F_CPU / 231) div = 231; else if (clock >= F_CPU / 232) div = 232; else if (clock >= F_CPU / 233) div = 233; else if (clock >= F_CPU / 234) div = 234; else if (clock >= F_CPU / 235) div = 235; else if (clock >= F_CPU / 236) div = 236; else if (clock >= F_CPU / 237) div = 237; else if (clock >= F_CPU / 238) div = 238; else if (clock >= F_CPU / 239) div = 239; else if (clock >= F_CPU / 240) div = 240; else if (clock >= F_CPU / 241) div = 241; else if (clock >= F_CPU / 242) div = 242; else if (clock >= F_CPU / 243) div = 243; else if (clock >= F_CPU / 244) div = 244; else if (clock >= F_CPU / 245) div = 245; else if (clock >= F_CPU / 246) div = 246; else if (clock >= F_CPU / 247) div = 247; else if (clock >= F_CPU / 248) div = 248; else if (clock >= F_CPU / 249) div = 249; else if (clock >= F_CPU / 250) div = 250; else if (clock >= F_CPU / 251) div = 251; else if (clock >= F_CPU / 252) div = 252; else if (clock >= F_CPU / 253) div = 253; else if (clock >= F_CPU / 254) div = 254; else /* clock >= F_CPU / 255 */ div = 255; /* #! /usr/bin/perl for ($i=2; $i<256; $i++) { printf "\t\t\telse if (clock >= F_CPU / %3d) div = %3d;\n", $i, $i; } */ } else { for (div=2; div<255; div++) { if (clock >= F_CPU / div) break; } } config = (dataMode & 3) | SPI_CSR_CSAAT | SPI_CSR_SCBR(div) | SPI_CSR_DLYBCT(1); } uint32_t config; BitOrder border; friend class SPIClass; }; class SPIClass { public: SPIClass(Spi *_spi, uint32_t _id, void(*_initCb)(void)); byte transfer(uint8_t _data, SPITransferMode _mode = SPI_LAST) { return transfer(BOARD_SPI_DEFAULT_SS, _data, _mode); } byte transfer(byte _channel, uint8_t _data, SPITransferMode _mode = SPI_LAST); // Transaction Functions void usingInterrupt(uint8_t interruptNumber); void beginTransaction(uint8_t pin, SPISettings settings); void beginTransaction(SPISettings settings) { beginTransaction(BOARD_SPI_DEFAULT_SS, settings); } void endTransaction(void); // SPI Configuration methods void attachInterrupt(void); void detachInterrupt(void); void begin(void); void end(void); // Attach/Detach pin to/from SPI controller void begin(uint8_t _pin); void end(uint8_t _pin); // These methods sets a parameter on a single pin void setBitOrder(uint8_t _pin, BitOrder); void setDataMode(uint8_t _pin, uint8_t); void setClockDivider(uint8_t _pin, uint8_t); // These methods sets the same parameters but on default pin BOARD_SPI_DEFAULT_SS void setBitOrder(BitOrder _order) { setBitOrder(BOARD_SPI_DEFAULT_SS, _order); }; void setDataMode(uint8_t _mode) { setDataMode(BOARD_SPI_DEFAULT_SS, _mode); }; void setClockDivider(uint8_t _div) { setClockDivider(BOARD_SPI_DEFAULT_SS, _div); }; private: void init(); Spi *spi; uint32_t id; BitOrder bitOrder[SPI_CHANNELS_NUM]; uint32_t divider[SPI_CHANNELS_NUM]; uint32_t mode[SPI_CHANNELS_NUM]; void (*initCb)(void); bool initialized; uint8_t interruptMode; // 0=none, 1=mask, 2=global uint8_t interruptMask; // bits 0:3=pin change uint8_t interruptSave; // temp storage, to restore state }; #endif extern SPIClass SPI; #if defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISL) extern SPI1Class SPI1; #endif #if defined(__MK64FX512__) || defined(__MK66FX1M0__) extern SPI1Class SPI1; #endif #endif