Skip to content

Commit

Permalink
Deduplicate SPI and SoftwareSPI routines (#2779)
Browse files Browse the repository at this point in the history
  • Loading branch information
earlephilhower authored Jan 28, 2025
1 parent acf81f4 commit 8c31705
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 229 deletions.
100 changes: 14 additions & 86 deletions libraries/SPI/src/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,47 +45,6 @@ SPIClassRP2040::SPIClassRP2040(spi_inst_t *spi, pin_size_t rx, pin_size_t cs, pi
_CS = cs;
}

inline spi_cpol_t SPIClassRP2040::cpol() {
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 SPIClassRP2040::cpha() {
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;
}

inline uint8_t SPIClassRP2040::reverseByte(uint8_t b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}

inline uint16_t SPIClassRP2040::reverse16Bit(uint16_t w) {
return (reverseByte(w & 0xff) << 8) | (reverseByte(w >> 8));
}

// The HW can't do LSB first, only MSB first, so need to bitreverse
void SPIClassRP2040::adjustBuffer(const void *s, void *d, size_t cnt, bool by16) {
if (_spis.getBitOrder() == MSBFIRST) {
Expand All @@ -94,13 +53,13 @@ void SPIClassRP2040::adjustBuffer(const void *s, void *d, size_t cnt, bool by16)
const uint8_t *src = (const uint8_t *)s;
uint8_t *dst = (uint8_t *)d;
for (size_t i = 0; i < cnt; i++) {
*(dst++) = reverseByte(*(src++));
*(dst++) = _helper.reverseByte(*(src++));
}
} else { /* by16 */
const uint16_t *src = (const uint16_t *)s;
uint16_t *dst = (uint16_t *)d;
for (size_t i = 0; i < cnt; i++) {
*(dst++) = reverse16Bit(*(src++));
*(dst++) = _helper.reverse16Bit(*(src++));
}
}
}
Expand All @@ -110,11 +69,11 @@ byte SPIClassRP2040::transfer(uint8_t data) {
if (!_initted) {
return 0;
}
data = (_spis.getBitOrder() == MSBFIRST) ? data : reverseByte(data);
DEBUGSPI("SPI::transfer(%02x), cpol=%d, cpha=%d\n", data, cpol(), cpha());
data = (_spis.getBitOrder() == MSBFIRST) ? data : _helper.reverseByte(data);
DEBUGSPI("SPI::transfer(%02x), cpol=%d, cpha=%d\n", data, _helper.cpol(_spis), _helper.cpha(_spis));
hw_write_masked(&spi_get_hw(_spi)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); // Fast set to 8-bits
spi_write_read_blocking(_spi, &data, &ret, 1);
ret = (_spis.getBitOrder() == MSBFIRST) ? ret : reverseByte(ret);
ret = (_spis.getBitOrder() == MSBFIRST) ? ret : _helper.reverseByte(ret);
DEBUGSPI("SPI: read back %02x\n", ret);
return ret;
}
Expand All @@ -124,11 +83,11 @@ uint16_t SPIClassRP2040::transfer16(uint16_t data) {
if (!_initted) {
return 0;
}
data = (_spis.getBitOrder() == MSBFIRST) ? data : reverse16Bit(data);
DEBUGSPI("SPI::transfer16(%04x), cpol=%d, cpha=%d\n", data, cpol(), cpha());
data = (_spis.getBitOrder() == MSBFIRST) ? data : _helper.reverse16Bit(data);
DEBUGSPI("SPI::transfer16(%04x), cpol=%d, cpha=%d\n", data, _helper.cpol(_spis), _helper.cpha(_spis));
hw_write_masked(&spi_get_hw(_spi)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); // Fast set to 16-bits
spi_write16_read16_blocking(_spi, &data, &ret, 1);
ret = (_spis.getBitOrder() == MSBFIRST) ? ret : reverse16Bit(ret);
ret = (_spis.getBitOrder() == MSBFIRST) ? ret : _helper.reverse16Bit(ret);
DEBUGSPI("SPI: read back %02x\n", ret);
return ret;
}
Expand Down Expand Up @@ -172,21 +131,14 @@ void SPIClassRP2040::transfer(const void *txbuf, void *rxbuf, size_t count) {
// If its LSB this isn't nearly as fun, we'll just let transfer(x) do it :(
for (size_t i = 0; i < count; i++) {
*rxbuff = transfer(*txbuff);
*rxbuff = (_spis.getBitOrder() == MSBFIRST) ? *rxbuff : reverseByte(*rxbuff);
*rxbuff = (_spis.getBitOrder() == MSBFIRST) ? *rxbuff : _helper.reverseByte(*rxbuff);
txbuff++;
rxbuff++;
}
DEBUGSPI("SPI::transfer completed\n");
}

#ifdef PICO_RP2350B
#define GPIOIRQREGS 6
#else
#define GPIOIRQREGS 4
#endif

void SPIClassRP2040::beginTransaction(SPISettings settings) {
noInterrupts(); // Avoid possible race conditions if IRQ comes in while main app is in middle of this
DEBUGSPI("SPI::beginTransaction(clk=%lu, bo=%s)\n", settings.getClockFreq(), (settings.getBitOrder() == MSBFIRST) ? "MSB" : "LSB");
if (_initted && settings == _spis) {
DEBUGSPI("SPI: Reusing existing initted SPI\n");
Expand All @@ -202,39 +154,15 @@ void SPIClassRP2040::beginTransaction(SPISettings settings) {
DEBUGSPI("SPI: actual baudrate=%u\n", spi_get_baudrate(_spi));
}
_spis = settings;
spi_set_format(_spi, 8, cpol(), cpha(), SPI_MSB_FIRST);
spi_set_format(_spi, 8, _helper.cpol(_spis), _helper.cpha(_spis), SPI_MSB_FIRST);
_initted = true;
}
// Disable any IRQs that are being used for SPI
io_bank0_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
DEBUGSPI("SPI: IRQ masks before = %08x %08x %08x %08x %08x %08x\n", (unsigned)irq_ctrl_base->inte[0], (unsigned)irq_ctrl_base->inte[1], (unsigned)irq_ctrl_base->inte[2], (unsigned)irq_ctrl_base->inte[3], (GPIOIRQREGS > 4) ? (unsigned)irq_ctrl_base->inte[4] : 0, (GPIOIRQREGS > 5) ? (unsigned)irq_ctrl_base->inte[5] : 0);
for (auto entry : _usingIRQs) {
int gpio = entry.first;

// There is no gpio_get_irq, so manually twiddle the register
io_rw_32 *en_reg = &irq_ctrl_base->inte[gpio / 8];
uint32_t val = ((*en_reg) >> (4 * (gpio % 8))) & 0xf;
_usingIRQs.insert_or_assign(gpio, val);
DEBUGSPI("SPI: GPIO %d = %lu\n", gpio, val);
(*en_reg) ^= val << (4 * (gpio % 8));
}
DEBUGSPI("SPI: IRQ masks after = %08x %08x %08x %08x %08x %08x\n", (unsigned)irq_ctrl_base->inte[0], (unsigned)irq_ctrl_base->inte[1], (unsigned)irq_ctrl_base->inte[2], (unsigned)irq_ctrl_base->inte[3], (GPIOIRQREGS > 4) ? (unsigned)irq_ctrl_base->inte[4] : 0, (GPIOIRQREGS > 5) ? (unsigned)irq_ctrl_base->inte[5] : 0);
interrupts();
_helper.maskInterrupts();
}

void SPIClassRP2040::endTransaction(void) {
noInterrupts(); // Avoid race condition so the GPIO IRQs won't come back until all state is restored
DEBUGSPI("SPI::endTransaction()\n");
// Re-enable IRQs
for (auto entry : _usingIRQs) {
int gpio = entry.first;
int mode = entry.second;
gpio_set_irq_enabled(gpio, mode, true);
}
io_bank0_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
(void) irq_ctrl_base;
DEBUGSPI("SPI: IRQ masks = %08x %08x %08x %08x %08x %08x\n", (unsigned)irq_ctrl_base->inte[0], (unsigned)irq_ctrl_base->inte[1], (unsigned)irq_ctrl_base->inte[2], (unsigned)irq_ctrl_base->inte[3], (GPIOIRQREGS > 4) ? (unsigned)irq_ctrl_base->inte[4] : 0, (GPIOIRQREGS > 5) ? (unsigned)irq_ctrl_base->inte[5] : 0);
interrupts();
_helper.unmaskInterrupts();
}

bool SPIClassRP2040::transferAsync(const void *send, void *recv, size_t bytes) {
Expand Down Expand Up @@ -265,7 +193,7 @@ bool SPIClassRP2040::transferAsync(const void *send, void *recv, size_t bytes) {
return false;
}
for (size_t i = 0; i < bytes; i++) {
_dmaBuffer[i] = reverseByte(txbuff[i]);
_dmaBuffer[i] = _helper.reverseByte(txbuff[i]);
}
}
_dmaBytes = bytes;
Expand Down Expand Up @@ -312,7 +240,7 @@ bool SPIClassRP2040::finishedAsync() {
spi_get_hw(_spi)->dmacr = 0;
if (_spis.getBitOrder() != MSBFIRST) {
for (int i = 0; i < _dmaBytes; i++) {
_rxFinalBuffer[i] = reverseByte(_rxFinalBuffer[i]);
_rxFinalBuffer[i] = _helper.reverseByte(_rxFinalBuffer[i]);
}
free(_dmaBuffer);
_dmaBuffer = nullptr;
Expand Down
14 changes: 5 additions & 9 deletions libraries/SPI/src/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include <Arduino.h>
#include <api/HardwareSPI.h>
#include <hardware/spi.h>
#include <map>
#include "SPIHelper.h"

class SPIClassRP2040 : public arduino::HardwareSPI {
public:
Expand Down Expand Up @@ -76,19 +76,16 @@ class SPIClassRP2040 : public arduino::HardwareSPI {

// List of GPIO IRQs to disable during a transaction
virtual void usingInterrupt(int interruptNumber) override {
_usingIRQs.insert({interruptNumber, 0});
_helper.usingInterrupt(interruptNumber);
}

virtual void notUsingInterrupt(int interruptNumber) override {
_usingIRQs.erase(interruptNumber);
_helper.notUsingInterrupt(interruptNumber);
}
virtual void attachInterrupt() override { /* noop */ }
virtual void detachInterrupt() override { /* noop */ }

private:
spi_cpol_t cpol();
spi_cpha_t cpha();
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;
Expand All @@ -98,15 +95,14 @@ class SPIClassRP2040 : public arduino::HardwareSPI {
bool _running; // SPI port active
bool _initted; // Transaction begun

std::map<int, int> _usingIRQs;

// DMA
int _channelDMA;
int _channelSendDMA;
uint8_t *_dmaBuffer = nullptr;
int _dmaBytes;
uint8_t *_rxFinalBuffer;
uint32_t _dummy;
SPIHelper _helper;
};

extern SPIClassRP2040 SPI;
Expand Down
Loading

0 comments on commit 8c31705

Please sign in to comment.