ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
spi_esp_idf.cpp
Go to the documentation of this file.
1#include "spi.h"
2#include <vector>
3
4#ifdef USE_SPI_PSRAM_DMA
5#include <esp_memory_utils.h>
6#endif
7
8namespace esphome::spi {
9
10#ifdef USE_ESP32
11static const char *const TAG = "spi";
12static const size_t MAX_TRANSFER_SIZE = 4092; // dictated by ESP-IDF API.
13
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;
18 return 0;
19}
20#endif
21
22class SPIDelegateHw : public SPIDelegate {
23 public:
24 SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
25 bool release_device, bool write_only)
26 : SPIDelegate(data_rate, bit_order, mode, cs_pin),
27 channel_(channel),
28 release_device_(release_device),
29 write_only_(write_only) {
30 if (!this->release_device_)
31 add_device_();
32
33 if (this->write_only_) {
34 ESP_LOGV(TAG, "SPI device with CS pin %d using half-duplex mode (write-only)",
36 }
37 }
38
39 bool is_ready() override { return this->handle_ != nullptr; }
40
41 void begin_transaction() override {
42 if (this->release_device_)
43 this->add_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");
47 }
49 } else {
50 ESP_LOGW(TAG, "SPI device not ready, cannot begin transaction");
51 }
52 }
53
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; // reset handle to indicate no device is registered
61 }
62 }
63 }
64
65 ~SPIDelegateHw() override {
66 esp_err_t const err = spi_bus_remove_device(this->handle_);
67 if (err != ESP_OK) {
68 ESP_LOGE(TAG, "Remove device failed - err %X", err);
69 }
70 }
71
72 // do a transfer. either txbuf or rxbuf (but not both) may be null.
73 // transfers above the maximum size will be split.
74 // TODO - make use of the queue for interrupt transfers to provide a (short) pipeline of blocks
75 // when splitting is required.
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");
79 return;
80 }
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;
84#endif
85 while (length != 0) {
86#ifdef USE_SPI_PSRAM_DMA
87 desc.flags = psram_flags;
88#endif
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;
94 // polling is used as it has about 10% less overhead than queuing an interrupt transfer
95 esp_err_t err = spi_device_polling_start(this->handle_, &desc, portMAX_DELAY);
96 if (err == ESP_OK) {
97 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
98 }
99 if (err != ESP_OK) {
100 ESP_LOGE(TAG, "Transmit failed - err %X", err);
101 break;
102 }
103#ifdef USE_SPI_PSRAM_DMA
104 if ((desc.flags & SPI_TRANS_DMA_TX_FAIL) != 0) {
105 ESP_LOGE(TAG, "PSRAM DMA TX underflow");
106 break;
107 }
108#endif
109 length -= partial;
110 if (txbuf != nullptr)
111 txbuf += partial;
112 if (rxbuf != nullptr)
113 rxbuf += partial;
114 }
115 }
116
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);
123 if (err == ESP_OK) {
124 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
125 }
126
127 if (err != ESP_OK) {
128 ESP_LOGE(TAG, "Transmit failed - err %X", err);
129 }
130 }
131
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");
147 return;
148 }
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;
154 }
155 desc.command_bits = cmd_bits;
156 desc.address_bits = addr_bits;
157 desc.dummy_bits = 0;
158 desc.base.rxlength = 0;
159 desc.base.cmd = cmd;
160 desc.base.addr = address;
161#ifdef USE_SPI_PSRAM_DMA
162 const uint32_t transaction_flags = desc.base.flags | get_psram_dma_flags(this->psram_dma_, data);
163#endif
164 do {
165#ifdef USE_SPI_PSRAM_DMA
166 desc.base.flags = transaction_flags;
167#endif
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;
172 length -= chunk_size;
173 data += chunk_size;
174 } else {
175 length = 0;
176 desc.base.length = 0;
177 }
178 esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY);
179 if (err == ESP_OK) {
180 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
181 }
182 if (err != ESP_OK) {
183 ESP_LOGE(TAG, "Transmit failed - err %X", err);
184 return;
185 }
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");
189 return;
190 }
191#endif
192 // if more data is to be sent, skip the command and address phases.
193 desc.command_bits = 0;
194 desc.address_bits = 0;
195 } while (length != 0);
196 }
197
198 void transfer(uint8_t *ptr, size_t length) override { this->transfer(ptr, ptr, length); }
199
200 uint8_t transfer(uint8_t data) override {
201 uint8_t rxbuf;
202 this->transfer(&data, &rxbuf, 1);
203 return rxbuf;
204 }
205
206 void write16(uint16_t data) override { this->write(data, 16); }
207
208 void write_array(const uint8_t *ptr, size_t length) override { this->transfer(ptr, nullptr, length); }
209
210 void write_array16(const uint16_t *data, size_t length) override {
211 if (this->bit_order_ == BIT_ORDER_LSB_FIRST) {
212 this->write_array((uint8_t *) data, length * 2);
213 } else {
214 uint16_t buffer[MAX_TRANSFER_SIZE / 2];
215 while (length != 0) {
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);
219 }
220 this->write_array((const uint8_t *) buffer, partial * 2);
221 length -= partial;
222 }
223 }
224 }
225
226 void read_array(uint8_t *ptr, size_t length) override { this->transfer(nullptr, ptr, length); }
227
228 protected:
229 bool add_device_() {
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;
234 config.flags = 0;
235 config.queue_size = 1;
236 config.pre_cb = nullptr;
237 config.post_cb = nullptr;
238 if (this->bit_order_ == BIT_ORDER_LSB_FIRST)
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_);
243 if (err != ESP_OK) {
244 ESP_LOGE(TAG, "Add device failed - err %X", err);
245 return false;
246 }
247 return true;
248 }
249
250 SPIInterface channel_{};
251 spi_device_handle_t handle_{};
252 bool release_device_{false};
253 bool write_only_{false};
254};
255
256class SPIBusHw : public SPIBus {
257 public:
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 = {};
261 buscfg.sclk_io_num = Utility::get_pin_no(clk);
262 buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK;
263 if (data_pins.empty()) {
264 buscfg.mosi_io_num = Utility::get_pin_no(sdo);
265 buscfg.miso_io_num = Utility::get_pin_no(sdi);
266 buscfg.quadwp_io_num = -1;
267 buscfg.quadhd_io_num = -1;
268 } else {
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;
279 } else {
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;
285 }
286 }
287 buscfg.max_transfer_sz = MAX_TRANSFER_SIZE;
288 auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO);
289 if (err != ESP_OK) {
290 ESP_LOGE(TAG, "Bus init failed - err %X", err);
291 }
292 }
293
294 SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
295 bool release_device, bool write_only) override {
296 return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, release_device,
297 write_only || Utility::get_pin_no(this->sdi_pin_) == -1);
298 }
299
300 protected:
301 SPIInterface channel_{};
302
303 bool is_hw() override { return true; }
304};
305
306SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi,
307 const std::vector<uint8_t> &data_pins) {
308 return new SPIBusHw(clk, sdo, sdi, interface, data_pins);
309}
310
311#endif // USE_ESP32
312} // namespace esphome::spi
BedjetMode mode
BedJet operating mode.
uint8_t address
Definition bl0906.h:4
GPIOPin * sdi_pin_
Definition spi.h:339
static SPIBus * get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, const std::vector< uint8_t > &data_pins)
Definition spi.cpp:123
virtual void end_transaction()
Definition spi.h:198
virtual void begin_transaction()
Definition spi.h:195
SPIBitOrder bit_order_
Definition spi.h:261
static int get_pin_no(GPIOPin *pin)
Definition spi.h:138
std::span< const uint8_t > data
Implementation of SPI Controller mode.
Definition spi.cpp:5
SPIMode
Modes mapping to clock phase and polarity.
Definition spi.h:78
const char *const TAG
Definition spi.cpp:7
SPIBitOrder
The bit-order for SPI devices. This defines how the data read from and written to the device is inter...
Definition spi.h:40
@ BIT_ORDER_LSB_FIRST
The least significant bit is transmitted/received first.
Definition spi.h:42
static void uint32_t
spi_host_device_t SPIInterface
Definition spi.h:14
uint16_t length
Definition tt21100.cpp:0