MakeBlock Drive Updated Updated library for MakeBlock Ranger

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