mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 22:26:36 +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_))
|
cg.add(var.set_writer(lambda_))
|
||||||
await display.register_display(var, config)
|
await display.register_display(var, config)
|
||||||
await spi.register_spi_device(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_FORCE_SW = "force_sw"
|
||||||
CONF_INTERFACE = "interface"
|
CONF_INTERFACE = "interface"
|
||||||
CONF_INTERFACE_INDEX = "interface_index"
|
CONF_INTERFACE_INDEX = "interface_index"
|
||||||
|
CONF_RELEASE_DEVICE = "release_device"
|
||||||
TYPE_SINGLE = "single"
|
TYPE_SINGLE = "single"
|
||||||
TYPE_QUAD = "quad"
|
TYPE_QUAD = "quad"
|
||||||
TYPE_OCTAL = "octal"
|
TYPE_OCTAL = "octal"
|
||||||
@ -378,6 +379,7 @@ def spi_device_schema(
|
|||||||
cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum(
|
cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum(
|
||||||
SPI_MODE_OPTIONS, upper=True
|
SPI_MODE_OPTIONS, upper=True
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_RELEASE_DEVICE): cv.All(cv.boolean, cv.only_with_esp_idf),
|
||||||
}
|
}
|
||||||
if cs_pin_required:
|
if cs_pin_required:
|
||||||
schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema
|
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):
|
async def register_spi_device(var, config):
|
||||||
parent = await cg.get_variable(config[CONF_SPI_ID])
|
parent = await cg.get_variable(config[CONF_SPI_ID])
|
||||||
cg.add(var.set_spi_parent(parent))
|
cg.add(var.set_spi_parent(parent))
|
||||||
if CONF_CS_PIN in config:
|
if cs_pin := config.get(CONF_CS_PIN):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_CS_PIN])
|
pin = await cg.gpio_pin_expression(cs_pin)
|
||||||
cg.add(var.set_cs_pin(pin))
|
cg.add(var.set_cs_pin(pin))
|
||||||
if CONF_DATA_RATE in config:
|
if data_rate := config.get(CONF_DATA_RATE):
|
||||||
cg.add(var.set_data_rate(config[CONF_DATA_RATE]))
|
cg.add(var.set_data_rate(data_rate))
|
||||||
if CONF_SPI_MODE in config:
|
if spi_mode := config.get(CONF_SPI_MODE):
|
||||||
cg.add(var.set_mode(config[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):
|
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)
|
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,
|
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) {
|
if (this->devices_.count(device) != 0) {
|
||||||
ESP_LOGE(TAG, "Device already registered");
|
ESP_LOGE(TAG, "Device already registered");
|
||||||
return this->devices_[device];
|
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;
|
this->devices_[device] = delegate;
|
||||||
return 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) {}
|
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_);
|
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 {
|
class SPIComponent : public Component {
|
||||||
public:
|
public:
|
||||||
SPIDelegate *register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate,
|
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 unregister_device(SPIClient *device);
|
||||||
|
|
||||||
void set_clk(GPIOPin *clk) { this->clk_pin_ = clk; }
|
void set_clk(GPIOPin *clk) { this->clk_pin_ = clk; }
|
||||||
@ -390,7 +391,8 @@ class SPIClient {
|
|||||||
|
|
||||||
virtual void spi_setup() {
|
virtual void spi_setup() {
|
||||||
esph_log_d("spi_device", "mode %u, data_rate %ukHz", (unsigned) this->mode_, (unsigned) (this->data_rate_ / 1000));
|
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() {
|
virtual void spi_teardown() {
|
||||||
@ -399,6 +401,8 @@ class SPIClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool spi_is_ready() { return this->delegate_->is_ready(); }
|
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:
|
protected:
|
||||||
SPIBitOrder bit_order_{BIT_ORDER_MSB_FIRST};
|
SPIBitOrder bit_order_{BIT_ORDER_MSB_FIRST};
|
||||||
@ -406,6 +410,8 @@ class SPIClient {
|
|||||||
uint32_t data_rate_{1000000};
|
uint32_t data_rate_{1000000};
|
||||||
SPIComponent *parent_{nullptr};
|
SPIComponent *parent_{nullptr};
|
||||||
GPIOPin *cs_{nullptr};
|
GPIOPin *cs_{nullptr};
|
||||||
|
bool release_device_{false};
|
||||||
|
bool write_only_{false};
|
||||||
SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE};
|
SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,10 +43,7 @@ class SPIDelegateHw : public SPIDelegate {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
// avoid overwriting the supplied buffer. Use vector for automatic deallocation
|
this->channel_->transfer(ptr, nullptr, length);
|
||||||
auto rxbuf = std::vector<uint8_t>(length);
|
|
||||||
memcpy(rxbuf.data(), ptr, length);
|
|
||||||
this->channel_->transfer((void *) rxbuf.data(), length);
|
|
||||||
#elif defined(USE_ESP8266)
|
#elif defined(USE_ESP8266)
|
||||||
// ESP8266 SPI library requires the pointer to be word aligned, but the data may not be
|
// 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
|
// so we need to copy the data to a temporary buffer
|
||||||
@ -89,7 +86,8 @@ class SPIBusHw : public SPIBus {
|
|||||||
#endif
|
#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);
|
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 {
|
class SPIDelegateHw : public SPIDelegate {
|
||||||
public:
|
public:
|
||||||
SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
|
SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
|
||||||
bool write_only)
|
bool release_device, bool write_only)
|
||||||
: SPIDelegate(data_rate, bit_order, mode, cs_pin), channel_(channel), write_only_(write_only) {
|
: SPIDelegate(data_rate, bit_order, mode, cs_pin),
|
||||||
spi_device_interface_config_t config = {};
|
channel_(channel),
|
||||||
config.mode = static_cast<uint8_t>(mode);
|
release_device_(release_device),
|
||||||
config.clock_speed_hz = static_cast<int>(data_rate);
|
write_only_(write_only) {
|
||||||
config.spics_io_num = -1;
|
if (!this->release_device_)
|
||||||
config.flags = 0;
|
add_device_();
|
||||||
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 is_ready() override { return this->handle_ != nullptr; }
|
bool is_ready() override { return this->handle_ != nullptr; }
|
||||||
|
|
||||||
void begin_transaction() override {
|
void begin_transaction() override {
|
||||||
|
if (this->release_device_)
|
||||||
|
this->add_device_();
|
||||||
if (this->is_ready()) {
|
if (this->is_ready()) {
|
||||||
if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK)
|
if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK)
|
||||||
ESP_LOGE(TAG, "Failed to acquire SPI bus");
|
ESP_LOGE(TAG, "Failed to acquire SPI bus");
|
||||||
SPIDelegate::begin_transaction();
|
SPIDelegate::begin_transaction();
|
||||||
} else {
|
} 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()) {
|
if (this->is_ready()) {
|
||||||
SPIDelegate::end_transaction();
|
SPIDelegate::end_transaction();
|
||||||
spi_device_release_bus(this->handle_);
|
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); }
|
void read_array(uint8_t *ptr, size_t length) override { this->transfer(nullptr, ptr, length); }
|
||||||
|
|
||||||
protected:
|
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_{};
|
SPIInterface channel_{};
|
||||||
spi_device_handle_t handle_{};
|
spi_device_handle_t handle_{};
|
||||||
|
bool release_device_{false};
|
||||||
bool write_only_{false};
|
bool write_only_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -231,9 +249,10 @@ class SPIBusHw : public SPIBus {
|
|||||||
ESP_LOGE(TAG, "Bus init failed - err %X", err);
|
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 {
|
SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
|
||||||
return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin,
|
bool release_device, bool write_only) override {
|
||||||
Utility::get_pin_no(this->sdi_pin_) == -1);
|
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:
|
protected:
|
||||||
|
@ -5,7 +5,7 @@ spi:
|
|||||||
miso_pin: ${miso_pin}
|
miso_pin: ${miso_pin}
|
||||||
|
|
||||||
spi_device:
|
spi_device:
|
||||||
id: spi_device_test
|
- id: spi_device_test
|
||||||
data_rate: 2MHz
|
data_rate: 2MHz
|
||||||
spi_mode: 3
|
spi_mode: 3
|
||||||
bit_order: lsb_first
|
bit_order: lsb_first
|
||||||
|
@ -4,3 +4,8 @@ substitutions:
|
|||||||
miso_pin: GPIO15
|
miso_pin: GPIO15
|
||||||
|
|
||||||
<<: !include common.yaml
|
<<: !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