diff --git a/hardware/arduino/avr/libraries/SPI/SPI.cpp b/hardware/arduino/avr/libraries/SPI/SPI.cpp index 9a7a400e18f..4466f7ff154 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,45 +12,55 @@ */ #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); + 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.inTransaction = false; + #endif + } + SREG = sreg; } // mapping of interrupt numbers to bits within SPI_AVR_EIMSK @@ -90,11 +102,9 @@ void SPIClass::end() { void SPIClass::usingInterrupt(uint8_t interruptNumber) { - uint8_t mask; - - if (interruptMode > 1) return; - - noInterrupts(); + 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; @@ -121,12 +131,53 @@ void SPIClass::usingInterrupt(uint8_t interruptNumber) case 7: mask = SPI_INT7_MASK; break; #endif default: - interruptMode = 2; - interrupts(); - return; + modeFlags.interruptMode = 2; + break; } - interruptMode = 1; interruptMask |= mask; - interrupts(); + 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..2e7eda6b900 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 @@ -19,9 +20,19 @@ // usingInterrupt(), and SPISetting(clock, bitOrder, dataMode) #define SPI_HAS_TRANSACTION 1 +// SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrup() method +#define SPI_HAS_NOTUSINGINTERRUPT 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 @@ -49,6 +60,14 @@ #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_AVR_EIMSK for AVR boards with external interrupt pins #if defined(EIMSK) #define SPI_AVR_EIMSK EIMSK @@ -147,36 +166,46 @@ class SPIClass { // Initialize the SPI library static void begin(); - // If SPI is to used from within an interrupt, this function registers + // 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); + // 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) { - if (interruptMode > 0) { - #ifdef SPI_AVR_EIMSK - if (interruptMode == 1) { - interruptSave = SPI_AVR_EIMSK; - SPI_AVR_EIMSK &= ~interruptMask; - } else - #endif - { - interruptSave = SREG; - cli(); - } - } + uint8_t sreg = SREG; + noInterrupts(); #ifdef SPI_TRANSACTION_MISMATCH_LED - if (inTransactionFlag) { + if (modeFlags.inTransaction) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } - inTransactionFlag = 1; + modeFlags.inTransaction = 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; } @@ -193,16 +222,20 @@ class SPIClass { 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; } @@ -225,23 +258,31 @@ class SPIClass { // 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 (!inTransactionFlag) { + if (!modeFlags.inTransaction) { pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT); digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH); } - inTransactionFlag = 0; + modeFlags.inTransaction = false; #endif - if (interruptMode > 0) { - #ifdef SPI_AVR_EIMSK - if (interruptMode == 1) { + #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; - } else - #endif - { - SREG = interruptSave; } + SREG = sreg; } + #endif } // Disable the SPI bus @@ -271,12 +312,9 @@ class SPIClass { inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); } private: - static uint8_t interruptMode; // 0=none, 1=mask, 2=global + 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 - #ifdef SPI_TRANSACTION_MISMATCH_LED - static uint8_t inTransactionFlag; - #endif }; extern SPIClass SPI;