ESPHome  2024.4.2
spi_esp_idf.cpp
Go to the documentation of this file.
1 #include "spi.h"
2 #include <vector>
3 
4 namespace esphome {
5 namespace spi {
6 
7 #ifdef USE_ESP_IDF
8 static const char *const TAG = "spi-esp-idf";
9 static const size_t MAX_TRANSFER_SIZE = 4092; // dictated by ESP-IDF API.
10 
11 class 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 
197 class 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  buscfg.data4_io_num = -1;
215  buscfg.data5_io_num = -1;
216  buscfg.data6_io_num = -1;
217  buscfg.data7_io_num = -1;
218  buscfg.flags |= SPICOMMON_BUSFLAG_QUAD;
219  }
220  buscfg.max_transfer_sz = MAX_TRANSFER_SIZE;
221  auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO);
222  if (err != ESP_OK)
223  ESP_LOGE(TAG, "Bus init failed - err %X", err);
224  }
225 
226  SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) override {
227  return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin,
228  Utility::get_pin_no(this->sdi_pin_) == -1);
229  }
230 
231  protected:
232  SPIInterface channel_{};
233 
234  bool is_hw() override { return true; }
235 };
236 
237 SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi,
238  const std::vector<uint8_t> &data_pins) {
239  return new SPIBusHw(clk, sdo, sdi, interface, data_pins);
240 }
241 
242 #endif
243 } // namespace spi
244 } // namespace esphome
SPIClassRP2040 * SPIInterface
Definition: spi.h:16
static int get_pin_no(GPIOPin *pin)
Definition: spi.h:130
const char *const TAG
Definition: spi.cpp:8
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:151
SPIMode
Modes mapping to clock phase and polarity.
Definition: spi.h:76
virtual void end_transaction()
Definition: spi.h:190
virtual void begin_transaction()
Definition: spi.h:187
static SPIBus * get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, const std::vector< uint8_t > &data_pins)
Definition: spi_arduino.cpp:88
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
uint16_t length
Definition: tt21100.cpp:12
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
The least significant bit is transmitted/received first.
Definition: spi.h:40
stm32_cmd_t * cmd
Definition: stm32flash.h:96