|
- /*
- * Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
- * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API)
- * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings)
- * 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.
- */
-
- #ifndef _SPI_H_INCLUDED
- #define _SPI_H_INCLUDED
-
- #include <Arduino.h>
-
- // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
- // usingInterrupt(), and the actual clock speed names
- #define SPI_HAS_TRANSACTION 1
-
- // define SPI_AVR_EIMSK for AVR boards with external interrupt pins
- #if defined(__AVR__)
- #if defined(EIMSK)
- #define SPI_AVR_EIMSK EIMSK
- #elif defined(GICR)
- #define SPI_AVR_EIMSK GICR
- #elif defined(GIMSK)
- #define SPI_AVR_EIMSK GIMSK
- #endif
- #endif
-
- #ifndef LSBFIRST
- #define LSBFIRST 0
- #endif
- #ifndef MSBFIRST
- #define MSBFIRST 1
- #endif
-
- #define SPI_MODE0 0x00
- #define SPI_MODE1 0x04
- #define SPI_MODE2 0x08
- #define SPI_MODE3 0x0C
-
- #define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR
- #define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR
- #define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR
-
-
- class SPISettings {
- public:
- SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
- if (__builtin_constant_p(clock)) {
- init_AlwaysInline(clock, bitOrder, dataMode);
- } else {
- init_MightInline(clock, bitOrder, dataMode);
- }
- }
- SPISettings() {
- init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0);
- }
- private:
- void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
- init_AlwaysInline(clock, bitOrder, dataMode);
- }
- #if defined(__AVR__)
- void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
- __attribute__((__always_inline__)) {
- // Clock settings are defined as follows. Note that this shows SPI2X
- // inverted, so the bits form increasing numbers. Also note that
- // fosc/64 appears twice
- // SPR1 SPR0 ~SPI2X Freq
- // 0 0 0 fosc/2
- // 0 0 1 fosc/4
- // 0 1 0 fosc/8
- // 0 1 1 fosc/16
- // 1 0 0 fosc/32
- // 1 0 1 fosc/64
- // 1 1 0 fosc/64
- // 1 1 1 fosc/128
-
- // We find the fastest clock that is less than or equal to the
- // given clock rate. The clock divider that results in clock_setting
- // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
- // slowest (128 == 2 ^^ 7, so clock_div = 6).
- uint8_t clockDiv;
-
- // When the clock is known at compiletime, use this if-then-else
- // cascade, which the compiler knows how to completely optimize
- // away. When clock is not known, use a loop instead, which generates
- // shorter code.
- if (__builtin_constant_p(clock)) {
- if (clock >= F_CPU / 2) {
- clockDiv = 0;
- } else if (clock >= F_CPU / 4) {
- clockDiv = 1;
- } else if (clock >= F_CPU / 8) {
- clockDiv = 2;
- } else if (clock >= F_CPU / 16) {
- clockDiv = 3;
- } else if (clock >= F_CPU / 32) {
- clockDiv = 4;
- } else if (clock >= F_CPU / 64) {
- clockDiv = 5;
- } else {
- clockDiv = 6;
- }
- } else {
- uint32_t clockSetting = F_CPU / 2;
- clockDiv = 0;
- while (clockDiv < 6 && clock < clockSetting) {
- clockSetting /= 2;
- clockDiv++;
- }
- }
-
- // Compensate for the duplicate fosc/64
- if (clockDiv == 6)
- clockDiv = 7;
-
- // Invert the SPI2X bit
- clockDiv ^= 0x1;
-
- // Pack into the SPISettings class
- spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) |
- (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK);
- spsr = clockDiv & SPI_2XCLOCK_MASK;
- }
- uint8_t spcr;
- uint8_t spsr;
- #elif defined(__arm__) && defined(TEENSYDUINO)
- void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
- __attribute__((__always_inline__)) {
- uint32_t t, c = SPI_CTAR_FMSZ(7);
- if (bitOrder == LSBFIRST) c |= SPI_CTAR_LSBFE;
- if (__builtin_constant_p(clock)) {
- if (clock >= F_BUS / 2) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0);
- } else if (clock >= F_BUS / 3) {
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0);
- } else if (clock >= F_BUS / 4) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
- } else if (clock >= F_BUS / 5) {
- t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0);
- } else if (clock >= F_BUS / 6) {
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
- } else if (clock >= F_BUS / 8) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
- } else if (clock >= F_BUS / 10) {
- t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0);
- } else if (clock >= F_BUS / 12) {
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
- } else if (clock >= F_BUS / 16) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
- } else if (clock >= F_BUS / 20) {
- t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(0);
- } else if (clock >= F_BUS / 24) {
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
- } else if (clock >= F_BUS / 32) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3);
- } else if (clock >= F_BUS / 40) {
- t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
- } else if (clock >= F_BUS / 56) {
- t = SPI_CTAR_PBR(3) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2);
- } else if (clock >= F_BUS / 64) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4);
- } else if (clock >= F_BUS / 96) {
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4);
- } else if (clock >= F_BUS / 128) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5);
- } else if (clock >= F_BUS / 192) {
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5);
- } else if (clock >= F_BUS / 256) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
- } else if (clock >= F_BUS / 384) {
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
- } else if (clock >= F_BUS / 512) {
- t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7);
- } else if (clock >= F_BUS / 640) {
- t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6);
- } else { /* F_BUS / 768 */
- t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7);
- }
- } else {
- for (uint32_t i=0; i<23; i++) {
- t = ctar_clock_table[i];
- if (clock >= F_BUS / ctar_div_table[i]) break;
- }
- }
- if (dataMode & 0x08) {
- c |= SPI_CTAR_CPOL;
- }
- if (dataMode & 0x04) {
- c |= SPI_CTAR_CPHA;
- t = (t & 0xFFFF0FFF) | ((t & 0xF000) >> 4);
- }
- ctar = c | t;
- }
- static const uint16_t ctar_div_table[23];
- static const uint32_t ctar_clock_table[23];
- uint32_t ctar;
- #endif
- friend class SPIClass;
- };
-
-
-
- class SPIClass {
- public:
- // Initialize the SPI library
- static void begin();
-
- // If SPI is to used from within an interrupt, this function registers
- // that interrupt with the SPI library, so beginTransaction() can
- // prevent conflicts. The input interruptNumber is the number used
- // with attachInterrupt. If SPI is used from a different interrupt
- // (eg, a timer), interruptNumber should be 255.
- static void usingInterrupt(uint8_t interruptNumber);
- #if defined(__arm__) && defined(TEENSYDUINO)
- static void usingInterrupt(IRQ_NUMBER_t interruptName);
- #endif
-
- // Before using SPI.transfer() or asserting chip select pins,
- // this function is used to gain exclusive access to the SPI bus
- // and configure the correct settings.
- inline static void beginTransaction(SPISettings settings) {
- if (interruptMode > 0) {
- #ifdef SPI_AVR_EIMSK
- if (interruptMode == 1) {
- interruptSave = SPI_AVR_EIMSK;
- SPI_AVR_EIMSK &= ~interruptMask;
- } else
- #endif
- {
- interruptSave = SREG;
- cli();
- }
- }
- #if defined(__AVR__)
- SPCR = settings.spcr;
- SPSR = settings.spsr;
- #elif defined(__arm__) && defined(TEENSYDUINO)
- if (SPI0_CTAR0 != settings.ctar) {
- SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
- SPI0_CTAR0 = settings.ctar;
- SPI0_CTAR1 = settings.ctar | SPI_CTAR_FMSZ(7);
- SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F);
- }
- #endif
- }
-
- // Write to the SPI bus (MOSI pin) and also receive (MISO pin)
- inline static uint8_t transfer(uint8_t data) {
- SPDR = data;
- asm volatile("nop");
- while (!(SPSR & _BV(SPIF))) ; // wait
- return SPDR;
- }
- inline static void transfer(void *buf, size_t count) {
- if (count == 0) return;
- uint8_t *p = (uint8_t *)buf;
- SPDR = *p;
- while (--count > 0) {
- uint8_t out = *(p + 1);
- while (!(SPSR & _BV(SPIF))) ;
- uint8_t in = SPDR;
- SPDR = out;
- *p++ = in;
- }
- while (!(SPSR & _BV(SPIF))) ;
- *p = SPDR;
- }
-
- // After performing a group of transfers and releasing the chip select
- // signal, this function allows others to access the SPI bus
- inline static void endTransaction(void) {
- if (interruptMode > 0) {
- #ifdef SPI_AVR_EIMSK
- if (interruptMode == 1) {
- SPI_AVR_EIMSK = interruptSave;
- } else
- #endif
- {
- SREG = interruptSave;
- }
- }
- }
-
- // Disable the SPI bus
- static void end();
-
- // This function is deprecated. New applications should use
- // beginTransaction() to configure SPI settings.
- inline static void setBitOrder(uint8_t bitOrder) {
- if (bitOrder == LSBFIRST) SPCR |= _BV(DORD);
- else SPCR &= ~(_BV(DORD));
- }
- // This function is deprecated. New applications should use
- // beginTransaction() to configure SPI settings.
- inline static void setDataMode(uint8_t dataMode) {
- SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode;
- }
- // This function is deprecated. New applications should use
- // beginTransaction() to configure SPI settings.
- inline static void setClockDivider(uint8_t clockDiv) {
- SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK);
- SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK);
- }
- // These undocumented functions should not be used. SPI.transfer()
- // polls the hardware flag which is automatically cleared as the
- // AVR responds to SPI's interrupt
- inline static void attachInterrupt() { SPCR |= _BV(SPIE); }
- inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); }
-
- #if defined(__arm__) && defined(TEENSYDUINO)
- inline void setMOSI(uint8_t pin) __attribute__((always_inline)) { SPCR.setMOSI(pin); }
- inline void setMISO(uint8_t pin) __attribute__((always_inline)) { SPCR.setMISO(pin); }
- inline void setSCK(uint8_t pin) __attribute__((always_inline)) { SPCR.setSCK(pin); }
- #endif
-
- private:
- static uint8_t interruptMode; // 0=none, 1=mask, 2=global
- static uint8_t interruptMask; // which interrupts to mask
- static uint8_t interruptSave; // temp storage, to restore state
- };
-
- extern SPIClass SPI;
-
-
- #define SPI_CLOCK_DIV4 0x00
- #define SPI_CLOCK_DIV16 0x01
- #define SPI_CLOCK_DIV64 0x02
- #define SPI_CLOCK_DIV128 0x03
- #define SPI_CLOCK_DIV2 0x04
- #define SPI_CLOCK_DIV8 0x05
- #define SPI_CLOCK_DIV32 0x06
-
- // mapping of interrupt numbers to bits within SPI_AVR_EIMSK
- #ifdef 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
- #endif //SPI_AVR_EIMSK
-
-
- #endif
|