mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 22:48:10 +01:00
Add quad spi features (#5925)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
2283b3b443
commit
1fef769496
6 changed files with 207 additions and 40 deletions
|
@ -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.mosi_io_num = Utility::get_pin_no(sdo);
|
|
||||||
buscfg.miso_io_num = Utility::get_pin_no(sdi);
|
|
||||||
buscfg.sclk_io_num = Utility::get_pin_no(clk);
|
buscfg.sclk_io_num = Utility::get_pin_no(clk);
|
||||||
buscfg.quadwp_io_num = -1;
|
buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK;
|
||||||
buscfg.quadhd_io_num = -1;
|
if (data_pins.empty()) {
|
||||||
|
buscfg.mosi_io_num = Utility::get_pin_no(sdo);
|
||||||
|
buscfg.miso_io_num = Utility::get_pin_no(sdi);
|
||||||
|
buscfg.quadwp_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
|
||||||
|
|
Loading…
Reference in a new issue