mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 06:06:33 +00:00
[spi] Enable >6 devices with ESP-IDF (#9128)
This commit is contained in:
parent
b693b8ccb1
commit
169db9cc0a
@ -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))
|
||||
|
@ -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):
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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};
|
||||
};
|
||||
|
||||
|
@ -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<uint8_t>(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);
|
||||
}
|
||||
|
||||
|
@ -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<uint8_t>(mode);
|
||||
config.clock_speed_hz = static_cast<int>(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<uint8_t>(this->mode_);
|
||||
config.clock_speed_hz = static_cast<int>(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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user