4#ifdef USE_SPI_PSRAM_DMA
5#include <esp_memory_utils.h>
11static const char *
const TAG =
"spi";
12static const size_t MAX_TRANSFER_SIZE = 4092;
14#ifdef USE_SPI_PSRAM_DMA
15static uint32_t get_psram_dma_flags(
bool enabled,
const void *tx_buffer) {
16 if (enabled && tx_buffer !=
nullptr && esp_ptr_dma_ext_capable(tx_buffer))
17 return SPI_TRANS_DMA_USE_PSRAM;
22class SPIDelegateHw :
public SPIDelegate {
25 bool release_device,
bool write_only)
28 release_device_(release_device),
29 write_only_(write_only) {
30 if (!this->release_device_)
33 if (this->write_only_) {
34 ESP_LOGV(
TAG,
"SPI device with CS pin %d using half-duplex mode (write-only)",
39 bool is_ready()
override {
return this->handle_ !=
nullptr; }
41 void begin_transaction()
override {
42 if (this->release_device_)
44 if (this->is_ready()) {
45 if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK) {
46 ESP_LOGE(
TAG,
"Failed to acquire SPI bus");
50 ESP_LOGW(
TAG,
"SPI device not ready, cannot begin transaction");
54 void end_transaction()
override {
55 if (this->is_ready()) {
57 spi_device_release_bus(this->handle_);
58 if (this->release_device_) {
59 spi_bus_remove_device(this->handle_);
60 this->handle_ =
nullptr;
65 ~SPIDelegateHw()
override {
66 esp_err_t
const err = spi_bus_remove_device(this->handle_);
68 ESP_LOGE(
TAG,
"Remove device failed - err %X", err);
76 void transfer(
const uint8_t *txbuf, uint8_t *rxbuf,
size_t length)
override {
77 if (rxbuf !=
nullptr && this->write_only_) {
78 ESP_LOGE(
TAG,
"Attempted read from write-only channel");
81 spi_transaction_t desc = {};
82#ifdef USE_SPI_PSRAM_DMA
83 const uint32_t psram_flags = rxbuf ==
nullptr ? get_psram_dma_flags(this->
psram_dma_, txbuf) : 0;
86#ifdef USE_SPI_PSRAM_DMA
87 desc.flags = psram_flags;
89 size_t const partial = std::min(
length, MAX_TRANSFER_SIZE);
90 desc.length = partial * 8;
91 desc.rxlength = this->write_only_ ? 0 : partial * 8;
92 desc.tx_buffer = txbuf;
93 desc.rx_buffer = rxbuf;
95 esp_err_t err = spi_device_polling_start(this->handle_, &desc, portMAX_DELAY);
97 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
100 ESP_LOGE(
TAG,
"Transmit failed - err %X", err);
103#ifdef USE_SPI_PSRAM_DMA
104 if ((desc.flags & SPI_TRANS_DMA_TX_FAIL) != 0) {
105 ESP_LOGE(
TAG,
"PSRAM DMA TX underflow");
110 if (txbuf !=
nullptr)
112 if (rxbuf !=
nullptr)
117 void write(uint16_t data,
size_t num_bits)
override {
118 spi_transaction_ext_t desc = {};
119 desc.command_bits = num_bits;
120 desc.base.flags = SPI_TRANS_VARIABLE_CMD;
121 desc.base.cmd =
data;
122 esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY);
124 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
128 ESP_LOGE(
TAG,
"Transmit failed - err %X", err);
142 void write_cmd_addr_data(
size_t cmd_bits,
uint32_t cmd,
size_t addr_bits,
uint32_t address,
const uint8_t *data,
143 size_t length, uint8_t bus_width)
override {
144 spi_transaction_ext_t desc = {};
145 if (
length == 0 && cmd_bits == 0 && addr_bits == 0) {
146 esph_log_w(
TAG,
"Nothing to transfer");
149 desc.base.flags = SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_DUMMY;
150 if (bus_width == 4) {
151 desc.base.flags |= SPI_TRANS_MODE_QIO;
152 }
else if (bus_width == 8) {
153 desc.base.flags |= SPI_TRANS_MODE_OCT;
155 desc.command_bits = cmd_bits;
156 desc.address_bits = addr_bits;
158 desc.base.rxlength = 0;
161#ifdef USE_SPI_PSRAM_DMA
162 const uint32_t transaction_flags = desc.base.flags | get_psram_dma_flags(this->
psram_dma_, data);
165#ifdef USE_SPI_PSRAM_DMA
166 desc.base.flags = transaction_flags;
168 size_t chunk_size = std::min(
length, MAX_TRANSFER_SIZE);
169 if (data !=
nullptr && chunk_size != 0) {
170 desc.base.length = chunk_size * 8;
171 desc.base.tx_buffer =
data;
176 desc.base.length = 0;
178 esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY);
180 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
183 ESP_LOGE(
TAG,
"Transmit failed - err %X", err);
186#ifdef USE_SPI_PSRAM_DMA
187 if ((desc.base.flags & SPI_TRANS_DMA_TX_FAIL) != 0) {
188 ESP_LOGE(
TAG,
"PSRAM DMA TX underflow");
193 desc.command_bits = 0;
194 desc.address_bits = 0;
198 void transfer(uint8_t *ptr,
size_t length)
override { this->transfer(ptr, ptr,
length); }
200 uint8_t transfer(uint8_t data)
override {
202 this->transfer(&data, &rxbuf, 1);
206 void write16(uint16_t data)
override { this->write(data, 16); }
208 void write_array(
const uint8_t *ptr,
size_t length)
override { this->transfer(ptr,
nullptr,
length); }
210 void write_array16(
const uint16_t *data,
size_t length)
override {
212 this->write_array((uint8_t *) data,
length * 2);
214 uint16_t buffer[MAX_TRANSFER_SIZE / 2];
216 size_t const partial = std::min(
length, MAX_TRANSFER_SIZE / 2);
217 for (
size_t i = 0; i != partial; i++) {
218 buffer[i] = SPI_SWAP_DATA_TX(*data++, 16);
220 this->write_array((
const uint8_t *) buffer, partial * 2);
226 void read_array(uint8_t *ptr,
size_t length)
override { this->transfer(
nullptr, ptr,
length); }
230 spi_device_interface_config_t config = {};
231 config.mode =
static_cast<uint8_t
>(this->
mode_);
232 config.clock_speed_hz =
static_cast<int>(this->
data_rate_);
233 config.spics_io_num = -1;
235 config.queue_size = 1;
236 config.pre_cb =
nullptr;
237 config.post_cb =
nullptr;
239 config.flags |= SPI_DEVICE_BIT_LSBFIRST;
240 if (this->write_only_)
241 config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY;
242 esp_err_t
const err = spi_bus_add_device(this->channel_, &config, &this->handle_);
244 ESP_LOGE(
TAG,
"Add device failed - err %X", err);
251 spi_device_handle_t handle_{};
252 bool release_device_{
false};
253 bool write_only_{
false};
256class SPIBusHw :
public SPIBus {
258 SPIBusHw(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi,
SPIInterface channel, std::vector<uint8_t> data_pins)
259 :
SPIBus(clk, sdo, sdi), channel_(channel) {
260 spi_bus_config_t buscfg = {};
262 buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK;
263 if (data_pins.empty()) {
266 buscfg.quadwp_io_num = -1;
267 buscfg.quadhd_io_num = -1;
269 buscfg.data0_io_num = data_pins[0];
270 buscfg.data1_io_num = data_pins[1];
271 buscfg.data2_io_num = data_pins[2];
272 buscfg.data3_io_num = data_pins[3];
273 if (data_pins.size() == 8) {
274 buscfg.data4_io_num = data_pins[4];
275 buscfg.data5_io_num = data_pins[5];
276 buscfg.data6_io_num = data_pins[6];
277 buscfg.data7_io_num = data_pins[7];
278 buscfg.flags |= SPICOMMON_BUSFLAG_OCTAL;
280 buscfg.data4_io_num = -1;
281 buscfg.data5_io_num = -1;
282 buscfg.data6_io_num = -1;
283 buscfg.data7_io_num = -1;
284 buscfg.flags |= SPICOMMON_BUSFLAG_QUAD;
287 buscfg.max_transfer_sz = MAX_TRANSFER_SIZE;
288 auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO);
290 ESP_LOGE(
TAG,
"Bus init failed - err %X", err);
295 bool release_device,
bool write_only)
override {
296 return new SPIDelegateHw(this->channel_, data_rate, bit_order,
mode, cs_pin, release_device,
303 bool is_hw()
override {
return true; }
307 const std::vector<uint8_t> &data_pins) {
308 return new SPIBusHw(clk, sdo, sdi, interface, data_pins);
BedjetMode mode
BedJet operating mode.
static SPIBus * get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, const std::vector< uint8_t > &data_pins)
virtual void end_transaction()
virtual void begin_transaction()
static int get_pin_no(GPIOPin *pin)
std::span< const uint8_t > data
Implementation of SPI Controller mode.
SPIMode
Modes mapping to clock phase and polarity.
SPIBitOrder
The bit-order for SPI devices. This defines how the data read from and written to the device is inter...
@ BIT_ORDER_LSB_FIRST
The least significant bit is transmitted/received first.
spi_host_device_t SPIInterface