SPI library for ESP32 which use DMA buffer to send/receive transactions
This is the SPI library to send/receive large transaction with DMA. Please use ESP32SPISlave for the simple SPI Slave mode without DMA.
- support DMA buffer (more than 64 byte transfer is available)
- support SPI Slave mode based on ESP32's SPI Slave Driver
- Master has two ways to send/receive transactions
transfer()
to send/receive transaction one by onequeue()
andyield()
to send/receive multiple transactions at once (more efficient thantransfer()
many times)
- Slave has two ways to receive/send transactions
wait()
to receive/send transaction one by onequeue()
andyield()
to receive/send multiple transactions at once (more efficient thanwait()
many times)
- There is known issue that last 4 bytes are missing if DMA is used with SPI Slave
- you need to send 4 bytes more to send all required bytes to ESP32 SPI Slave with DMA
- see
examples/master_slave_polling_avoid_drop_last_4byte
for more detail
- There is also a known issue that received data is bit shifted depending on the SPI mode
- Please try SPI mode 0 for this issue
- But SPI mode 1 or 3 is required based on the official doc
- Please use this library and SPI modes for your own risk
#include <ESP32DMASPIMaster.h>
ESP32DMASPI::Master master;
static const uint32_t BUFFER_SIZE = 8192;
uint8_t* spi_master_tx_buf;
uint8_t* spi_master_rx_buf;
void setup() {
Serial.begin(115200);
// to use DMA buffer, use these methods to allocate buffer
spi_master_tx_buf = master.allocDMABuffer(BUFFER_SIZE);
spi_master_rx_buf = master.allocDMABuffer(BUFFER_SIZE);
master.setDataMode(SPI_MODE3);
master.setFrequency(SPI_MASTER_FREQ_8M);
master.setMaxTransferSize(BUFFER_SIZE);
master.setDMAChannel(1); // 1 or 2 only
master.setQueueSize(1); // transaction queue size
// begin() after setting
// HSPI = CS: 15, CLK: 14, MOSI: 13, MISO: 12
// VSPI = CS: 5, CLK: 18, MOSI: 23, MISO: 19
master.begin(HSPI);
}
void loop() {
// set buffer data here
// start and wait to complete transaction
master.transfer(spi_master_tx_buf, spi_master_rx_buf, BUFFER_SIZE);
// do something here with received data (if needed)
for (size_t i = 0; i < BUFFER_SIZE; ++i)
printf("%d ", spi_master_rx_buf[i]);
printf("\n");
delay(2000);
}
#include <ESP32DMASPISlave.h>
ESP32DMASPI::Slave slave;
static const uint32_t BUFFER_SIZE = 8192;
uint8_t* spi_slave_tx_buf;
uint8_t* spi_slave_rx_buf;
void setup() {
Serial.begin(115200);
// to use DMA buffer, use these methods to allocate buffer
spi_slave_tx_buf = slave.allocDMABuffer(BUFFER_SIZE);
spi_slave_rx_buf = slave.allocDMABuffer(BUFFER_SIZE);
slave.setDataMode(SPI_MODE3);
slave.setMaxTransferSize(BUFFER_SIZE);
slave.setDMAChannel(2); // 1 or 2 only
slave.setQueueSize(1); // transaction queue size
// begin() after setting
// HSPI = CS: 15, CLK: 14, MOSI: 13, MISO: 12
// VSPI = CS: 5, CLK: 18, MOSI: 23, MISO: 19
slave.begin(VSPI);
}
void loop() {
// set buffer (reply to master) data here
// if there is no transaction in queue, add transaction
if (slave.remained() == 0)
slave.queue(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);
// if transaction has completed from master,
// available() returns size of results of transaction,
// and buffer is automatically updated
while (slave.available()) {
// do something here with received data
for (size_t i = 0; i < BUFFER_SIZE; ++i)
printf("%d ", spi_slave_rx_buf[i]);
printf("\n");
slave.pop();
}
}
bool begin(const uint8_t spi_bus = HSPI, const int8_t sck = -1, const int8_t miso = -1, const int8_t mosi = -1, const int8_t ss = -1);
bool end();
uint8_t* allocDMABuffer(const size_t s);
// execute transaction and wait for transmission one by one
size_t transfer(const uint8_t* tx_buf, const size_t size);
size_t transfer(const uint8_t* tx_buf, uint8_t* rx_buf, const size_t size);
// queueing transaction and execute simultaneously
// wait (blocking) and timeout occurs if queue is full with transaction
// (but designed not to queue transaction more than queue_size, so there is no timeout argument)
bool queue(const uint8_t* tx_buf, const size_t size);
bool queue(const uint8_t* tx_buf, uint8_t* rx_buf, const size_t size);
void yield();
// set these optional parameters before begin() if you want
void setDataMode(const uint8_t m);
void setFrequency(const uint32_t f);
void setMaxTransferSize(const int s);
void setDMAChannel(const int c);
void setQueueSize(const int s);
bool begin(const uint8_t spi_bus = HSPI, const int8_t sck = -1, const int8_t miso = -1, const int8_t mosi = -1, const int8_t ss = -1);
bool end();
uint8_t* allocDMABuffer(const size_t s);
// wait for transaction one by one
bool wait(uint8_t* rx_buf, const size_t size); // no data to master
bool wait(uint8_t* rx_buf, const uint8_t* tx_buf, const size_t size);
// queueing transaction
// wait (blocking) and timeout occurs if queue is full with transaction
// (but designed not to queue transaction more than queue_size, so there is no timeout argument)
bool queue(uint8_t* rx_buf, const size_t size); // no data to master
bool queue(uint8_t* rx_buf, const uint8_t* tx_buf, const size_t size);
// wait until all queued transaction will be done by master
// if yield is finished, all the buffer is updated to latest
void yield();
// transaction result info
size_t available() const;
size_t remained() const;
uint32_t size() const;
void pop();
// set these optional parameters before begin() if you want
void setDataMode(const uint8_t m);
void setMaxTransferSize(const int s);
void setDMAChannel(const int c); // 1 or 2 only
void setQueueSize(const int s);
- more configs? (SPI ISR callbacks, SPI cmd/addr/user feature, etc...)
MIT