1
+ /*
2
+ * SPI Master library for Arduino Zero.
3
+ * Copyright (c) 2015 Arduino LLC
4
+ *
5
+ * This library is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * This library is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with this library; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+ #include " SPI.h"
21
+ #include < Arduino.h>
22
+
23
+ #define SPI_IMODE_NONE 0
24
+ #define SPI_IMODE_EXTINT 1
25
+ #define SPI_IMODE_GLOBAL 2
26
+
27
+ const SPISettings DEFAULT_SPI_SETTINGS = SPISettings();
28
+
29
+ SPIClass::SPIClass (uint8_t uc_pinMISO, uint8_t uc_pinSCK, uint8_t uc_pinMOSI, uint8_t uc_pinSS, uint8_t uc_mux)
30
+ {
31
+ initialized = false ;
32
+
33
+ // pins
34
+ _uc_mux = uc_mux;
35
+ _uc_pinMiso = uc_pinMISO;
36
+ _uc_pinSCK = uc_pinSCK;
37
+ _uc_pinMosi = uc_pinMOSI;
38
+ _uc_pinSS = uc_pinSS;
39
+ }
40
+
41
+ void SPIClass::begin ()
42
+ {
43
+ init ();
44
+
45
+ PORTMUX.TWISPIROUTEA |= _uc_mux;
46
+
47
+ // We don't need HW SS since salve/master mode is selected via registers, so make it simply INPUT
48
+ pinMode (_uc_pinSS, INPUT);
49
+ pinMode (_uc_pinMosi, OUTPUT);
50
+ pinMode (_uc_pinSCK, OUTPUT);
51
+ // MISO is set to input by the controller
52
+
53
+ SPI0.CTRLB |= (SPI_SSD_bm);
54
+ SPI0.CTRLA |= (SPI_ENABLE_bm | SPI_MASTER_bm);
55
+
56
+ config (DEFAULT_SPI_SETTINGS);
57
+ }
58
+
59
+ void SPIClass::init ()
60
+ {
61
+ if (initialized)
62
+ return ;
63
+ interruptMode = SPI_IMODE_NONE;
64
+ interruptSave = 0 ;
65
+ interruptMask = 0 ;
66
+ initialized = true ;
67
+ }
68
+
69
+ void SPIClass::config (SPISettings settings)
70
+ {
71
+ SPI0.CTRLA = settings.ctrla ;
72
+ SPI0.CTRLB = settings.ctrlb ;
73
+ }
74
+
75
+ void SPIClass::end ()
76
+ {
77
+ SPI0.CTRLA &= ~(SPI_ENABLE_bm);
78
+ initialized = false ;
79
+ }
80
+
81
+ void SPIClass::usingInterrupt (int interruptNumber)
82
+ {
83
+ if ((interruptNumber == NOT_AN_INTERRUPT))
84
+ return ;
85
+
86
+ if (interruptNumber >= EXTERNAL_NUM_INTERRUPTS)
87
+ interruptMode = SPI_IMODE_GLOBAL;
88
+ else
89
+ {
90
+ interruptMode |= SPI_IMODE_EXTINT;
91
+ interruptMask |= (1 << interruptNumber);
92
+ }
93
+ }
94
+
95
+ void SPIClass::notUsingInterrupt (int interruptNumber)
96
+ {
97
+ if ((interruptNumber == NOT_AN_INTERRUPT))
98
+ return ;
99
+
100
+ if (interruptMode & SPI_IMODE_GLOBAL)
101
+ return ; // can't go back, as there is no reference count
102
+
103
+ interruptMask &= ~(1 << interruptNumber);
104
+
105
+ if (interruptMask == 0 )
106
+ interruptMode = SPI_IMODE_NONE;
107
+ }
108
+
109
+
110
+ void SPIClass::detachMaskedInterrupts () {
111
+ }
112
+
113
+ void SPIClass::reattachMaskedInterrupts () {
114
+
115
+ }
116
+
117
+ void SPIClass::beginTransaction (SPISettings settings)
118
+ {
119
+ if (interruptMode != SPI_IMODE_NONE)
120
+ {
121
+ if (interruptMode & SPI_IMODE_GLOBAL)
122
+ {
123
+ noInterrupts ();
124
+ }
125
+ else if (interruptMode & SPI_IMODE_EXTINT)
126
+ {
127
+ detachMaskedInterrupts ();
128
+ }
129
+ config (settings);
130
+ }
131
+ }
132
+
133
+ void SPIClass::endTransaction (void )
134
+ {
135
+ if (interruptMode != SPI_IMODE_NONE)
136
+ {
137
+ if (interruptMode & SPI_IMODE_GLOBAL)
138
+ {
139
+ interrupts ();
140
+ }
141
+ else if (interruptMode & SPI_IMODE_EXTINT)
142
+ reattachMaskedInterrupts ();
143
+ }
144
+ }
145
+
146
+ void SPIClass::setBitOrder (BitOrder order)
147
+ {
148
+ if (order == LSBFIRST)
149
+ SPI0.CTRLA |= (SPI_DORD_bm);
150
+ else
151
+ SPI0.CTRLA &= ~(SPI_DORD_bm);
152
+ }
153
+
154
+ void SPIClass::setDataMode (uint8_t mode)
155
+ {
156
+ SPI0.CTRLB = ((SPI0.CTRLB & (~SPI_MODE_gm)) | mode );
157
+ }
158
+
159
+ void SPIClass::setClockDivider (uint8_t div)
160
+ {
161
+ SPI0.CTRLA = ((SPI0.CTRLA &
162
+ ((~SPI_PRESC_gm) | (~SPI_CLK2X_bm) )) // mask out values
163
+ | div ); // write value
164
+ }
165
+
166
+ byte SPIClass::transfer (uint8_t data)
167
+ {
168
+ /*
169
+ * The following NOP introduces a small delay that can prevent the wait
170
+ * loop from iterating when running at the maximum speed. This gives
171
+ * about 10% more speed, even if it seems counter-intuitive. At lower
172
+ * speeds it is unnoticed.
173
+ */
174
+ asm volatile (" nop" );
175
+
176
+ SPI0.DATA = data;
177
+ while ((SPI0.INTFLAGS & SPI_RXCIF_bm) == 0 ); // wait for complete send
178
+ return SPI0.DATA ; // read data back
179
+ }
180
+
181
+ uint16_t SPIClass::transfer16 (uint16_t data) {
182
+ union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } t;
183
+
184
+ t.val = data;
185
+
186
+ if ((SPI0.CTRLA & SPI_DORD_bm) == 0 ) {
187
+ t.msb = transfer (t.msb );
188
+ t.lsb = transfer (t.lsb );
189
+ } else {
190
+ t.lsb = transfer (t.lsb );
191
+ t.msb = transfer (t.msb );
192
+ }
193
+
194
+ return t.val ;
195
+ }
196
+
197
+ void SPIClass::transfer (void *buf, size_t count)
198
+ {
199
+ uint8_t *buffer = reinterpret_cast <uint8_t *>(buf);
200
+ for (size_t i=0 ; i<count; i++) {
201
+ *buffer = transfer (*buffer);
202
+ buffer++;
203
+ }
204
+ }
205
+
206
+ #if SPI_INTERFACES_COUNT > 0
207
+ SPIClass SPI (PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_MOSI, PIN_SPI_SS, MUX_SPI);
208
+ #endif
0 commit comments