ESPHome 2026.1.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
4namespace esphome::spi {
5
6#ifdef USE_ESP32
7static const char *const TAG = "spi-esp-idf";
8static const size_t MAX_TRANSFER_SIZE = 4092; // dictated by ESP-IDF API.
9
10class SPIDelegateHw : public SPIDelegate {
11 public:
12 SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
13 bool release_device, bool write_only)
14 : SPIDelegate(data_rate, bit_order, mode, cs_pin),
15 channel_(channel),
16 release_device_(release_device),
17 write_only_(write_only) {
18 if (!this->release_device_)
19 add_device_();
20 }
21
22 bool is_ready() override { return this->handle_ != nullptr; }
23
24 void begin_transaction() override {
25 if (this->release_device_)
26 this->add_device_();
27 if (this->is_ready()) {
28 if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK)
29 ESP_LOGE(TAG, "Failed to acquire SPI bus");
31 } else {
32 ESP_LOGW(TAG, "SPI device not ready, cannot begin transaction");
33 }
34 }
35
36 void end_transaction() override {
37 if (this->is_ready()) {
39 spi_device_release_bus(this->handle_);
40 if (this->release_device_) {
41 spi_bus_remove_device(this->handle_);
42 this->handle_ = nullptr; // reset handle to indicate no device is registered
43 }
44 }
45 }
46
47 ~SPIDelegateHw() override {
48 esp_err_t const err = spi_bus_remove_device(this->handle_);
49 if (err != ESP_OK)
50 ESP_LOGE(TAG, "Remove device failed - err %X", err);
51 }
52
53 // do a transfer. either txbuf or rxbuf (but not both) may be null.
54 // transfers above the maximum size will be split.
55 // TODO - make use of the queue for interrupt transfers to provide a (short) pipeline of blocks
56 // when splitting is required.
57 void transfer(const uint8_t *txbuf, uint8_t *rxbuf, size_t length) override {
58 if (rxbuf != nullptr && this->write_only_) {
59 ESP_LOGE(TAG, "Attempted read from write-only channel");
60 return;
61 }
62 spi_transaction_t desc = {};
63 desc.flags = 0;
64 while (length != 0) {
65 size_t const partial = std::min(length, MAX_TRANSFER_SIZE);
66 desc.length = partial * 8;
67 desc.rxlength = this->write_only_ ? 0 : partial * 8;
68 desc.tx_buffer = txbuf;
69 desc.rx_buffer = rxbuf;
70 // polling is used as it has about 10% less overhead than queuing an interrupt transfer
71 esp_err_t err = spi_device_polling_start(this->handle_, &desc, portMAX_DELAY);
72 if (err == ESP_OK) {
73 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
74 }
75 if (err != ESP_OK) {
76 ESP_LOGE(TAG, "Transmit failed - err %X", err);
77 break;
78 }
79 length -= partial;
80 if (txbuf != nullptr)
81 txbuf += partial;
82 if (rxbuf != nullptr)
83 rxbuf += partial;
84 }
85 }
86
87 void write(uint16_t data, size_t num_bits) override {
88 spi_transaction_ext_t desc = {};
89 desc.command_bits = num_bits;
90 desc.base.flags = SPI_TRANS_VARIABLE_CMD;
91 desc.base.cmd = data;
92 esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY);
93 if (err == ESP_OK) {
94 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
95 }
96
97 if (err != ESP_OK) {
98 ESP_LOGE(TAG, "Transmit failed - err %X", err);
99 }
100 }
101
112 void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, const uint8_t *data,
113 size_t length, uint8_t bus_width) override {
114 spi_transaction_ext_t desc = {};
115 if (length == 0 && cmd_bits == 0 && addr_bits == 0) {
116 esph_log_w(TAG, "Nothing to transfer");
117 return;
118 }
119 desc.base.flags = SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_DUMMY;
120 if (bus_width == 4) {
121 desc.base.flags |= SPI_TRANS_MODE_QIO;
122 } else if (bus_width == 8) {
123 desc.base.flags |= SPI_TRANS_MODE_OCT;
124 }
125 desc.command_bits = cmd_bits;
126 desc.address_bits = addr_bits;
127 desc.dummy_bits = 0;
128 desc.base.rxlength = 0;
129 desc.base.cmd = cmd;
130 desc.base.addr = address;
131 do {
132 size_t chunk_size = std::min(length, MAX_TRANSFER_SIZE);
133 if (data != nullptr && chunk_size != 0) {
134 desc.base.length = chunk_size * 8;
135 desc.base.tx_buffer = data;
136 length -= chunk_size;
137 data += chunk_size;
138 } else {
139 length = 0;
140 desc.base.length = 0;
141 }
142 esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY);
143 if (err == ESP_OK) {
144 err = spi_device_polling_end(this->handle_, portMAX_DELAY);
145 }
146 if (err != ESP_OK) {
147 ESP_LOGE(TAG, "Transmit failed - err %X", err);
148 return;
149 }
150 // if more data is to be sent, skip the command and address phases.
151 desc.command_bits = 0;
152 desc.address_bits = 0;
153 } while (length != 0);
154 }
155
156 void transfer(uint8_t *ptr, size_t length) override { this->transfer(ptr, ptr, length); }
157
158 uint8_t transfer(uint8_t data) override {
159 uint8_t rxbuf;
160 this->transfer(&data, &rxbuf, 1);
161 return rxbuf;
162 }
163
164 void write16(uint16_t data) override { this->write(data, 16); }
165
166 void write_array(const uint8_t *ptr, size_t length) override { this->transfer(ptr, nullptr, length); }
167
168 void write_array16(const uint16_t *data, size_t length) override {
169 if (this->bit_order_ == BIT_ORDER_LSB_FIRST) {
170 this->write_array((uint8_t *) data, length * 2);
171 } else {
172 uint16_t buffer[MAX_TRANSFER_SIZE / 2];
173 while (length != 0) {
174 size_t const partial = std::min(length, MAX_TRANSFER_SIZE / 2);
175 for (size_t i = 0; i != partial; i++) {
176 buffer[i] = SPI_SWAP_DATA_TX(*data++, 16);
177 }
178 this->write_array((const uint8_t *) buffer, partial * 2);
179 length -= partial;
180 }
181 }
182 }
183
184 void read_array(uint8_t *ptr, size_t length) override { this->transfer(nullptr, ptr, length); }
185
186 protected:
187 bool add_device_() {
188 spi_device_interface_config_t config = {};
189 config.mode = static_cast<uint8_t>(this->mode_);
190 config.clock_speed_hz = static_cast<int>(this->data_rate_);
191 config.spics_io_num = -1;
192 config.flags = 0;
193 config.queue_size = 1;
194 config.pre_cb = nullptr;
195 config.post_cb = nullptr;
196 if (this->bit_order_ == BIT_ORDER_LSB_FIRST)
197 config.flags |= SPI_DEVICE_BIT_LSBFIRST;
198 if (this->write_only_)
199 config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY;
200 esp_err_t const err = spi_bus_add_device(this->channel_, &config, &this->handle_);
201 if (err != ESP_OK) {
202 ESP_LOGE(TAG, "Add device failed - err %X", err);
203 return false;
204 }
205 return true;
206 }
207
208 SPIInterface channel_{};
209 spi_device_handle_t handle_{};
210 bool release_device_{false};
211 bool write_only_{false};
212};
213
214class SPIBusHw : public SPIBus {
215 public:
216 SPIBusHw(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, SPIInterface channel, std::vector<uint8_t> data_pins)
217 : SPIBus(clk, sdo, sdi), channel_(channel) {
218 spi_bus_config_t buscfg = {};
219 buscfg.sclk_io_num = Utility::get_pin_no(clk);
220 buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK;
221 if (data_pins.empty()) {
222 buscfg.mosi_io_num = Utility::get_pin_no(sdo);
223 buscfg.miso_io_num = Utility::get_pin_no(sdi);
224 buscfg.quadwp_io_num = -1;
225 buscfg.quadhd_io_num = -1;
226 } else {
227 buscfg.data0_io_num = data_pins[0];
228 buscfg.data1_io_num = data_pins[1];
229 buscfg.data2_io_num = data_pins[2];
230 buscfg.data3_io_num = data_pins[3];
231 if (data_pins.size() == 8) {
232 buscfg.data4_io_num = data_pins[4];
233 buscfg.data5_io_num = data_pins[5];
234 buscfg.data6_io_num = data_pins[6];
235 buscfg.data7_io_num = data_pins[7];
236 buscfg.flags |= SPICOMMON_BUSFLAG_OCTAL;
237 } else {
238 buscfg.data4_io_num = -1;
239 buscfg.data5_io_num = -1;
240 buscfg.data6_io_num = -1;
241 buscfg.data7_io_num = -1;
242 buscfg.flags |= SPICOMMON_BUSFLAG_QUAD;
243 }
244 }
245 buscfg.max_transfer_sz = MAX_TRANSFER_SIZE;
246 auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO);
247 if (err != ESP_OK)
248 ESP_LOGE(TAG, "Bus init failed - err %X", err);
249 }
250
251 SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
252 bool release_device, bool write_only) override {
253 return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, release_device,
254 write_only || Utility::get_pin_no(this->sdi_pin_) == -1);
255 }
256
257 protected:
258 SPIInterface channel_{};
259
260 bool is_hw() override { return true; }
261};
262
263SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi,
264 const std::vector<uint8_t> &data_pins) {
265 return new SPIBusHw(clk, sdo, sdi, interface, data_pins);
266}
267
268#endif // USE_ESP32
269} // namespace esphome::spi
BedjetMode mode
BedJet operating mode.
uint8_t address
Definition bl0906.h:4
GPIOPin * sdi_pin_
Definition spi.h:326
static SPIBus * get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, const std::vector< uint8_t > &data_pins)
virtual void end_transaction()
Definition spi.h:192
virtual void begin_transaction()
Definition spi.h:189
SPIBitOrder bit_order_
Definition spi.h:251
static int get_pin_no(GPIOPin *pin)
Definition spi.h:132
Implementation of SPI Controller mode.
Definition spi.cpp:5
SPIMode
Modes mapping to clock phase and polarity.
Definition spi.h:76
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:38
@ BIT_ORDER_LSB_FIRST
The least significant bit is transmitted/received first.
Definition spi.h:40
spi_host_device_t SPIInterface
Definition spi.h:14
uint16_t length
Definition tt21100.cpp:0