AVRTools
A Library for the AVR ATmega328 and ATmega2560 Microcontrollers
Loading...
Searching...
No Matches
SPI.h
Go to the documentation of this file.
1/*
2 SPI.h - an interface to the SPI subsystem of the
3 AVR ATMega328p (Arduino Uno) and ATMega2560 (Arduino Mega).
4 This is part of the AVRTools library.
5 Copyright (c) 2015 Igor Mikolic-Torreira. All right reserved.
6 Various portions of this code adapted from Arduino SPI code that
7 is Copyright (c) 2010 by Cristian Maglie, Copyright (c) 2014 by Paul Stoffregen,
8 Copyright (c) 2014 by Matthijs Kooijman, and Copyright (c) 2014 by Andrew J. Kroll
9 and licensed under the terms of either the GNU General Public License version 2
10 or the GNU Lesser General Public License version 2.1.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Lesser General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Lesser General Public License for more details.
21
22 You should have received a copy of the GNU Lesser General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24*/
25
26
36#ifndef SPI_h
37#define SPI_h
38
39#include <stdint.h>
40#include <stddef.h>
41
42#include <avr/io.h>
43
44
109namespace SPI
110{
111
116 {
118 kMsbFirst = 1
119 };
120
121
131 {
132 kSpiMode0 = 0x00,
133 kSpiMode1 = 0x04,
134 kSpiMode2 = 0x08,
135 kSpiMode3 = 0x0C
136 };
137
138
159 {
160 public:
161
179 SPISettings( uint32_t maxSpeed, uint8_t bitOrder, uint8_t dataMode )
180 {
181 if ( __builtin_constant_p( maxSpeed ) )
182 {
183 initAlwaysInline( maxSpeed, bitOrder, dataMode );
184 }
185 else
186 {
187 initMightInline( maxSpeed, bitOrder, dataMode );
188 }
189 }
190
191
192
200 {
201 initAlwaysInline( 8000000, kMsbFirst, kSpiMode0 );
202 }
203
204
205
212 uint8_t getSpcr() const
213 {
214 return mSpcr;
215 }
216
217
224 uint8_t getSpsr() const
225 {
226 return mSpsr;
227 }
228
229
230
231
232
233 private:
234
235
236 void initMightInline( uint32_t maxSpeed, uint8_t bitOrder, uint8_t dataMode )
237 {
238 initAlwaysInline( maxSpeed, bitOrder, dataMode );
239 }
240
241
242#pragma GCC diagnostic push
243#pragma GCC diagnostic ignored "-Wunused-variable"
244
245 void initAlwaysInline( uint32_t maxSpeed, uint8_t bitOrder, uint8_t dataMode ) __attribute__((__always_inline__))
246 {
247 /*
248 * The following are internal constants
249 */
250 const uint8_t kSpiClockDiv4 = 0x00;
251 const uint8_t kSpiClockDiv16 = 0x01;
252 const uint8_t kSpiClockDiv64 = 0x02;
253 const uint8_t kSpiClockDiv128 = 0x03;
254 const uint8_t kSpiClockDiv2 = 0x04;
255 const uint8_t kSpiClockDiv8 = 0x05;
256 const uint8_t kSpiClockDiv32 = 0x06;
257
258 const uint8_t kSpiModeMask = 0x0C; // CPOL = bit 3, CPHA = bit 2 on SPCR
259 const uint8_t kSpiClockMask = 0x03; // SPR1 = bit 1, SPR0 = bit 0 on SPCR
260 const uint8_t kSpi2xClockMask = 0x01; // SPI2X = bit 0 on SPSR
261
262
263 // Clock settings are defined as follows. Note that this shows SPI2X
264 // inverted, so the bits form increasing numbers. Also note that
265 // fosc/64 appears twice
266 // SPR1 SPR0 ~SPI2X Freq
267 // 0 0 0 fosc/2
268 // 0 0 1 fosc/4
269 // 0 1 0 fosc/8
270 // 0 1 1 fosc/16
271 // 1 0 0 fosc/32
272 // 1 0 1 fosc/64
273 // 1 1 0 fosc/64
274 // 1 1 1 fosc/128
275
276 // We find the fastest clock that is less than or equal to the
277 // given clock rate. The clock divider that results in clock_setting
278 // is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
279 // slowest (128 == 2 ^^ 7, so clock_div = 6).
280 uint8_t clockDiv;
281
282 // When the clock is known at compile time, use this if-then-else
283 // cascade, which the compiler knows how to completely optimize
284 // away. When clock is not known, use a loop instead, which generates
285 // shorter code.
286 if ( __builtin_constant_p( maxSpeed ) )
287 {
288 if ( maxSpeed >= F_CPU / 2 )
289 {
290 clockDiv = 0;
291 }
292 else if ( maxSpeed >= F_CPU / 4 )
293 {
294 clockDiv = 1;
295 }
296 else if ( maxSpeed >= F_CPU / 8 )
297 {
298 clockDiv = 2;
299 }
300 else if ( maxSpeed >= F_CPU / 16 )
301 {
302 clockDiv = 3;
303 }
304 else if ( maxSpeed >= F_CPU / 32 )
305 {
306 clockDiv = 4;
307 }
308 else if ( maxSpeed >= F_CPU / 64 )
309 {
310 clockDiv = 5;
311 }
312 else
313 {
314 clockDiv = 6;
315 }
316 }
317 else
318 {
319 uint32_t clockSetting = F_CPU / 2;
320 clockDiv = 0;
321 while ( clockDiv < 6 && maxSpeed < clockSetting )
322 {
323 clockSetting /= 2;
324 clockDiv++;
325 }
326 }
327
328 // Compensate for the duplicate fosc/64
329 if ( clockDiv == 6 )
330 {
331 clockDiv = 7;
332 }
333
334 // Invert the SPI2X bit
335 clockDiv ^= 0x1;
336
337 // Pack into the SPISettings class
338 mSpcr = _BV(SPE)
339 | _BV(MSTR)
340 | ( (bitOrder == kLsbFirst) ? _BV(DORD) : 0 )
341 | ( dataMode & kSpiModeMask )
342 | ( (clockDiv >> 1) & kSpiClockMask );
343
344 mSpsr = clockDiv & kSpi2xClockMask;
345 }
346
347#pragma GCC diagnostic pop
348
349
350 uint8_t mSpcr;
351 uint8_t mSpsr;
352 };
353
354
355
356
371 void enable();
372
373
374
384 void disable();
385
386
426 inline void configure( SPISettings settings )
427 {
428 SPCR = settings.getSpcr();
429 SPSR = settings.getSpsr();
430 }
431
432
441 inline uint8_t transmit( uint8_t data )
442 {
443 SPDR = data;
444 /*
445 * The following NOP introduces a small delay that can prevent the wait
446 * loop from iterating when running at the maximum speed. This gives
447 * about 10% more speed, even if it seems counter-intuitive. At lower
448 * speeds it is unnoticed.
449 */
450 asm volatile( "nop" );
451 while ( !( SPSR & _BV(SPIF) ) )
452 ; // wait
453 return SPDR;
454 }
455
456
467 inline uint16_t transmit16( uint16_t data )
468 {
469 union
470 {
471 uint16_t val;
472 struct
473 {
474 uint8_t lsb;
475 uint8_t msb;
476
477 };
478 } in, out;
479
480 in.val = data;
481
482 if ( SPCR & _BV(DORD) )
483 {
484 SPDR = in.lsb;
485 asm volatile( "nop" ); // See transmit( uint8_t ) function
486 while ( !( SPSR & _BV(SPIF) ) )
487 ;
488 out.lsb = SPDR;
489
490 SPDR = in.msb;
491 asm volatile( "nop" );
492 while ( !( SPSR & _BV(SPIF) ) )
493 ;
494 out.msb = SPDR;
495 }
496 else
497 {
498 SPDR = in.msb;
499 asm volatile( "nop" ); // See transmit( uint8_t ) function
500 while ( !( SPSR & _BV(SPIF) ) )
501 ;
502 out.msb = SPDR;
503 SPDR = in.lsb;
504 asm volatile( "nop" );
505 while ( !( SPSR & _BV(SPIF) ) )
506 ;
507 out.lsb = SPDR;
508 }
509
510 return out.val;
511 }
512
513
524 uint32_t transmit32( uint32_t data );
525
526
527
539 inline void transmit( uint8_t* buffer, size_t count )
540 {
541 if ( count )
542 {
543 uint8_t* p = buffer;
544 SPDR = *p;
545
546 while ( --count > 0 )
547 {
548 uint8_t out = *(p + 1);
549 while ( !( SPSR & _BV(SPIF) ) )
550 ;
551 uint8_t in = SPDR;
552 SPDR = out;
553 *p++ = in;
554 }
555
556 while ( !(SPSR & _BV(SPIF) ) )
557 ;
558 *p = SPDR;
559 }
560 }
561
562
563} // End namespace
564
565#endif
566
A class that binds settings for configuring SPI transmissions.
Definition SPI.h:159
SPISettings()
The constructor builds an SPISettings object with default settings corresponding to a maximum transmi...
Definition SPI.h:199
SPISettings(uint32_t maxSpeed, uint8_t bitOrder, uint8_t dataMode)
The constructor builds an SPISettings object out of three parameters describing the maximum transmiss...
Definition SPI.h:179
uint8_t getSpcr() const
Return the appropriate configure value for the SPCR register.
Definition SPI.h:212
uint8_t getSpsr() const
Return the appropriate configure value for the SPSR register.
Definition SPI.h:224
This namespace bundles an interface to the SPI hardware subsystem on the AVR ATMega328p (Arduino Uno)...
Definition SPI.h:110
uint32_t transmit32(uint32_t data)
Transmit a long-word-sized integer (four bytes) using the SPI subsystem. The order in which the bytes...
Definition SPI.cpp:90
SpiMode
An enumeration that defines the modes available for SPI transmissions.
Definition SPI.h:131
@ kSpiMode0
Phase falling, idle low (CPHA = 0, CPOL = 0)
Definition SPI.h:132
@ kSpiMode1
Phase rising, idle low (CPHA = 1, CPOL = 0)
Definition SPI.h:133
@ kSpiMode3
Phase rising, idle high (CPHA = 1, CPOL = 1)
Definition SPI.h:135
@ kSpiMode2
Phase falling, idle high (CPHA = 0, CPOL = 1)
Definition SPI.h:134
void enable()
Enable the SPI subsystem for transmission.
Definition SPI.cpp:38
ByteOrder
An enumeration that defines the byte order for multibyte SPI transmissions.
Definition SPI.h:116
@ kLsbFirst
Least significant byte first.
Definition SPI.h:117
@ kMsbFirst
Most significant byte first.
Definition SPI.h:118
uint8_t transmit(uint8_t data)
Transmit a single byte using the SPI subsystem.
Definition SPI.h:441
void configure(SPISettings settings)
Set the configuration of SPI subsystem to match the needs of the system you are going to communicate ...
Definition SPI.h:426
void disable()
Disable the SPI subsystem, precluding further transmissions.
Definition SPI.cpp:79
uint16_t transmit16(uint16_t data)
Transmit a word-sized integer (two bytes) using the SPI subsystem. The order in which the bytes are s...
Definition SPI.h:467