summaryrefslogtreecommitdiff
path: root/lib/Arduino-SPI.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Arduino-SPI.cpp')
-rw-r--r--lib/Arduino-SPI.cpp201
1 files changed, 201 insertions, 0 deletions
diff --git a/lib/Arduino-SPI.cpp b/lib/Arduino-SPI.cpp
new file mode 100644
index 0000000..0a7ac91
--- /dev/null
+++ b/lib/Arduino-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 "Arduino-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;
+}