diff options
Diffstat (limited to 'libraries/SPI/SPI.cpp')
-rw-r--r-- | libraries/SPI/SPI.cpp | 211 |
1 files changed, 173 insertions, 38 deletions
diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 5e48073..af14e07 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -1,5 +1,8 @@ /* - * Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st> + * Copyright (c) 2010 by Cristian Maglie <c.maglie@arduino.cc> + * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API) + * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR) + * Copyright (c) 2014 by Andrew J. Kroll <xxxajk@gmail.com> (atomicity fixes) * SPI Master library for arduino. * * This file is free software; you can redistribute it and/or modify @@ -8,59 +11,191 @@ * published by the Free Software Foundation. */ -#include "pins_arduino.h" #include "SPI.h" SPIClass SPI; -void SPIClass::begin() { +uint8_t SPIClass::initialized = 0; +uint8_t SPIClass::interruptMode = 0; +uint8_t SPIClass::interruptMask = 0; +uint8_t SPIClass::interruptSave = 0; +#ifdef SPI_TRANSACTION_MISMATCH_LED +uint8_t SPIClass::inTransactionFlag = 0; +#endif - // 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); +void SPIClass::begin() +{ + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + if (!initialized) { + // Set SS to high so a connected chip will be "deselected" by default + uint8_t port = digitalPinToPort(SS); + uint8_t bit = digitalPinToBitMask(SS); + volatile uint8_t *reg = portModeRegister(port); - // 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); + // if the SS pin is not already configured as an output + // then set it high (to enable the internal pull-up resistor) + if(!(*reg & bit)){ + digitalWrite(SS, HIGH); + } - // 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); -} + // 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); -void SPIClass::end() { - 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); + } + initialized++; // reference count + SREG = sreg; } -void SPIClass::setBitOrder(uint8_t bitOrder) -{ - if(bitOrder == LSBFIRST) { - SPCR |= _BV(DORD); - } else { - SPCR &= ~(_BV(DORD)); +void SPIClass::end() { + uint8_t sreg = SREG; + noInterrupts(); // Protect from a scheduler and prevent transactionBegin + // Decrease the reference counter + if (initialized) + initialized--; + // If there are no more references disable SPI + if (!initialized) { + SPCR &= ~_BV(SPE); + interruptMode = 0; + #ifdef SPI_TRANSACTION_MISMATCH_LED + inTransactionFlag = 0; + #endif } + SREG = sreg; } -void SPIClass::setDataMode(uint8_t mode) +// mapping of interrupt numbers to bits within SPI_AVR_EIMSK +#if defined(__AVR_ATmega32U4__) + #define SPI_INT0_MASK (1<<INT0) + #define SPI_INT1_MASK (1<<INT1) + #define SPI_INT2_MASK (1<<INT2) + #define SPI_INT3_MASK (1<<INT3) + #define SPI_INT4_MASK (1<<INT6) +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) + #define SPI_INT0_MASK (1<<INT0) + #define SPI_INT1_MASK (1<<INT1) + #define SPI_INT2_MASK (1<<INT2) + #define SPI_INT3_MASK (1<<INT3) + #define SPI_INT4_MASK (1<<INT4) + #define SPI_INT5_MASK (1<<INT5) + #define SPI_INT6_MASK (1<<INT6) + #define SPI_INT7_MASK (1<<INT7) +#elif defined(EICRA) && defined(EICRB) && defined(EIMSK) + #define SPI_INT0_MASK (1<<INT4) + #define SPI_INT1_MASK (1<<INT5) + #define SPI_INT2_MASK (1<<INT0) + #define SPI_INT3_MASK (1<<INT1) + #define SPI_INT4_MASK (1<<INT2) + #define SPI_INT5_MASK (1<<INT3) + #define SPI_INT6_MASK (1<<INT6) + #define SPI_INT7_MASK (1<<INT7) +#else + #ifdef INT0 + #define SPI_INT0_MASK (1<<INT0) + #endif + #ifdef INT1 + #define SPI_INT1_MASK (1<<INT1) + #endif + #ifdef INT2 + #define SPI_INT2_MASK (1<<INT2) + #endif +#endif + +void SPIClass::usingInterrupt(uint8_t interruptNumber) { - SPCR = (SPCR & ~SPI_MODE_MASK) | mode; + 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: + interruptMode = 2; + break; + } + interruptMask |= mask; + if (!interruptMode) + interruptMode = 1; + SREG = sreg; } -void SPIClass::setClockDivider(uint8_t rate) +void SPIClass::notUsingInterrupt(uint8_t interruptNumber) { - SPCR = (SPCR & ~SPI_CLOCK_MASK) | (rate & SPI_CLOCK_MASK); - SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((rate >> 2) & SPI_2XCLOCK_MASK); + // Once in mode 2 we can't go back to 0 without a proper reference count + if (interruptMode == 2) + return; + 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: + break; + // this case can't be reached + } + interruptMask &= ~mask; + if (!interruptMask) + interruptMode = 0; + SREG = sreg; } - |