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