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
This commit is contained in:
parent
c48cdeeea0
commit
f60b7831c8
9 changed files with 526 additions and 7 deletions
|
|
@ -214,7 +214,7 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
|
|||
* Analog stereo audio in using DMA and the built-in ADC
|
||||
* Analog stereo audio out using PWM hardware
|
||||
* USB drive mode for data loggers (SingleFileDrive)
|
||||
* Peripherals: SPI master, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input, I2S audio output, Servo
|
||||
* Peripherals: SPI master/slave, Wire(I2C) master/slave, dual UART, emulated EEPROM, I2S audio input/output, Servo
|
||||
* printf (i.e. debug) output over USB serial
|
||||
|
||||
The RP2040 PIO state machines (SMs) are used to generate jitter-free:
|
||||
|
|
|
|||
29
docs/spi.rst
29
docs/spi.rst
|
|
@ -1,5 +1,5 @@
|
|||
SPI (Serial Peripheral Interface)
|
||||
=================================
|
||||
SPI Master (Serial Peripheral Interface)
|
||||
========================================
|
||||
|
||||
The RP2040 has two hardware SPI interfaces, ``spi0 (SPI)`` and ``spi1 (SPI1)``.
|
||||
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
|
|||
a detailed overview of the library, except for the following RP2040-specific
|
||||
changes:
|
||||
|
||||
* ``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.
|
||||
* The interrupt calls (``usingInterrupt``, ``notUsingInterrupt``, ``attachInterrupt``, and ``detachInterrpt``) are not implemented.
|
||||
* ``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.
|
||||
|
||||
* The interrupt calls (``attachInterrupt``, and ``detachInterrpt``) are not implemented.
|
||||
|
||||
|
||||
SPI Slave (SPISlave)
|
||||
====================
|
||||
|
||||
Slave mode operation is also supported on either SPI interface. Two callbacks are
|
||||
needed in your app, set through ``SPISlave.onDataRecv`` and ``SPISlave.onDataSent``,
|
||||
in order to consunme the received data and provide data to transmit.
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
See the SPItoMyself example for a complete Master and Slave application.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public:
|
|||
void setDataMode(uint8_t uc_mode) __attribute__((deprecated));
|
||||
void setClockDivider(uint8_t uc_div) __attribute__((deprecated));
|
||||
|
||||
// Unimplemented
|
||||
// List of GPIO IRQs to disable during a transaction
|
||||
virtual void usingInterrupt(int interruptNumber) override {
|
||||
_usingIRQs.insert({interruptNumber, 0});
|
||||
}
|
||||
|
|
|
|||
89
libraries/SPISlave/examples/SPItoMyself/SPItoMyself.ino
Normal file
89
libraries/SPISlave/examples/SPItoMyself/SPItoMyself.ino
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// Shows how to use SPISlave on a single device.
|
||||
// Core0 runs as an SPI master and initiates a transmission to the slave
|
||||
// Core1 runs the SPI Slave mode and provides a unique reply to messages from the master
|
||||
//
|
||||
// Released to the public domain 2023 by Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
#include <SPI.h>
|
||||
#include <SPISlave.h>
|
||||
|
||||
// Wiring:
|
||||
// Master RX GP0 <-> GP11 Slave TX
|
||||
// Master CS GP1 <-> GP9 Slave CS
|
||||
// Master CK GP2 <-> GP10 Slave CK
|
||||
// Master TX GP3 <-> GP8 Slave RX
|
||||
|
||||
SPISettings spisettings(1000000, MSBFIRST, SPI_MODE0);
|
||||
|
||||
// Core 0 will be SPI master
|
||||
void setup() {
|
||||
SPI.setRX(0);
|
||||
SPI.setCS(1);
|
||||
SPI.setSCK(2);
|
||||
SPI.setTX(3);
|
||||
SPI.begin(true);
|
||||
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
int transmits = 0;
|
||||
void loop() {
|
||||
char msg[42];
|
||||
memset(msg, 0, sizeof(msg));
|
||||
sprintf(msg, "What's up? This is transmission %d", transmits);
|
||||
Serial.printf("\n\nM-SEND: '%s'\n", msg);
|
||||
SPI.beginTransaction(spisettings);
|
||||
SPI.transfer(msg, sizeof(msg));
|
||||
SPI.endTransaction();
|
||||
Serial.printf("M-RECV: '%s'\n", msg);
|
||||
transmits++;
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
// Core 1 will be SPI slave
|
||||
|
||||
volatile bool recvBuffReady = false;
|
||||
char recvBuff[42] = "";
|
||||
int recvIdx = 0;
|
||||
void recvCallback(uint8_t *data, size_t len) {
|
||||
memcpy(recvBuff + recvIdx, data, len);
|
||||
recvIdx += len;
|
||||
if (recvIdx == sizeof(recvBuff)) {
|
||||
recvBuffReady = true;
|
||||
recvIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sendcbs = 0;
|
||||
// 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.
|
||||
char sendBuff[42];
|
||||
void sentCallback() {
|
||||
memset(sendBuff, 0, sizeof(sendBuff));
|
||||
sprintf(sendBuff, "Slave to Master Xmission %d", sendcbs++);
|
||||
SPISlave1.setData((uint8_t*)sendBuff, sizeof(sendBuff));
|
||||
}
|
||||
|
||||
// Note that we use SPISlave1 here **not** because we're running on
|
||||
// Core 1, but because SPI0 is being used already. You can use
|
||||
// SPISlave or SPISlave1 on any core.
|
||||
void setup1() {
|
||||
SPISlave1.setRX(8);
|
||||
SPISlave1.setCS(9);
|
||||
SPISlave1.setSCK(10);
|
||||
SPISlave1.setTX(11);
|
||||
// Ensure we start with something to send...
|
||||
sentCallback();
|
||||
// Hook our callbacks into the slave
|
||||
SPISlave1.onDataRecv(recvCallback);
|
||||
SPISlave1.onDataSent(sentCallback);
|
||||
SPISlave1.begin(spisettings);
|
||||
delay(3000);
|
||||
Serial.println("S-INFO: SPISlave started");
|
||||
}
|
||||
|
||||
void loop1() {
|
||||
if (recvBuffReady) {
|
||||
Serial.printf("S-RECV: '%s'\n", recvBuff);
|
||||
recvBuffReady = false;
|
||||
}
|
||||
}
|
||||
40
libraries/SPISlave/keywords.txt
Normal file
40
libraries/SPISlave/keywords.txt
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map SPI
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
SPISlave KEYWORD1
|
||||
SPISlave1 KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
SPISettings KEYWORD2
|
||||
setRX KEYWORD2
|
||||
setTX KEYWORD2
|
||||
setSCK KEYWORD2
|
||||
setCS KEYWORD2
|
||||
setData KEYWORD2
|
||||
onDataRecv KEYWORD2
|
||||
onDataSent KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
SPI_CLOCK_DIV4 LITERAL1
|
||||
SPI_CLOCK_DIV16 LITERAL1
|
||||
SPI_CLOCK_DIV64 LITERAL1
|
||||
SPI_CLOCK_DIV128 LITERAL1
|
||||
SPI_CLOCK_DIV2 LITERAL1
|
||||
SPI_CLOCK_DIV8 LITERAL1
|
||||
SPI_CLOCK_DIV32 LITERAL1
|
||||
SPI_CLOCK_DIV64 LITERAL1
|
||||
SPI_MODE0 LITERAL1
|
||||
SPI_MODE1 LITERAL1
|
||||
SPI_MODE2 LITERAL1
|
||||
SPI_MODE3 LITERAL1
|
||||
10
libraries/SPISlave/library.properties
Normal file
10
libraries/SPISlave/library.properties
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
name=SPISlave
|
||||
version=1.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Enables the communication as a slave SPI devices.
|
||||
paragraph=
|
||||
category=Signal Input/Output
|
||||
url=https://github.com/earlephilhower/arduino-pico
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
263
libraries/SPISlave/src/SPISlave.cpp
Normal file
263
libraries/SPISlave/src/SPISlave.cpp
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
SPI Slave library for the Raspberry Pi Pico RP2040
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include "SPISlave.h"
|
||||
#include <hardware/spi.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/structs/iobank0.h>
|
||||
#include <hardware/irq.h>
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
// For Serial when selecting TinyUSB. Can't include in the core because Arduino IDE
|
||||
// will not link in libraries called from the core. Instead, add the header to all
|
||||
// the standard libraries in the hope it will still catch some user cases where they
|
||||
// use these libraries.
|
||||
// See https://github.com/earlephilhower/arduino-pico/issues/167#issuecomment-848622174
|
||||
#include <Adafruit_TinyUSB.h>
|
||||
#endif
|
||||
|
||||
SPISlaveClass::SPISlaveClass(spi_inst_t *spi, pin_size_t rx, pin_size_t cs, pin_size_t sck, pin_size_t tx) {
|
||||
_spi = spi;
|
||||
_running = false;
|
||||
_initted = false;
|
||||
_spis = SPISettings();
|
||||
_RX = rx;
|
||||
_TX = tx;
|
||||
_SCK = sck;
|
||||
_CS = cs;
|
||||
_recvCB = nullptr;
|
||||
_sentCB = nullptr;
|
||||
_dataOut = nullptr;
|
||||
_dataLeft = 0;
|
||||
}
|
||||
|
||||
inline spi_cpol_t SPISlaveClass::cpol(SPISettings _spis) {
|
||||
switch (_spis.getDataMode()) {
|
||||
case SPI_MODE0:
|
||||
return SPI_CPOL_0;
|
||||
case SPI_MODE1:
|
||||
return SPI_CPOL_0;
|
||||
case SPI_MODE2:
|
||||
return SPI_CPOL_1;
|
||||
case SPI_MODE3:
|
||||
return SPI_CPOL_1;
|
||||
}
|
||||
// Error
|
||||
return SPI_CPOL_0;
|
||||
}
|
||||
|
||||
inline spi_cpha_t SPISlaveClass::cpha(SPISettings _spis) {
|
||||
switch (_spis.getDataMode()) {
|
||||
case SPI_MODE0:
|
||||
return SPI_CPHA_0;
|
||||
case SPI_MODE1:
|
||||
return SPI_CPHA_1;
|
||||
case SPI_MODE2:
|
||||
return SPI_CPHA_0;
|
||||
case SPI_MODE3:
|
||||
return SPI_CPHA_1;
|
||||
}
|
||||
// Error
|
||||
return SPI_CPHA_0;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setRX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({0, 4, 16, 20}) /* SPI0 */,
|
||||
__bitset({8, 12, 24, 28}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_RX = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_RX == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.RX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.RX to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setCS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({1, 5, 17, 21}) /* SPI0 */,
|
||||
__bitset({9, 13, 25, 29}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_CS = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_CS == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.CS while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.CS to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setSCK(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({2, 6, 18, 22}) /* SPI0 */,
|
||||
__bitset({10, 14, 26}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_SCK = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_SCK == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.SCK while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.SCK to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPISlaveClass::setTX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({3, 7, 19, 23}) /* SPI0 */,
|
||||
__bitset({11, 15, 27}) /* SPI1 */
|
||||
};
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_TX = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_TX == pin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.TX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.TX to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SPISlaveClass::_handleIRQ() {
|
||||
// Attempt to read out all RX FIFO datra and return to callback
|
||||
uint8_t buff[8]; // SPI FIFO 8 deep max
|
||||
int cnt;
|
||||
for (cnt = 0; (cnt < 8) && spi_is_readable(_spi); cnt++) {
|
||||
buff[cnt] = spi_get_hw(_spi)->dr;
|
||||
}
|
||||
if (cnt && _recvCB) {
|
||||
_recvCB(buff, cnt);
|
||||
}
|
||||
// Attempt to send as many ytes to the TX FIFO as we have/are free
|
||||
while (spi_is_writable(_spi)) {
|
||||
for (; _dataLeft && spi_is_writable(_spi); _dataLeft--) {
|
||||
spi_get_hw(_spi)->dr = *(_dataOut++);
|
||||
}
|
||||
if (!_dataLeft && _sentCB) {
|
||||
_sentCB();
|
||||
}
|
||||
}
|
||||
// Disable the TX FIFO IRQ if there is still no data to send or we'd always be stuck in an IRQ
|
||||
// Will be re-enabled once user does a setData
|
||||
if (!_dataLeft) {
|
||||
spi_get_hw(_spi)->imsc = 2 | 4; // RTIM + RXIM
|
||||
}
|
||||
}
|
||||
|
||||
void SPISlaveClass::_irq0() {
|
||||
SPISlave._handleIRQ();
|
||||
}
|
||||
|
||||
void SPISlaveClass::_irq1() {
|
||||
SPISlave1._handleIRQ();
|
||||
}
|
||||
|
||||
void SPISlaveClass::setData(const uint8_t *data, size_t len) {
|
||||
_dataOut = data;
|
||||
_dataLeft = len;
|
||||
if (_initted) {
|
||||
spi_get_hw(_spi)->imsc = 2 | 4 | 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SPISlaveClass::begin(SPISettings spis) {
|
||||
DEBUGSPI("SPISlave::begin(%d), rx=%d, cs=%d, sck=%d, tx=%d\n", hwCS, _RX, _CS, _SCK, _TX);
|
||||
gpio_set_function(_RX, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_CS, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_SCK, GPIO_FUNC_SPI);
|
||||
gpio_set_function(_TX, GPIO_FUNC_SPI);
|
||||
if (_initted) {
|
||||
DEBUGSPI("SPISlave: deinitting currently active SPI\n");
|
||||
spi_deinit(_spi);
|
||||
}
|
||||
DEBUGSPI("SPISlave: initting SPI\n");
|
||||
spi_init(_spi, _spis.getClockFreq());
|
||||
DEBUGSPI("SPISlave: actual baudrate=%u\n", spi_get_baudrate(_spi));
|
||||
spi_set_slave(_spi, true);
|
||||
spi_set_format(_spi, 8, cpol(spis), cpha(spis), SPI_MSB_FIRST);
|
||||
|
||||
// Install our IRQ handler
|
||||
if (_spi == spi0) {
|
||||
irq_set_exclusive_handler(SPI0_IRQ, _irq0);
|
||||
} else {
|
||||
irq_set_exclusive_handler(SPI1_IRQ, _irq1);
|
||||
}
|
||||
|
||||
// Set to get IRQs on transmit and receive
|
||||
spi_get_hw(_spi)->imsc = 2 | 4 | 8 ; // RTIM + RXIM + TXIM (not RORIM)
|
||||
_initted = true;
|
||||
irq_set_enabled(_spi == spi0 ? SPI0_IRQ : SPI1_IRQ, true);
|
||||
}
|
||||
|
||||
void SPISlaveClass::end() {
|
||||
DEBUGSPI("SPISlave::end()\n");
|
||||
if (_initted) {
|
||||
DEBUGSPI("SPISlave: deinitting currently active SPI\n");
|
||||
if (_spi == spi0) {
|
||||
irq_remove_handler(SPI0_IRQ, _irq0);
|
||||
} else {
|
||||
irq_remove_handler(SPI1_IRQ, _irq1);
|
||||
}
|
||||
spi_deinit(_spi);
|
||||
_initted = false;
|
||||
}
|
||||
gpio_set_function(_RX, GPIO_FUNC_SIO);
|
||||
gpio_set_function(_CS, GPIO_FUNC_SIO);
|
||||
gpio_set_function(_SCK, GPIO_FUNC_SIO);
|
||||
gpio_set_function(_TX, GPIO_FUNC_SIO);
|
||||
}
|
||||
|
||||
#ifndef __SPI0_DEVICE
|
||||
#define __SPI0_DEVICE spi0
|
||||
#endif
|
||||
#ifndef __SPI1_DEVICE
|
||||
#define __SPI1_DEVICE spi1
|
||||
#endif
|
||||
|
||||
SPISlaveClass SPISlave(__SPI0_DEVICE, PIN_SPI0_MISO, PIN_SPI0_SS, PIN_SPI0_SCK, PIN_SPI0_MOSI);
|
||||
SPISlaveClass SPISlave1(__SPI1_DEVICE, PIN_SPI1_MISO, PIN_SPI1_SS, PIN_SPI1_SCK, PIN_SPI1_MOSI);
|
||||
95
libraries/SPISlave/src/SPISlave.h
Normal file
95
libraries/SPISlave/src/SPISlave.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
SPI Slave library for the Raspberry Pi Pico RP2040
|
||||
|
||||
Copyright (c) 2023 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <api/HardwareSPI.h>
|
||||
#include <hardware/spi.h>
|
||||
#include <functional>
|
||||
|
||||
typedef std::function<void(uint8_t *data, size_t len)> SPISlaveRecvHandler;
|
||||
typedef std::function<void(void)> SPISlaveSentHandler;
|
||||
|
||||
class SPISlaveClass {
|
||||
public:
|
||||
SPISlaveClass(spi_inst_t *spi, pin_size_t rx, pin_size_t cs, pin_size_t sck, pin_size_t tx);
|
||||
|
||||
// Assign pins, call before begin()
|
||||
bool setRX(pin_size_t pin);
|
||||
bool setCS(pin_size_t pin);
|
||||
bool setSCK(pin_size_t pin);
|
||||
bool setTX(pin_size_t pin);
|
||||
|
||||
void begin(SPISettings spis);
|
||||
void end();
|
||||
|
||||
// May be set before the initial begin(). If not, then as soon as
|
||||
// begin() is called you will get a callback.
|
||||
void setData(const uint8_t * data, size_t len);
|
||||
inline void setData(const char * data) {
|
||||
setData((const uint8_t *)data, strlen(data));
|
||||
}
|
||||
|
||||
// NOTE: These two callbacks are called from an IRQ context
|
||||
// NOTE: They should also be called before begin()
|
||||
|
||||
// Called when bytes are available to be read from the SPI slave reception buffer
|
||||
void onDataRecv(SPISlaveRecvHandler cb) {
|
||||
_recvCB = cb;
|
||||
}
|
||||
// Called when there is space in the SPI transmission buffer
|
||||
void onDataSent(SPISlaveSentHandler cb) {
|
||||
_sentCB = cb;
|
||||
}
|
||||
|
||||
private:
|
||||
// Naked IRQ callbacks, will thunk to real object ones below
|
||||
static void _irq0();
|
||||
static void _irq1();
|
||||
|
||||
public:
|
||||
void _handleIRQ();
|
||||
|
||||
private:
|
||||
spi_cpol_t cpol(SPISettings _spis);
|
||||
spi_cpha_t cpha(SPISettings _spis);
|
||||
uint8_t reverseByte(uint8_t b);
|
||||
uint16_t reverse16Bit(uint16_t w);
|
||||
void adjustBuffer(const void *s, void *d, size_t cnt, bool by16);
|
||||
|
||||
spi_inst_t *_spi;
|
||||
SPISettings _spis;
|
||||
pin_size_t _RX, _TX, _SCK, _CS;
|
||||
bool _running; // SPI port active
|
||||
bool _initted; // Transaction begun
|
||||
|
||||
SPISlaveRecvHandler _recvCB;
|
||||
SPISlaveSentHandler _sentCB;
|
||||
|
||||
// The current data to be pumped into the transmit FIFO
|
||||
const uint8_t *_dataOut;
|
||||
size_t _dataLeft;
|
||||
|
||||
// Received data will be returned in small chunks directly from a local buffer in _handleIRQ()
|
||||
};
|
||||
|
||||
extern SPISlaveClass SPISlave;
|
||||
extern SPISlaveClass SPISlave1;
|
||||
|
|
@ -13,7 +13,8 @@ for dir in ./cores/rp2040 ./libraries/EEPROM ./libraries/I2S ./libraries/SingleF
|
|||
./libraries/JoystickBT ./libraries/KeyboardBT ./variants ./libraries/BTstackLib \
|
||||
./libraries/MouseBT ./libraries/SerialBT ./libraries/HID_Bluetooth \
|
||||
./libraries/JoystickBLE ./libraries/KeyboardBLE ./libraries/MouseBLE \
|
||||
./libraries/lwIP_w5500 ./libraries/lwIP_w5100 ./libraries/lwIP_enc28j60; do
|
||||
./libraries/lwIP_w5500 ./libraries/lwIP_w5100 ./libraries/lwIP_enc28j60 \
|
||||
./libraries/SPISlave ; do
|
||||
find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" \) -a \! -path '*api*' -exec astyle --suffix=none --options=./tests/astyle_core.conf \{\} \;
|
||||
find $dir -type f -name "*.ino" -exec astyle --suffix=none --options=./tests/astyle_examples.conf \{\} \;
|
||||
done
|
||||
|
|
|
|||
Loading…
Reference in a new issue