Engduino  3.1.0
A fun device for learning coding
SPI.h
1 /*
2  * Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st>
3  * Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API)
4  * Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR)
5  * SPI Master library for arduino.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of either the GNU General Public License version 2
9  * or the GNU Lesser General Public License version 2.1, both as
10  * published by the Free Software Foundation.
11  */
12 
13 #ifndef _SPI_H_INCLUDED
14 #define _SPI_H_INCLUDED
15 
16 #include <Arduino.h>
17 
18 // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
19 // usingInterrupt(), and SPISetting(clock, bitOrder, dataMode)
20 #define SPI_HAS_TRANSACTION 1
21 
22 // Uncomment this line to add detection of mismatched begin/end transactions.
23 // A mismatch occurs if other libraries fail to use SPI.endTransaction() for
24 // each SPI.beginTransaction(). Connect a LED to this pin. The LED will turn
25 // on if any mismatch is ever detected.
26 //#define SPI_TRANSACTION_MISMATCH_LED 5
27 
28 #ifndef LSBFIRST
29 #define LSBFIRST 0
30 #endif
31 #ifndef MSBFIRST
32 #define MSBFIRST 1
33 #endif
34 
35 #define SPI_CLOCK_DIV4 0x00
36 #define SPI_CLOCK_DIV16 0x01
37 #define SPI_CLOCK_DIV64 0x02
38 #define SPI_CLOCK_DIV128 0x03
39 #define SPI_CLOCK_DIV2 0x04
40 #define SPI_CLOCK_DIV8 0x05
41 #define SPI_CLOCK_DIV32 0x06
42 
43 #define SPI_MODE0 0x00
44 #define SPI_MODE1 0x04
45 #define SPI_MODE2 0x08
46 #define SPI_MODE3 0x0C
47 
48 #define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR
49 #define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR
50 #define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR
51 
52 // define SPI_AVR_EIMSK for AVR boards with external interrupt pins
53 #if defined(EIMSK)
54  #define SPI_AVR_EIMSK EIMSK
55 #elif defined(GICR)
56  #define SPI_AVR_EIMSK GICR
57 #elif defined(GIMSK)
58  #define SPI_AVR_EIMSK GIMSK
59 #endif
60 
61 class SPISettings {
62 public:
63  SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
64  if (__builtin_constant_p(clock)) {
65  init_AlwaysInline(clock, bitOrder, dataMode);
66  } else {
67  init_MightInline(clock, bitOrder, dataMode);
68  }
69  }
70  SPISettings() {
71  init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0);
72  }
73 private:
74  void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
75  init_AlwaysInline(clock, bitOrder, dataMode);
76  }
77  void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
78  __attribute__((__always_inline__)) {
79  // Clock settings are defined as follows. Note that this shows SPI2X
80  // inverted, so the bits form increasing numbers. Also note that
81  // fosc/64 appears twice
82  // SPR1 SPR0 ~SPI2X Freq
83  // 0 0 0 fosc/2
84  // 0 0 1 fosc/4
85  // 0 1 0 fosc/8
86  // 0 1 1 fosc/16
87  // 1 0 0 fosc/32
88  // 1 0 1 fosc/64
89  // 1 1 0 fosc/64
90  // 1 1 1 fosc/128
91 
92  // We find the fastest clock that is less than or equal to the
93  // given clock rate. The clock divider that results in clock_setting
94  // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
95  // slowest (128 == 2 ^^ 7, so clock_div = 6).
96  uint8_t clockDiv;
97 
98  // When the clock is known at compiletime, use this if-then-else
99  // cascade, which the compiler knows how to completely optimize
100  // away. When clock is not known, use a loop instead, which generates
101  // shorter code.
102  if (__builtin_constant_p(clock)) {
103  if (clock >= F_CPU / 2) {
104  clockDiv = 0;
105  } else if (clock >= F_CPU / 4) {
106  clockDiv = 1;
107  } else if (clock >= F_CPU / 8) {
108  clockDiv = 2;
109  } else if (clock >= F_CPU / 16) {
110  clockDiv = 3;
111  } else if (clock >= F_CPU / 32) {
112  clockDiv = 4;
113  } else if (clock >= F_CPU / 64) {
114  clockDiv = 5;
115  } else {
116  clockDiv = 6;
117  }
118  } else {
119  uint32_t clockSetting = F_CPU / 2;
120  clockDiv = 0;
121  while (clockDiv < 6 && clock < clockSetting) {
122  clockSetting /= 2;
123  clockDiv++;
124  }
125  }
126 
127  // Compensate for the duplicate fosc/64
128  if (clockDiv == 6)
129  clockDiv = 7;
130 
131  // Invert the SPI2X bit
132  clockDiv ^= 0x1;
133 
134  // Pack into the SPISettings class
135  spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) |
136  (dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK);
137  spsr = clockDiv & SPI_2XCLOCK_MASK;
138  }
139  uint8_t spcr;
140  uint8_t spsr;
141  friend class SPIClass;
142 };
143 
144 
145 class SPIClass {
146 public:
147  // Initialize the SPI library
148  static void begin();
149 
150  // If SPI is to used from within an interrupt, this function registers
151  // that interrupt with the SPI library, so beginTransaction() can
152  // prevent conflicts. The input interruptNumber is the number used
153  // with attachInterrupt. If SPI is used from a different interrupt
154  // (eg, a timer), interruptNumber should be 255.
155  static void usingInterrupt(uint8_t interruptNumber);
156 
157  // Before using SPI.transfer() or asserting chip select pins,
158  // this function is used to gain exclusive access to the SPI bus
159  // and configure the correct settings.
160  inline static void beginTransaction(SPISettings settings) {
161  if (interruptMode > 0) {
162  #ifdef SPI_AVR_EIMSK
163  if (interruptMode == 1) {
164  interruptSave = SPI_AVR_EIMSK;
165  SPI_AVR_EIMSK &= ~interruptMask;
166  } else
167  #endif
168  {
169  interruptSave = SREG;
170  cli();
171  }
172  }
173  #ifdef SPI_TRANSACTION_MISMATCH_LED
174  if (inTransactionFlag) {
175  pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
176  digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
177  }
178  inTransactionFlag = 1;
179  #endif
180  SPCR = settings.spcr;
181  SPSR = settings.spsr;
182  }
183 
184  // Write to the SPI bus (MOSI pin) and also receive (MISO pin)
185  inline static uint8_t transfer(uint8_t data) {
186  SPDR = data;
187  asm volatile("nop");
188  while (!(SPSR & _BV(SPIF))) ; // wait
189  return SPDR;
190  }
191  inline static uint16_t transfer16(uint16_t data) {
192  union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out;
193  in.val = data;
194  if (!(SPCR & _BV(DORD))) {
195  SPDR = in.msb;
196  while (!(SPSR & _BV(SPIF))) ;
197  out.msb = SPDR;
198  SPDR = in.lsb;
199  while (!(SPSR & _BV(SPIF))) ;
200  out.lsb = SPDR;
201  } else {
202  SPDR = in.lsb;
203  while (!(SPSR & _BV(SPIF))) ;
204  out.lsb = SPDR;
205  SPDR = in.msb;
206  while (!(SPSR & _BV(SPIF))) ;
207  out.msb = SPDR;
208  }
209  return out.val;
210  }
211  inline static void transfer(void *buf, size_t count) {
212  if (count == 0) return;
213  uint8_t *p = (uint8_t *)buf;
214  SPDR = *p;
215  while (--count > 0) {
216  uint8_t out = *(p + 1);
217  while (!(SPSR & _BV(SPIF))) ;
218  uint8_t in = SPDR;
219  SPDR = out;
220  *p++ = in;
221  }
222  while (!(SPSR & _BV(SPIF))) ;
223  *p = SPDR;
224  }
225  // After performing a group of transfers and releasing the chip select
226  // signal, this function allows others to access the SPI bus
227  inline static void endTransaction(void) {
228  #ifdef SPI_TRANSACTION_MISMATCH_LED
229  if (!inTransactionFlag) {
230  pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
231  digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
232  }
233  inTransactionFlag = 0;
234  #endif
235  if (interruptMode > 0) {
236  #ifdef SPI_AVR_EIMSK
237  if (interruptMode == 1) {
238  SPI_AVR_EIMSK = interruptSave;
239  } else
240  #endif
241  {
242  SREG = interruptSave;
243  }
244  }
245  }
246 
247  // Disable the SPI bus
248  static void end();
249 
250  // This function is deprecated. New applications should use
251  // beginTransaction() to configure SPI settings.
252  inline static void setBitOrder(uint8_t bitOrder) {
253  if (bitOrder == LSBFIRST) SPCR |= _BV(DORD);
254  else SPCR &= ~(_BV(DORD));
255  }
256  // This function is deprecated. New applications should use
257  // beginTransaction() to configure SPI settings.
258  inline static void setDataMode(uint8_t dataMode) {
259  SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode;
260  }
261  // This function is deprecated. New applications should use
262  // beginTransaction() to configure SPI settings.
263  inline static void setClockDivider(uint8_t clockDiv) {
264  SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK);
265  SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK);
266  }
267  // These undocumented functions should not be used. SPI.transfer()
268  // polls the hardware flag which is automatically cleared as the
269  // AVR responds to SPI's interrupt
270  inline static void attachInterrupt() { SPCR |= _BV(SPIE); }
271  inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); }
272 
273 private:
274  static uint8_t interruptMode; // 0=none, 1=mask, 2=global
275  static uint8_t interruptMask; // which interrupts to mask
276  static uint8_t interruptSave; // temp storage, to restore state
277  #ifdef SPI_TRANSACTION_MISMATCH_LED
278  static uint8_t inTransactionFlag;
279  #endif
280 };
281 
282 extern SPIClass SPI;
283 
284 #endif
Definition: SPI.h:61
Definition: SPI.h:145