From 2e6d01ddff06e2b5f006b01ca16df740fcf6446c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 27 Nov 2023 07:54:12 +1100 Subject: [PATCH] Implement variable length single word SPI writes. (#5678) --- esphome/components/spi/spi.cpp | 14 +++++++----- esphome/components/spi/spi.h | 16 ++++++++++++++ esphome/components/spi/spi_esp_idf.cpp | 30 ++++++++++++++++++-------- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 935399500f..9d06ac0e45 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -77,15 +77,19 @@ void SPIComponent::dump_config() { void SPIDelegateDummy::begin_transaction() { ESP_LOGE(TAG, "SPIDevice not initialised - did you call spi_setup()?"); } -uint8_t SPIDelegateBitBash::transfer(uint8_t data) { +uint8_t SPIDelegateBitBash::transfer(uint8_t data) { return this->transfer_(data, 8); } + +void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_(data, num_bits); } + +uint16_t SPIDelegateBitBash::transfer_(uint16_t data, size_t num_bits) { // Clock starts out at idle level this->clk_pin_->digital_write(clock_polarity_); uint8_t out_data = 0; - for (uint8_t i = 0; i < 8; i++) { + for (uint8_t i = 0; i != num_bits; i++) { uint8_t shift; if (bit_order_ == BIT_ORDER_MSB_FIRST) { - shift = 7 - i; + shift = num_bits - 1 - i; } else { shift = i; } @@ -94,7 +98,7 @@ uint8_t SPIDelegateBitBash::transfer(uint8_t data) { // sampling on leading edge this->sdo_pin_->digital_write(data & (1 << shift)); this->cycle_clock_(); - out_data |= uint8_t(this->sdi_pin_->digital_read()) << shift; + out_data |= uint16_t(this->sdi_pin_->digital_read()) << shift; this->clk_pin_->digital_write(!this->clock_polarity_); this->cycle_clock_(); this->clk_pin_->digital_write(this->clock_polarity_); @@ -104,7 +108,7 @@ uint8_t SPIDelegateBitBash::transfer(uint8_t data) { this->clk_pin_->digital_write(!this->clock_polarity_); this->sdo_pin_->digital_write(data & (1 << shift)); this->cycle_clock_(); - out_data |= uint8_t(this->sdi_pin_->digital_read()) << shift; + out_data |= uint16_t(this->sdi_pin_->digital_read()) << shift; this->clk_pin_->digital_write(this->clock_polarity_); } } diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 107ffb7cb5..0eb4cd7eb6 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -199,6 +199,15 @@ class SPIDelegate { rxbuf[i] = this->transfer(txbuf[i]); } + /** + * write a variable length data item, up to 16 bits. + * @param data The data to send. Should be LSB-aligned (i.e. top bits will be discarded.) + * @param num_bits The number of bits to send + */ + virtual void write(uint16_t data, size_t num_bits) { + esph_log_e("spi_device", "variable length write not implemented"); + } + // write 16 bits virtual void write16(uint16_t data) { if (this->bit_order_ == BIT_ORDER_MSB_FIRST) { @@ -270,6 +279,10 @@ class SPIDelegateBitBash : public SPIDelegate { uint8_t transfer(uint8_t data) override; + void write(uint16_t data, size_t num_bits) override; + + void write16(uint16_t data) override { this->write(data, 16); }; + protected: GPIOPin *clk_pin_; GPIOPin *sdo_pin_; @@ -284,6 +297,7 @@ class SPIDelegateBitBash : public SPIDelegate { continue; this->last_transition_ += this->wait_cycle_; } + uint16_t transfer_(uint16_t data, size_t num_bits); }; class SPIBus { @@ -408,6 +422,8 @@ class SPIDevice : public SPIClient { void read_array(uint8_t *data, size_t length) { return this->delegate_->read_array(data, length); } + void write(uint16_t data, size_t num_bits) { this->delegate_->write(data, num_bits); }; + void write_byte(uint8_t data) { this->delegate_->write_array(&data, 1); } void transfer_array(uint8_t *data, size_t length) { this->delegate_->transfer(data, length); } diff --git a/esphome/components/spi/spi_esp_idf.cpp b/esphome/components/spi/spi_esp_idf.cpp index f9e4bfcca6..03ab298019 100644 --- a/esphome/components/spi/spi_esp_idf.cpp +++ b/esphome/components/spi/spi_esp_idf.cpp @@ -72,7 +72,11 @@ class SPIDelegateHw : public SPIDelegate { desc.rxlength = this->write_only_ ? 0 : partial * 8; desc.tx_buffer = txbuf; desc.rx_buffer = rxbuf; - esp_err_t const err = spi_device_transmit(this->handle_, &desc); + // polling is used as it has about 10% less overhead than queuing an interrupt transfer + esp_err_t err = spi_device_polling_start(this->handle_, &desc, portMAX_DELAY); + if (err == ESP_OK) { + err = spi_device_polling_end(this->handle_, portMAX_DELAY); + } if (err != ESP_OK) { ESP_LOGE(TAG, "Transmit failed - err %X", err); break; @@ -85,6 +89,21 @@ class SPIDelegateHw : public SPIDelegate { } } + void write(uint16_t data, size_t num_bits) override { + spi_transaction_ext_t desc = {}; + desc.command_bits = num_bits; + desc.base.flags = SPI_TRANS_VARIABLE_CMD; + desc.base.cmd = data; + esp_err_t err = spi_device_polling_start(this->handle_, (spi_transaction_t *) &desc, portMAX_DELAY); + if (err == ESP_OK) { + err = spi_device_polling_end(this->handle_, portMAX_DELAY); + } + + if (err != ESP_OK) { + ESP_LOGE(TAG, "Transmit failed - err %X", err); + } + } + void transfer(uint8_t *ptr, size_t length) override { this->transfer(ptr, ptr, length); } uint8_t transfer(uint8_t data) override { @@ -93,14 +112,7 @@ class SPIDelegateHw : public SPIDelegate { return rxbuf; } - void write16(uint16_t data) override { - if (this->bit_order_ == BIT_ORDER_MSB_FIRST) { - uint16_t txbuf = SPI_SWAP_DATA_TX(data, 16); - this->transfer((uint8_t *) &txbuf, nullptr, 2); - } else { - this->transfer((uint8_t *) &data, nullptr, 2); - } - } + void write16(uint16_t data) override { this->write(data, 16); } void write_array(const uint8_t *ptr, size_t length) override { this->transfer(ptr, nullptr, length); }