From cb722cfd0699b157ba5bcc1f333fb7e5313065a5 Mon Sep 17 00:00:00 2001
From: Claudio Indellicati <c.indellicati@arduino.cc>
Date: Fri, 27 Mar 2015 13:13:47 +0100
Subject: [PATCH] Implemented SPI Transaction API.

---
 libraries/SPI/SPI.cpp | 93 +++++++++++++++++++++++++++++++++++++------
 libraries/SPI/SPI.h   | 55 ++++++++++++++++++-------
 2 files changed, 121 insertions(+), 27 deletions(-)

diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp
index 18a7c853..c3252cab 100644
--- a/libraries/SPI/SPI.cpp
+++ b/libraries/SPI/SPI.cpp
@@ -1,6 +1,6 @@
 /*
- * SPI Master library for arduino.
- * Copyright (c) 2014 Arduino.
+ * SPI Master library for Arduino.
+ * Copyright (c) 2015 Arduino LLC
  *
  * 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
@@ -13,8 +13,15 @@
 #include "assert.h"
 #include "variant.h"
 
+#define SPI_IMODE_NONE   0
+#define SPI_IMODE_EXTINT 1
+#define SPI_IMODE_GLOBAL 2
+
+const SPISettings DEFAULT_SPI_SETTINGS = SPISettings();
+
 SPIClass::SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI)
 {
+  initialized = false;
   assert(p_sercom != NULL);
   _p_sercom = p_sercom;
 
@@ -25,14 +32,32 @@ SPIClass::SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint
 
 void SPIClass::begin()
 {
+  init();
+
   // PIO init
   pinPeripheral(_uc_pinMiso, g_APinDescription[_uc_pinMiso].ulPinType);
   pinPeripheral(_uc_pinSCK, g_APinDescription[_uc_pinSCK].ulPinType);
   pinPeripheral(_uc_pinMosi, g_APinDescription[_uc_pinMosi].ulPinType);
 
-  // Default speed set to 4Mhz, SPI mode set to MODE 0 and Bit order set to MSB first.
-  _p_sercom->initSPI(SPI_PAD_2_SCK_3, SERCOM_RX_PAD_0, SPI_CHAR_SIZE_8_BITS, MSB_FIRST);
-  _p_sercom->initSPIClock(SERCOM_SPI_MODE_0, 4000000);
+  config(DEFAULT_SPI_SETTINGS);
+}
+
+void SPIClass::init()
+{
+  if (initialized)
+    return;
+  interruptMode = SPI_IMODE_NONE;
+  interruptSave = 0;
+  interruptMask = 0;
+  initialized = true;
+}
+
+void SPIClass::config(SPISettings settings)
+{
+  _p_sercom->disableSPI();
+
+  _p_sercom->initSPI(SPI_PAD_2_SCK_3, SERCOM_RX_PAD_0, SPI_CHAR_SIZE_8_BITS, settings.bitOrder);
+  _p_sercom->initSPIClock(settings.dataMode, settings.clockFreq);
 
   _p_sercom->enableSPI();
 }
@@ -40,25 +65,67 @@ void SPIClass::begin()
 void SPIClass::end()
 {
   _p_sercom->resetSPI();
+  initialized = false;
 }
 
+#ifndef interruptsStatus
+#define interruptsStatus() __interruptsStatus()
+static inline unsigned char __interruptsStatus(void) __attribute__((always_inline, unused));
+static inline unsigned char __interruptsStatus(void)
+{
+  // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDBIBGJ.html
+  return (__get_PRIMASK() ? 0 : 1);
+}
+#endif
 
-void SPIClass::usingInterrupt(uint8_t interruptNumber)
+void SPIClass::usingInterrupt(int interruptNumber)
 {
-  // XXX: TODO
+  if ((interruptNumber == NOT_AN_INTERRUPT) || (interruptNumber == EXTERNAL_INT_NMI))
+    return;
+
+  uint8_t irestore = interruptsStatus();
+  noInterrupts();
+
+  if (interruptNumber >= EXTERNAL_NUM_INTERRUPTS)
+    interruptMode = SPI_IMODE_GLOBAL;
+  else
+  {
+    interruptMode |= SPI_IMODE_EXTINT;
+    interruptMask |= (1 << interruptNumber);
+  }
+
+  if (irestore)
+    interrupts();
 }
 
 void SPIClass::beginTransaction(SPISettings settings)
 {
-  // XXX: TODO
-  setBitOrder(settings.bitOrder);
-  setClockDivider(settings.clockDiv);
-  setDataMode(settings.dataMode);
+  if (interruptMode != SPI_IMODE_NONE)
+  {
+    if (interruptMode & SPI_IMODE_GLOBAL)
+    {
+      interruptSave = interruptsStatus();
+      noInterrupts();
+    }
+    else if (interruptMode & SPI_IMODE_EXTINT)
+      EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(interruptMask);
+  }
+
+  config(settings);
 }
 
 void SPIClass::endTransaction(void)
 {
-  // XXX: TODO
+  if (interruptMode != SPI_IMODE_NONE)
+  {
+    if (interruptMode & SPI_IMODE_GLOBAL)
+    {
+      if (interruptSave)
+        interrupts();
+    }
+    else if (interruptMode & SPI_IMODE_EXTINT)
+      EIC->INTENSET.reg = EIC_INTENSET_EXTINT(interruptMask);
+  }
 }
 
 void SPIClass::setBitOrder(BitOrder order)
@@ -110,7 +177,7 @@ byte SPIClass::transfer(uint8_t data)
   _p_sercom->writeDataSPI(data);
 
   // Read data
-  return _p_sercom->readDataSPI();
+  return _p_sercom->readDataSPI() & 0xFF;
 }
 
 void SPIClass::attachInterrupt() {
diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h
index 65a825ad..bedaf9e4 100644
--- a/libraries/SPI/SPI.h
+++ b/libraries/SPI/SPI.h
@@ -1,6 +1,7 @@
 /*
+ * SPI Master library for Arduino.
  * Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
- * SPI Master library for arduino.
+ * Copyright (c) 2015 Arduino LLC
  *
  * 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
@@ -13,6 +14,13 @@
 
 #include <Arduino.h>
 
+// SPI_HAS_TRANSACTION means SPI has
+//   - beginTransaction()
+//   - endTransaction()
+//   - usingInterrupt()
+//   - SPISetting(clock, bitOrder, dataMode)
+#define SPI_HAS_TRANSACTION 1
+
 #define SPI_MODE0 0x02
 #define SPI_MODE1 0x00
 #define SPI_MODE2 0x03
@@ -38,6 +46,7 @@ class SPISettings {
     }
   }
 
+  // Default speed set to 4MHz, SPI mode set to MODE 0 and Bit order set to MSB first.
   SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); }
 
   private:
@@ -46,21 +55,32 @@ class SPISettings {
   }
 
   void init_AlwaysInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) {
-    uint8_t div;
-    if (clock < (F_CPU / 255)) {
-      div = 255;
-    } else if (clock >= (F_CPU / SPI_MIN_CLOCK_DIVIDER)) {
-      div = SPI_MIN_CLOCK_DIVIDER;
-    } else {
-      div = (F_CPU / (clock + 1)) + 1;
+    this->clockFreq = (clock >= (F_CPU / SPI_MIN_CLOCK_DIVIDER) ? F_CPU / SPI_MIN_CLOCK_DIVIDER : clock);
+
+    this->bitOrder = (bitOrder == MSBFIRST ? MSB_FIRST : LSB_FIRST);
+
+    switch (dataMode)
+    {
+      case SPI_MODE0:
+        this->dataMode = SERCOM_SPI_MODE_0;
+
+      case SPI_MODE1:
+        this->dataMode = SERCOM_SPI_MODE_1;
+
+      case SPI_MODE2:
+        this->dataMode = SERCOM_SPI_MODE_2;
+
+      case SPI_MODE3:
+        this->dataMode = SERCOM_SPI_MODE_3;
+
+      default:
+        this->dataMode = SERCOM_SPI_MODE_0;
     }
-    this->clockDiv = div;
-    this->dataMode = dataMode;
-    this->bitOrder = bitOrder;
   }
 
-  uint8_t clockDiv, dataMode;
-  BitOrder bitOrder;
+  uint32_t clockFreq;
+  SercomSpiClockMode dataMode;
+  SercomDataOrder bitOrder;
 
   friend class SPIClass;
 };
@@ -73,7 +93,7 @@ class SPIClass {
   inline void transfer(void *buf, size_t count);
 
   // Transaction Functions
-  void usingInterrupt(uint8_t interruptNumber);
+  void usingInterrupt(int interruptNumber);
   void beginTransaction(SPISettings settings);
   void endTransaction(void);
 
@@ -89,10 +109,17 @@ class SPIClass {
   void setClockDivider(uint8_t uc_div);
 
   private:
+  void init();
+  void config(SPISettings settings);
+
   SERCOM *_p_sercom;
   uint8_t _uc_pinMiso;
   uint8_t _uc_pinMosi;
   uint8_t _uc_pinSCK;
+  bool initialized;
+  uint8_t interruptMode;
+  char interruptSave;
+  uint32_t interruptMask;
 };
 
 void SPIClass::transfer(void *buf, size_t count)
-- 
GitLab