diff options
Diffstat (limited to 'libraries/SPI/src/SPI.cpp')
-rw-r--r-- | libraries/SPI/src/SPI.cpp | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp new file mode 100644 index 0000000..af14e07 --- /dev/null +++ b/libraries/SPI/src/SPI.cpp @@ -0,0 +1,201 @@ +/* + * 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 + * 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. + */ + +#include "SPI.h" + +SPIClass SPI; + +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 + +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); + + // 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); + } + + // 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); + + // 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::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; +} + +// 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) +{ + 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::notUsingInterrupt(uint8_t interruptNumber) +{ + // 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; +} |