Skip to content

Commit 7ae6ecf

Browse files
Dan-Lightsourcecalvinatintel
authored andcommitted
SPI library implementation for Intel EDU
The following functions/features have been implemented: * Standard SPI API: - SPI.begin() - SPI.setClockDivider() - SPI.setDataMode() - SPI.transfer() (single-byte and multi-byte versions) - SPI.transfer16() - SPI.end() * SPI Transaction API - SPI.beginTransaction() - SPI.endTransaction() - SPI.usingInterrupt() - SPI.notUsingInterrupt() * New Intel EDU specific API functions - SPI.transfer24() - SPI.transfer32() Known issues/limitations: * LSBFIRST bit order not supported in hardware, requires (slower) software emulation * SPI Transaction API has not been fully tested at time of writing Signed-off-by: Dan O'Donovan <dan@emutex.com>
1 parent 1cdc4f0 commit 7ae6ecf

File tree

11 files changed

+807
-729
lines changed

11 files changed

+807
-729
lines changed

libraries/SPI/SPI.cpp

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright (c) 2015 Intel Corporation. All rights reserved.
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
*/
19+
#include "SPI.h"
20+
21+
SPIClass SPI;
22+
23+
void SPIClass::setClockDivider(uint8_t clockDiv)
24+
{
25+
/* disable controller */
26+
SPI1_M_REG_VAL(SPIEN) &= SPI_DISABLE;
27+
28+
/* Set SPI Clock Divider */
29+
SPI1_M_REG_VAL(BAUDR) = clockDiv & SPI_CLOCK_MASK;
30+
31+
/* re-enable controller */
32+
SPI1_M_REG_VAL(SPIEN) |= SPI_ENABLE;
33+
}
34+
35+
void SPIClass::setDataMode(uint8_t dataMode)
36+
{
37+
/* disable controller */
38+
SPI1_M_REG_VAL(SPIEN) &= SPI_DISABLE;
39+
40+
/* Set frame size, bus mode and transfer mode */
41+
SPI1_M_REG_VAL(CTRL0) = (SPI1_M_REG_VAL(CTRL0) & ~(SPI_MODE_MASK)) | ((dataMode << SPI_MODE_SHIFT) & SPI_MODE_MASK);
42+
43+
/* re-enable controller */
44+
SPI1_M_REG_VAL(SPIEN) |= SPI_ENABLE;
45+
}
46+
47+
void SPIClass::begin()
48+
{
49+
uint32_t flags = interrupt_lock(); // Protect from a scheduler and prevent transactionBegin
50+
if (!initialized) {
51+
interruptMode = 0;
52+
interruptMask[0] = 0;
53+
interruptMask[1] = 0;
54+
interruptMask[2] = 0;
55+
#ifdef SPI_TRANSACTION_MISMATCH_LED
56+
inTransactionFlag = 0;
57+
#endif
58+
lsbFirst = false;
59+
frameSize = SPI_8_BIT;
60+
61+
// Set SS to high so a connected chip will be "deselected" by default
62+
// TODO - confirm that data register is updated even if pin is set as input
63+
digitalWrite(SS, HIGH);
64+
65+
// When the SS pin is set as OUTPUT, it can be used as
66+
// a general purpose output port (it doesn't influence
67+
// SPI operations).
68+
pinMode(SS, OUTPUT);
69+
70+
/* disable controller */
71+
SPI1_M_REG_VAL(SPIEN) &= SPI_DISABLE;
72+
73+
/* Configure defaults for clock divider, frame size and data mode */
74+
SPI1_M_REG_VAL(BAUDR) = SPI_CLOCK_DIV4;
75+
SPI1_M_REG_VAL(CTRL0) = (frameSize << SPI_FSIZE_SHIFT) | (SPI_MODE0 << SPI_MODE_SHIFT) | (1 << 11);
76+
77+
/* Disable interrupts */
78+
SPI1_M_REG_VAL(IMR) = SPI_DISABLE_INT;
79+
/* Enable at least one slave device (mandatory, though SS signals are unused) */
80+
SPI1_M_REG_VAL(SER) = 0x1;
81+
/* Enable controller */
82+
SPI1_M_REG_VAL(SPIEN) |= SPI_ENABLE;
83+
84+
/* Set SoC pin mux configuration */
85+
SET_PIN_MODE(g_APinDescription[MOSI].ulSocPin, SPI_MUX_MODE);
86+
SET_PIN_MODE(g_APinDescription[MISO].ulSocPin, SPI_MUX_MODE);
87+
SET_PIN_MODE(g_APinDescription[SCK].ulSocPin, SPI_MUX_MODE);
88+
g_APinDescription[MOSI].ulPinMode = SPI_MUX_MODE;
89+
g_APinDescription[MISO].ulPinMode = SPI_MUX_MODE;
90+
g_APinDescription[SCK].ulPinMode = SPI_MUX_MODE;
91+
92+
/* Enable clock to peripheral */
93+
MMIO_REG_VAL(PERIPH_CLK_GATE_CTRL) |= ENABLE_SPI_MASTER_1;
94+
}
95+
initialized++; // reference count
96+
interrupt_unlock(flags);
97+
}
98+
99+
void SPIClass::end() {
100+
uint32_t flags = interrupt_lock(); // Protect from a scheduler and prevent transactionBegin
101+
// Decrease the reference counter
102+
if (initialized)
103+
initialized--;
104+
// If there are no more references disable SPI
105+
if (!initialized) {
106+
SPI1_M_REG_VAL(SPIEN) &= SPI_DISABLE;
107+
MMIO_REG_VAL(PERIPH_CLK_GATE_CTRL) &= DISABLE_SPI_MASTER_1;
108+
#ifdef SPI_TRANSACTION_MISMATCH_LED
109+
inTransactionFlag = 0;
110+
#endif
111+
}
112+
interrupt_unlock(flags);
113+
}
114+
115+
void SPIClass::usingInterrupt(uint8_t interruptNumber) {
116+
noInterrupts();
117+
if (interruptMode < 8) {
118+
if (interruptNumber >= NUM_DIGITAL_PINS) {
119+
interruptMode = 8;
120+
} else {
121+
uint32_t pin = interruptNumber;
122+
PinDescription *p = &g_APinDescription[pin];
123+
if (p->ulGPIOPort == SS_GPIO_8B0) {
124+
interruptMode |= 1;
125+
interruptMask[0] |= (1 << p->ulGPIOId);
126+
} else if (p->ulGPIOPort == SS_GPIO_8B1) {
127+
interruptMode |= 2;
128+
interruptMask[1] |= (1 << p->ulGPIOId);
129+
} else if (p->ulGPIOPort == SOC_GPIO_32) {
130+
interruptMode |= 4;
131+
interruptMask[2] |= (1 << p->ulGPIOId);
132+
} else {
133+
interruptMode = 8;
134+
}
135+
}
136+
}
137+
interrupts();
138+
}
139+
140+
void SPIClass::notUsingInterrupt(uint8_t interruptNumber) {
141+
// Once in mode 8 we can't go back to 0 without a proper reference count
142+
if (interruptMode == 8)
143+
return;
144+
145+
noInterrupts();
146+
if (interruptNumber < NUM_DIGITAL_PINS) {
147+
uint32_t pin = interruptNumber;
148+
PinDescription *p = &g_APinDescription[pin];
149+
if (p->ulGPIOPort == SS_GPIO_8B0) {
150+
interruptMask[0] &= ~(1 << p->ulGPIOId);
151+
if (!interruptMask[0])
152+
interruptMode &= ~1;
153+
} else if (p->ulGPIOPort == SS_GPIO_8B1) {
154+
interruptMask[1] &= ~(1 << p->ulGPIOId);
155+
if (!interruptMask[1])
156+
interruptMode &= ~2;
157+
} else if (p->ulGPIOPort == SOC_GPIO_32) {
158+
interruptMask[2] &= ~(1 << p->ulGPIOId);
159+
if (!interruptMask[2])
160+
interruptMode &= ~4;
161+
}
162+
}
163+
interrupts();
164+
}

0 commit comments

Comments
 (0)