Skip to content

Commit f60b783

Browse files
Add SPISlave class (#1717)
Allows the Pico to behave as an SPI slave and allows apps to respond with appropriate data through callbacks. Fixes #1680
1 parent c48cdee commit f60b783

File tree

9 files changed

+526
-7
lines changed

9 files changed

+526
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
214214
* Analog stereo audio in using DMA and the built-in ADC
215215
* Analog stereo audio out using PWM hardware
216216
* USB drive mode for data loggers (SingleFileDrive)
217-
* Peripherals: SPI master, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input, I2S audio output, Servo
217+
* Peripherals: SPI master/slave, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input/output, Servo
218218
* printf (i.e. debug) output over USB serial
219219
220220
The RP2040 PIO state machines (SMs) are used to generate jitter-free:

docs/spi.rst

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
SPI (Serial Peripheral Interface)
2-
=================================
1+
SPI Master (Serial Peripheral Interface)
2+
========================================
33

44
The RP2040 has two hardware SPI interfaces, ``spi0 (SPI)`` and ``spi1 (SPI1)``.
55
These interfaces are supported by the ``SPI`` library in master mode.
@@ -20,5 +20,26 @@ The Arduino `SPI documentation <https://www.arduino.cc/en/reference/SPI>`_ gives
2020
a detailed overview of the library, except for the following RP2040-specific
2121
changes:
2222

23-
* ``SPI.begin(bool hwCS)`` can take an options ``hwCS`` parameter. By passing in ``true`` for ``hwCS`` the sketch does not need to worry about asserting and deasserting the ``CS`` pin between transactions. The default is ``false`` and requires the sketch to handle the CS pin itself, as is the standard way in Arduino.
24-
* The interrupt calls (``usingInterrupt``, ``notUsingInterrupt``, ``attachInterrupt``, and ``detachInterrpt``) are not implemented.
23+
* ``SPI.begin(bool hwCS)`` can take an options ``hwCS`` parameter.
24+
By passing in ``true`` for ``hwCS`` the sketch does not need to worry
25+
about asserting and deasserting the ``CS`` pin between transactions.
26+
The default is ``false`` and requires the sketch to handle the CS
27+
pin itself, as is the standard way in Arduino.
28+
29+
* The interrupt calls (``attachInterrupt``, and ``detachInterrpt``) are not implemented.
30+
31+
32+
SPI Slave (SPISlave)
33+
====================
34+
35+
Slave mode operation is also supported on either SPI interface. Two callbacks are
36+
needed in your app, set through ``SPISlave.onDataRecv`` and ``SPISlave.onDataSent``,
37+
in order to consunme the received data and provide data to transmit.
38+
39+
* The callbacks operate at IRQ time and may be called very frequently at high SPI frequencies. So, make then small, fast, and with no memory allocations or locking.
40+
41+
42+
Examples
43+
========
44+
45+
See the SPItoMyself example for a complete Master and Slave application.

libraries/SPI/src/SPI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class SPIClassRP2040 : public arduino::HardwareSPI {
6161
void setDataMode(uint8_t uc_mode) __attribute__((deprecated));
6262
void setClockDivider(uint8_t uc_div) __attribute__((deprecated));
6363

64-
// Unimplemented
64+
// List of GPIO IRQs to disable during a transaction
6565
virtual void usingInterrupt(int interruptNumber) override {
6666
_usingIRQs.insert({interruptNumber, 0});
6767
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Shows how to use SPISlave on a single device.
2+
// Core0 runs as an SPI master and initiates a transmission to the slave
3+
// Core1 runs the SPI Slave mode and provides a unique reply to messages from the master
4+
//
5+
// Released to the public domain 2023 by Earle F. Philhower, III <[email protected]>
6+
7+
#include <SPI.h>
8+
#include <SPISlave.h>
9+
10+
// Wiring:
11+
// Master RX GP0 <-> GP11 Slave TX
12+
// Master CS GP1 <-> GP9 Slave CS
13+
// Master CK GP2 <-> GP10 Slave CK
14+
// Master TX GP3 <-> GP8 Slave RX
15+
16+
SPISettings spisettings(1000000, MSBFIRST, SPI_MODE0);
17+
18+
// Core 0 will be SPI master
19+
void setup() {
20+
SPI.setRX(0);
21+
SPI.setCS(1);
22+
SPI.setSCK(2);
23+
SPI.setTX(3);
24+
SPI.begin(true);
25+
26+
delay(5000);
27+
}
28+
29+
int transmits = 0;
30+
void loop() {
31+
char msg[42];
32+
memset(msg, 0, sizeof(msg));
33+
sprintf(msg, "What's up? This is transmission %d", transmits);
34+
Serial.printf("\n\nM-SEND: '%s'\n", msg);
35+
SPI.beginTransaction(spisettings);
36+
SPI.transfer(msg, sizeof(msg));
37+
SPI.endTransaction();
38+
Serial.printf("M-RECV: '%s'\n", msg);
39+
transmits++;
40+
delay(5000);
41+
}
42+
43+
// Core 1 will be SPI slave
44+
45+
volatile bool recvBuffReady = false;
46+
char recvBuff[42] = "";
47+
int recvIdx = 0;
48+
void recvCallback(uint8_t *data, size_t len) {
49+
memcpy(recvBuff + recvIdx, data, len);
50+
recvIdx += len;
51+
if (recvIdx == sizeof(recvBuff)) {
52+
recvBuffReady = true;
53+
recvIdx = 0;
54+
}
55+
}
56+
57+
int sendcbs = 0;
58+
// Note that the buffer needs to be long lived, the SPISlave doesn't copy it. So no local stack variables, only globals or heap(malloc/new) allocations.
59+
char sendBuff[42];
60+
void sentCallback() {
61+
memset(sendBuff, 0, sizeof(sendBuff));
62+
sprintf(sendBuff, "Slave to Master Xmission %d", sendcbs++);
63+
SPISlave1.setData((uint8_t*)sendBuff, sizeof(sendBuff));
64+
}
65+
66+
// Note that we use SPISlave1 here **not** because we're running on
67+
// Core 1, but because SPI0 is being used already. You can use
68+
// SPISlave or SPISlave1 on any core.
69+
void setup1() {
70+
SPISlave1.setRX(8);
71+
SPISlave1.setCS(9);
72+
SPISlave1.setSCK(10);
73+
SPISlave1.setTX(11);
74+
// Ensure we start with something to send...
75+
sentCallback();
76+
// Hook our callbacks into the slave
77+
SPISlave1.onDataRecv(recvCallback);
78+
SPISlave1.onDataSent(sentCallback);
79+
SPISlave1.begin(spisettings);
80+
delay(3000);
81+
Serial.println("S-INFO: SPISlave started");
82+
}
83+
84+
void loop1() {
85+
if (recvBuffReady) {
86+
Serial.printf("S-RECV: '%s'\n", recvBuff);
87+
recvBuffReady = false;
88+
}
89+
}

libraries/SPISlave/keywords.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#######################################
2+
# Syntax Coloring Map SPI
3+
#######################################
4+
5+
#######################################
6+
# Instances (KEYWORD2)
7+
#######################################
8+
9+
SPISlave KEYWORD1
10+
SPISlave1 KEYWORD1
11+
12+
#######################################
13+
# Methods and Functions (KEYWORD2)
14+
#######################################
15+
begin KEYWORD2
16+
end KEYWORD2
17+
SPISettings KEYWORD2
18+
setRX KEYWORD2
19+
setTX KEYWORD2
20+
setSCK KEYWORD2
21+
setCS KEYWORD2
22+
setData KEYWORD2
23+
onDataRecv KEYWORD2
24+
onDataSent KEYWORD2
25+
26+
#######################################
27+
# Constants (LITERAL1)
28+
#######################################
29+
SPI_CLOCK_DIV4 LITERAL1
30+
SPI_CLOCK_DIV16 LITERAL1
31+
SPI_CLOCK_DIV64 LITERAL1
32+
SPI_CLOCK_DIV128 LITERAL1
33+
SPI_CLOCK_DIV2 LITERAL1
34+
SPI_CLOCK_DIV8 LITERAL1
35+
SPI_CLOCK_DIV32 LITERAL1
36+
SPI_CLOCK_DIV64 LITERAL1
37+
SPI_MODE0 LITERAL1
38+
SPI_MODE1 LITERAL1
39+
SPI_MODE2 LITERAL1
40+
SPI_MODE3 LITERAL1

libraries/SPISlave/library.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=SPISlave
2+
version=1.0
3+
author=Earle F. Philhower, III <[email protected]>
4+
maintainer=Earle F. Philhower, III <[email protected]>
5+
sentence=Enables the communication as a slave SPI devices.
6+
paragraph=
7+
category=Signal Input/Output
8+
url=https://github.com/earlephilhower/arduino-pico
9+
architectures=rp2040
10+
dot_a_linkage=true

0 commit comments

Comments
 (0)