FastArduino  v1.8
C++ library to build fast but small Arduino/AVR projects
spi.h
Go to the documentation of this file.
1 // Copyright 2016-2021 Jean-Francois Poilpret
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
21 #ifndef FASTSPI_HH
22 #define FASTSPI_HH
23 
24 #include "boards/board.h"
25 #include "gpio.h"
26 
34 namespace spi
35 {
40  void init();
41 
47  enum class ClockRate : uint8_t
48  {
49  CLOCK_DIV_4 = 0x00,
50  CLOCK_DIV_16 = 0x01,
51  CLOCK_DIV_64 = 0x02,
52  CLOCK_DIV_128 = 0x03,
53  CLOCK_DIV_2 = 0x10,
54  CLOCK_DIV_8 = 0x11,
55  CLOCK_DIV_32 = 0x12
56  };
57 
65  constexpr ClockRate compute_clockrate(uint32_t frequency)
66  {
67  if (frequency >= (F_CPU / 2))
68  return ClockRate::CLOCK_DIV_2;
69  else if (frequency >= (F_CPU / 4))
70  return ClockRate::CLOCK_DIV_4;
71  else if (frequency >= (F_CPU / 8))
72  return ClockRate::CLOCK_DIV_8;
73  else if (frequency >= (F_CPU / 16))
74  return ClockRate::CLOCK_DIV_16;
75  else if (frequency >= (F_CPU / 32))
76  return ClockRate::CLOCK_DIV_32;
77  else if (frequency >= (F_CPU / 64))
78  return ClockRate::CLOCK_DIV_64;
79  else
80  return ClockRate::CLOCK_DIV_128;
81  }
82 
83 #ifdef SPDR
84 
87  enum class DataOrder : uint8_t
88  {
90  MSB_FIRST = 0,
95  LSB_FIRST = bits::BV8(DORD)
96  };
97 #else
98  enum class DataOrder : uint8_t
99  {
100  MSB_FIRST = 0
101  };
102 #endif
103 
104 #ifdef SPDR
105 
109  enum class Mode : uint8_t
110  {
112  MODE_0 = 0,
114  MODE_1 = bits::BV8(CPHA),
119  MODE_2 = bits::BV8(CPOL),
124  MODE_3 = bits::BV8(CPHA, CPOL)
125  };
126 #else
127  enum class Mode : uint8_t
128  {
129  MODE_0 = bits::BV8(USIWM0, USICLK, USICS1),
130  MODE_1 = bits::BV8(USIWM0, USICLK, USICS1, USICS0)
131  };
132 #endif
133 
137  enum class ChipSelect : uint8_t
138  {
140  ACTIVE_LOW = 0,
142  ACTIVE_HIGH = 1
143  };
144 
153  {
154  protected:
155  AbstractSPIDevice() = default;
156  AbstractSPIDevice(const AbstractSPIDevice&) = delete;
157  AbstractSPIDevice& operator=(const AbstractSPIDevice&) = delete;
158 
159 #ifdef SPDR
160 
169  uint8_t transfer(uint8_t data)
170  {
171  SPDR_ = data;
172  SPSR_.loop_until_bit_set(SPIF);
173  return SPDR_;
174  }
175 #else
176  uint8_t transfer(uint8_t data)
177  {
178  USIDR_ = data;
179  // Clear counter overflow before transmission
180  USISR_ = bits::BV8(USIOIF);
181  synchronized
182  {
183  // Note: the following loop means 7 clock cycles per bit transmitted
184  // Hence at 8MHz, that gives a SPI rate > 1MHz, which might be incompatible
185  // with some devices
186  // FIXME add a NOP to ensure 8 clock cycles per bit, hence 1MHz SPI clock
187  while ((USISR_ & bits::BV8(USIOIF)) == 0) USICR_ |= bits::BV8(USITC);
188  }
189  return USIDR_;
190  }
191 #endif
192 
205  void transfer(uint8_t* data, uint16_t size)
206  {
207  while (size--)
208  {
209  uint8_t value = *data;
210  *data++ = transfer(value);
211  }
212  }
213 
225  void transfer(const uint8_t* data, uint16_t size)
226  {
227  while (size--) transfer(*data++);
228  }
229 
248  void transfer(uint8_t* data, uint16_t size, uint8_t sent)
249  {
250  while (size--) *data++ = transfer(sent);
251  }
252 
265  void transfer(uint16_t size, uint8_t sent)
266  {
267  while (size--) transfer(sent);
268  }
269 
270  private:
271  using REG8 = board_traits::REG8;
272 #ifdef SPDR
273  static constexpr const REG8 SPDR_{SPDR};
274  static constexpr const REG8 SPSR_{SPSR};
275 #else
276  static constexpr const REG8 USIDR_{USIDR};
277  static constexpr const REG8 USISR_{USISR};
278  static constexpr const REG8 USICR_{USICR};
279 #endif
280  };
281 
325  template<board::DigitalPin CS, ChipSelect CS_MODE = ChipSelect::ACTIVE_LOW, ClockRate RATE = ClockRate::CLOCK_DIV_4,
326  Mode MODE = Mode::MODE_0, DataOrder ORDER = DataOrder::MSB_FIRST>
328  {
329  protected:
334  SPIDevice() INLINE = default;
335 
336 #ifdef SPDR
337 
342  {
343  cs_.toggle();
344  SPCR_ = SPCR_START_;
345  SPSR_ = SPSR_START_;
346  }
347 #else
348  void start_transfer()
349  {
350  cs_.toggle();
351  // Set 3-wire mode (SPI) and requested SPI mode (0 or 1) and use software clock strobe (through USITC)
352  USICR_ = USICR_START_;
353  }
354 #endif
355 
360  {
361  cs_.toggle();
362  }
363 
364  private:
365  using REG8 = board_traits::REG8;
366 #ifdef SPDR
367  static constexpr const REG8 SPCR_{SPCR};
368  static constexpr const REG8 SPDR_{SPDR};
369  static constexpr const REG8 SPSR_{SPSR};
370  // Configuration values to reset at beginning of each transfer
371  static const constexpr uint8_t SPCR_START_ =
372  bits::BV8(SPE, MSTR) | (uint8_t(RATE) & 0x03U) | uint8_t(ORDER) | uint8_t(MODE);
373  static const constexpr uint8_t SPSR_START_ = (uint8_t(RATE) & 0x10U) ? bits::BV8(SPI2X) : 0;
374 #else
375  static constexpr const REG8 USIDR_{USIDR};
376  static constexpr const REG8 USISR_{USISR};
377  static constexpr const REG8 USICR_{USICR};
378  static const constexpr uint8_t USICR_START_ = uint8_t(MODE);
379 #endif
380  gpio::FAST_PIN<CS> cs_ = gpio::FAST_PIN<CS>{gpio::PinMode::OUTPUT, CS_MODE == ChipSelect::ACTIVE_LOW};
381  };
382 };
383 
384 #endif /* FASTSPI_HH */
385 
spi::DataOrder::MSB_FIRST
@ MSB_FIRST
Most significant bit transferred first.
spi::ClockRate
ClockRate
Define SPI clock rate as a divider of MCU clock frequency.
Definition: spi.h:48
spi::AbstractSPIDevice
Contain general payload transfer API for an SPI device.
Definition: spi.h:153
spi::AbstractSPIDevice::transfer
void transfer(const uint8_t *data, uint16_t size)
Transfer an array of payload data to the currently selected SPI slave device through MOSI pin; any da...
Definition: spi.h:225
spi::SPIDevice::start_transfer
void start_transfer()
Start an SPI transfer to this device.
Definition: spi.h:341
gpio::FAST_PIN
typename FastPinType< DPIN_ >::TYPE FAST_PIN
Useful alias type to the FastPin type matching a given board::DigitalPin.
Definition: gpio.h:688
spi::AbstractSPIDevice::transfer
void transfer(uint16_t size, uint8_t sent)
Transfer the provided byte sent several times to the currently selected SPI slave device through MOSI...
Definition: spi.h:265
bits::BV8
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
spi::DataOrder
DataOrder
Bit ordering per byte.
Definition: spi.h:88
spi::Mode::MODE_0
@ MODE_0
SPI mode 0: CPOL = 0 and CPHA = 0.
spi
Define API to define and manage SPI devices.
Definition: spi.h:35
spi::Mode
Mode
SPI transmission mode.
Definition: spi.h:110
spi::SPIDevice::end_transfer
void end_transfer() INLINE
End the current SPI ransfer tot hsi device.
Definition: spi.h:359
spi::SPIDevice::SPIDevice
SPIDevice() INLINE=default
Create a new SPIDevice; this sets up the CS pin for later use during transfers.
spi::ChipSelect
ChipSelect
Active polarity of slave selection pin.
Definition: spi.h:138
spi::init
void init()
This function must be called once in your program, before any use of an SPI device.
Definition: spi.cpp:20
spi::SPIDevice
Base class for any SPI slave device.
Definition: spi.h:328
gpio.h
General Purpose (digital) Input Output API.
spi::AbstractSPIDevice::transfer
void transfer(uint8_t *data, uint16_t size)
Transfer an array of payload data to the currently selected SPI slave device through MOSI pin,...
Definition: spi.h:205
spi::ChipSelect::ACTIVE_LOW
@ ACTIVE_LOW
Slave device is active when SS pin is low.
spi::AbstractSPIDevice::transfer
void transfer(uint8_t *data, uint16_t size, uint8_t sent)
Transfer the provided byte sent several times to the currently selected SPI slave device through MOSI...
Definition: spi.h:248
spi::AbstractSPIDevice::transfer
uint8_t transfer(uint8_t data)
Transfer one byte to the currently selected SPI slave device through MOSI pin, and get the byte retur...
Definition: spi.h:169
spi::compute_clockrate
constexpr ClockRate compute_clockrate(uint32_t frequency)
Calculate ClockRate for the given frequency.
Definition: spi.h:65
gpio::PinMode::OUTPUT
@ OUTPUT
Digital pin is configured as output.
board::DigitalPin
DigitalPin
Defines all available digital input/output pins of ATmega644, with reference to Arduino MEGA pins.
Definition: atmega_xx4.h:76
INLINE
#define INLINE
Specific GCC attribute to force the compiler to always inline code of a given function.
Definition: defines.h:57