AVRTools
A Library for the AVR ATmega328 and ATmega2560 Microcontrollers
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 
109 namespace SPI
110 {
111 
116  {
117  kLsbFirst = 0,
119  };
120 
121 
130  enum SpiMode
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 
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
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
A class that binds settings for configuring SPI transmissions.
Definition: SPI.h:158
SpiMode
An enumeration that defines the modes available for SPI transmissions.
Definition: SPI.h:130
Least significant byte first.
Definition: SPI.h:117
Phase falling, idle high (CPHA = 0, CPOL = 1)
Definition: SPI.h:134
This namespace bundles an interface to the SPI hardware subsystem on the AVR ATMega328p (Arduino Uno)...
Definition: SPI.h:109
Phase rising, idle low (CPHA = 1, CPOL = 0)
Definition: SPI.h:133
uint8_t transmit(uint8_t data)
Transmit a single byte using the SPI subsystem.
Definition: SPI.h:441
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
Phase falling, idle low (CPHA = 0, CPOL = 0)
Definition: SPI.h:132
void disable()
Disable the SPI subsystem, precluding further transmissions.
Definition: SPI.cpp:79
Phase rising, idle high (CPHA = 1, CPOL = 1)
Definition: SPI.h:135
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
void enable()
Enable the SPI subsystem for transmission.
Definition: SPI.cpp:38
uint8_t getSpsr() const
Return the appropriate configure value for the SPSR register.
Definition: SPI.h:224
uint8_t getSpcr() const
Return the appropriate configure value for the SPCR register.
Definition: SPI.h:212
ByteOrder
An enumeration that defines the byte order for multibyte SPI transmissions.
Definition: SPI.h:115
Most significant byte first.
Definition: SPI.h:118