FastArduino v1.10
C++ library to build fast but small Arduino/AVR projects
Loading...
Searching...
No Matches
spi.h
Go to the documentation of this file.
1// Copyright 2016-2023 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
34namespace 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
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
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
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
328 template<board::DigitalPin CS, ChipSelect CS_MODE = ChipSelect::ACTIVE_LOW, ClockRate RATE = ClockRate::CLOCK_DIV_4,
331 {
332 protected:
337 SPIDevice() INLINE = default;
338
339#ifdef SPDR
345 {
346 cs_.toggle();
347 SPCR_ = SPCR_START_;
348 SPSR_ = SPSR_START_;
349 }
350#else
351 void start_transfer()
352 {
353 cs_.toggle();
354 // Set 3-wire mode (SPI) and requested SPI mode (0 or 1) and use software clock strobe (through USITC)
355 USICR_ = USICR_START_;
356 }
357#endif
363 {
364 cs_.toggle();
365 }
366
367 private:
368 using REG8 = board_traits::REG8;
369#ifdef SPDR
370 static constexpr const REG8 SPCR_{SPCR};
371 static constexpr const REG8 SPDR_{SPDR};
372 static constexpr const REG8 SPSR_{SPSR};
373 // Configuration values to reset at beginning of each transfer
374 static const constexpr uint8_t SPCR_START_ =
375 bits::BV8(SPE, MSTR) | (uint8_t(RATE) & 0x03U) | uint8_t(ORDER) | uint8_t(MODE);
376 static const constexpr uint8_t SPSR_START_ = (uint8_t(RATE) & 0x10U) ? bits::BV8(SPI2X) : 0;
377#else
378 static constexpr const REG8 USIDR_{USIDR};
379 static constexpr const REG8 USISR_{USISR};
380 static constexpr const REG8 USICR_{USICR};
381 static const constexpr uint8_t USICR_START_ = uint8_t(MODE);
382#endif
384 };
385};
386
387#endif /* FASTSPI_HH */
Contain general payload transfer API for an SPI device.
Definition: spi.h:153
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
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
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
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
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
Base class for any SPI slave device.
Definition: spi.h:331
void start_transfer()
Start an SPI transfer to this device.
Definition: spi.h:344
SPIDevice() INLINE=default
Create a new SPIDevice; this sets up the CS pin for later use during transfers.
void end_transfer() INLINE
End the current SPI ransfer tot hsi device.
Definition: spi.h:362
#define INLINE
Specific GCC attribute to force the compiler to always inline code of a given function.
Definition: defines.h:57
General Purpose (digital) Input Output API.
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
DigitalPin
Defines all available digital input/output pins of the target MCU.
Definition: empty.h:56
typename FastPinType< DPIN_ >::TYPE FAST_PIN
Useful alias type to the FastPin type matching a given board::DigitalPin.
Definition: gpio.h:694
@ OUTPUT
Digital pin is configured as output.
Define API to define and manage SPI devices.
Definition: spi.h:35
ClockRate
Define SPI clock rate as a divider of MCU clock frequency.
Definition: spi.h:48
ChipSelect
Active polarity of slave selection pin.
Definition: spi.h:138
@ ACTIVE_HIGH
Slave device is active when SS pin is high.
@ ACTIVE_LOW
Slave device is active when SS pin is low.
constexpr ClockRate compute_clockrate(uint32_t frequency)
Calculate ClockRate for the given frequency.
Definition: spi.h:65
DataOrder
Bit ordering per byte.
Definition: spi.h:88
@ MSB_FIRST
Most significant bit transferred first.
@ LSB_FIRST
Least significant bit transferred first.
void init()
This function must be called once in your program, before any use of an SPI device.
Definition: spi.cpp:20
Mode
SPI transmission mode.
Definition: spi.h:110
@ MODE_0
SPI mode 0: CPOL = 0 and CPHA = 0.
@ MODE_3
SPI mode 3: CPOL = 1 and CPHA = 1.
@ MODE_2
SPI mode 2: CPOL = 1 and CPHA = 0.
@ MODE_1
SPI mode 1: CPOL = 0 and CPHA = 1.