diff --git a/hardware/arduino/avr/libraries/SPI/SPI.cpp b/hardware/arduino/avr/libraries/SPI/SPI.cpp index 9a7a400e18f..ecd8bf6bfe7 100644 --- a/hardware/arduino/avr/libraries/SPI/SPI.cpp +++ b/hardware/arduino/avr/libraries/SPI/SPI.cpp @@ -1,6 +1,8 @@ /* * Copyright (c) 2010 by Cristian Maglie * Copyright (c) 2014 by Paul Stoffregen (Transaction API) + * Copyright (c) 2014 by Matthijs Kooijman (SPISettings AVR) + * Copyright (c) 2014 by Andrew J. Kroll (atomicity fixes) * SPI Master library for arduino. * * This file is free software; you can redistribute it and/or modify @@ -10,123 +12,184 @@ */ #include "SPI.h" -#include "pins_arduino.h" SPIClass SPI; - -uint8_t SPIClass::interruptMode = 0; +SPIflags_t SPIClass::modeFlags = {false, false, 0}; uint8_t SPIClass::interruptMask = 0; uint8_t SPIClass::interruptSave = 0; -#ifdef SPI_TRANSACTION_MISMATCH_LED -uint8_t SPIClass::inTransactionFlag = 0; -#endif -void SPIClass::begin() -{ - // Set SS to high so a connected chip will be "deselected" by default - digitalWrite(SS, HIGH); +void SPIClass::begin() { + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + if(!modeFlags.initialized) { + modeFlags.initialized = true; + // Set SS to high so a connected chip will be "deselected" by default + digitalWrite(SS, HIGH); - // When the SS pin is set as OUTPUT, it can be used as - // a general purpose output port (it doesn't influence - // SPI operations). - pinMode(SS, OUTPUT); + // When the SS pin is set as OUTPUT, it can be used as + // a general purpose output port (it doesn't influence + // SPI operations). + pinMode(SS, OUTPUT); - // Warning: if the SS pin ever becomes a LOW INPUT then SPI - // automatically switches to Slave, so the data direction of - // the SS pin MUST be kept as OUTPUT. - SPCR |= _BV(MSTR); - SPCR |= _BV(SPE); + // Warning: if the SS pin ever becomes a LOW INPUT then SPI + // automatically switches to Slave, so the data direction of + // the SS pin MUST be kept as OUTPUT. + SPCR |= _BV(MSTR); + SPCR |= _BV(SPE); - // Set direction register for SCK and MOSI pin. - // MISO pin automatically overrides to INPUT. - // By doing this AFTER enabling SPI, we avoid accidentally - // clocking in a single bit since the lines go directly - // from "input" to SPI control. - // http://code.google.com/p/arduino/issues/detail?id=888 - pinMode(SCK, OUTPUT); - pinMode(MOSI, OUTPUT); + // Set direction register for SCK and MOSI pin. + // MISO pin automatically overrides to INPUT. + // By doing this AFTER enabling SPI, we avoid accidentally + // clocking in a single bit since the lines go directly + // from "input" to SPI control. + // http://code.google.com/p/arduino/issues/detail?id=888 + pinMode(SCK, OUTPUT); + pinMode(MOSI, OUTPUT); + } + SREG = sreg; } void SPIClass::end() { - SPCR &= ~_BV(SPE); + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + if(!modeFlags.interruptMode && modeFlags.initialized) { + SPCR &= ~_BV(SPE); + modeFlags.initialized = false; +#ifdef SPI_TRANSACTION_MISMATCH_LED + modeFlags.inTransactionFlag = false; +#endif + } + SREG = sreg; } // mapping of interrupt numbers to bits within SPI_AVR_EIMSK #if defined(__AVR_ATmega32U4__) - #define SPI_INT0_MASK (1< 1) return; - - noInterrupts(); - switch (interruptNumber) { - #ifdef SPI_INT0_MASK - case 0: mask = SPI_INT0_MASK; break; - #endif - #ifdef SPI_INT1_MASK - case 1: mask = SPI_INT1_MASK; break; - #endif - #ifdef SPI_INT2_MASK - case 2: mask = SPI_INT2_MASK; break; - #endif - #ifdef SPI_INT3_MASK - case 3: mask = SPI_INT3_MASK; break; - #endif - #ifdef SPI_INT4_MASK - case 4: mask = SPI_INT4_MASK; break; - #endif - #ifdef SPI_INT5_MASK - case 5: mask = SPI_INT5_MASK; break; - #endif - #ifdef SPI_INT6_MASK - case 6: mask = SPI_INT6_MASK; break; - #endif - #ifdef SPI_INT7_MASK - case 7: mask = SPI_INT7_MASK; break; - #endif - default: - interruptMode = 2; - interrupts(); - return; - } - interruptMode = 1; - interruptMask |= mask; - interrupts(); +void SPIClass::usingInterrupt(uint8_t interruptNumber) { + uint8_t mask = 0; + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + switch(interruptNumber) { +#ifdef SPI_INT0_MASK + case 0: mask = SPI_INT0_MASK; + break; +#endif +#ifdef SPI_INT1_MASK + case 1: mask = SPI_INT1_MASK; + break; +#endif +#ifdef SPI_INT2_MASK + case 2: mask = SPI_INT2_MASK; + break; +#endif +#ifdef SPI_INT3_MASK + case 3: mask = SPI_INT3_MASK; + break; +#endif +#ifdef SPI_INT4_MASK + case 4: mask = SPI_INT4_MASK; + break; +#endif +#ifdef SPI_INT5_MASK + case 5: mask = SPI_INT5_MASK; + break; +#endif +#ifdef SPI_INT6_MASK + case 6: mask = SPI_INT6_MASK; + break; +#endif +#ifdef SPI_INT7_MASK + case 7: mask = SPI_INT7_MASK; + break; +#endif + default: + modeFlags.interruptMode = 2; + break; + } + interruptMask |= mask; + if(!modeFlags.interruptMode) modeFlags.interruptMode = 1; + SREG = sreg; } +void SPIClass::notUsingInterrupt(uint8_t interruptNumber) { + uint8_t mask = 0; + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + switch(interruptNumber) { +#ifdef SPI_INT0_MASK + case 0: mask = SPI_INT0_MASK; + break; +#endif +#ifdef SPI_INT1_MASK + case 1: mask = SPI_INT1_MASK; + break; +#endif +#ifdef SPI_INT2_MASK + case 2: mask = SPI_INT2_MASK; + break; +#endif +#ifdef SPI_INT3_MASK + case 3: mask = SPI_INT3_MASK; + break; +#endif +#ifdef SPI_INT4_MASK + case 4: mask = SPI_INT4_MASK; + break; +#endif +#ifdef SPI_INT5_MASK + case 5: mask = SPI_INT5_MASK; + break; +#endif +#ifdef SPI_INT6_MASK + case 6: mask = SPI_INT6_MASK; + break; +#endif +#ifdef SPI_INT7_MASK + case 7: mask = SPI_INT7_MASK; + break; +#endif + default: + if(interruptMask) { + modeFlags.interruptMode = 1; + } else { + modeFlags.interruptMode = 0; + } + break; + } + interruptMask &= ~mask; + SREG = sreg; +} diff --git a/hardware/arduino/avr/libraries/SPI/SPI.h b/hardware/arduino/avr/libraries/SPI/SPI.h index 962100bc016..a4ef74d13f0 100644 --- a/hardware/arduino/avr/libraries/SPI/SPI.h +++ b/hardware/arduino/avr/libraries/SPI/SPI.h @@ -2,6 +2,7 @@ * Copyright (c) 2010 by Cristian Maglie * Copyright (c) 2014 by Paul Stoffregen (Transaction API) * Copyright (c) 2014 by Matthijs Kooijman (SPISettings AVR) + * Copyright (c) 2014 by Andrew J. Kroll (atomicity fixes) * SPI Master library for arduino. * * This file is free software; you can redistribute it and/or modify @@ -14,14 +15,22 @@ #define _SPI_H_INCLUDED #include +#include // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(), // usingInterrupt(), and SPISetting(clock, bitOrder, dataMode) #define SPI_HAS_TRANSACTION 1 +// SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version. +// This way when there is a bug fix you can check this define to alert users +// of your code if it uses better version of this library. +// This also implies everything that SPI_HAS_TRANSACTION as documented above is +// available too. +#define SPI_ATOMIC_VERSION 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 +// each SPI.beginTransaction(). Connect an LED to this pin. The LED will turn // on if any mismatch is ever detected. //#define SPI_TRANSACTION_MISMATCH_LED 5 @@ -32,6 +41,11 @@ #define MSBFIRST 1 #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 @@ -40,245 +54,296 @@ #define SPI_CLOCK_DIV8 0x05 #define SPI_CLOCK_DIV32 0x06 -#define SPI_MODE0 0x00 -#define SPI_MODE1 0x04 -#define SPI_MODE2 0x08 -#define SPI_MODE3 0x0C - #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 +// Flags for the state of SPI, used as needed. +// Normally inTransaction is not used. + +typedef struct SPIflags { + bool initialized : 1; // tells us that begin() was called + bool inTransaction : 1; + uint8_t interruptMode : 6; // 0=none, 1=mask, 2=global (more can be added) +} __attribute__((packed)) SPIflags_t; + +#define SPI_HAS_NOTUSINGINTERRUPT 1 + // define SPI_AVR_EIMSK for AVR boards with external interrupt pins #if defined(EIMSK) - #define SPI_AVR_EIMSK EIMSK +#define SPI_AVR_EIMSK EIMSK #elif defined(GICR) - #define SPI_AVR_EIMSK GICR +#define SPI_AVR_EIMSK GICR #elif defined(GIMSK) - #define SPI_AVR_EIMSK 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); - } + + 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; -}; + 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 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 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 - { - interruptSave = SREG; - cli(); - } - } - #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.msb; - while (!(SPSR & _BV(SPIF))) ; - out.msb = SPDR; - SPDR = in.lsb; - while (!(SPSR & _BV(SPIF))) ; - out.lsb = SPDR; - } else { - SPDR = in.lsb; - while (!(SPSR & _BV(SPIF))) ; - out.lsb = SPDR; - SPDR = in.msb; - while (!(SPSR & _BV(SPIF))) ; - out.msb = 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); } + + // 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 interruptNumber); + // And this does the opposite. + static void notUsingInterrupt(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) { + uint8_t sreg = SREG; + noInterrupts(); +#ifdef SPI_TRANSACTION_MISMATCH_LED + if(modeFlags.inTransactionFlag) { + pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); + digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); + } + modeFlags.inTransactionFlag = true; +#endif + +#ifndef SPI_AVR_EIMSK + if(modeFlags.interruptMode) { + interruptSave = sreg; + } +#else + if(modeFlags.interruptMode == 2) { + interruptSave = sreg; + } else if(modeFlags.interruptMode == 1) { + interruptSave = SPI_AVR_EIMSK; + SPI_AVR_EIMSK &= ~interruptMask; + SREG = sreg; + } +#endif + else { + SREG = sreg; + } + + 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.msb; + asm volatile("nop"); + while(!(SPSR & _BV(SPIF))); + out.msb = SPDR; + SPDR = in.lsb; + asm volatile("nop"); + while(!(SPSR & _BV(SPIF))); + out.lsb = SPDR; + } else { + 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; + } + return out.val; + } + + inline static void transfer(void *buf, size_t count) { + if(count) { + 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) { + uint8_t sreg = SREG; + noInterrupts(); +#ifdef SPI_TRANSACTION_MISMATCH_LED + if(!modeFlags.inTransactionFlag) { + pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); + digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); + } + modeFlags.inTransactionFlag = false; +#endif +#ifndef SPI_AVR_EIMSK + if(modeFlags.interruptMode) { + SREG = interruptSave; + } else { + SREG = sreg; + } +#else + if(modeFlags.interruptMode == 2) { + SREG = interruptSave; + } else { + if(modeFlags.interruptMode == 1) { + SPI_AVR_EIMSK = interruptSave; + } + SREG = sreg; + } +#endif + } + + // 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 + static SPIflags_t modeFlags; // Flags for the state and mode of SPI + static uint8_t interruptMask; // which interrupts to mask + static uint8_t interruptSave; // temp storage, to restore state }; -extern SPIClass SPI; - #endif diff --git a/hardware/arduino/sam/libraries/SPI/SPI.cpp b/hardware/arduino/sam/libraries/SPI/SPI.cpp index 377918cec82..5dc6cb4a8ae 100644 --- a/hardware/arduino/sam/libraries/SPI/SPI.cpp +++ b/hardware/arduino/sam/libraries/SPI/SPI.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2010 by Cristian Maglie * Copyright (c) 2014 by Paul Stoffregen (Transaction API) + * Copyright (c) 2014 by Andrew J. Kroll (atomicity fixes) * SPI Master library for arduino. * * This file is free software; you can redistribute it and/or modify @@ -12,267 +13,270 @@ #include "SPI.h" SPIClass::SPIClass(Spi *_spi, uint32_t _id, void(*_initCb)(void)) : - spi(_spi), id(_id), initCb(_initCb), initialized(false) -{ - // Empty +spi(_spi), id(_id), initCb(_initCb), initialized(false) { + // Empty } void SPIClass::begin() { - init(); - // NPCS control is left to the user - - // Default speed set to 4Mhz - setClockDivider(BOARD_SPI_DEFAULT_SS, 21); - setDataMode(BOARD_SPI_DEFAULT_SS, SPI_MODE0); - setBitOrder(BOARD_SPI_DEFAULT_SS, MSBFIRST); + uint8_t irestore = interruptsStatus(); + noInterrupts(); + init(); + // NPCS control is left to the user + + // Default speed set to 4Mhz + setClockDivider(BOARD_SPI_DEFAULT_SS, 21); + setDataMode(BOARD_SPI_DEFAULT_SS, SPI_MODE0); + setBitOrder(BOARD_SPI_DEFAULT_SS, MSBFIRST); + if(irestore) interrupts(); } void SPIClass::begin(uint8_t _pin) { - init(); - - uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin); - PIO_Configure( - g_APinDescription[spiPin].pPort, - g_APinDescription[spiPin].ulPinType, - g_APinDescription[spiPin].ulPin, - g_APinDescription[spiPin].ulPinConfiguration); - - // Default speed set to 4Mhz - setClockDivider(_pin, 21); - setDataMode(_pin, SPI_MODE0); - setBitOrder(_pin, MSBFIRST); + uint8_t irestore = interruptsStatus(); + noInterrupts(); + init(); + + uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin); + PIO_Configure( + g_APinDescription[spiPin].pPort, + g_APinDescription[spiPin].ulPinType, + g_APinDescription[spiPin].ulPin, + g_APinDescription[spiPin].ulPinConfiguration); + + // Default speed set to 4Mhz + setClockDivider(_pin, 21); + setDataMode(_pin, SPI_MODE0); + setBitOrder(_pin, MSBFIRST); + if(irestore) interrupts(); } void SPIClass::init() { - if (initialized) - return; - interruptMode = 0; - interruptSave = 0; - interruptMask[0] = 0; - interruptMask[1] = 0; - interruptMask[2] = 0; - interruptMask[3] = 0; - initCb(); - SPI_Configure(spi, id, SPI_MR_MSTR | SPI_MR_PS | SPI_MR_MODFDIS); - SPI_Enable(spi); - initialized = true; + if(initialized) + return; + interruptMode = 0; + interruptSave = 0; + interruptMask[0] = 0; + interruptMask[1] = 0; + interruptMask[2] = 0; + interruptMask[3] = 0; + initCb(); + SPI_Configure(spi, id, SPI_MR_MSTR | SPI_MR_PS | SPI_MR_MODFDIS); + SPI_Enable(spi); + initialized = true; } -#ifndef interruptsStatus -#define interruptsStatus() __interruptsStatus() -static inline unsigned char __interruptsStatus(void) __attribute__((always_inline, unused)); -static inline unsigned char __interruptsStatus(void) { - unsigned int primask, faultmask; - asm volatile ("mrs %0, primask" : "=r" (primask)); - if (primask) return 0; - asm volatile ("mrs %0, faultmask" : "=r" (faultmask)); - if (faultmask) return 0; - return 1; +void SPIClass::usingInterrupt(uint8_t interruptNumber) { + uint8_t irestore; + + irestore = interruptsStatus(); + noInterrupts(); + if(interruptMode < 16) { + if(interruptNumber > NUM_DIGITAL_PINS) { + interruptMode = 16; + } else { + Pio *pio = g_APinDescription[interruptNumber].pPort; + uint32_t mask = g_APinDescription[interruptNumber].ulPin; + if(pio == PIOA) { + interruptMode |= 1; + interruptMask[0] |= mask; + } else if(pio == PIOB) { + interruptMode |= 2; + interruptMask[1] |= mask; + } else if(pio == PIOC) { + interruptMode |= 4; + interruptMask[2] |= mask; + } else if(pio == PIOD) { + interruptMode |= 8; + interruptMask[3] |= mask; + } else { + interruptMode = 16; + } + } + } + if(irestore) interrupts(); } -#endif -void SPIClass::usingInterrupt(uint8_t interruptNumber) -{ - uint8_t irestore; - - irestore = interruptsStatus(); - noInterrupts(); - if (interruptMode < 16) { - if (interruptNumber > NUM_DIGITAL_PINS) { - interruptMode = 16; - } else { - Pio *pio = g_APinDescription[interruptNumber].pPort; - uint32_t mask = g_APinDescription[interruptNumber].ulPin; - if (pio == PIOA) { - interruptMode |= 1; - interruptMask[0] |= mask; - } else if (pio == PIOB) { - interruptMode |= 2; - interruptMask[1] |= mask; - } else if (pio == PIOC) { - interruptMode |= 4; - interruptMask[2] |= mask; - } else if (pio == PIOD) { - interruptMode |= 8; - interruptMask[3] |= mask; - } else { - interruptMode = 16; - } - } - } - if (irestore) interrupts(); +void SPIClass::beginTransaction(uint8_t pin, SPISettings settings) { + irestore = interruptsStatus(); + noInterrupts(); + //uint8_t mode = interruptMode; + + uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(pin); + bitOrder[ch] = settings.border; + SPI_ConfigureNPCS(spi, ch, settings.config); + //setBitOrder(pin, settings.border); + //setDataMode(pin, settings.datamode); + //setClockDivider(pin, settings.clockdiv); + if(interruptMode > 16) { + interruptSave = irestore; + } else { + if(interruptMode) { + if(interruptMode & 1) PIOA->PIO_IDR = interruptMask[0]; + if(interruptMode & 2) PIOB->PIO_IDR = interruptMask[1]; + if(interruptMode & 4) PIOC->PIO_IDR = interruptMask[2]; + if(interruptMode & 8) PIOD->PIO_IDR = interruptMask[3]; + } + if(irestore) interrupts(); + } } -void SPIClass::beginTransaction(uint8_t pin, SPISettings settings) -{ - uint8_t mode = interruptMode; - if (mode > 0) { - if (mode < 16) { - if (mode & 1) PIOA->PIO_IDR = interruptMask[0]; - if (mode & 2) PIOB->PIO_IDR = interruptMask[1]; - if (mode & 4) PIOC->PIO_IDR = interruptMask[2]; - if (mode & 8) PIOD->PIO_IDR = interruptMask[3]; - } else { - interruptSave = interruptsStatus(); - noInterrupts(); - } - } - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(pin); - bitOrder[ch] = settings.border; - SPI_ConfigureNPCS(spi, ch, settings.config); - //setBitOrder(pin, settings.border); - //setDataMode(pin, settings.datamode); - //setClockDivider(pin, settings.clockdiv); -} - -void SPIClass::endTransaction(void) -{ - uint8_t mode = interruptMode; - if (mode > 0) { - if (mode < 16) { - if (mode & 1) PIOA->PIO_IER = interruptMask[0]; - if (mode & 2) PIOB->PIO_IER = interruptMask[1]; - if (mode & 4) PIOC->PIO_IER = interruptMask[2]; - if (mode & 8) PIOD->PIO_IER = interruptMask[3]; - } else { - if (interruptSave) interrupts(); - } - } +void SPIClass::endTransaction(void) { + irestore = interruptsStatus(); + noInterrupts(); + + //uint8_t mode = interruptMode; + if(interruptMode > 16) { + if(interruptSave) interrupts(); + } else { + if(interruptMode) { + if(interruptMode & 1) PIOA->PIO_IER = interruptMask[0]; + if(interruptMode & 2) PIOB->PIO_IER = interruptMask[1]; + if(interruptMode & 4) PIOC->PIO_IER = interruptMask[2]; + if(interruptMode & 8) PIOD->PIO_IER = interruptMask[3]; + } + if(irestore) interrupts(); + } } void SPIClass::end(uint8_t _pin) { - uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin); - // Setting the pin as INPUT will disconnect it from SPI peripheral - pinMode(spiPin, INPUT); + irestore = interruptsStatus(); + noInterrupts(); + uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin); + // Setting the pin as INPUT will disconnect it from SPI peripheral + pinMode(spiPin, INPUT); + if(irestore) interrupts(); } void SPIClass::end() { - SPI_Disable(spi); - initialized = false; + irestore = interruptsStatus(); + noInterrupts(); + SPI_Disable(spi); + initialized = false; + if(irestore) interrupts(); } void SPIClass::setBitOrder(uint8_t _pin, BitOrder _bitOrder) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - bitOrder[ch] = _bitOrder; + uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); + bitOrder[ch] = _bitOrder; } void SPIClass::setDataMode(uint8_t _pin, uint8_t _mode) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - mode[ch] = _mode | SPI_CSR_CSAAT; - // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed - // transfer. Some device needs that for working properly. - SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1)); + uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); + mode[ch] = _mode | SPI_CSR_CSAAT; + // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed + // transfer. Some device needs that for working properly. + SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1)); } void SPIClass::setClockDivider(uint8_t _pin, uint8_t _divider) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - divider[ch] = _divider; - // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed - // transfer. Some device needs that for working properly. - SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1)); + uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); + divider[ch] = _divider; + // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed + // transfer. Some device needs that for working properly. + SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1)); } byte SPIClass::transfer(byte _pin, uint8_t _data, SPITransferMode _mode) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - // Reverse bit order - if (bitOrder[ch] == LSBFIRST) - _data = __REV(__RBIT(_data)); - uint32_t d = _data | SPI_PCS(ch); - if (_mode == SPI_LAST) - d |= SPI_TDR_LASTXFER; - - // SPI_Write(spi, _channel, _data); - while ((spi->SPI_SR & SPI_SR_TDRE) == 0) - ; - spi->SPI_TDR = d; - - // return SPI_Read(spi); - while ((spi->SPI_SR & SPI_SR_RDRF) == 0) - ; - d = spi->SPI_RDR; - // Reverse bit order - if (bitOrder[ch] == LSBFIRST) - d = __REV(__RBIT(d)); - return d & 0xFF; + uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); + // Reverse bit order + if(bitOrder[ch] == LSBFIRST) + _data = __REV(__RBIT(_data)); + uint32_t d = _data | SPI_PCS(ch); + if(_mode == SPI_LAST) + d |= SPI_TDR_LASTXFER; + + // SPI_Write(spi, _channel, _data); + while((spi->SPI_SR & SPI_SR_TDRE) == 0) + ; + spi->SPI_TDR = d; + + // return SPI_Read(spi); + while((spi->SPI_SR & SPI_SR_RDRF) == 0) + ; + d = spi->SPI_RDR; + // Reverse bit order + if(bitOrder[ch] == LSBFIRST) + d = __REV(__RBIT(d)); + return d & 0xFF; } void SPIClass::transfer(byte _pin, void *_buf, size_t _count, SPITransferMode _mode) { - if (_count == 0) - return; - - uint8_t *buffer = (uint8_t *)_buf; - if (_count == 1) { - *buffer = transfer(_pin, *buffer, _mode); - return; - } - - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - bool reverse = (bitOrder[ch] == LSBFIRST); - - // Send the first byte - uint32_t d = *buffer; - if (reverse) - d = __REV(__RBIT(d)); - while ((spi->SPI_SR & SPI_SR_TDRE) == 0) - ; - spi->SPI_TDR = d | SPI_PCS(ch); - - while (_count > 1) { - // Prepare next byte - d = *(buffer+1); - if (reverse) - d = __REV(__RBIT(d)); - if (_count == 2 && _mode == SPI_LAST) - d |= SPI_TDR_LASTXFER; - - // Read transferred byte and send next one straight away - while ((spi->SPI_SR & SPI_SR_RDRF) == 0) - ; - uint8_t r = spi->SPI_RDR; - spi->SPI_TDR = d | SPI_PCS(ch); - - // Save read byte - if (reverse) - r = __REV(__RBIT(r)); - *buffer = r; - buffer++; - _count--; - } - - // Receive the last transferred byte - while ((spi->SPI_SR & SPI_SR_RDRF) == 0) - ; - uint8_t r = spi->SPI_RDR; - if (reverse) - r = __REV(__RBIT(r)); - *buffer = r; + if(_count == 0) + return; + + uint8_t *buffer = (uint8_t *)_buf; + if(_count == 1) { + *buffer = transfer(_pin, *buffer, _mode); + return; + } + + uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); + bool reverse = (bitOrder[ch] == LSBFIRST); + + // Send the first byte + uint32_t d = *buffer; + if(reverse) + d = __REV(__RBIT(d)); + while((spi->SPI_SR & SPI_SR_TDRE) == 0) + ; + spi->SPI_TDR = d | SPI_PCS(ch); + + while(_count > 1) { + // Prepare next byte + d = *(buffer + 1); + if(reverse) + d = __REV(__RBIT(d)); + if(_count == 2 && _mode == SPI_LAST) + d |= SPI_TDR_LASTXFER; + + // Read transferred byte and send next one straight away + while((spi->SPI_SR & SPI_SR_RDRF) == 0) + ; + uint8_t r = spi->SPI_RDR; + spi->SPI_TDR = d | SPI_PCS(ch); + + // Save read byte + if(reverse) + r = __REV(__RBIT(r)); + *buffer = r; + buffer++; + _count--; + } + + // Receive the last transferred byte + while((spi->SPI_SR & SPI_SR_RDRF) == 0) + ; + uint8_t r = spi->SPI_RDR; + if(reverse) + r = __REV(__RBIT(r)); + *buffer = r; } void SPIClass::attachInterrupt(void) { - // Should be enableInterrupt() + // Should be enableInterrupt() } void SPIClass::detachInterrupt(void) { - // Should be disableInterrupt() + // Should be disableInterrupt() } #if SPI_INTERFACES_COUNT > 0 + static void SPI_0_Init(void) { - PIO_Configure( - g_APinDescription[PIN_SPI_MOSI].pPort, - g_APinDescription[PIN_SPI_MOSI].ulPinType, - g_APinDescription[PIN_SPI_MOSI].ulPin, - g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); - PIO_Configure( - g_APinDescription[PIN_SPI_MISO].pPort, - g_APinDescription[PIN_SPI_MISO].ulPinType, - g_APinDescription[PIN_SPI_MISO].ulPin, - g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); - PIO_Configure( - g_APinDescription[PIN_SPI_SCK].pPort, - g_APinDescription[PIN_SPI_SCK].ulPinType, - g_APinDescription[PIN_SPI_SCK].ulPin, - g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); + PIO_Configure( + g_APinDescription[PIN_SPI_MOSI].pPort, + g_APinDescription[PIN_SPI_MOSI].ulPinType, + g_APinDescription[PIN_SPI_MOSI].ulPin, + g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); + PIO_Configure( + g_APinDescription[PIN_SPI_MISO].pPort, + g_APinDescription[PIN_SPI_MISO].ulPinType, + g_APinDescription[PIN_SPI_MISO].ulPin, + g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); + PIO_Configure( + g_APinDescription[PIN_SPI_SCK].pPort, + g_APinDescription[PIN_SPI_SCK].ulPinType, + g_APinDescription[PIN_SPI_SCK].ulPin, + g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); } SPIClass SPI(SPI_INTERFACE, SPI_INTERFACE_ID, SPI_0_Init); diff --git a/hardware/arduino/sam/libraries/SPI/SPI.h b/hardware/arduino/sam/libraries/SPI/SPI.h index c68784a2ee0..b57de82f44b 100644 --- a/hardware/arduino/sam/libraries/SPI/SPI.h +++ b/hardware/arduino/sam/libraries/SPI/SPI.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2010 by Cristian Maglie * Copyright (c) 2014 by Paul Stoffregen (Transaction API) + * Copyright (c) 2014 by Andrew J. Kroll (atomicity fixes) * SPI Master library for arduino. * * This file is free software; you can redistribute it and/or modify @@ -22,7 +23,7 @@ // - SPISetting(clock, bitOrder, dataMode) #define SPI_HAS_TRANSACTION 1 -// SPI_HAS_EXTENDED_CS_PIN_HANDLING means SPI has automatic +// SPI_HAS_EXTENDED_CS_PIN_HANDLING means SPI has automatic // CS pin handling and provides the following methods: // - begin(pin) // - end(pin) @@ -33,6 +34,13 @@ // - beginTransaction(pin, SPISettings settings) (if transactions are available) #define SPI_HAS_EXTENDED_CS_PIN_HANDLING 1 +// SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version. +// This way when there is a bug fix you can check this define to alert users +// of your code if it uses better version of this library. +// This also implies everything that SPI_HAS_TRANSACTION and SPI_HAS_EXTENDED_CS_PIN_HANDLING +// as documented above is available too. +#define SPI_ATOMIC_VERSION 1 + #define SPI_MODE0 0x02 #define SPI_MODE1 0x00 #define SPI_MODE2 0x03 @@ -43,6 +51,20 @@ enum SPITransferMode { SPI_LAST }; +#ifndef interruptsStatus +#define interruptsStatus() __interruptsStatus() +static inline unsigned char __interruptsStatus(void) __attribute__((always_inline, unused)); +static inline unsigned char __interruptsStatus(void) { + unsigned int primask, faultmask; + asm volatile ("mrs %0, primask" : "=r" (primask)); + if (primask) return 0; + asm volatile ("mrs %0, faultmask" : "=r" (faultmask)); + if (faultmask) return 0; + return 1; +} +#endif + + class SPISettings { public: SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) {