diff --git a/esphome/components/mipi_spi/display.py b/esphome/components/mipi_spi/display.py index e9ed97a2a2..061257e859 100644 --- a/esphome/components/mipi_spi/display.py +++ b/esphome/components/mipi_spi/display.py @@ -472,3 +472,4 @@ async def to_code(config): cg.add(var.set_writer(lambda_)) await display.register_display(var, config) await spi.register_spi_device(var, config) + cg.add(var.set_write_only(True)) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index ffb5e11f79..55a4b9c8f6 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -79,6 +79,7 @@ CONF_SPI_MODE = "spi_mode" CONF_FORCE_SW = "force_sw" CONF_INTERFACE = "interface" CONF_INTERFACE_INDEX = "interface_index" +CONF_RELEASE_DEVICE = "release_device" TYPE_SINGLE = "single" TYPE_QUAD = "quad" TYPE_OCTAL = "octal" @@ -378,6 +379,7 @@ def spi_device_schema( cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum( SPI_MODE_OPTIONS, upper=True ), + cv.Optional(CONF_RELEASE_DEVICE): cv.All(cv.boolean, cv.only_with_esp_idf), } if cs_pin_required: schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema @@ -389,13 +391,15 @@ def spi_device_schema( async def register_spi_device(var, config): parent = await cg.get_variable(config[CONF_SPI_ID]) cg.add(var.set_spi_parent(parent)) - if CONF_CS_PIN in config: - pin = await cg.gpio_pin_expression(config[CONF_CS_PIN]) + if cs_pin := config.get(CONF_CS_PIN): + pin = await cg.gpio_pin_expression(cs_pin) cg.add(var.set_cs_pin(pin)) - if CONF_DATA_RATE in config: - cg.add(var.set_data_rate(config[CONF_DATA_RATE])) - if CONF_SPI_MODE in config: - cg.add(var.set_mode(config[CONF_SPI_MODE])) + if data_rate := config.get(CONF_DATA_RATE): + cg.add(var.set_data_rate(data_rate)) + if spi_mode := config.get(CONF_SPI_MODE): + cg.add(var.set_mode(spi_mode)) + if release_device := config.get(CONF_RELEASE_DEVICE): + cg.add(var.set_release_device(release_device)) def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: bool): diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 76d9d8ae86..805a774ceb 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -16,12 +16,13 @@ bool SPIDelegate::is_ready() { return true; } GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) SPIDelegate *SPIComponent::register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, - GPIOPin *cs_pin) { + GPIOPin *cs_pin, bool release_device, bool write_only) { if (this->devices_.count(device) != 0) { ESP_LOGE(TAG, "Device already registered"); return this->devices_[device]; } - SPIDelegate *delegate = this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin); // NOLINT + SPIDelegate *delegate = + this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin, release_device, write_only); // NOLINT this->devices_[device] = delegate; return delegate; } diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index f96d3da251..5bc80350da 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -317,7 +317,8 @@ class SPIBus { SPIBus(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) : clk_pin_(clk), sdo_pin_(sdo), sdi_pin_(sdi) {} - virtual SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) { + virtual SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, + bool release_device, bool write_only) { return new SPIDelegateBitBash(data_rate, bit_order, mode, cs_pin, this->clk_pin_, this->sdo_pin_, this->sdi_pin_); } @@ -334,7 +335,7 @@ class SPIClient; class SPIComponent : public Component { public: SPIDelegate *register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, - GPIOPin *cs_pin); + GPIOPin *cs_pin, bool release_device, bool write_only); void unregister_device(SPIClient *device); void set_clk(GPIOPin *clk) { this->clk_pin_ = clk; } @@ -390,7 +391,8 @@ class SPIClient { virtual void spi_setup() { esph_log_d("spi_device", "mode %u, data_rate %ukHz", (unsigned) this->mode_, (unsigned) (this->data_rate_ / 1000)); - this->delegate_ = this->parent_->register_device(this, this->mode_, this->bit_order_, this->data_rate_, this->cs_); + this->delegate_ = this->parent_->register_device(this, this->mode_, this->bit_order_, this->data_rate_, this->cs_, + this->release_device_, this->write_only_); } virtual void spi_teardown() { @@ -399,6 +401,8 @@ class SPIClient { } bool spi_is_ready() { return this->delegate_->is_ready(); } + void set_release_device(bool release) { this->release_device_ = release; } + void set_write_only(bool write_only) { this->write_only_ = write_only; } protected: SPIBitOrder bit_order_{BIT_ORDER_MSB_FIRST}; @@ -406,6 +410,8 @@ class SPIClient { uint32_t data_rate_{1000000}; SPIComponent *parent_{nullptr}; GPIOPin *cs_{nullptr}; + bool release_device_{false}; + bool write_only_{false}; SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE}; }; diff --git a/esphome/components/spi/spi_arduino.cpp b/esphome/components/spi/spi_arduino.cpp index 432f7cf2cd..a34e3c3c82 100644 --- a/esphome/components/spi/spi_arduino.cpp +++ b/esphome/components/spi/spi_arduino.cpp @@ -43,10 +43,7 @@ class SPIDelegateHw : public SPIDelegate { return; } #ifdef USE_RP2040 - // avoid overwriting the supplied buffer. Use vector for automatic deallocation - auto rxbuf = std::vector(length); - memcpy(rxbuf.data(), ptr, length); - this->channel_->transfer((void *) rxbuf.data(), length); + this->channel_->transfer(ptr, nullptr, length); #elif defined(USE_ESP8266) // ESP8266 SPI library requires the pointer to be word aligned, but the data may not be // so we need to copy the data to a temporary buffer @@ -89,7 +86,8 @@ class SPIBusHw : public SPIBus { #endif } - SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) override { + SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, + bool release_device, bool write_only) override { return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin); } diff --git a/esphome/components/spi/spi_esp_idf.cpp b/esphome/components/spi/spi_esp_idf.cpp index a78da2cd9a..549f516eb1 100644 --- a/esphome/components/spi/spi_esp_idf.cpp +++ b/esphome/components/spi/spi_esp_idf.cpp @@ -11,34 +11,26 @@ static const size_t MAX_TRANSFER_SIZE = 4092; // dictated by ESP-IDF API. class SPIDelegateHw : public SPIDelegate { public: SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, - bool write_only) - : SPIDelegate(data_rate, bit_order, mode, cs_pin), channel_(channel), write_only_(write_only) { - spi_device_interface_config_t config = {}; - config.mode = static_cast(mode); - config.clock_speed_hz = static_cast(data_rate); - config.spics_io_num = -1; - config.flags = 0; - config.queue_size = 1; - config.pre_cb = nullptr; - config.post_cb = nullptr; - if (bit_order == BIT_ORDER_LSB_FIRST) - config.flags |= SPI_DEVICE_BIT_LSBFIRST; - if (write_only) - config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY; - esp_err_t const err = spi_bus_add_device(channel, &config, &this->handle_); - if (err != ESP_OK) - ESP_LOGE(TAG, "Add device failed - err %X", err); + bool release_device, bool write_only) + : SPIDelegate(data_rate, bit_order, mode, cs_pin), + channel_(channel), + release_device_(release_device), + write_only_(write_only) { + if (!this->release_device_) + add_device_(); } bool is_ready() override { return this->handle_ != nullptr; } void begin_transaction() override { + if (this->release_device_) + this->add_device_(); if (this->is_ready()) { if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK) ESP_LOGE(TAG, "Failed to acquire SPI bus"); SPIDelegate::begin_transaction(); } else { - ESP_LOGW(TAG, "spi_setup called before initialisation"); + ESP_LOGW(TAG, "SPI device not ready, cannot begin transaction"); } } @@ -46,6 +38,10 @@ class SPIDelegateHw : public SPIDelegate { if (this->is_ready()) { SPIDelegate::end_transaction(); spi_device_release_bus(this->handle_); + if (this->release_device_) { + spi_bus_remove_device(this->handle_); + this->handle_ = nullptr; // reset handle to indicate no device is registered + } } } @@ -189,8 +185,30 @@ class SPIDelegateHw : public SPIDelegate { void read_array(uint8_t *ptr, size_t length) override { this->transfer(nullptr, ptr, length); } protected: + bool add_device_() { + spi_device_interface_config_t config = {}; + config.mode = static_cast(this->mode_); + config.clock_speed_hz = static_cast(this->data_rate_); + config.spics_io_num = -1; + config.flags = 0; + config.queue_size = 1; + config.pre_cb = nullptr; + config.post_cb = nullptr; + if (this->bit_order_ == BIT_ORDER_LSB_FIRST) + config.flags |= SPI_DEVICE_BIT_LSBFIRST; + if (this->write_only_) + config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY; + esp_err_t const err = spi_bus_add_device(this->channel_, &config, &this->handle_); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Add device failed - err %X", err); + return false; + } + return true; + } + SPIInterface channel_{}; spi_device_handle_t handle_{}; + bool release_device_{false}; bool write_only_{false}; }; @@ -231,9 +249,10 @@ class SPIBusHw : public SPIBus { ESP_LOGE(TAG, "Bus init failed - err %X", err); } - SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) override { - return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, - Utility::get_pin_no(this->sdi_pin_) == -1); + SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, + bool release_device, bool write_only) override { + return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, release_device, + write_only || Utility::get_pin_no(this->sdi_pin_) == -1); } protected: diff --git a/tests/components/spi_device/common.yaml b/tests/components/spi_device/common.yaml index 636d82202b..0f6a5038fb 100644 --- a/tests/components/spi_device/common.yaml +++ b/tests/components/spi_device/common.yaml @@ -5,7 +5,7 @@ spi: miso_pin: ${miso_pin} spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first + - id: spi_device_test + data_rate: 2MHz + spi_mode: 3 + bit_order: lsb_first diff --git a/tests/components/spi_device/test.esp32-idf.yaml b/tests/components/spi_device/test.esp32-idf.yaml index 448e54fea6..c4989cccbf 100644 --- a/tests/components/spi_device/test.esp32-idf.yaml +++ b/tests/components/spi_device/test.esp32-idf.yaml @@ -4,3 +4,8 @@ substitutions: miso_pin: GPIO15 <<: !include common.yaml +spi_device: + - id: spi_device_test + release_device: true + data_rate: 1MHz + spi_mode: 0