diff --git a/esphome/components/qspi_dbi/display.py b/esphome/components/qspi_dbi/display.py index ab6dd66cf2..8c29991f37 100644 --- a/esphome/components/qspi_dbi/display.py +++ b/esphome/components/qspi_dbi/display.py @@ -113,7 +113,7 @@ BASE_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( cs_pin_required=False, default_mode="MODE0", default_data_rate=10e6, - quad=True, + mode=spi.TYPE_QUAD, ) ) ) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 3e6d680b89..5b28b3546b 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -37,6 +37,7 @@ CODEOWNERS = ["@esphome/core", "@clydebarrow"] spi_ns = cg.esphome_ns.namespace("spi") SPIComponent = spi_ns.class_("SPIComponent", cg.Component) QuadSPIComponent = spi_ns.class_("QuadSPIComponent", cg.Component) +OctalSPIComponent = spi_ns.class_("OctalSPIComponent", cg.Component) SPIDevice = spi_ns.class_("SPIDevice") SPIDataRate = spi_ns.enum("SPIDataRate") SPIMode = spi_ns.enum("SPIMode") @@ -78,6 +79,13 @@ CONF_INTERFACE = "interface" CONF_INTERFACE_INDEX = "interface_index" TYPE_SINGLE = "single" TYPE_QUAD = "quad" +TYPE_OCTAL = "octal" + +TYPE_CLASS = { + TYPE_SINGLE: SPIComponent, + TYPE_QUAD: QuadSPIComponent, + TYPE_OCTAL: OctalSPIComponent, +} # RP2040 SPI pin assignments are complicated; # refer to GPIO function select table in https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf @@ -230,7 +238,7 @@ def validate_spi_config(config): ): raise cv.Invalid("Invalid pin selections for hardware SPI interface") if CONF_DATA_PINS in spi and CONF_INTERFACE_INDEX not in spi: - raise cv.Invalid("Quad mode requires a hardware interface") + raise cv.Invalid("Quad and octal modes requires a hardware interface") return config @@ -251,7 +259,7 @@ def get_spi_interface(index): return "new SPIClass(HSPI)" -SPI_SCHEMA = cv.All( +SPI_SINGLE_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(SPIComponent), @@ -266,7 +274,7 @@ SPI_SCHEMA = cv.All( lower=True, ), cv.Optional(CONF_DATA_PINS): cv.invalid( - "'data_pins' should be used with 'type: quad' only" + "'data_pins' should be used with 'type: quad or octal' only" ), } ), @@ -274,38 +282,41 @@ SPI_SCHEMA = cv.All( cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), ) -SPI_QUAD_SCHEMA = cv.All( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(QuadSPIComponent), - cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_DATA_PINS): cv.All( - cv.ensure_list(pins.internal_gpio_output_pin_number), - cv.Length(min=4, max=4), - ), - cv.Optional(CONF_INTERFACE, default="hardware"): cv.one_of( - *sum(get_hw_interface_list(), ["hardware"]), - lower=True, - ), - cv.Optional(CONF_MISO_PIN): cv.invalid( - "'miso_pin' should not be used with quad SPI" - ), - cv.Optional(CONF_MOSI_PIN): cv.invalid( - "'mosi_pin' should not be used with quad SPI" - ), - } - ), - cv.only_on([PLATFORM_ESP32]), - cv.only_with_esp_idf, -) + +def spi_mode_schema(mode): + if mode == TYPE_SINGLE: + return SPI_SINGLE_SCHEMA + pin_count = 4 if mode == TYPE_QUAD else 8 + return cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TYPE_CLASS[mode]), + cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DATA_PINS): cv.All( + cv.ensure_list(pins.internal_gpio_output_pin_number), + cv.Length(min=pin_count, max=pin_count), + ), + cv.Optional(CONF_INTERFACE, default="hardware"): cv.one_of( + *sum(get_hw_interface_list(), ["hardware"]), + lower=True, + ), + cv.Optional(CONF_MISO_PIN): cv.invalid( + f"'miso_pin' should not be used with {mode} SPI" + ), + cv.Optional(CONF_MOSI_PIN): cv.invalid( + f"'mosi_pin' should not be used with {mode} SPI" + ), + } + ), + cv.only_on([PLATFORM_ESP32]), + cv.only_with_esp_idf, + ) + CONFIG_SCHEMA = cv.All( cv.ensure_list( cv.typed_schema( - { - TYPE_SINGLE: SPI_SCHEMA, - TYPE_QUAD: SPI_QUAD_SCHEMA, - }, + {k: spi_mode_schema(k) for k in TYPE_CLASS}, default_type=TYPE_SINGLE, ) ), @@ -344,19 +355,17 @@ def spi_device_schema( cs_pin_required=True, default_data_rate=cv.UNDEFINED, default_mode=cv.UNDEFINED, - quad=False, + mode=TYPE_SINGLE, ): """Create a schema for an SPI device. :param cs_pin_required: If true, make the CS_PIN required in the config. :param default_data_rate: Optional data_rate to use as default :param default_mode Optional. The default SPI mode to use. - :param quad If set, will require an SPI component configured as quad data bits. + :param mode Choose single, quad or octal mode. :return: The SPI device schema, `extend` this in your config schema. """ schema = { - cv.GenerateID(CONF_SPI_ID): cv.use_id( - QuadSPIComponent if quad else SPIComponent - ), + cv.GenerateID(CONF_SPI_ID): cv.use_id(TYPE_CLASS[mode]), cv.Optional(CONF_DATA_RATE, default=default_data_rate): SPI_DATA_RATE_SCHEMA, cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum( SPI_MODE_OPTIONS, upper=True diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 64463747a2..378d95e7b9 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -369,6 +369,7 @@ class SPIComponent : public Component { }; using QuadSPIComponent = SPIComponent; +using OctalSPIComponent = SPIComponent; /** * Base class for SPIDevice, un-templated. */ diff --git a/esphome/components/spi/spi_esp_idf.cpp b/esphome/components/spi/spi_esp_idf.cpp index 55680f72d3..a78da2cd9a 100644 --- a/esphome/components/spi/spi_esp_idf.cpp +++ b/esphome/components/spi/spi_esp_idf.cpp @@ -211,11 +211,19 @@ class SPIBusHw : public SPIBus { buscfg.data1_io_num = data_pins[1]; buscfg.data2_io_num = data_pins[2]; buscfg.data3_io_num = data_pins[3]; - buscfg.data4_io_num = -1; - buscfg.data5_io_num = -1; - buscfg.data6_io_num = -1; - buscfg.data7_io_num = -1; - buscfg.flags |= SPICOMMON_BUSFLAG_QUAD; + if (data_pins.size() == 8) { + buscfg.data4_io_num = data_pins[4]; + buscfg.data5_io_num = data_pins[5]; + buscfg.data6_io_num = data_pins[6]; + buscfg.data7_io_num = data_pins[7]; + buscfg.flags |= SPICOMMON_BUSFLAG_OCTAL; + } else { + buscfg.data4_io_num = -1; + buscfg.data5_io_num = -1; + buscfg.data6_io_num = -1; + buscfg.data7_io_num = -1; + buscfg.flags |= SPICOMMON_BUSFLAG_QUAD; + } } buscfg.max_transfer_sz = MAX_TRANSFER_SIZE; auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO); diff --git a/tests/components/spi/test.esp32-s3-idf.yaml b/tests/components/spi/test.esp32-s3-idf.yaml index d394c5d7a4..061e3dd44a 100644 --- a/tests/components/spi/test.esp32-s3-idf.yaml +++ b/tests/components/spi/test.esp32-s3-idf.yaml @@ -1,22 +1,36 @@ spi: - - id: spi_id_1 - type: single - clk_pin: - number: GPIO0 - ignore_strapping_warning: true - allow_other_uses: false - mosi_pin: GPIO6 - interface: hardware - id: quad_spi type: quad interface: spi3 - clk_pin: 47 + clk_pin: + number: 47 data_pins: - - number: 40 - allow_other_uses: false - - 41 - - 42 - - 43 + - allow_other_uses: true + number: 40 + - allow_other_uses: true + number: 41 + - allow_other_uses: true + number: 42 + - allow_other_uses: true + number: 43 + - id: octal_spi + type: octal + interface: hardware + clk_pin: + number: 0 + data_pins: + - 36 + - 37 + - 38 + - 39 + - allow_other_uses: true + number: 40 + - allow_other_uses: true + number: 41 + - allow_other_uses: true + number: 42 + - allow_other_uses: true + number: 43 - id: spi_id_3 interface: any clk_pin: 8