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