-
-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathSPI.h
225 lines (183 loc) · 7.35 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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
* 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>
#include <api/HardwareSPI.h>
#ifndef USE_MALLOC_FOR_IRQ_MAP
#define USE_MALLOC_FOR_IRQ_MAP 0
#endif
// SPI_HAS_TRANSACTION means SPI has
// - beginTransaction()
// - endTransaction()
// - usingInterrupt()
// - SPISetting(clock, bitOrder, dataMode)
// #define SPI_HAS_TRANSACTION 1
// SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method
#define SPI_HAS_NOTUSINGINTERRUPT 1
#define SPI_MODE0 ( SPI_MODE_0_gc )
#define SPI_MODE1 ( SPI_MODE_1_gc )
#define SPI_MODE2 ( SPI_MODE_2_gc )
#define SPI_MODE3 ( SPI_MODE_3_gc )
#define SPI_MODE_MASK ( SPI_MODE_gm )
#define SPI_CLOCK_MASK ( SPI_PRESC_gm )
#define SPI_2XCLOCK_MASK ( SPI_CLK2X_bm )
#define SPI_INTERRUPT_DISABLE 0
#define SPI_INTERRUPT_ENABLE 1
#ifndef EXTERNAL_NUM_INTERRUPTS
#define EXTERNAL_NUM_INTERRUPTS NUM_TOTAL_PINS
#endif
class SPISettingsMegaAVR : public arduino::SPISettings {
public:
SPISettingsMegaAVR(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.
SPISettingsMegaAVR() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); }
SPISettingsMegaAVR(SPISettings& x) { SPISettingsMegaAVR(x.getClockFreq(), x.getBitOrder(), x.getDataMode()); }
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__)) {
// Clock settings are defined as follows. Note that this shows SPI2X
// inverted, so the bits form increasing numbers. Also note that
// fosc/64 appears twice. If FOSC is 16 Mhz
// PRESC[1:0] ~SPI2X Freq
// 0 0 0 fosc/2 8.00 MHz
// 0 0 1 fosc/4 4.00 MHz
// 0 1 0 fosc/8 2.00 MHz
// 0 1 1 fosc/16 1.00 MHz
// 1 0 0 fosc/32 500 kHz
// 1 0 1 fosc/64 250 kHz
// 1 1 0 fosc/64 250 kHz
// 1 1 1 fosc/128 125 kHz
// We find the fastest clock that is less than or equal to the
// given clock rate. The clock divider that results in clock_setting
// is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
// slowest (128 == 2 ^^ 7, so clock_div = 6).
uint8_t clockDiv;
// When the clock is known at compiletime, use this if-then-else
// cascade, which the compiler knows how to completely optimize
// away. When clock is not known, use a loop instead, which generates
// shorter code.
/* This is no longer the case since, F_CPU_CORRECTED is variable */
/* set at run time. */
uint32_t clockSetting = 0;
clockSetting = F_CPU_CORRECTED / 2;
clockDiv = 0;
while ((clockDiv < 6) && (clock < clockSetting)) {
clockSetting /= 2;
clockDiv++;
}
// Compensate for the duplicate fosc/64,
// should be fosc/128 if clockdiv 6.
if (clockDiv == 6)
{
clockDiv++;
}
// Invert the SPI2X bit
clockDiv ^= 0x1;
/* Pack into the SPISettings::ctrlb class */
/* Set mode, disable master slave select, and disable buffering. */
/* dataMode is register correct, when using SPI_MODE defines */
ctrlb = (dataMode) |
(SPI_SSD_bm) |
(0 << SPI_BUFWR_bp) |
(0 << SPI_BUFEN_bp);
/* Get Clock related values.*/
uint8_t clockDiv_mult = (clockDiv & 0x1);
uint8_t clockDiv_pres = (clockDiv >> 1);
/* Pack into the SPISettings::ctrlb class */
/* Set Prescaler, x2, SPI to Master, and Bit Order. */
ctrla = (clockDiv_pres << SPI_PRESC_gp) |
(clockDiv_mult << SPI_CLK2X_bp) |
(SPI_ENABLE_bm) |
(SPI_MASTER_bm) |
((bitOrder == LSBFIRST) << SPI_DORD_bp);
}
/* member variables containing the desired SPI settings */
uint8_t ctrla;
uint8_t ctrlb;
friend class SPIClassMegaAVR;
};
class SPIClassMegaAVR : public arduino::HardwareSPI {
public:
SPIClassMegaAVR(uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI, uint8_t uc_pinSS, uint8_t uc_mux);
byte transfer(uint8_t data);
uint16_t transfer16(uint16_t data);
void transfer(void *buf, size_t count);
// Transaction Functions
void usingInterrupt(int interruptNumber);
void notUsingInterrupt(int interruptNumber);
void beginTransaction(SPISettingsMegaAVR settings);
void beginTransaction(SPISettings settings) {
beginTransaction(SPISettingsMegaAVR(settings));
}
void endTransaction(void);
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(SPISettingsMegaAVR settings);
void config(SPISettings settings) {
config(SPISettingsMegaAVR(settings));
}
// These undocumented functions should not be used. SPI.transfer()
// polls the hardware flag which is automatically cleared as the
// AVR responds to SPI's interrupt
inline void attachInterrupt() { SPI0.INTCTRL |= (SPI_IE_bm); }
inline void detachInterrupt() { SPI0.INTCTRL &= ~(SPI_IE_bm); }
void detachMaskedInterrupts();
void reattachMaskedInterrupts();
uint8_t _uc_pinMiso;
uint8_t _uc_pinMosi;
uint8_t _uc_pinSCK;
uint8_t _uc_pinSS;
uint8_t _uc_mux;
bool initialized;
uint8_t interruptMode;
char interruptSave;
uint32_t interruptMask_lo;
uint32_t interruptMask_hi;
#if USE_MALLOC_FOR_IRQ_MAP
uint8_t* irqMap = NULL;
#else
volatile uint8_t irqMap[EXTERNAL_NUM_INTERRUPTS];
#endif
};
#define SPIClass SPIClassMegaAVR
#if SPI_INTERFACES_COUNT > 0
extern SPIClass SPI;
#endif
#define SPI_CLOCK_DIV2 ( SPI_PRESC_DIV4_gc | SPI_CLK2X_bm )
#define SPI_CLOCK_DIV4 ( SPI_PRESC_DIV4_gc )
#define SPI_CLOCK_DIV8 ( SPI_PRESC_DIV16_gc | SPI_CLK2X_bm )
#define SPI_CLOCK_DIV16 ( SPI_PRESC_DIV16_gc )
#define SPI_CLOCK_DIV32 ( SPI_PRESC_DIV64_gc | SPI_CLK2X_bm )
#define SPI_CLOCK_DIV64 ( SPI_PRESC_DIV64_gc )
#define SPI_CLOCK_DIV128 ( SPI_PRESC_DIV128_gc )
#endif