Advertisement
Guest User

Untitled

a guest
Apr 18th, 2020
317
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.96 KB | None | 0 0
  1. /*-------------------------------------------------------------------------*
  2.  * tinySPI.h - Arduino hardware SPI master library for ATtiny24/44/84,     *
  3.  * ATtiny25/45/85, and Attiny2313/4313.                                    *
  4.  *                                                                         *
  5.  * Original version of tinyISP by Jack Christensen 24Oct2013               *
  6.  *                                                                         *
  7.  * Added support for Attiny24/25, and Attiny2313/4313                      *
  8.  * by Leonardo Miliani 28Nov2014                                           *
  9.  *                                                                         *
  10.  * Reworked to add support for 87/167, 261/461/861, and include the normal *
  11.  * SPI library on chips that have hardware SPI.                            *
  12.  * Spence Konde 2016 (in progress)                                         *
  13.  *                                                                         *
  14.  * CC BY-SA-NC:                                                            *
  15.  * This work is licensed under the Creative Commons Attribution-           *
  16.  * ShareAlike- Not Commercial 4.0 Unported License. To view a copy of this *
  17.  * license, visit                                                          *
  18.  * http://creativecommons.org/licenses/by-sa/4.0/ or send a                *
  19.  * letter to Creative Commons, 171 Second Street, Suite 300,               *
  20.  * San Francisco, California, 94105, USA.                                  *
  21.  *-------------------------------------------------------------------------*/
  22.  
  23. #ifndef tinycoreSPI_h
  24. #define tinycoreSPI_h
  25.  
  26. #include <stdint.h>
  27. #include <avr/io.h>
  28. #include <util/atomic.h>
  29. #include <Arduino.h>
  30.  
  31. #ifndef LSBFIRST
  32. #define LSBFIRST 0
  33. #endif
  34. #ifndef MSBFIRST
  35. #define MSBFIRST 1
  36. #endif
  37.  
  38. // define SPI_AVR_EIMSK for AVR boards with external interrupt pins
  39. #if defined(EIMSK)
  40.   #define SPI_AVR_EIMSK  EIMSK
  41. #elif defined(GICR)
  42.   #define SPI_AVR_EIMSK  GICR
  43. #elif defined(GIMSK)
  44.   #define SPI_AVR_EIMSK  GIMSK
  45. #endif
  46.  
  47.  
  48. #ifdef SPDR //Then we have hardware SPI, let's use it:
  49.  
  50. // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
  51. // usingInterrupt(), and SPISetting(clock, bitOrder, dataMode)
  52. #define SPI_HAS_TRANSACTION 1
  53.  
  54. // SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method
  55. #define SPI_HAS_NOTUSINGINTERRUPT 1
  56.  
  57. // SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version.
  58. // This way when there is a bug fix you can check this define to alert users
  59. // of your code if it uses better version of this library.
  60. // This also implies everything that SPI_HAS_TRANSACTION as documented above is
  61. // available too.
  62. #define SPI_ATOMIC_VERSION 1
  63.  
  64. // Uncomment this line to add detection of mismatched begin/end transactions.
  65. // A mismatch occurs if other libraries fail to use SPI.endTransaction() for
  66. // each SPI.beginTransaction().  Connect an LED to this pin.  The LED will turn
  67. // on if any mismatch is ever detected.
  68. //#define SPI_TRANSACTION_MISMATCH_LED 5
  69.  
  70. #ifndef LSBFIRST
  71. #define LSBFIRST 0
  72. #endif
  73. #ifndef MSBFIRST
  74. #define MSBFIRST 1
  75. #endif
  76.  
  77. #define SPI_CLOCK_DIV4 0x00
  78. #define SPI_CLOCK_DIV16 0x01
  79. #define SPI_CLOCK_DIV64 0x02
  80. #define SPI_CLOCK_DIV128 0x03
  81. #define SPI_CLOCK_DIV2 0x04
  82. #define SPI_CLOCK_DIV8 0x05
  83. #define SPI_CLOCK_DIV32 0x06
  84.  
  85. #define SPI_MODE0 0x00
  86. #define SPI_MODE1 0x04
  87. #define SPI_MODE2 0x08
  88. #define SPI_MODE3 0x0C
  89.  
  90. #define SPI_MODE_MASK 0x0C  // CPOL = bit 3, CPHA = bit 2 on SPCR
  91. #define SPI_CLOCK_MASK 0x03  // SPR1 = bit 1, SPR0 = bit 0 on SPCR
  92. #define SPI_2XCLOCK_MASK 0x01  // SPI2X = bit 0 on SPSR
  93.  
  94. class SPISettings {
  95. public:
  96.   SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
  97.     if (__builtin_constant_p(clock)) {
  98.       init_AlwaysInline(clock, bitOrder, dataMode);
  99.     } else {
  100.       init_MightInline(clock, bitOrder, dataMode);
  101.     }
  102.   }
  103.   SPISettings() {
  104.     init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0);
  105.   }
  106. private:
  107.   void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
  108.     init_AlwaysInline(clock, bitOrder, dataMode);
  109.   }
  110.   void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
  111.     __attribute__((__always_inline__)) {
  112.     // Clock settings are defined as follows. Note that this shows SPI2X
  113.     // inverted, so the bits form increasing numbers. Also note that
  114.     // fosc/64 appears twice
  115.     // SPR1 SPR0 ~SPI2X Freq
  116.     //   0    0     0   fosc/2
  117.     //   0    0     1   fosc/4
  118.     //   0    1     0   fosc/8
  119.     //   0    1     1   fosc/16
  120.     //   1    0     0   fosc/32
  121.     //   1    0     1   fosc/64
  122.     //   1    1     0   fosc/64
  123.     //   1    1     1   fosc/128
  124.  
  125.     // We find the fastest clock that is less than or equal to the
  126.     // given clock rate. The clock divider that results in clock_setting
  127.     // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
  128.     // slowest (128 == 2 ^^ 7, so clock_div = 6).
  129.     uint8_t clockDiv;
  130.  
  131.     // When the clock is known at compile time, use this if-then-else
  132.     // cascade, which the compiler knows how to completely optimize
  133.     // away. When clock is not known, use a loop instead, which generates
  134.     // shorter code.
  135.     if (__builtin_constant_p(clock)) {
  136.       if (clock >= F_CPU / 2) {
  137.         clockDiv = 0;
  138.       } else if (clock >= F_CPU / 4) {
  139.         clockDiv = 1;
  140.       } else if (clock >= F_CPU / 8) {
  141.         clockDiv = 2;
  142.       } else if (clock >= F_CPU / 16) {
  143.         clockDiv = 3;
  144.       } else if (clock >= F_CPU / 32) {
  145.         clockDiv = 4;
  146.       } else if (clock >= F_CPU / 64) {
  147.         clockDiv = 5;
  148.       } else {
  149.         clockDiv = 6;
  150.       }
  151.     } else {
  152.       uint32_t clockSetting = F_CPU / 2;
  153.       clockDiv = 0;
  154.       while (clockDiv < 6 && clock < clockSetting) {
  155.         clockSetting /= 2;
  156.         clockDiv++;
  157.       }
  158.     }
  159.  
  160.     // Compensate for the duplicate fosc/64
  161.     if (clockDiv == 6)
  162.     clockDiv = 7;
  163.  
  164.     // Invert the SPI2X bit
  165.     clockDiv ^= 0x1;
  166.  
  167.     // Pack into the SPISettings class
  168.     spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) |
  169.       (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK);
  170.     spsr = clockDiv & SPI_2XCLOCK_MASK;
  171.   }
  172.   uint8_t spcr;
  173.   uint8_t spsr;
  174.   friend class SPIClass;
  175. };
  176.  
  177.  
  178. class SPIClass {
  179. public:
  180.   // Initialize the SPI library
  181.   static void begin();
  182.  
  183.   // If SPI is used from within an interrupt, this function registers
  184.   // that interrupt with the SPI library, so beginTransaction() can
  185.   // prevent conflicts.  The input interruptNumber is the number used
  186.   // with attachInterrupt.  If SPI is used from a different interrupt
  187.   // (eg, a timer), interruptNumber should be 255.
  188.   static void usingInterrupt(uint8_t interruptNumber);
  189.   // And this does the opposite.
  190.   static void notUsingInterrupt(uint8_t interruptNumber);
  191.   // Note: the usingInterrupt and notUsingInterrupt functions should
  192.   // not to be called from ISR context or inside a transaction.
  193.   // For details see:
  194.   // https://github.com/arduino/Arduino/pull/2381
  195.   // https://github.com/arduino/Arduino/pull/2449
  196.  
  197.   // Before using SPI.transfer() or asserting chip select pins,
  198.   // this function is used to gain exclusive access to the SPI bus
  199.   // and configure the correct settings.
  200.   inline static void beginTransaction(SPISettings settings) {
  201.     if (interruptMode > 0) {
  202.       uint8_t sreg = SREG;
  203.       noInterrupts();
  204.  
  205.       #ifdef SPI_AVR_EIMSK
  206.       if (interruptMode == 1) {
  207.         interruptSave = SPI_AVR_EIMSK;
  208.         SPI_AVR_EIMSK &= ~interruptMask;
  209.         SREG = sreg;
  210.       } else
  211.       #endif
  212.       {
  213.         interruptSave = sreg;
  214.       }
  215.     }
  216.  
  217.     #ifdef SPI_TRANSACTION_MISMATCH_LED
  218.     if (inTransactionFlag) {
  219.       pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
  220.       digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
  221.     }
  222.     inTransactionFlag = 1;
  223.     #endif
  224.  
  225.     SPCR = settings.spcr;
  226.     SPSR = settings.spsr;
  227.   }
  228.  
  229.   // Write to the SPI bus (MOSI pin) and also receive (MISO pin)
  230.   inline static uint8_t transfer(uint8_t data) {
  231.     SPDR = data;
  232.     /*
  233.      * The following NOP introduces a small delay that can prevent the wait
  234.      * loop form iterating when running at the maximum speed. This gives
  235.      * about 10% more speed, even if it seems counter-intuitive. At lower
  236.      * speeds it is unnoticed.
  237.      */
  238.     asm volatile("nop");
  239.     while (!(SPSR & _BV(SPIF))) ; // wait
  240.     return SPDR;
  241.   }
  242.   inline static uint16_t transfer16(uint16_t data) {
  243.     union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out;
  244.     in.val = data;
  245.     if (!(SPCR & _BV(DORD))) {
  246.       SPDR = in.msb;
  247.       asm volatile("nop"); // See transfer(uint8_t) function
  248.       while (!(SPSR & _BV(SPIF))) ;
  249.       out.msb = SPDR;
  250.       SPDR = in.lsb;
  251.       asm volatile("nop");
  252.       while (!(SPSR & _BV(SPIF))) ;
  253.       out.lsb = SPDR;
  254.     } else {
  255.       SPDR = in.lsb;
  256.       asm volatile("nop");
  257.       while (!(SPSR & _BV(SPIF))) ;
  258.       out.lsb = SPDR;
  259.       SPDR = in.msb;
  260.       asm volatile("nop");
  261.       while (!(SPSR & _BV(SPIF))) ;
  262.       out.msb = SPDR;
  263.     }
  264.     return out.val;
  265.   }
  266.   inline static void transfer(void *buf, size_t count) {
  267.     if (count == 0) return;
  268.     uint8_t *p = (uint8_t *)buf;
  269.     SPDR = *p;
  270.     while (--count > 0) {
  271.       uint8_t out = *(p + 1);
  272.       while (!(SPSR & _BV(SPIF))) ;
  273.       uint8_t in = SPDR;
  274.       SPDR = out;
  275.       *p++ = in;
  276.     }
  277.     while (!(SPSR & _BV(SPIF))) ;
  278.     *p = SPDR;
  279.   }
  280.   // After performing a group of transfers and releasing the chip select
  281.   // signal, this function allows others to access the SPI bus
  282.   inline static void endTransaction(void) {
  283.     #ifdef SPI_TRANSACTION_MISMATCH_LED
  284.     if (!inTransactionFlag) {
  285.       pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
  286.       digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
  287.     }
  288.     inTransactionFlag = 0;
  289.     #endif
  290.  
  291.     if (interruptMode > 0) {
  292.       #ifdef SPI_AVR_EIMSK
  293.       uint8_t sreg = SREG;
  294.       #endif
  295.       noInterrupts();
  296.       #ifdef SPI_AVR_EIMSK
  297.       if (interruptMode == 1) {
  298.         SPI_AVR_EIMSK = interruptSave;
  299.         SREG = sreg;
  300.       } else
  301.       #endif
  302.       {
  303.         SREG = interruptSave;
  304.       }
  305.     }
  306.   }
  307.  
  308.   // Disable the SPI bus
  309.   static void end();
  310.  
  311.   // This function is deprecated.  New applications should use
  312.   // beginTransaction() to configure SPI settings.
  313.   inline static void setBitOrder(uint8_t bitOrder) {
  314.     if (bitOrder == LSBFIRST) SPCR |= _BV(DORD);
  315.     else SPCR &= ~(_BV(DORD));
  316.   }
  317.   // This function is deprecated.  New applications should use
  318.   // beginTransaction() to configure SPI settings.
  319.   inline static void setDataMode(uint8_t dataMode) {
  320.     SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode;
  321.   }
  322.   // This function is deprecated.  New applications should use
  323.   // beginTransaction() to configure SPI settings.
  324.   inline static void setClockDivider(uint8_t clockDiv) {
  325.     SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK);
  326.     SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK);
  327.   }
  328.   // These undocumented functions should not be used.  SPI.transfer()
  329.   // polls the hardware flag which is automatically cleared as the
  330.   // AVR responds to SPI's interrupt
  331.   inline static void attachInterrupt() { SPCR |= _BV(SPIE); }
  332.   inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); }
  333.  
  334. private:
  335.   static uint8_t initialized;
  336.   static uint8_t interruptMode; // 0=none, 1=mask, 2=global
  337.   static uint8_t interruptMask; // which interrupts to mask
  338.   static uint8_t interruptSave; // temp storage, to restore state
  339.   #ifdef SPI_TRANSACTION_MISMATCH_LED
  340.   static uint8_t inTransactionFlag;
  341.   #endif
  342. };
  343.  
  344. extern SPIClass SPI;
  345.  
  346.  
  347. #else
  348.  
  349. #ifdef USICR //if we have a USI instead, use that
  350.  
  351. //SPI data modes
  352. #define SPI_MODE0 0x00
  353. #define SPI_MODE1 0x01
  354. #define SPI_MODE2 0x02
  355. #define SPI_MODE3 0x03
  356.  
  357. #define SPI_CLOCK_DIV2       2
  358. #define SPI_CLOCK_DIV4       4
  359. #define SPI_CLOCK_DIV8       8
  360. #define SPI_CLOCK_DIV16     16
  361. #define SPI_CLOCK_DIV32     32
  362. #define SPI_CLOCK_DIV64     64
  363. #define SPI_CLOCK_DIV128   128
  364.  
  365. //This implementation does have transaction:
  366. #define SPI_HAS_TRANSACTION 1
  367. #define SPI_HAS_NOTUSINGINTERRUPT 1
  368. // Settings for default USI based SPI bus for different chips
  369.  
  370. namespace USI_impl {
  371.     using ClockOut = uint8_t (*)(uint8_t,uint8_t);
  372.     uint8_t clockoutUSI(uint8_t data, uint8_t delay);
  373.     uint8_t clockoutUSI2(uint8_t data, uint8_t delay);
  374.     uint8_t clockoutUSI4(uint8_t data, uint8_t delay);
  375.     uint8_t clockoutUSI8(uint8_t data, uint8_t delay);
  376.  
  377.     __attribute__((always_inline))
  378.     inline ClockOut dispatchClockout(uint8_t div, uint8_t* delay)
  379.     {
  380.       *delay = 0;
  381.       if (div <= 2) {
  382.           return clockoutUSI2;
  383.       } else if (div <= 4) {
  384.           return clockoutUSI4;
  385.       } else if (div <= 8) {
  386.           return clockoutUSI8;
  387.       } else {
  388.         // Slow mode, convert clockdiv into delay loop count.
  389.         // Calculated inline to allow compile-time evaluation.
  390.         *delay = ((uint16_t)div*100 - 780) / 59;
  391.         // Round it to nearest integer.
  392.         *delay = *delay < 10 ? 1 : (*delay + 5) / 10;
  393.         return clockoutUSI;
  394.       }
  395.     }
  396.  
  397.     ClockOut dispatchClockout_slow(uint8_t div, uint8_t* delay)
  398.     __attribute__((warning("SPI clock is not a runtime constant, increasing code size a lot.")));
  399. }
  400.  
  401. class SPISettings {
  402. public:
  403.   SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
  404.     init_AlwaysInline(clock, bitOrder, dataMode);
  405.   }
  406.   SPISettings() {
  407.     init_AlwaysInline(F_CPU / 16, MSBFIRST, SPI_MODE0);
  408.   }
  409. private:
  410.   void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
  411.     __attribute__((always_inline)) {
  412.     usicr = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK);
  413.     if (dataMode == SPI_MODE1 || dataMode == SPI_MODE3) {
  414.         usicr |= _BV(USICS0);
  415.     }
  416.     msb1st = bitOrder;
  417.     cpol = dataMode == SPI_MODE2 || dataMode == SPI_MODE3;
  418.     // Round up.
  419.     uint8_t div = F_CPU / clock + (F_CPU % clock ? 1 : 0);
  420.     if (__builtin_constant_p(clock)) {
  421.       clockoutfn = USI_impl::dispatchClockout(div, &delay);
  422.     } else {
  423.       clockoutfn = USI_impl::dispatchClockout_slow(div, &delay);
  424.     }
  425.   }
  426.  
  427.   uint8_t msb1st;
  428.   uint8_t cpol;
  429.   uint8_t usicr;
  430.   uint8_t delay;
  431.   USI_impl::ClockOut clockoutfn;
  432.   friend class SPIClass;
  433. };
  434.  
  435. class SPIClass
  436. {
  437.  public:
  438.   SPIClass();
  439.   static void begin();
  440.   static void beginTransaction(SPISettings settings);
  441.   static uint8_t transfer(uint8_t data);
  442.   static uint16_t transfer16(uint16_t data);
  443.   static void transfer(void *buf, size_t count);
  444.   static void endTransaction(void);
  445.   static void end();
  446.  
  447.   // This function is deprecated.  New applications should use
  448.   // beginTransaction() to configure SPI settings.
  449.   static void setBitOrder(uint8_t bitOrder) {msb1st = bitOrder;}
  450.   // This function is deprecated.  New applications should use
  451.   // beginTransaction() to configure SPI settings.
  452.   static void setDataMode(uint8_t dataMode);
  453.   // This function is deprecated.  New applications should use
  454.   // beginTransaction() to configure SPI settings.
  455.   static void setClockDivider(uint8_t div) {
  456.       if (__builtin_constant_p(div)) {
  457.         clockoutfn = USI_impl::dispatchClockout(div, &delay);
  458.       } else {
  459.         clockoutfn = USI_impl::dispatchClockout_slow(div, &delay);
  460.       }
  461.   }
  462.  
  463.   static void usingInterrupt(uint8_t interruptNumber);
  464.   static void notUsingInterrupt(uint8_t interruptNumber);
  465.  
  466. private:
  467.   static void applySettings(SPISettings settings);
  468.  
  469.   static uint8_t msb1st;
  470.   static uint8_t delay;
  471.   static USI_impl::ClockOut clockoutfn;
  472.   static uint8_t interruptMode; // 0=none, 1=mask, 2=global
  473.   static uint8_t interruptMask; // which interrupts to mask
  474.   static uint8_t interruptSave; // temp storage, to restore state
  475. };
  476.  
  477. extern SPIClass SPI;
  478.  
  479.  
  480.  
  481. #else
  482. //if no USICR and no TWBR
  483.  
  484. #error No supported hardware
  485.  
  486. #endif //end if USICR
  487. #endif //end if TWBR
  488. #endif //end of module include guard
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement