Merge branch 'fix/spi_master_bit_trans_add_check_v5.5' into 'release/v5.5'

fix(driver_spi): spi master bit length check, dma internal align check and set idle level for all data pin (v5.5)

See merge request espressif/esp-idf!45134
This commit is contained in:
morris
2026-04-02 12:14:16 +08:00
20 changed files with 316 additions and 99 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2010-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2010-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,17 +9,15 @@
#pragma once
#include <esp_intr_alloc.h>
#include "esp_pm.h"
#include "driver/spi_common.h"
#include "freertos/FreeRTOS.h"
#include "hal/spi_types.h"
#include "hal/dma_types.h"
#include "soc/ext_mem_defs.h" //for SOC_NON_CACHEABLE_OFFSET
#include "esp_private/spi_dma.h"
#include "esp_pm.h"
#include "esp_private/spi_share_hw_ctrl.h"
#if SOC_GDMA_SUPPORTED
#include "esp_private/gdma.h"
#endif
#include "esp_private/spi_share_hw_ctrl.h"
#ifdef __cplusplus
extern "C"
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -339,7 +339,7 @@ static esp_err_t spi_master_init_driver(spi_host_device_t host_id)
spi_ll_enable_clock(host_id, true);
}
spi_hal_init(&host->hal, host_id);
spi_hal_config_io_default_level(&host->hal, bus_attr->bus_cfg.data_io_default_level);
spi_hal_set_data_pin_idle_level(&host->hal, bus_attr->bus_cfg.data_io_default_level);
if (host_id != SPI1_HOST) {
//SPI1 attributes are already initialized at start up.
@@ -1110,6 +1110,8 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
const spi_bus_attr_t* bus_attr = host->bus_attr;
bool tx_enabled = (trans_desc->flags & SPI_TRANS_USE_TXDATA) || (trans_desc->tx_buffer);
bool rx_enabled = (trans_desc->flags & SPI_TRANS_USE_RXDATA) || (trans_desc->rx_buffer);
uint8_t txlen_extra = trans_desc->length % 8;
uint8_t rxlen_extra = trans_desc->rxlength % 8;
spi_transaction_ext_t *t_ext = (spi_transaction_ext_t *)trans_desc;
bool dummy_enabled = (((trans_desc->flags & SPI_TRANS_VARIABLE_DUMMY) ? t_ext->dummy_bits : handle->cfg.dummy_bits) != 0);
bool extra_dummy_enabled = handle->hal_dev.timing_conf.timing_dummy;
@@ -1131,6 +1133,7 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO | SPI_TRANS_MODE_QIO)) && !is_half_duplex), "Incompatible when setting to both multi-line mode and half duplex mode", ESP_ERR_INVALID_ARG);
#ifdef CONFIG_IDF_TARGET_ESP32
SPI_CHECK(!is_half_duplex || !bus_attr->dma_enabled || !rx_enabled || !tx_enabled, "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG);
SPI_CHECK(!bus_attr->dma_enabled || !rxlen_extra, "rx unaligned byte with DMA is not supported", ESP_ERR_NOT_SUPPORTED);
#endif
#if !SOC_SPI_HD_BOTH_INOUT_SUPPORTED
//On these chips, HW doesn't support using both TX and RX phases when in halfduplex mode
@@ -1150,6 +1153,8 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
//Dummy phase is not available when both data out and in are enabled, regardless of FD or HD mode.
SPI_CHECK(!tx_enabled || !rx_enabled || !dummy_enabled || !extra_dummy_enabled, "Dummy phase is not available when both data out and in are enabled", ESP_ERR_INVALID_ARG);
SPI_CHECK(!txlen_extra || (txlen_extra >= SPI_LL_TX_MINI_EXTRA_BITS), "tx %d-bit is not supported on this(or this version) chip", ESP_ERR_NOT_SUPPORTED, trans_desc->length);
SPI_CHECK(!rxlen_extra || (rxlen_extra >= SPI_LL_RX_MINI_EXTRA_BITS), "rx %d-bit is not supported on this chip", ESP_ERR_NOT_SUPPORTED, trans_desc->rxlength);
if (bus_attr->dma_enabled) {
SPI_CHECK(trans_desc->length <= SPI_LL_DMA_MAX_BIT_LEN, "txdata transfer > hardware max supported len", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->rxlength <= SPI_LL_DMA_MAX_BIT_LEN, "rxdata transfer > hardware max supported len", ESP_ERR_INVALID_ARG);
@@ -1199,12 +1204,13 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_dma_priv_buffer(spi_host_t *host, uin
alignment = MAX(host->dma_ctx->dma_align_rx_int, host->bus_attr->cache_align_int);
}
}
need_malloc |= (((uint32_t)buffer | len) & (alignment - 1));
// length also must be aligned if cache sync is required, otherwise don't need
need_malloc |= (use_psram || host->bus_attr->cache_align_int > 1) ? (((uint32_t)buffer | len) & (alignment - 1)) : (((uint32_t)buffer) & (alignment - 1));
ESP_EARLY_LOGV(SPI_TAG, "%s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", is_tx ? "TX" : "RX", buffer, len, is_ptr_ext, use_psram, alignment, need_malloc, (mem_cap & MALLOC_CAP_SPIRAM) ? "psram" : "internal");
uint32_t align_len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
if (need_malloc) {
ESP_RETURN_ON_FALSE_ISR(!(flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but %s addr&len not align to %d, or not dma_capable", is_tx ? "TX" : "RX", alignment);
len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
uint32_t *temp = heap_caps_aligned_alloc(alignment, len, mem_cap);
uint32_t *temp = heap_caps_aligned_alloc(alignment, align_len, mem_cap);
ESP_RETURN_ON_FALSE_ISR(temp != NULL, ESP_ERR_NO_MEM, SPI_TAG, "Failed to allocate priv %s buffer", is_tx ? "TX" : "RX");
if (is_tx) {
@@ -1214,7 +1220,8 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_dma_priv_buffer(spi_host_t *host, uin
}
*ret_buffer = buffer;
if (use_psram || (host->bus_attr->cache_align_int > 1)) {
esp_err_t ret = esp_cache_msync((void *)buffer, len, is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C);
uint32_t sync_flags = is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C;
esp_err_t ret = esp_cache_msync((void *)buffer, need_malloc ? align_len : len, sync_flags);
ESP_RETURN_ON_FALSE_ISR(ret == ESP_OK, ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
}
return ESP_OK;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -36,6 +36,19 @@ const static char TAG[] = "test_spi";
// There is no input-only pin except on esp32 and esp32s2
#define TEST_SOC_HAS_INPUT_ONLY_PINS (CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
static uint8_t bitswap(uint8_t in)
{
uint8_t out = 0;
for (int i = 0; i < 8; i++) {
out = out >> 1;
if (in & 0x80) {
out |= 0x80;
}
in = in << 1;
}
return out;
}
static void check_spi_pre_n_for(int clk, int pre, int n)
{
spi_device_handle_t handle;
@@ -124,7 +137,7 @@ TEST_CASE("SPI Master clockdiv calculation routines", "[spi]")
// Test All clock source
#define TEST_CLK_BYTE_LEN 10000
#define TEST_TRANS_TIME_BIAS_RATIO (float)8.0/100 // think 8% transfer time bias as acceptable
#define TEST_TRANS_TIME_BIAS_RATIO (float)10.0/100 // think 8% transfer time bias as acceptable
TEST_CASE("SPI Master clk_source and divider accuracy", "[spi]")
{
int64_t start = 0, end = 0;
@@ -880,22 +893,40 @@ TEST_CASE("SPI Master DMA test: length, start, not aligned", "[spi]")
TEST_ASSERT(spi_bus_free(TEST_SPI_HOST) == ESP_OK);
}
#if !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE // targets who need cache sync don't support unaligned trans
TEST_CASE("SPI Master DMA manually unaligned RX test", "[spi]")
{
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.miso_io_num = buscfg.mosi_io_num;
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
spi_device_handle_t dev0;
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
WORD_ALIGNED_ATTR uint8_t tx_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
WORD_ALIGNED_ATTR uint8_t rx_data[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
spi_transaction_t trans_cfg = {
.tx_buffer = tx_data,
.rx_buffer = rx_data,
.length = 5 * 8,
.flags = SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL,
};
printf("Sending %d bytes\n", trans_cfg.length / 8);
TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg));
ESP_LOG_BUFFER_HEX("rx", rx_data, 8);
#if !CONFIG_IDF_TARGET_ESP32 // esp32 dma not work with unaligned RX
TEST_ASSERT_EQUAL(rx_data[6], 0xAA);
#endif
TEST_ESP_OK(spi_bus_remove_device(dev0));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
#endif
#if (TEST_SPI_PERIPH_NUM >= 2)
//These will only be enabled on chips with 2 or more SPI peripherals
static uint8_t bitswap(uint8_t in)
{
uint8_t out = 0;
for (int i = 0; i < 8; i++) {
out = out >> 1;
if (in & 0x80) {
out |= 0x80;
}
in = in << 1;
}
return out;
}
void test_cmd_addr(spi_slave_task_context_t *slave_context, bool lsb_first)
{
spi_device_handle_t spi;
@@ -1843,6 +1874,90 @@ TEST_CASE("test_bus_free_safty_to_remain_devices", "[spi]")
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
TEST_CASE("Test master 1-9 bits tx/rx", "[spi]")
{
spi_device_handle_t dev0;
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
buscfg.miso_io_num = buscfg.mosi_io_num;
spi_transaction_t trans_cfg = {
.tx_data[0] = 0xc9,
.tx_data[1] = 0xad,
.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA,
};
uint8_t exp[2], reversed[2] = {bitswap(trans_cfg.tx_data[0]), bitswap(trans_cfg.tx_data[1])};
for (int dma = 0; dma < 2; dma++) {
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED));
for (int cfg = 0; cfg < 2; cfg++) {
printf("Test %s with DMA: %s\n", cfg ? "LSB" : "MSB", dma ? "AUTO" : "DISABLED");
devcfg.flags = cfg ? SPI_DEVICE_RXBIT_LSBFIRST : 0;
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
for (int i = 1; i <= 9; i++) {
trans_cfg.length = i;
trans_cfg.rxlength = i;
if (i <= 8) {
exp[0] = devcfg.flags & SPI_DEVICE_RXBIT_LSBFIRST ? reversed[0] & (0xff >> (8 - i)) : trans_cfg.tx_data[0] & (0xff << (8 - i));
}
exp[1] = devcfg.flags & SPI_DEVICE_RXBIT_LSBFIRST ? reversed[1] & (0xff >> (16 - i)) : trans_cfg.tx_data[1] & (0xff << (16 - i));
#if CONFIG_IDF_TARGET_ESP32
if ((i % 8) && dma) {
#else
if ((i % 8) && ((i % 8 < SPI_LL_TX_MINI_EXTRA_BITS) || (i % 8 < SPI_LL_RX_MINI_EXTRA_BITS))) {
#endif
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, spi_device_transmit(dev0, &trans_cfg));
continue;
}
TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg));
printf("len %d bits tx %x %x reversed %x %x exp %2x %2x rx %2x %2x\n", i, trans_cfg.tx_data[0], trans_cfg.tx_data[1], reversed[0], reversed[1], exp[0], exp[1], trans_cfg.rx_data[0], trans_cfg.rx_data[1]);
TEST_ASSERT_EQUAL_HEX8_ARRAY(exp, trans_cfg.rx_data, 2);
memset(trans_cfg.rx_data, 0, sizeof(trans_cfg.rx_data));
}
TEST_ESP_OK(spi_bus_remove_device(dev0));
printf("--------------------------------\n");
}
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
}
TEST_CASE("Test master 1-9 bits rx only", "[spi]")
{
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_DISABLED));
spi_device_handle_t dev0;
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
devcfg.flags |= SPI_DEVICE_HALFDUPLEX;
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
spi_transaction_t trans_cfg = {
.flags = SPI_TRANS_USE_RXDATA,
};
gpio_set_level(buscfg.miso_io_num, 1);
spitest_gpio_output_sel(buscfg.miso_io_num, FUNC_GPIO, SIG_GPIO_OUT_IDX);
for (int i = 1; i <= 9; i++) {
trans_cfg.rxlength = i;
if ((i % 8) && (i % 8 < SPI_LL_RX_MINI_EXTRA_BITS)) {
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, spi_device_transmit(dev0, &trans_cfg));
continue;
}
TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg));
printf("len %d bits rx %2x %2x\n", i, trans_cfg.rx_data[0], trans_cfg.rx_data[1]);
if (i <= 8) {
TEST_ASSERT_EQUAL_HEX8((0xff << (8 - i)), trans_cfg.rx_data[0]);
}
TEST_ASSERT_EQUAL_HEX8((0xff << (16 - i)), trans_cfg.rx_data[1]);
memset(trans_cfg.rx_data, 0, sizeof(trans_cfg.rx_data));
}
TEST_ESP_OK(spi_bus_remove_device(dev0));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
}
#if SOC_LIGHT_SLEEP_SUPPORTED
TEST_CASE("test_spi_master_sleep_retention", "[spi]")
{
@@ -42,6 +42,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 24) //reg len: 24 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 1 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 0 //Default level after bus initialized
// CS_WORKAROUND: SPI slave with using DMA, the rx dma suffers from unexpected transactions
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -41,6 +41,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 2 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
/**
@@ -245,13 +247,16 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -41,6 +41,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 2 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
/**
@@ -247,13 +249,16 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+14 -14
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -798,6 +798,19 @@ static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pcm2pdm_en)
hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pcm2pdm_en;
}
/**
* @brief Enable I2S RX PDM mode
*
* @param hw Peripheral I2S hardware instance address.
* @param pdm2pcm_en Set true to enable RX PDM to PCM filter
*/
static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm2pcm_en)
{
(void)pdm2pcm_en;
hw->rx_conf.rx_pdm_en = true;
hw->rx_conf.rx_tdm_en = false;
}
/**
* @brief Set I2S TX PDM prescale
*
@@ -957,19 +970,6 @@ static inline uint32_t i2s_ll_tx_get_pdm_fs(i2s_dev_t *hw)
return hw->tx_pcm2pdm_conf1.tx_pdm_fs;
}
/**
* @brief Enable RX PDM mode.
*
* @param hw Peripheral I2S hardware instance address.
* @param pdm_enable Set true to RX enable PDM mode (ignored)
*/
static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm_enable)
{
(void)pdm_enable;
hw->rx_conf.rx_pdm_en = 0;
hw->rx_conf.rx_tdm_en = 1;
}
/**
* @brief Configura TX a/u-law decompress or compress
*
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -39,6 +39,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN SPI_MS_DATA_BITLEN
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 1 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
#define SPI_LL_SUPPORT_CLK_SRC_PRE_DIV 1 //clock source have divider before peripheral
#define SPI_LL_PERIPH_CLK_DIV_MAX ((SPI_CLKCNT_N + 1) * (SPI_CLKDIV_PRE + 1)) //peripheral internal maxmum clock divider
@@ -898,13 +900,16 @@ static inline void spi_ll_master_set_cs_setup(spi_dev_t *hw, uint8_t setup)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/*------------------------------------------------------------------------------
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -41,6 +41,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 2 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
/**
@@ -238,13 +240,16 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+14 -14
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -798,6 +798,19 @@ static inline void i2s_ll_tx_enable_pdm(i2s_dev_t *hw, bool pcm2pdm_en)
hw->tx_pcm2pdm_conf.pcm2pdm_conv_en = pcm2pdm_en;
}
/**
* @brief Enable I2S RX PDM mode
*
* @param hw Peripheral I2S hardware instance address.
* @param pdm2pcm_en Set true to enable RX PDM to PCM filter
*/
static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm2pcm_en)
{
(void)pdm2pcm_en;
hw->rx_conf.rx_pdm_en = true;
hw->rx_conf.rx_tdm_en = false;
}
/**
* @brief Set I2S TX PDM prescale
*
@@ -957,19 +970,6 @@ static inline uint32_t i2s_ll_tx_get_pdm_fs(i2s_dev_t *hw)
return hw->tx_pcm2pdm_conf1.tx_pdm_fs;
}
/**
* @brief Enable RX PDM mode.
*
* @param hw Peripheral I2S hardware instance address.
* @param pdm2pcm Set true to RX enable PDM mode (ignored)
*/
static inline void i2s_ll_rx_enable_pdm(i2s_dev_t *hw, bool pdm2pcm)
{
(void)pdm2pcm;
hw->rx_conf.rx_pdm_en = 0;
hw->rx_conf.rx_tdm_en = 1;
}
/**
* @brief Configura TX a/u-law decompress or compress
*
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -39,6 +39,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN SPI_MS_DATA_BITLEN
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 1 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
#define SPI_LL_SUPPORT_CLK_SRC_PRE_DIV 1 //clock source have divider before peripheral
#define SPI_LL_PERIPH_CLK_DIV_MAX ((SPI_CLKCNT_N + 1) * (SPI_CLKDIV_PRE + 1)) //peripheral internal maxmum clock divider
@@ -262,13 +264,16 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -43,6 +43,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS (ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102) ? 1 : 2) //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
/**
@@ -239,13 +241,16 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+9 -5
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -40,8 +40,9 @@ extern "C" {
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 1 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
/**
* The data structure holding calculated clock configuration. Since the
* calculation needs long time, it should be calculated during initialization and
@@ -225,13 +226,16 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -41,6 +41,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN SPI_MS_DATA_BITLEN
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 1 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_SUPPORT_CLK_SRC_PRE_DIV 1 //clock source have divider before peripheral
#define SPI_LL_PERIPH_CLK_DIV_MAX ((SPI_CLKCNT_N + 1) * (SPI_CLKDIV_PRE + 1)) //peripheral internal maxmum clock divider
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
@@ -916,13 +918,16 @@ static inline void spi_ll_set_mosi_delay(spi_dev_t *hw, int delay_mode, int dela
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+7 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -48,6 +48,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 23) //reg len: 23 bits
#define SPI_LL_CPU_MAX_BIT_LEN (18 * 32) //Fifo len: 18 words
#define SPI_LL_TX_MINI_EXTRA_BITS 1 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 8 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
#define SPI_LL_DMA_SHARED 1 //spi_dma shared with adc and dac on S2
@@ -263,13 +265,14 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
}
/**
+9 -4
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -43,6 +43,8 @@ extern "C" {
#define SPI_LL_DMA_MAX_BIT_LEN (1 << 18) //reg len: 18 bits
#define SPI_LL_CPU_MAX_BIT_LEN (16 * 32) //Fifo len: 16 words
#define SPI_LL_TX_MINI_EXTRA_BITS 2 //Minimum length of TX non byte aligned data in bits
#define SPI_LL_RX_MINI_EXTRA_BITS 1 //Minimum length of RX non byte aligned data in bits
#define SPI_LL_MOSI_FREE_LEVEL 1 //Default level after bus initialized
/**
@@ -256,13 +258,16 @@ static inline void spi_ll_slave_hd_init(spi_dev_t *hw)
}
/**
* Determine and unify the default level of mosi line when bus free
* Determine and unify the default level of data line when bus idle
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_mosi_free_level(spi_dev_t *hw, bool level)
static inline void spi_ll_set_data_pin_idle_level(spi_dev_t *hw, bool level)
{
hw->ctrl.d_pol = level; //set default level for MOSI only on IDLE state
hw->ctrl.d_pol = level;
hw->ctrl.q_pol = level;
hw->ctrl.wp_pol = level;
hw->ctrl.hold_pol = level;
}
/**
+2 -2
View File
@@ -172,12 +172,12 @@ typedef struct {
void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id);
/**
* Config default output IO level when don't have transaction
* Config default output data IO level when bus idle
*
* @param hal Context of the HAL layer.
* @param level IO level to config
*/
void spi_hal_config_io_default_level(spi_hal_context_t *hal, bool level);
void spi_hal_set_data_pin_idle_level(spi_hal_context_t *hal, bool level);
/**
* Deinit the peripheral (and the context if needed).
+2 -2
View File
@@ -27,11 +27,11 @@ void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id)
spi_ll_apply_config(hw);
}
void spi_hal_config_io_default_level(spi_hal_context_t *hal, bool level)
void spi_hal_set_data_pin_idle_level(spi_hal_context_t *hal, bool level)
{
#if SPI_LL_MOSI_FREE_LEVEL
// Config default output data line level when don't have transaction
spi_ll_set_mosi_free_level(hal->hw, level);
spi_ll_set_data_pin_idle_level(hal->hw, level);
spi_ll_apply_config(hal->hw);
#endif
}
@@ -375,6 +375,25 @@ An SPI Host reads and writes data into memory byte by byte. By default, data is
For example, if ``0b00010`` needs to be sent, it should be written into a ``uint8_t`` variable, and the length for reading should be set to 5 bits. The Device will still receive 8 bits with 3 additional "random" bits, so the reading must be performed correctly.
Not all chips support data transmission with any bit lengths. Sending or receiving unsupported bit lengths will return :c:macro:`ESP_ERR_NOT_SUPPORTED` error. The supported lengths are shown in the table below (**YES** means support any bits length, **NO** means bytes (8 bits) only):
+------+--------+-------+----------+--------------------+---------------------+
| | ESP32 | ESP32-S2 | ESP32-S3/C2/C3/C6 | ESP32-H2/P4/C5/C61 |
+======+========+=======+==========+====================+=====================+
| TX | DMA | YES | YES | (bit_len % 8) != 1 | YES |
+ +--------+-------+----------+--------------------+---------------------+
| | NO DMA | YES | YES | (bit_len % 8) != 1 | YES |
+------+--------+-------+----------+--------------------+---------------------+
| RX | DMA | NO | NO | YES | YES |
+ +--------+-------+----------+--------------------+---------------------+
| | NO DMA | YES | NO | YES | YES |
+------+--------+-------+----------+--------------------+---------------------+
If you still need to use unsupported bit lengths, you can use the following alternatives:
1. Use :cpp:member:`spi_transaction_t::cmd` and :cpp:member:`spi_transaction_t::addr` and data phase combination. The drawback is that the command and address phases do not receive data from the slave device.
2. Use two supported length transmissions combination, like ``2+7`` to implement ``9 bit`` transmission, while keeping the CS line valid. The drawback is that there is a minimum time interval between two transmissions (see :ref:`transaction_time_cost`), and the overall transfer rate is lower.
On top of that, {IDF_TARGET_NAME} is a little-endian chip, which means that the least significant byte of ``uint16_t`` and ``uint32_t`` variables is stored at the smallest address. Hence, if ``uint16_t`` is stored in memory, bits [7:0] are sent first, followed by bits [15:8].
For cases when the data to be transmitted has a size differing from ``uint8_t`` arrays, the following macros can be used to transform data to the format that can be sent by the SPI driver directly:
@@ -536,6 +555,7 @@ There are three factors limiting the transfer speed:
The main parameter that determines the transfer speed for large transactions is clock frequency. For multiple small transactions, the transfer speed is mostly determined by the length of transaction intervals.
.. _transaction_time_cost:
Transaction Duration
^^^^^^^^^^^^^^^^^^^^
@@ -832,6 +852,10 @@ Please note that the ISR is disabled during flash operation by default. To keep
4. ``cs_ena_pretrans`` is not compatible with the Command and Address phases of full-duplex transactions.
.. only:: esp32
5. If DMA is enabled, the RX buffer should be word-aligned (starting from a 32-bit boundary and having a length of multiples of 4 bytes). Otherwise, DMA may overwrite the data in the unaligned part.
Application Examples
--------------------
@@ -375,6 +375,25 @@ SPI 主机逐字节地将数据读入和写入内存。默认情况下,数据
例如,如果需要发送 ``0b00010``,则应将其写成 ``uint8_t`` 变量,读取长度设置为 5 位。此时,设备仍然会收到 8 位数据,并另有 3 个“随机”位,所以读取过程必须准确。
不是所有芯片都支持任意位长度的数据传输,发送或接收不支持的位长度时会返回 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。支持的长度如下表所示(**YES** 表示支持任意长度,**NO** 表示只支持整字节 8 bits 长度):
+------+--------+-------+----------+--------------------+---------------------+
| | ESP32 | ESP32-S2 | ESP32-S3/C2/C3/C6 | ESP32-H2/P4/C5/C61 |
+======+========+=======+==========+====================+=====================+
| TX | DMA | YES | YES | (bit_len % 8) != 1 | YES |
+ +--------+-------+----------+--------------------+---------------------+
| | NO DMA | YES | YES | (bit_len % 8) != 1 | YES |
+------+--------+-------+----------+--------------------+---------------------+
| RX | DMA | NO | NO | YES | YES |
+ +--------+-------+----------+--------------------+---------------------+
| | NO DMA | YES | NO | YES | YES |
+------+--------+-------+----------+--------------------+---------------------+
如仍需使用不支持的长度传输,根据表格可以有以下替代方法:
1. 使用 :cpp:member:`spi_transaction_t::cmd`:cpp:member:`spi_transaction_t::addr` 和数据阶段组合。缺点:命令和地址阶段不接收从机数据。
2. 使用两次支持长度的传输组合,比如 ``2+7`` 实现 ``9 bit`` 传输,期间保持 CS 线有效。缺点:两次传输之间有最小时间间隔(见 :ref:`transaction_time_cost`),整体速率较低。
此外,{IDF_TARGET_NAME} 属于小端芯片,即 ``uint16_t````uint32_t`` 变量的最低有效位存储在最小的地址。因此,如果 ``uint16_t`` 存储在内存中,则首先发送位 [7:0],其次是位 [15:8]。
在某些情况下,要传输的数据大小与 ``uint8_t`` 数组不同,可使用以下宏将数据转换为可由 SPI 驱动直接发送的格式:
@@ -536,6 +555,7 @@ GPIO 矩阵与 IO_MUX 管脚
影响大传输事务传输速度的主要参数是时钟频率。而多个小传输事务的传输速度主要由传输事务间隔时长决定。
.. _transaction_time_cost:
传输事务持续时间
^^^^^^^^^^^^^^^^^^^^
@@ -832,6 +852,10 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_interface_con
4. 全双工传输事务模式中,命令阶段和地址阶段与 ``cs_ena_pretrans`` 不兼容。
.. only:: esp32
5. 若启用了 DMA,则 RX 缓冲区应该以字对齐(从 32 位边界开始,字节长度为 4 的倍数)。否则 DMA 可能覆盖未对齐部分的数据。
应用示例
-------------------