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