mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Add quad spi features (#5925)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -29,12 +29,15 @@ from esphome.const import ( | |||||||
|     PLATFORM_ESP32, |     PLATFORM_ESP32, | ||||||
|     PLATFORM_ESP8266, |     PLATFORM_ESP8266, | ||||||
|     PLATFORM_RP2040, |     PLATFORM_RP2040, | ||||||
|  |     CONF_ALLOW_OTHER_USES, | ||||||
|  |     CONF_DATA_PINS, | ||||||
| ) | ) | ||||||
| from esphome.core import coroutine_with_priority, CORE | from esphome.core import coroutine_with_priority, CORE | ||||||
|  |  | ||||||
| CODEOWNERS = ["@esphome/core", "@clydebarrow"] | CODEOWNERS = ["@esphome/core", "@clydebarrow"] | ||||||
| spi_ns = cg.esphome_ns.namespace("spi") | spi_ns = cg.esphome_ns.namespace("spi") | ||||||
| SPIComponent = spi_ns.class_("SPIComponent", cg.Component) | SPIComponent = spi_ns.class_("SPIComponent", cg.Component) | ||||||
|  | QuadSPIComponent = spi_ns.class_("QuadSPIComponent", cg.Component) | ||||||
| SPIDevice = spi_ns.class_("SPIDevice") | SPIDevice = spi_ns.class_("SPIDevice") | ||||||
| SPIDataRate = spi_ns.enum("SPIDataRate") | SPIDataRate = spi_ns.enum("SPIDataRate") | ||||||
| SPIMode = spi_ns.enum("SPIMode") | SPIMode = spi_ns.enum("SPIMode") | ||||||
| @@ -190,12 +193,9 @@ def get_hw_spi(config, available): | |||||||
| def validate_spi_config(config): | def validate_spi_config(config): | ||||||
|     available = list(range(len(get_hw_interface_list()))) |     available = list(range(len(get_hw_interface_list()))) | ||||||
|     for spi in config: |     for spi in config: | ||||||
|  |         # map pin number to schema | ||||||
|  |         spi[CONF_CLK_PIN] = pins.gpio_output_pin_schema(spi[CONF_CLK_PIN]) | ||||||
|         interface = spi[CONF_INTERFACE] |         interface = spi[CONF_INTERFACE] | ||||||
|         if spi[CONF_FORCE_SW]: |  | ||||||
|             if interface == "any": |  | ||||||
|                 spi[CONF_INTERFACE] = interface = "software" |  | ||||||
|             elif interface != "software": |  | ||||||
|                 raise cv.Invalid("force_sw is deprecated - use interface: software") |  | ||||||
|         if interface == "software": |         if interface == "software": | ||||||
|             pass |             pass | ||||||
|         elif interface == "any": |         elif interface == "any": | ||||||
| @@ -229,6 +229,8 @@ def validate_spi_config(config): | |||||||
|             spi, spi[CONF_INTERFACE_INDEX] |             spi, spi[CONF_INTERFACE_INDEX] | ||||||
|         ): |         ): | ||||||
|             raise cv.Invalid("Invalid pin selections for hardware SPI interface") |             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") | ||||||
|  |  | ||||||
|     return config |     return config | ||||||
|  |  | ||||||
| @@ -249,14 +251,26 @@ def get_spi_interface(index): | |||||||
|     return "new SPIClass(HSPI)" |     return "new SPIClass(HSPI)" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Do not use a pin schema for the number, as that will trigger a pin reuse error due to duplication of the | ||||||
|  | # clock pin in the standard and quad schemas. | ||||||
|  | clk_pin_validator = cv.maybe_simple_value( | ||||||
|  |     { | ||||||
|  |         cv.Required(CONF_NUMBER): cv.Any(cv.int_, cv.string), | ||||||
|  |         cv.Optional(CONF_ALLOW_OTHER_USES): cv.boolean, | ||||||
|  |     }, | ||||||
|  |     key=CONF_NUMBER, | ||||||
|  | ) | ||||||
|  |  | ||||||
| SPI_SCHEMA = cv.All( | SPI_SCHEMA = cv.All( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
|             cv.GenerateID(): cv.declare_id(SPIComponent), |             cv.GenerateID(): cv.declare_id(SPIComponent), | ||||||
|             cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, |             cv.Required(CONF_CLK_PIN): clk_pin_validator, | ||||||
|             cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, |             cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, | ||||||
|             cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, |             cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, | ||||||
|             cv.Optional(CONF_FORCE_SW, default=False): cv.boolean, |             cv.Optional(CONF_FORCE_SW): cv.invalid( | ||||||
|  |                 "force_sw is deprecated - use interface: software" | ||||||
|  |             ), | ||||||
|             cv.Optional(CONF_INTERFACE, default="any"): cv.one_of( |             cv.Optional(CONF_INTERFACE, default="any"): cv.one_of( | ||||||
|                 *sum(get_hw_interface_list(), ["software", "hardware", "any"]), |                 *sum(get_hw_interface_list(), ["software", "hardware", "any"]), | ||||||
|                 lower=True, |                 lower=True, | ||||||
| @@ -267,8 +281,34 @@ SPI_SCHEMA = cv.All( | |||||||
|     cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), |     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): clk_pin_validator, | ||||||
|  |             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.only_with_esp_idf, | ||||||
|  | ) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.All( | CONFIG_SCHEMA = cv.All( | ||||||
|     cv.ensure_list(SPI_SCHEMA), |     # Order is important. SPI_SCHEMA is the default. | ||||||
|  |     cv.ensure_list( | ||||||
|  |         cv.Any( | ||||||
|  |             SPI_SCHEMA, | ||||||
|  |             SPI_QUAD_SCHEMA, | ||||||
|  |             msg="Standard SPI requires mosi_pin and/or miso_pin; quad SPI requires data_pins only." | ||||||
|  |             + " A clock pin is always required", | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|     validate_spi_config, |     validate_spi_config, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -277,43 +317,46 @@ CONFIG_SCHEMA = cv.All( | |||||||
| async def to_code(configs): | async def to_code(configs): | ||||||
|     cg.add_define("USE_SPI") |     cg.add_define("USE_SPI") | ||||||
|     cg.add_global(spi_ns.using) |     cg.add_global(spi_ns.using) | ||||||
|  |     if CORE.using_arduino: | ||||||
|  |         cg.add_library("SPI", None) | ||||||
|     for spi in configs: |     for spi in configs: | ||||||
|         var = cg.new_Pvariable(spi[CONF_ID]) |         var = cg.new_Pvariable(spi[CONF_ID]) | ||||||
|         await cg.register_component(var, spi) |         await cg.register_component(var, spi) | ||||||
|  |  | ||||||
|         clk = await cg.gpio_pin_expression(spi[CONF_CLK_PIN]) |         clk = await cg.gpio_pin_expression(spi[CONF_CLK_PIN]) | ||||||
|         cg.add(var.set_clk(clk)) |         cg.add(var.set_clk(clk)) | ||||||
|         if CONF_MISO_PIN in spi: |         if miso := spi.get(CONF_MISO_PIN): | ||||||
|             miso = await cg.gpio_pin_expression(spi[CONF_MISO_PIN]) |             cg.add(var.set_miso(await cg.gpio_pin_expression(miso))) | ||||||
|             cg.add(var.set_miso(miso)) |         if mosi := spi.get(CONF_MOSI_PIN): | ||||||
|         if CONF_MOSI_PIN in spi: |             cg.add(var.set_mosi(await cg.gpio_pin_expression(mosi))) | ||||||
|             mosi = await cg.gpio_pin_expression(spi[CONF_MOSI_PIN]) |         if data_pins := spi.get(CONF_DATA_PINS): | ||||||
|             cg.add(var.set_mosi(mosi)) |             cg.add(var.set_data_pins(data_pins)) | ||||||
|         if CONF_INTERFACE_INDEX in spi: |         if (index := spi.get(CONF_INTERFACE_INDEX)) is not None: | ||||||
|             index = spi[CONF_INTERFACE_INDEX] |             interface = get_spi_interface(index) | ||||||
|             cg.add(var.set_interface(cg.RawExpression(get_spi_interface(index)))) |             cg.add(var.set_interface(cg.RawExpression(interface))) | ||||||
|             cg.add( |             cg.add( | ||||||
|                 var.set_interface_name( |                 var.set_interface_name( | ||||||
|                     re.sub( |                     re.sub(r"\W", "", interface.replace("new SPIClass", "")) | ||||||
|                         r"\W", "", get_spi_interface(index).replace("new SPIClass", "") |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     if CORE.using_arduino: |  | ||||||
|         cg.add_library("SPI", None) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def spi_device_schema( | def spi_device_schema( | ||||||
|     cs_pin_required=True, default_data_rate=cv.UNDEFINED, default_mode=cv.UNDEFINED |     cs_pin_required=True, | ||||||
|  |     default_data_rate=cv.UNDEFINED, | ||||||
|  |     default_mode=cv.UNDEFINED, | ||||||
|  |     quad=False, | ||||||
| ): | ): | ||||||
|     """Create a schema for an SPI device. |     """Create a schema for an SPI device. | ||||||
|     :param cs_pin_required: If true, make the CS_PIN required in the config. |     :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_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. | ||||||
|     :return: The SPI device schema, `extend` this in your config schema. |     :return: The SPI device schema, `extend` this in your config schema. | ||||||
|     """ |     """ | ||||||
|     schema = { |     schema = { | ||||||
|         cv.GenerateID(CONF_SPI_ID): cv.use_id(SPIComponent), |         cv.GenerateID(CONF_SPI_ID): cv.use_id( | ||||||
|  |             QuadSPIComponent if quad else SPIComponent | ||||||
|  |         ), | ||||||
|         cv.Optional(CONF_DATA_RATE, default=default_data_rate): SPI_DATA_RATE_SCHEMA, |         cv.Optional(CONF_DATA_RATE, default=default_data_rate): SPI_DATA_RATE_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 | ||||||
|   | |||||||
| @@ -49,7 +49,8 @@ void SPIComponent::setup() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (this->using_hw_) { |   if (this->using_hw_) { | ||||||
|     this->spi_bus_ = SPIComponent::get_bus(this->interface_, this->clk_pin_, this->sdo_pin_, this->sdi_pin_); |     this->spi_bus_ = | ||||||
|  |         SPIComponent::get_bus(this->interface_, this->clk_pin_, this->sdo_pin_, this->sdi_pin_, this->data_pins_); | ||||||
|     if (this->spi_bus_ == nullptr) { |     if (this->spi_bus_ == nullptr) { | ||||||
|       ESP_LOGE(TAG, "Unable to allocate SPI interface"); |       ESP_LOGE(TAG, "Unable to allocate SPI interface"); | ||||||
|       this->mark_failed(); |       this->mark_failed(); | ||||||
| @@ -68,6 +69,9 @@ void SPIComponent::dump_config() { | |||||||
|   LOG_PIN("  CLK Pin: ", this->clk_pin_) |   LOG_PIN("  CLK Pin: ", this->clk_pin_) | ||||||
|   LOG_PIN("  SDI Pin: ", this->sdi_pin_) |   LOG_PIN("  SDI Pin: ", this->sdi_pin_) | ||||||
|   LOG_PIN("  SDO Pin: ", this->sdo_pin_) |   LOG_PIN("  SDO Pin: ", this->sdo_pin_) | ||||||
|  |   for (size_t i = 0; i != this->data_pins_.size(); i++) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  Data pin %u: GPIO%d", i, this->data_pins_[i]); | ||||||
|  |   } | ||||||
|   if (this->spi_bus_->is_hw()) { |   if (this->spi_bus_->is_hw()) { | ||||||
|     ESP_LOGCONFIG(TAG, "  Using HW SPI: %s", this->interface_name_); |     ESP_LOGCONFIG(TAG, "  Using HW SPI: %s", this->interface_name_); | ||||||
|   } else { |   } else { | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/application.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
| #include "esphome/core/application.h" |  | ||||||
| #include <vector> |  | ||||||
| #include <map> | #include <map> | ||||||
|  | #include <utility> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|  |  | ||||||
| @@ -208,6 +209,10 @@ class SPIDelegate { | |||||||
|     esph_log_e("spi_device", "variable length write not implemented"); |     esph_log_e("spi_device", "variable length write not implemented"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   virtual void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, | ||||||
|  |                                    const uint8_t *data, size_t length, uint8_t bus_width) { | ||||||
|  |     esph_log_e("spi_device", "write_cmd_addr_data not implemented"); | ||||||
|  |   } | ||||||
|   // write 16 bits |   // write 16 bits | ||||||
|   virtual void write16(uint16_t data) { |   virtual void write16(uint16_t data) { | ||||||
|     if (this->bit_order_ == BIT_ORDER_MSB_FIRST) { |     if (this->bit_order_ == BIT_ORDER_MSB_FIRST) { | ||||||
| @@ -331,6 +336,7 @@ class SPIComponent : public Component { | |||||||
|   void set_miso(GPIOPin *sdi) { this->sdi_pin_ = sdi; } |   void set_miso(GPIOPin *sdi) { this->sdi_pin_ = sdi; } | ||||||
|  |  | ||||||
|   void set_mosi(GPIOPin *sdo) { this->sdo_pin_ = sdo; } |   void set_mosi(GPIOPin *sdo) { this->sdo_pin_ = sdo; } | ||||||
|  |   void set_data_pins(std::vector<uint8_t> pins) { this->data_pins_ = std::move(pins); } | ||||||
|  |  | ||||||
|   void set_interface(SPIInterface interface) { |   void set_interface(SPIInterface interface) { | ||||||
|     this->interface_ = interface; |     this->interface_ = interface; | ||||||
| @@ -348,15 +354,19 @@ class SPIComponent : public Component { | |||||||
|   GPIOPin *clk_pin_{nullptr}; |   GPIOPin *clk_pin_{nullptr}; | ||||||
|   GPIOPin *sdi_pin_{nullptr}; |   GPIOPin *sdi_pin_{nullptr}; | ||||||
|   GPIOPin *sdo_pin_{nullptr}; |   GPIOPin *sdo_pin_{nullptr}; | ||||||
|  |   std::vector<uint8_t> data_pins_{}; | ||||||
|  |  | ||||||
|   SPIInterface interface_{}; |   SPIInterface interface_{}; | ||||||
|   bool using_hw_{false}; |   bool using_hw_{false}; | ||||||
|   const char *interface_name_{nullptr}; |   const char *interface_name_{nullptr}; | ||||||
|   SPIBus *spi_bus_{}; |   SPIBus *spi_bus_{}; | ||||||
|   std::map<SPIClient *, SPIDelegate *> devices_; |   std::map<SPIClient *, SPIDelegate *> devices_; | ||||||
|  |  | ||||||
|   static SPIBus *get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi); |   static SPIBus *get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, | ||||||
|  |                          const std::vector<uint8_t> &data_pins); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | using QuadSPIComponent = SPIComponent; | ||||||
| /** | /** | ||||||
|  * Base class for SPIDevice, un-templated. |  * Base class for SPIDevice, un-templated. | ||||||
|  */ |  */ | ||||||
| @@ -422,18 +432,49 @@ class SPIDevice : public SPIClient { | |||||||
|  |  | ||||||
|   void read_array(uint8_t *data, size_t length) { return this->delegate_->read_array(data, length); } |   void read_array(uint8_t *data, size_t length) { return this->delegate_->read_array(data, length); } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Write a single data item, up to 32 bits. | ||||||
|  |    * @param data    The data | ||||||
|  |    * @param num_bits The number of bits to write. The lower num_bits of data will be sent. | ||||||
|  |    */ | ||||||
|   void write(uint16_t data, size_t num_bits) { this->delegate_->write(data, num_bits); }; |   void write(uint16_t data, size_t num_bits) { this->delegate_->write(data, num_bits); }; | ||||||
|  |  | ||||||
|  |   /* Write command, address and data. Command and address will be written as single-bit SPI, | ||||||
|  |    * data phase can be multiple bit (currently only 1 or 4) | ||||||
|  |    * @param cmd_bits Number of bits to write in the command phase | ||||||
|  |    * @param cmd The command value to write | ||||||
|  |    * @param addr_bits Number of bits to write in addr phase | ||||||
|  |    * @param address Address data | ||||||
|  |    * @param data Plain data bytes | ||||||
|  |    * @param length Number of data bytes | ||||||
|  |    * @param bus_width The number of data lines to use for the data phase. | ||||||
|  |    */ | ||||||
|  |   void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, const uint8_t *data, | ||||||
|  |                            size_t length, uint8_t bus_width = 1) { | ||||||
|  |     this->delegate_->write_cmd_addr_data(cmd_bits, cmd, addr_bits, address, data, length, bus_width); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   void write_byte(uint8_t data) { this->delegate_->write_array(&data, 1); } |   void write_byte(uint8_t data) { this->delegate_->write_array(&data, 1); } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Write the array data, replace with received data. | ||||||
|  |    * @param data | ||||||
|  |    * @param length | ||||||
|  |    */ | ||||||
|   void transfer_array(uint8_t *data, size_t length) { this->delegate_->transfer(data, length); } |   void transfer_array(uint8_t *data, size_t length) { this->delegate_->transfer(data, length); } | ||||||
|  |  | ||||||
|   uint8_t transfer_byte(uint8_t data) { return this->delegate_->transfer(data); } |   uint8_t transfer_byte(uint8_t data) { return this->delegate_->transfer(data); } | ||||||
|  |  | ||||||
|   // the driver will byte-swap if required. |   /** Write 16 bit data. The driver will byte-swap if required. | ||||||
|  |    */ | ||||||
|   void write_byte16(uint16_t data) { this->delegate_->write16(data); } |   void write_byte16(uint16_t data) { this->delegate_->write16(data); } | ||||||
|  |  | ||||||
|   // avoid use of this if possible. It's inefficient and ugly. |   /** | ||||||
|  |    * Write an array of data as 16 bit values, byte-swapping if required. Use of this should be avoided as | ||||||
|  |    * it is horribly slow. | ||||||
|  |    * @param data | ||||||
|  |    * @param length | ||||||
|  |    */ | ||||||
|   void write_array16(const uint16_t *data, size_t length) { this->delegate_->write_array16(data, length); } |   void write_array16(const uint16_t *data, size_t length) { this->delegate_->write_array16(data, length); } | ||||||
|  |  | ||||||
|   void enable() { this->delegate_->begin_transaction(); } |   void enable() { this->delegate_->begin_transaction(); } | ||||||
|   | |||||||
| @@ -85,7 +85,8 @@ class SPIBusHw : public SPIBus { | |||||||
|   bool is_hw() override { return true; } |   bool is_hw() override { return true; } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) { | SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, | ||||||
|  |                               const std::vector<uint8_t> &data_pins) { | ||||||
|   return new SPIBusHw(clk, sdo, sdi, interface); |   return new SPIBusHw(clk, sdo, sdi, interface); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -104,6 +104,60 @@ class SPIDelegateHw : public SPIDelegate { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Write command, address and data | ||||||
|  |    * @param cmd_bits Number of bits to write in the command phase | ||||||
|  |    * @param cmd The command value to write | ||||||
|  |    * @param addr_bits Number of bits to write in addr phase | ||||||
|  |    * @param address Address data | ||||||
|  |    * @param data Remaining data bytes | ||||||
|  |    * @param length Number of data bytes | ||||||
|  |    * @param bus_width The number of data lines to use | ||||||
|  |    */ | ||||||
|  |   void write_cmd_addr_data(size_t cmd_bits, uint32_t cmd, size_t addr_bits, uint32_t address, const uint8_t *data, | ||||||
|  |                            size_t length, uint8_t bus_width) override { | ||||||
|  |     spi_transaction_ext_t desc = {}; | ||||||
|  |     if (length == 0 && cmd_bits == 0 && addr_bits == 0) { | ||||||
|  |       esph_log_w(TAG, "Nothing to transfer"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     desc.base.flags = SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_DUMMY; | ||||||
|  |     if (bus_width == 4) { | ||||||
|  |       desc.base.flags |= SPI_TRANS_MODE_QIO; | ||||||
|  |     } else if (bus_width == 8) { | ||||||
|  |       desc.base.flags |= SPI_TRANS_MODE_OCT; | ||||||
|  |     } | ||||||
|  |     desc.command_bits = cmd_bits; | ||||||
|  |     desc.address_bits = addr_bits; | ||||||
|  |     desc.dummy_bits = 0; | ||||||
|  |     desc.base.rxlength = 0; | ||||||
|  |     desc.base.cmd = cmd; | ||||||
|  |     desc.base.addr = address; | ||||||
|  |     do { | ||||||
|  |       size_t chunk_size = std::min(length, MAX_TRANSFER_SIZE); | ||||||
|  |       if (data != nullptr && chunk_size != 0) { | ||||||
|  |         desc.base.length = chunk_size * 8; | ||||||
|  |         desc.base.tx_buffer = data; | ||||||
|  |         length -= chunk_size; | ||||||
|  |         data += chunk_size; | ||||||
|  |       } else { | ||||||
|  |         length = 0; | ||||||
|  |         desc.base.length = 0; | ||||||
|  |       } | ||||||
|  |       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); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       // if more data is to be sent, skip the command and address phases. | ||||||
|  |       desc.command_bits = 0; | ||||||
|  |       desc.address_bits = 0; | ||||||
|  |     } while (length != 0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   void transfer(uint8_t *ptr, size_t length) override { this->transfer(ptr, ptr, length); } |   void transfer(uint8_t *ptr, size_t length) override { this->transfer(ptr, ptr, length); } | ||||||
|  |  | ||||||
|   uint8_t transfer(uint8_t data) override { |   uint8_t transfer(uint8_t data) override { | ||||||
| @@ -142,13 +196,27 @@ class SPIDelegateHw : public SPIDelegate { | |||||||
|  |  | ||||||
| class SPIBusHw : public SPIBus { | class SPIBusHw : public SPIBus { | ||||||
|  public: |  public: | ||||||
|   SPIBusHw(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, SPIInterface channel) : SPIBus(clk, sdo, sdi), channel_(channel) { |   SPIBusHw(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, SPIInterface channel, std::vector<uint8_t> data_pins) | ||||||
|  |       : SPIBus(clk, sdo, sdi), channel_(channel) { | ||||||
|     spi_bus_config_t buscfg = {}; |     spi_bus_config_t buscfg = {}; | ||||||
|  |     buscfg.sclk_io_num = Utility::get_pin_no(clk); | ||||||
|  |     buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK; | ||||||
|  |     if (data_pins.empty()) { | ||||||
|       buscfg.mosi_io_num = Utility::get_pin_no(sdo); |       buscfg.mosi_io_num = Utility::get_pin_no(sdo); | ||||||
|       buscfg.miso_io_num = Utility::get_pin_no(sdi); |       buscfg.miso_io_num = Utility::get_pin_no(sdi); | ||||||
|     buscfg.sclk_io_num = Utility::get_pin_no(clk); |  | ||||||
|       buscfg.quadwp_io_num = -1; |       buscfg.quadwp_io_num = -1; | ||||||
|       buscfg.quadhd_io_num = -1; |       buscfg.quadhd_io_num = -1; | ||||||
|  |     } else { | ||||||
|  |       buscfg.data0_io_num = data_pins[0]; | ||||||
|  |       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; | ||||||
|  |     } | ||||||
|     buscfg.max_transfer_sz = MAX_TRANSFER_SIZE; |     buscfg.max_transfer_sz = MAX_TRANSFER_SIZE; | ||||||
|     auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO); |     auto err = spi_bus_initialize(channel, &buscfg, SPI_DMA_CH_AUTO); | ||||||
|     if (err != ESP_OK) |     if (err != ESP_OK) | ||||||
| @@ -166,8 +234,9 @@ class SPIBusHw : public SPIBus { | |||||||
|   bool is_hw() override { return true; } |   bool is_hw() override { return true; } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) { | SPIBus *SPIComponent::get_bus(SPIInterface interface, GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi, | ||||||
|   return new SPIBusHw(clk, sdo, sdi, interface); |                               const std::vector<uint8_t> &data_pins) { | ||||||
|  |   return new SPIBusHw(clk, sdo, sdi, interface, data_pins); | ||||||
| } | } | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -28,6 +28,15 @@ spi: | |||||||
|       allow_other_uses: false |       allow_other_uses: false | ||||||
|     mosi_pin: GPIO6 |     mosi_pin: GPIO6 | ||||||
|     interface: any |     interface: any | ||||||
|  |   - id: quad_spi | ||||||
|  |     clk_pin: 47 | ||||||
|  |     data_pins: | ||||||
|  |       - | ||||||
|  |         number: 40 | ||||||
|  |         allow_other_uses: false | ||||||
|  |       - 41 | ||||||
|  |       - 42 | ||||||
|  |       - 43 | ||||||
|  |  | ||||||
| spi_device: | spi_device: | ||||||
|   id: spidev |   id: spidev | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user