feat(spi_master): p4 add master driver supported

This commit is contained in:
wanlei
2023-08-31 19:17:40 +08:00
parent 5306b3308f
commit 00fcdce725
29 changed files with 1728 additions and 540 deletions

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2010-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -12,6 +12,7 @@
#include "driver/spi_common.h"
#include "freertos/FreeRTOS.h"
#include "hal/spi_types.h"
#include "hal/dma_types.h"
#include "esp_pm.h"
#if SOC_GDMA_SUPPORTED
#include "esp_private/gdma.h"
@@ -45,6 +46,13 @@ extern "C"
#define BUS_LOCK_DEBUG_EXECUTE_CHECK(x)
#endif
#if SOC_GPSPI_SUPPORTED && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI)
#define DMA_DESC_MEM_ALIGN_SIZE 8
typedef dma_descriptor_align8_t spi_dma_desc_t;
#else
#define DMA_DESC_MEM_ALIGN_SIZE 4
typedef dma_descriptor_align4_t spi_dma_desc_t;
#endif
struct spi_bus_lock_t;
struct spi_bus_lock_dev_t;
@@ -56,22 +64,21 @@ typedef struct spi_bus_lock_dev_t* spi_bus_lock_dev_handle_t;
/// Background operation control function
typedef void (*bg_ctrl_func_t)(void*);
typedef struct lldesc_s lldesc_t;
/// Attributes of an SPI bus
typedef struct {
spi_bus_config_t bus_cfg; ///< Config used to initialize the bus
uint32_t flags; ///< Flags (attributes) of the bus
int max_transfer_sz; ///< Maximum length of bytes available to send
bool dma_enabled; ///< To enable DMA or not
int tx_dma_chan; ///< TX DMA channel, on ESP32 and ESP32S2, tx_dma_chan and rx_dma_chan are same
int rx_dma_chan; ///< RX DMA channel, on ESP32 and ESP32S2, tx_dma_chan and rx_dma_chan are same
int dma_desc_num; ///< DMA descriptor number of dmadesc_tx or dmadesc_rx.
lldesc_t *dmadesc_tx; ///< DMA descriptor array for TX
lldesc_t *dmadesc_rx; ///< DMA descriptor array for RX
spi_bus_config_t bus_cfg; ///< Config used to initialize the bus
uint32_t flags; ///< Flags (attributes) of the bus
int max_transfer_sz; ///< Maximum length of bytes available to send
bool dma_enabled; ///< To enable DMA or not
uint16_t internal_mem_align_size; ///< Buffer align byte requirement for internal memory
int tx_dma_chan; ///< TX DMA channel, on ESP32 and ESP32S2, tx_dma_chan and rx_dma_chan are same
int rx_dma_chan; ///< RX DMA channel, on ESP32 and ESP32S2, tx_dma_chan and rx_dma_chan are same
int dma_desc_num; ///< DMA descriptor number of dmadesc_tx or dmadesc_rx.
spi_dma_desc_t *dmadesc_tx; ///< DMA descriptor array for TX
spi_dma_desc_t *dmadesc_rx; ///< DMA descriptor array for RX
spi_bus_lock_handle_t lock;
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; ///< Power management lock
esp_pm_lock_handle_t pm_lock; ///< Power management lock
#endif
} spi_bus_attr_t;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -13,7 +13,6 @@
#include "esp_check.h"
#include "esp_rom_gpio.h"
#include "esp_heap_caps.h"
#include "soc/lldesc.h"
#include "soc/spi_periph.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
@@ -26,6 +25,14 @@
#endif
#if SOC_GDMA_SUPPORTED
#include "esp_private/gdma.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#endif
#if SOC_PERIPH_CLK_CTRL_SHARED
#define SPI_COMMON_RCC_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define SPI_COMMON_RCC_CLOCK_ATOMIC()
#endif
static const char *SPI_TAG = "spi";
@@ -100,7 +107,15 @@ bool spicommon_periph_claim(spi_host_device_t host, const char* source)
bool ret = atomic_compare_exchange_strong(&spi_periph_claimed[host], &false_var, true);
if (ret) {
spi_claiming_func[host] = source;
#if CONFIG_IDF_TARGET_ESP32P4 //deprecate clk_gate_ll start from p4, others in TODO: IDF-8159
SPI_COMMON_RCC_CLOCK_ATOMIC() {
spi_ll_enable_bus_clock(host, true);
spi_ll_reset_register(host);
spi_ll_enable_clock(host, true);
}
#else
periph_module_enable(spi_periph_signal[host].module);
#endif
} else {
ESP_EARLY_LOGE(SPI_TAG, "SPI%d already claimed by %s.", host+1, spi_claiming_func[host]);
}
@@ -117,7 +132,15 @@ bool spicommon_periph_free(spi_host_device_t host)
{
bool true_var = true;
bool ret = atomic_compare_exchange_strong(&spi_periph_claimed[host], &true_var, false);
if (ret) periph_module_disable(spi_periph_signal[host].module);
if (ret) {
#if CONFIG_IDF_TARGET_ESP32P4
SPI_COMMON_RCC_CLOCK_ATOMIC() {
spi_ll_enable_bus_clock(host, false);
}
#else
periph_module_disable(spi_periph_signal[host].module);
#endif
}
return ret;
}
@@ -218,6 +241,12 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
}
#else //SOC_GDMA_SUPPORTED
#if (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB)
static esp_err_t (*spi_gdma_chan_allocator)(const gdma_channel_alloc_config_t *, gdma_channel_handle_t *) = gdma_new_ahb_channel;
#elif (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI)
static esp_err_t (*spi_gdma_chan_allocator)(const gdma_channel_alloc_config_t *, gdma_channel_handle_t *) = gdma_new_axi_channel;
#endif
static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_chan, uint32_t *out_actual_tx_dma_chan, uint32_t *out_actual_rx_dma_chan)
{
assert(is_valid_host(host_id));
@@ -231,19 +260,13 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
.flags.reserve_sibling = 1,
.direction = GDMA_CHANNEL_DIRECTION_TX,
};
ret = gdma_new_channel(&tx_alloc_config, &ctx->tx_channel);
if (ret != ESP_OK) {
return ret;
}
ESP_RETURN_ON_ERROR(spi_gdma_chan_allocator(&tx_alloc_config, &ctx->tx_channel), SPI_TAG, "alloc gdma tx failed");
gdma_channel_alloc_config_t rx_alloc_config = {
.direction = GDMA_CHANNEL_DIRECTION_RX,
.sibling_chan = ctx->tx_channel,
};
ret = gdma_new_channel(&rx_alloc_config, &ctx->rx_channel);
if (ret != ESP_OK) {
return ret;
}
ESP_RETURN_ON_ERROR(spi_gdma_chan_allocator(&rx_alloc_config, &ctx->rx_channel), SPI_TAG, "alloc gdma rx failed");
if (host_id == SPI2_HOST) {
gdma_connect(ctx->rx_channel, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2));
@@ -802,17 +825,22 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
bus_attr->tx_dma_chan = actual_tx_dma_chan;
bus_attr->rx_dma_chan = actual_rx_dma_chan;
int dma_desc_ct = lldesc_get_required_num(bus_config->max_transfer_sz);
int dma_desc_ct = (bus_config->max_transfer_sz + DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED - 1) / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
if (dma_desc_ct == 0) dma_desc_ct = 1; //default to 4k when max is not given
bus_attr->max_transfer_sz = dma_desc_ct * LLDESC_MAX_NUM_PER_DESC;
bus_attr->dmadesc_tx = heap_caps_malloc(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
bus_attr->dmadesc_rx = heap_caps_malloc(sizeof(lldesc_t) * dma_desc_ct, MALLOC_CAP_DMA);
bus_attr->max_transfer_sz = dma_desc_ct * DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
bus_attr->dmadesc_tx = heap_caps_aligned_alloc(DMA_DESC_MEM_ALIGN_SIZE, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA);
bus_attr->dmadesc_rx = heap_caps_aligned_alloc(DMA_DESC_MEM_ALIGN_SIZE, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA);
if (bus_attr->dmadesc_tx == NULL || bus_attr->dmadesc_rx == NULL) {
err = ESP_ERR_NO_MEM;
goto cleanup;
}
bus_attr->dma_desc_num = dma_desc_ct;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
bus_attr->internal_mem_align_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
#else
bus_attr->internal_mem_align_size = 4;
#endif
} else {
bus_attr->dma_enabled = 0;
bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;

View File

@@ -112,6 +112,7 @@ We have two bits to control the interrupt:
#include <string.h>
#include <sys/param.h>
#include "esp_private/periph_ctrl.h"
#include "esp_private/spi_common_internal.h"
#include "driver/spi_master.h"
#include "esp_clk_tree.h"
@@ -126,6 +127,9 @@ We have two bits to control the interrupt:
#include "hal/spi_hal.h"
#include "hal/spi_ll.h"
#include "esp_heap_caps.h"
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "esp_cache.h"
#endif
typedef struct spi_device_t spi_device_t;
@@ -172,6 +176,11 @@ static spi_host_t* bus_driver_ctx[SOC_SPI_PERIPH_NUM] = {};
static const char *SPI_TAG = "spi_master";
#define SPI_CHECK(a, str, ret_val) ESP_RETURN_ON_FALSE_ISR(a, ret_val, SPI_TAG, str)
#if SOC_PERIPH_CLK_CTRL_SHARED
#define SPI_MASTER_RCC_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define SPI_MASTER_RCC_CLOCK_ATOMIC()
#endif
static void spi_intr(void *arg);
static void spi_bus_intr_enable(void *host);
@@ -547,6 +556,9 @@ static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev)
if (spi_bus_lock_touch(dev_lock)) {
/* Configuration has not been applied yet. */
spi_hal_setup_device(hal, hal_dev);
SPI_MASTER_RCC_CLOCK_ATOMIC() {
spi_ll_set_clk_source(hal->hw, hal_dev->timing_conf.clock_source);
}
}
}
@@ -680,13 +692,24 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
const int cs = host->cur_cs;
//Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset.
#if CONFIG_IDF_TARGET_ESP32
if (bus_attr->dma_enabled) {
#if CONFIG_IDF_TARGET_ESP32
//This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same
spicommon_dmaworkaround_idle(bus_attr->tx_dma_chan);
}
#endif //#if CONFIG_IDF_TARGET_ESP32
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible
if (host->cur_trans_buf.buffer_to_rcv) {
uint16_t alignment = bus_attr->internal_mem_align_size;
uint32_t buffer_byte_len = (host->cur_trans_buf.trans->rxlength + 7) / 8;
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1));
// invalidate priv_trans.buffer_to_rcv anyway, only user provide aligned buffer can rcv correct data in post_cb
esp_err_t ret = esp_cache_msync((void *)host->cur_trans_buf.buffer_to_rcv, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
assert(ret == ESP_OK);
}
#endif
}
//cur_cs is changed to DEV_NUM_MAX here
spi_post_trans(host);
@@ -824,9 +847,7 @@ static SPI_MASTER_ISR_ATTR void uninstall_priv_desc(spi_trans_priv_t* trans_buf)
free((void *)trans_buf->buffer_to_send); //force free, ignore const
}
// copy data from temporary DMA-capable buffer back to IRAM buffer and free the temporary one.
if (trans_buf->buffer_to_rcv &&
(void *)trans_buf->buffer_to_rcv != &trans_desc->rx_data[0] &&
trans_buf->buffer_to_rcv != trans_desc->rx_buffer) { // NOLINT(clang-analyzer-unix.Malloc)
if (trans_buf->buffer_to_rcv && (void *)trans_buf->buffer_to_rcv != &trans_desc->rx_data[0] && trans_buf->buffer_to_rcv != trans_desc->rx_buffer) { // NOLINT(clang-analyzer-unix.Malloc)
if (trans_desc->flags & SPI_TRANS_USE_RXDATA) {
memcpy((uint8_t *) & trans_desc->rx_data[0], trans_buf->buffer_to_rcv, (trans_desc->rxlength + 7) / 8);
} else {
@@ -836,9 +857,11 @@ static SPI_MASTER_ISR_ATTR void uninstall_priv_desc(spi_trans_priv_t* trans_buf)
}
}
static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_transaction_t *trans_desc, spi_trans_priv_t* new_desc, bool isdma)
static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans_priv_t* priv_desc)
{
*new_desc = (spi_trans_priv_t) { .trans = trans_desc, };
spi_transaction_t *trans_desc = priv_desc->trans;
const spi_bus_attr_t *bus_attr = host->bus_attr;
uint16_t alignment = bus_attr->internal_mem_align_size;
// rx memory assign
uint32_t* rcv_ptr;
@@ -848,13 +871,6 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_transaction_t *trans_de
//if not use RXDATA neither rx_buffer, buffer_to_rcv assigned to NULL
rcv_ptr = trans_desc->rx_buffer;
}
if (rcv_ptr && isdma && (!esp_ptr_dma_capable(rcv_ptr) || ((int)rcv_ptr % 4 != 0))) {
//if rxbuf in the desc not DMA-capable, malloc a new one. The rx buffer need to be length of multiples of 32 bits to avoid heap corruption.
ESP_LOGD(SPI_TAG, "Allocate RX buffer for DMA" );
rcv_ptr = heap_caps_malloc(((trans_desc->rxlength + 31) / 32) * 4, MALLOC_CAP_DMA);
if (rcv_ptr == NULL) goto clean_up;
}
new_desc->buffer_to_rcv = rcv_ptr;
// tx memory assign
const uint32_t *send_ptr;
@@ -864,21 +880,53 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_transaction_t *trans_de
//if not use TXDATA neither tx_buffer, tx data assigned to NULL
send_ptr = trans_desc->tx_buffer ;
}
if (send_ptr && isdma && !esp_ptr_dma_capable( send_ptr )) {
//if txbuf in the desc not DMA-capable, malloc a new one
ESP_LOGD(SPI_TAG, "Allocate TX buffer for DMA" );
uint32_t *temp = heap_caps_malloc((trans_desc->length + 7) / 8, MALLOC_CAP_DMA);
if (temp == NULL) goto clean_up;
memcpy( temp, send_ptr, (trans_desc->length + 7) / 8 );
send_ptr = temp;
uint32_t tx_byte_len = (trans_desc->length + 7) / 8;
uint32_t rx_byte_len = (trans_desc->rxlength + 7) / 8;
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
bool tx_un_align = ((((uint32_t)send_ptr) | tx_byte_len) & (alignment - 1));
bool rx_un_align = ((((uint32_t)rcv_ptr) | rx_byte_len) & (alignment - 1));
#else
bool tx_un_align = false; //tx don't need align on addr or length, for other chips
bool rx_un_align = (((uint32_t)rcv_ptr) & (alignment - 1));
#endif
if (send_ptr && bus_attr->dma_enabled) {
if ((!esp_ptr_dma_capable(send_ptr) || tx_un_align )) {
ESP_RETURN_ON_FALSE(!(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but TX buffer addr&len not align to %d, or not dma_capable", alignment);
//if txbuf in the desc not DMA-capable, or not bytes aligned to alignment, malloc a new one
ESP_EARLY_LOGD(SPI_TAG, "Allocate TX buffer for DMA" );
tx_byte_len = (tx_byte_len + alignment - 1) & (~(alignment - 1)); // up align alignment
uint32_t *temp = heap_caps_aligned_alloc(alignment, tx_byte_len, MALLOC_CAP_DMA);
if (temp == NULL) {
goto clean_up;
}
memcpy( temp, send_ptr, (trans_desc->length + 7) / 8 );
send_ptr = temp;
}
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
esp_err_t ret = esp_cache_msync((void *)send_ptr, tx_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
assert(ret == ESP_OK);
#endif
}
new_desc->buffer_to_send = send_ptr;
if (rcv_ptr && bus_attr->dma_enabled && (!esp_ptr_dma_capable(rcv_ptr) || rx_un_align )) {
ESP_RETURN_ON_FALSE(!(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but RX buffer addr&len not align to %d, or not dma_capable", alignment);
//if rxbuf in the desc not DMA-capable, or not aligned to alignment, malloc a new one
ESP_EARLY_LOGD(SPI_TAG, "Allocate RX buffer for DMA" );
rx_byte_len = (rx_byte_len + alignment - 1) & (~(alignment - 1)); // up align alignment
rcv_ptr = heap_caps_aligned_alloc(alignment, rx_byte_len, MALLOC_CAP_DMA);
if (rcv_ptr == NULL) {
goto clean_up;
}
}
priv_desc->buffer_to_send = send_ptr;
priv_desc->buffer_to_rcv = rcv_ptr;
return ESP_OK;
clean_up:
uninstall_priv_desc(new_desc);
uninstall_priv_desc(priv_desc);
return ESP_ERR_NO_MEM;
}
@@ -897,8 +945,8 @@ esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi
return ESP_ERR_INVALID_ARG;
}
spi_trans_priv_t trans_buf;
ret = setup_priv_desc(trans_desc, &trans_buf, (host->bus_attr->dma_enabled));
spi_trans_priv_t trans_buf = { .trans = trans_desc, };
ret = setup_priv_desc(host, &trans_buf);
if (ret != ESP_OK) return ret;
#ifdef CONFIG_PM_ENABLE
@@ -935,6 +983,7 @@ esp_err_t SPI_MASTER_ATTR spi_device_get_trans_result(spi_device_handle_t handle
BaseType_t r;
spi_trans_priv_t trans_buf;
SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG);
bool use_dma = handle->host->bus_attr->dma_enabled;
//if SPI_DEVICE_NO_RETURN_RESULT is set, ret_queue will always be empty
SPI_CHECK(!(handle->cfg.flags & SPI_DEVICE_NO_RETURN_RESULT), "API not Supported!", ESP_ERR_NOT_SUPPORTED);
@@ -947,8 +996,10 @@ esp_err_t SPI_MASTER_ATTR spi_device_get_trans_result(spi_device_handle_t handle
// Every in-flight transaction request occupies internal memory as DMA buffer if needed.
return ESP_ERR_TIMEOUT;
}
//release temporary buffers
uninstall_priv_desc(&trans_buf);
//release temporary buffers used by dma
if (use_dma) {
uninstall_priv_desc(&trans_buf);
}
(*trans_desc) = trans_buf.trans;
return ESP_OK;
@@ -1043,8 +1094,8 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handl
SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot send polling transaction while the previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE );
spi_host_t *host = handle->host;
spi_trans_priv_t priv_polling_trans;
ret = setup_priv_desc(trans_desc, &priv_polling_trans, (host->bus_attr->dma_enabled));
spi_trans_priv_t priv_polling_trans = { .trans = trans_desc, };
ret = setup_priv_desc(host, &priv_polling_trans);
if (ret!=ESP_OK) return ret;
/* If device_acquiring_lock is set to handle, it means that the user has already
@@ -1065,6 +1116,7 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handl
ESP_LOGE(SPI_TAG, "polling can't get buslock");
return ret;
}
//After holding the buslock, common resource can be accessed !!
//Polling, no interrupt is used.
host->polling = true;

View File

@@ -163,6 +163,9 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
bool use_dma = (dma_chan != SPI_DMA_DISABLED);
spihost[host]->dma_enabled = use_dma;
if (use_dma) {
#if CONFIG_IDF_TARGET_ESP32P4
abort(); //will supported in IDF-7503
#endif
ret = spicommon_dma_chan_alloc(host, dma_chan, &actual_tx_dma_chan, &actual_rx_dma_chan);
if (ret != ESP_OK) {
goto cleanup;

View File

@@ -115,6 +115,7 @@ typedef struct {
#define SPI_TRANS_MULTILINE_CMD (1<<9) ///< The data lines used at command phase is the same as data phase (otherwise, only one data line is used at command phase)
#define SPI_TRANS_MODE_OCT (1<<10) ///< Transmit/receive data in 8-bit mode
#define SPI_TRANS_MULTILINE_ADDR SPI_TRANS_MODE_DIOQIO_ADDR ///< The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase)
#define SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL (1<<11) ///< By default driver will automatically re-alloc dma buffer if it doesn't meet hardware alignment or dma_capable requirements, this flag is for you to disable this feature, you will need to take care of the alignment otherwise driver will return you error ESP_ERR_INVALID_ARG
/**
* This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes.
@@ -208,6 +209,7 @@ esp_err_t spi_bus_remove_device(spi_device_handle_t handle);
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid. This can happen if SPI_TRANS_CS_KEEP_ACTIVE flag is specified while
* the bus was not acquired (`spi_device_acquire_bus()` should be called first)
* or set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but tx or rx buffer not DMA-capable, or addr&len not align to cache line size
* - ESP_ERR_TIMEOUT if there was no room in the queue before ticks_to_wait expired
* - ESP_ERR_NO_MEM if allocating DMA-capable temporary buffer failed
* - ESP_ERR_INVALID_STATE if previous transactions are not finished
@@ -273,6 +275,7 @@ esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *tra
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid. This can happen if SPI_TRANS_CS_KEEP_ACTIVE flag is specified while
* the bus was not acquired (`spi_device_acquire_bus()` should be called first)
* or set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but tx or rx buffer not DMA-capable, or addr&len not align to cache line size
* - ESP_ERR_TIMEOUT if the device cannot get control of the bus before ``ticks_to_wait`` expired
* - ESP_ERR_NO_MEM if allocating DMA-capable temporary buffer failed
* - ESP_ERR_INVALID_STATE if previous transactions are not finished