forked from arduino/ArduinoCore-samd
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSPI.h
159 lines (131 loc) · 4.39 KB
/
SPI.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
* SPI Master library for Arduino Zero.
* Copyright (c) 2015 Arduino LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SPI_H_INCLUDED
#define _SPI_H_INCLUDED
#include <Arduino.h>
// SPI_HAS_TRANSACTION means SPI has
// - beginTransaction()
// - endTransaction()
// - usingInterrupt()
// - SPISetting(clock, bitOrder, dataMode)
#define SPI_HAS_TRANSACTION 1
#define SPI_MODE0 0x02
#define SPI_MODE1 0x00
#define SPI_MODE2 0x03
#define SPI_MODE3 0x01
#if defined(__SAMD21G18A__)
// Even if not specified on the datasheet, the SAMD21G18A MCU
// doesn't operate correctly with clock dividers lower than 4.
// This allows a theoretical maximum SPI clock speed of 12Mhz
#define SPI_MIN_CLOCK_DIVIDER 4
// Other SAMD21xxxxx MCU may be affected as well
#else
#define SPI_MIN_CLOCK_DIVIDER 2
#endif
class SPISettings {
public:
SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) {
if (__builtin_constant_p(clock)) {
init_AlwaysInline(clock, bitOrder, dataMode);
} else {
init_MightInline(clock, bitOrder, dataMode);
}
}
// Default speed set to 4MHz, SPI mode set to MODE 0 and Bit order set to MSB first.
SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); }
private:
void init_MightInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) {
init_AlwaysInline(clock, bitOrder, dataMode);
}
void init_AlwaysInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) {
this->clockFreq = (clock >= (F_CPU / SPI_MIN_CLOCK_DIVIDER) ? F_CPU / SPI_MIN_CLOCK_DIVIDER : clock);
this->bitOrder = (bitOrder == MSBFIRST ? MSB_FIRST : LSB_FIRST);
switch (dataMode)
{
case SPI_MODE0:
this->dataMode = SERCOM_SPI_MODE_0; break;
case SPI_MODE1:
this->dataMode = SERCOM_SPI_MODE_1; break;
case SPI_MODE2:
this->dataMode = SERCOM_SPI_MODE_2; break;
case SPI_MODE3:
this->dataMode = SERCOM_SPI_MODE_3; break;
default:
this->dataMode = SERCOM_SPI_MODE_0; break;
}
}
uint32_t clockFreq;
SercomSpiClockMode dataMode;
SercomDataOrder bitOrder;
friend class SPIClass;
};
class SPIClass {
public:
SPIClass(SERCOM *p_sercom, uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI, SercomSpiTXPad, SercomRXPad);
byte transfer(uint8_t data);
uint16_t transfer16(uint16_t data);
inline void transfer(void *buf, size_t count);
// Transaction Functions
void usingInterrupt(int interruptNumber);
void beginTransaction(SPISettings settings);
void endTransaction(void);
// SPI Configuration methods
void attachInterrupt();
void detachInterrupt();
void begin();
void end();
void setBitOrder(BitOrder order);
void setDataMode(uint8_t uc_mode);
void setClockDivider(uint8_t uc_div);
private:
void init();
void config(SPISettings settings);
SERCOM *_p_sercom;
uint8_t _uc_pinMiso;
uint8_t _uc_pinMosi;
uint8_t _uc_pinSCK;
SercomSpiTXPad _padTx;
SercomRXPad _padRx;
bool initialized;
uint8_t interruptMode;
char interruptSave;
uint32_t interruptMask;
};
void SPIClass::transfer(void *buf, size_t count)
{
// TODO: Optimize for faster block-transfer
uint8_t *buffer = reinterpret_cast<uint8_t *>(buf);
for (size_t i=0; i<count; i++)
buffer[i] = transfer(buffer[i]);
}
#if SPI_INTERFACES_COUNT > 0
extern SPIClass SPI;
#endif
// For compatibility with sketches designed for AVR @ 16 MHz
// New programs should use SPI.beginTransaction to set the SPI clock
#if F_CPU == 48000000
#define SPI_CLOCK_DIV2 6
#define SPI_CLOCK_DIV4 12
#define SPI_CLOCK_DIV8 24
#define SPI_CLOCK_DIV16 48
#define SPI_CLOCK_DIV32 96
#define SPI_CLOCK_DIV64 192
#define SPI_CLOCK_DIV128 255
#endif
#endif