From a9ce9e4abf137b75bb8e0c1dbdaa69d5c4b97a23 Mon Sep 17 00:00:00 2001 From: wanckl Date: Thu, 18 Jun 2026 15:58:13 +0800 Subject: [PATCH] feat(driver_spi): master driver support DDR(DTR) clock mode --- .../include/driver/spi_master.h | 1 + .../esp_driver_spi/src/gpspi/spi_master.c | 34 +++++++++-- .../test_apps/master/main/CMakeLists.txt | 2 +- .../test_apps/master/main/test_spi_master.c | 58 +++++++++++++++++++ .../esp32p4/include/hal/spi_ll.h | 11 ++++ .../esp32s2/include/hal/spi_ll.h | 11 ++++ .../esp32s3/include/hal/spi_ll.h | 11 ++++ .../esp32s31/include/hal/spi_ll.h | 11 ++++ .../esp_hal_gpspi/include/hal/spi_hal.h | 2 + components/esp_hal_gpspi/spi_hal_iram.c | 11 +++- .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32p4/include/soc/soc_caps.h | 1 + .../esp32s2/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32s2/include/soc/soc_caps.h | 1 + .../esp32s3/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32s3/include/soc/soc_caps.h | 1 + .../esp32s31/include/soc/Kconfig.soc_caps.in | 4 ++ .../soc/esp32s31/include/soc/soc_caps.h | 1 + .../api-reference/peripherals/spi_master.rst | 7 +++ .../api-reference/peripherals/spi_master.rst | 7 +++ 20 files changed, 178 insertions(+), 8 deletions(-) diff --git a/components/esp_driver_spi/include/driver/spi_master.h b/components/esp_driver_spi/include/driver/spi_master.h index 67cdcda5aa8..950cb759de2 100644 --- a/components/esp_driver_spi/include/driver/spi_master.h +++ b/components/esp_driver_spi/include/driver/spi_master.h @@ -118,6 +118,7 @@ typedef struct { #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 #define SPI_TRANS_DMA_USE_PSRAM (1<<12) ///< Use PSRAM for DMA buffer directly, has speed limit, but no temp buffer and save memory +#define SPI_TRANS_DDRCLK (1<<13) ///< Use DDRCLK (double clock edge) for current transaction. // Output flags #define SPI_TRANS_DMA_RX_FAIL (1<<30) ///< RX transaction data lose flag, indicate DMA RX overflow diff --git a/components/esp_driver_spi/src/gpspi/spi_master.c b/components/esp_driver_spi/src/gpspi/spi_master.c index 35a94ad2bbf..3110e17dd4d 100644 --- a/components/esp_driver_spi/src/gpspi/spi_master.c +++ b/components/esp_driver_spi/src/gpspi/spi_master.c @@ -649,6 +649,17 @@ int spi_get_actual_clock(int fapb, int hz, int duty_cycle) return spi_hal_master_cal_clock(fapb, hz, duty_cycle); } +static SPI_MASTER_ISR_ATTR bool s_spi_clock_need_reconfig(spi_device_t *dev, spi_trans_priv_t *trans_buf) +{ + if (!trans_buf) { + return false; + } + if (dev->hal_dev.timing_conf.use_ddr_clk != !!(trans_buf->trans->flags & SPI_TRANS_DDRCLK)) { + return true; + } + return ((trans_buf->trans->override_freq_hz > 0) && (dev->hal_dev.timing_conf.expect_freq != trans_buf->trans->override_freq_hz)); +} + // Setup the device-specified configuration registers. Called every time a new // transaction is to be sent, but only apply new configurations when the device // changes or timing change is required. @@ -659,21 +670,31 @@ static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev, spi_trans_pr spi_hal_dev_config_t *hal_dev = &(dev->hal_dev); bool clock_changed = false; - // check if timing config update is required - if (trans_buf && (trans_buf->trans->override_freq_hz > 0) && (hal_dev->timing_conf.expect_freq != trans_buf->trans->override_freq_hz)) { + if (s_spi_clock_need_reconfig(dev, trans_buf)) { // check if timing config update is required + const bool want_ddr = !!(trans_buf->trans->flags & SPI_TRANS_DDRCLK); + uint32_t target_freq = trans_buf->trans->override_freq_hz ? trans_buf->trans->override_freq_hz : dev->cfg.clock_speed_hz; spi_hal_timing_param_t timing_param = { - .expected_freq = trans_buf->trans->override_freq_hz, + .use_ddr_clk = want_ddr, + .expected_freq = target_freq, .clk_src_hz = dev->hal_dev.timing_conf.source_real_freq, .duty_cycle = dev->cfg.duty_cycle_pos, .input_delay_ns = dev->cfg.input_delay_ns, .half_duplex = dev->hal_dev.half_duplex, + .no_compensate = dev->hal_dev.no_compensate, .use_gpio = !(dev->host->bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS), }; - if ((trans_buf->trans->override_freq_hz <= SPI_PERIPH_SRC_FREQ_MAX) && (ESP_OK == spi_hal_cal_clock_conf(&timing_param, &dev->hal_dev.timing_conf))) { + if ((target_freq <= SPI_PERIPH_SRC_FREQ_MAX) && (ESP_OK == spi_hal_cal_clock_conf(&timing_param, &dev->hal_dev.timing_conf))) { clock_changed = true; } else { - ESP_EARLY_LOGW(SPI_TAG, "assigned override_freq_hz %d not supported", trans_buf->trans->override_freq_hz); + // Keep previous timing_conf; clarify why reconfig failed. + if (want_ddr != !!dev->hal_dev.timing_conf.use_ddr_clk) { + ESP_EARLY_LOGW(SPI_TAG, "failed to switch to %s at %lu Hz, keep previous clock config", + want_ddr ? "DDR" : "SDR", (unsigned long)target_freq); + } else { + ESP_EARLY_LOGW(SPI_TAG, "failed to apply override_freq_hz %lu, keep previous frequency", + (unsigned long)target_freq); + } } } @@ -1116,6 +1137,9 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl SPI_CHECK(!(host->id == SPI3_HOST && trans_desc->flags & SPI_TRANS_MODE_OCT), "SPI3 does not support octal mode", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both Octal mode and 3-wire-mode", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && !is_half_duplex), "Incompatible when setting to both Octal mode and half duplex mode", ESP_ERR_INVALID_ARG); +#endif +#if !SOC_SPI_SUPPORT_DDR_CLOCK + SPI_CHECK(!(trans_desc->flags & SPI_TRANS_DDRCLK), "DDRCLK is not supported on this chip", ESP_ERR_NOT_SUPPORTED); #endif SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO | SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both multi-line mode and 3-wire-mode", ESP_ERR_INVALID_ARG); 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); diff --git a/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt b/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt index 2d7f098c3c8..1617e4ac6d0 100644 --- a/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt +++ b/components/esp_driver_spi/test_apps/master/main/CMakeLists.txt @@ -21,6 +21,6 @@ endif() # the component can be registered as WHOLE_ARCHIVE idf_component_register( SRCS ${srcs} - PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio esp_mm + PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio esp_mm esp_driver_uart WHOLE_ARCHIVE ) diff --git a/components/esp_driver_spi/test_apps/master/main/test_spi_master.c b/components/esp_driver_spi/test_apps/master/main/test_spi_master.c index b324dfe49d7..3be4dcdc65b 100644 --- a/components/esp_driver_spi/test_apps/master/main/test_spi_master.c +++ b/components/esp_driver_spi/test_apps/master/main/test_spi_master.c @@ -10,6 +10,7 @@ #include "sdkconfig.h" #include "driver/spi_master.h" #include "driver/spi_slave.h" +#include "driver/uart.h" #include "sys/param.h" #include "driver/gpio.h" #include "hal/spi_ll.h" // for SPI_LL_SRC_PRE_DIV_MAX @@ -2192,3 +2193,60 @@ TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]") spi_bus_free(TEST_SPI_HOST); } #endif + +#if SOC_SPI_SUPPORT_DDR_CLOCK +TEST_CASE("Test master cmd/data DDR/SDR", "[spi]") +{ + spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG(); + buscfg.miso_io_num = buscfg.mosi_io_num; // same pin to test data loopback + 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.command_bits = 16; + devcfg.address_bits = 16; + TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0)); + + // Tap SCLK with UART bitrate detection to count clock edges. + uart_bitrate_detect_config_t uart_cfg = { + .rx_io_num = buscfg.sclk_io_num, + .source_clk = UART_SCLK_DEFAULT, + }; + + spi_transaction_t trans_cfg = { + .cmd = 0x1234, + .addr = 0x5678, + .length = 32, + .tx_data = {0xAA, 0x34, 0x56, 0x5f}, + }; + uint32_t clk_edges[2]; + for (int i = 0; i < 4; i++) { + const bool use_ddr = (i % 2) != 0; + printf("\nTest trans %s\n", use_ddr ? "DDR" : "SDR"); + trans_cfg.flags = use_ddr ? SPI_TRANS_DDRCLK : 0; + trans_cfg.flags |= SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA; + trans_cfg.tx_data[3] *= 2; + memset(trans_cfg.rx_data, 0, sizeof(trans_cfg.rx_data)); + + TEST_ESP_OK(uart_detect_bitrate_start(UART_NUM_1, &uart_cfg)); + TEST_ESP_OK(spi_device_polling_transmit(dev0, &trans_cfg)); + uart_bitrate_res_t uart_res = {}; + TEST_ESP_OK(uart_detect_bitrate_stop(UART_NUM_1, true, &uart_res)); + + int measured_freq_khz = (uart_res.clk_freq_hz / uart_res.pos_period) / 1000; + printf("clk edge %lu, measured freq %d kHz\n", uart_res.edge_cnt, measured_freq_khz); + clk_edges[i % 2] = uart_res.edge_cnt; + int bit_num = devcfg.command_bits + devcfg.address_bits + trans_cfg.length; + // SDR: one bit per clock period => 2 edges/bit; DDR: two bits per period => 1 edge/bit + TEST_ASSERT_INT_WITHIN(5, use_ddr ? bit_num : bit_num * 2, (int)uart_res.edge_cnt); + TEST_ASSERT_INT_WITHIN(devcfg.clock_speed_hz / 100000, measured_freq_khz, devcfg.clock_speed_hz / 1000); + ESP_LOG_BUFFER_HEX("Tx", trans_cfg.tx_data, 4); + ESP_LOG_BUFFER_HEX("Rx", trans_cfg.rx_data, 4); + TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_data, trans_cfg.rx_data, 4); + } + TEST_ASSERT_INT_WITHIN(50, clk_edges[1] * 2, clk_edges[0]); // DDR should cost half clk edges of SDR + + TEST_ESP_OK(spi_bus_remove_device(dev0)); + TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST)); +} +#endif diff --git a/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h index e1c44fef8d4..250ad57f826 100644 --- a/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32p4/include/hal/spi_ll.h @@ -1068,6 +1068,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen) hw->user.usr_addr = bitlen ? 1 : 0; } +/** + * Set the DDR mode for the SPI. + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable DDR mode, false to disable. + */ +static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable) +{ + hw->misc.clk_data_dtr_en = enable; +} + /** * Set the address value in an intuitive way. * diff --git a/components/esp_hal_gpspi/esp32s2/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32s2/include/hal/spi_ll.h index d7ab3ef5764..529a6138a6a 100644 --- a/components/esp_hal_gpspi/esp32s2/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32s2/include/hal/spi_ll.h @@ -1010,6 +1010,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen) hw->user.usr_addr = bitlen ? 1 : 0; } +/** + * Set the DDR mode for the SPI. + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable DDR mode, false to disable. + */ +static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable) +{ + hw->misc.clk_data_dtr_en = enable; +} + /** * Set the address value in an intuitive way. * diff --git a/components/esp_hal_gpspi/esp32s3/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32s3/include/hal/spi_ll.h index ffb7e300188..209e9f561ce 100644 --- a/components/esp_hal_gpspi/esp32s3/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32s3/include/hal/spi_ll.h @@ -1011,6 +1011,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen) hw->user.usr_addr = bitlen ? 1 : 0; } +/** + * Set the DDR mode for the SPI. + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable DDR mode, false to disable. + */ +static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable) +{ + hw->misc.clk_data_dtr_en = enable; +} + /** * Set the address value in an intuitive way. * diff --git a/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h b/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h index 8b75c3f0484..fa89d272182 100644 --- a/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h +++ b/components/esp_hal_gpspi/esp32s31/include/hal/spi_ll.h @@ -1064,6 +1064,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen) hw->user.usr_addr = bitlen ? 1 : 0; } +/** + * Set the DDR mode for the SPI. + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable DDR mode, false to disable. + */ +static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable) +{ + hw->misc.clk_data_dtr_en = enable; +} + /** * Set the address value in an intuitive way. * diff --git a/components/esp_hal_gpspi/include/hal/spi_hal.h b/components/esp_hal_gpspi/include/hal/spi_hal.h index a6f457e7f3e..032600d73bd 100644 --- a/components/esp_hal_gpspi/include/hal/spi_hal.h +++ b/components/esp_hal_gpspi/include/hal/spi_hal.h @@ -53,6 +53,7 @@ typedef struct { * Left 0 if not known. */ bool use_gpio; ///< True if the GPIO matrix is used, otherwise false + bool use_ddr_clk; ///< Whether to use DDR mode for clock } spi_hal_timing_param_t; /** @@ -70,6 +71,7 @@ typedef struct { int timing_dummy; ///< Extra dummy needed to compensate the timing int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing spi_sampling_point_t rx_sample_point;///< Sample data follow standard SPI timing in master mode + bool use_ddr_clk; ///< Whether to use DDR mode for clock } spi_hal_timing_conf_t; /** diff --git a/components/esp_hal_gpspi/spi_hal_iram.c b/components/esp_hal_gpspi/spi_hal_iram.c index 10386c34cb6..ce996d69b4c 100644 --- a/components/esp_hal_gpspi/spi_hal_iram.c +++ b/components/esp_hal_gpspi/spi_hal_iram.c @@ -37,13 +37,18 @@ void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *de spi_ll_master_set_cs_setup(hw, dev->cs_setup); spi_ll_master_set_cs_hold(hw, dev->cs_hold); spi_ll_master_select_cs(hw, dev->cs_pin_id); +#if SOC_SPI_SUPPORT_DDR_CLOCK + spi_ll_enable_ddr_mode(hw, dev->timing_conf.use_ddr_clk); +#endif } esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, spi_hal_timing_conf_t *timing_conf) { spi_ll_clock_val_t reg_val; int dummy, miso_delay; - int eff_clk_n = spi_ll_master_cal_clock(timing_param->clk_src_hz, timing_param->expected_freq, timing_param->duty_cycle, ®_val); + // Hardware halves the output frequency when DDR is enabled; calculate with 2x so the effective rate stays the same. + uint32_t expected_freq = timing_param->use_ddr_clk ? timing_param->expected_freq * 2 : timing_param->expected_freq; + int eff_clk_n = spi_ll_master_cal_clock(timing_param->clk_src_hz, expected_freq, timing_param->duty_cycle, ®_val); //When the speed is too fast, we may need to use dummy cycles to compensate the reading. //But these don't work for full-duplex connections. @@ -63,8 +68,10 @@ esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, spi if (timing_conf) { timing_conf->clock_reg = reg_val; + timing_conf->use_ddr_clk = timing_param->use_ddr_clk; timing_conf->expect_freq = timing_param->expected_freq; - timing_conf->real_freq = eff_clk_n; + // eff_clk_n was calculated for 2x in DDR; report the effective data rate to the user. + timing_conf->real_freq = timing_param->use_ddr_clk ? eff_clk_n / 2 : eff_clk_n; timing_conf->timing_dummy = dummy; timing_conf->timing_miso_delay = miso_delay; } diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 1f58eb3ff43..463e38acfa2 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1343,6 +1343,10 @@ config SOC_SPI_SUPPORT_OCT bool default y +config SOC_SPI_SUPPORT_DDR_CLOCK + bool + default y + config SOC_SPIRAM_XIP_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index ff426d0dd10..53cbb225a61 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -508,6 +508,7 @@ #define SOC_SPI_SUPPORT_SLEEP_RETENTION 1 #define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1 #define SOC_SPI_SUPPORT_OCT 1 +#define SOC_SPI_SUPPORT_DDR_CLOCK 1 /*-------------------------- SPIRAM CAPS ----------------------------------------*/ #define SOC_SPIRAM_XIP_SUPPORTED 1 diff --git a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in index be5ca42c12d..84584bd1cd6 100644 --- a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in @@ -571,6 +571,10 @@ config SOC_SPI_SUPPORT_OCT bool default y +config SOC_SPI_SUPPORT_DDR_CLOCK + bool + default y + config SOC_MEMSPI_SUPPORT_CONTROL_DUMMY_OUT bool default y diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 0de0b7bb449..2b1e8874282 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -262,6 +262,7 @@ #define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1 #define SOC_SPI_HD_BOTH_INOUT_SUPPORTED 1 //Support enabling MOSI and MISO phases together under Halfduplex mode #define SOC_SPI_SUPPORT_OCT 1 +#define SOC_SPI_SUPPORT_DDR_CLOCK 1 // Peripheral supports output given level during its "dummy phase" // Only SPI1 supports this feature diff --git a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in index 38044dfe746..ed3da70ea48 100644 --- a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in @@ -675,6 +675,10 @@ config SOC_SPI_SUPPORT_OCT bool default y +config SOC_SPI_SUPPORT_DDR_CLOCK + bool + default y + config SOC_SPIRAM_SUPPORTED bool default y diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 00321c9cf39..dc142aea64f 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -282,6 +282,7 @@ #define SOC_SPI_MAXIMUM_BUFFER_SIZE 64 #define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1 #define SOC_SPI_SUPPORT_OCT 1 +#define SOC_SPI_SUPPORT_DDR_CLOCK 1 /*-------------------------- SPIRAM CAPS ----------------------------------------*/ #define SOC_SPIRAM_SUPPORTED 1 diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index dce605a45ec..e342f87c205 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -895,6 +895,10 @@ config SOC_SPI_SUPPORT_OCT bool default y +config SOC_SPI_SUPPORT_DDR_CLOCK + bool + default y + config SOC_SPIRAM_XIP_SUPPORTED bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 29466ea4116..00d56d50055 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -333,6 +333,7 @@ #define SOC_SPI_SUPPORT_SLEEP_RETENTION 1 #define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1 #define SOC_SPI_SUPPORT_OCT 1 +#define SOC_SPI_SUPPORT_DDR_CLOCK 1 /*-------------------------- SPIRAM CAPS ----------------------------------------*/ #define SOC_SPIRAM_XIP_SUPPORTED 1 diff --git a/docs/en/api-reference/peripherals/spi_master.rst b/docs/en/api-reference/peripherals/spi_master.rst index 0a1a932971b..3576667f006 100644 --- a/docs/en/api-reference/peripherals/spi_master.rst +++ b/docs/en/api-reference/peripherals/spi_master.rst @@ -264,6 +264,13 @@ Supported line modes for {IDF_TARGET_NAME} are listed as follows, to make use of SPI_TRANS_MULTILINE_CMD - SPICOMMON_BUSFLAG_OCTAL +.. only:: SOC_SPI_SUPPORT_DDR_CLOCK + + DDR Clock + ^^^^^^^^^ + + Set :c:macro:`SPI_TRANS_DDRCLK` in :cpp:member:`spi_transaction_t::flags` to use double data rate mode for the current transaction. In this mode, the command, address, and data phases are sent/sampled on both the rising and falling edges of the clock. If this flag is not set, the transaction uses the traditional single data rate mode. DDRCLK mode is supported in 2/4/8 line modes as well. + Command and Address Phases ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/zh_CN/api-reference/peripherals/spi_master.rst b/docs/zh_CN/api-reference/peripherals/spi_master.rst index 8719f4e2d3f..be9d3c8e6b6 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_master.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_master.rst @@ -264,6 +264,13 @@ SPI 总线传输事务由五个阶段构成,详见下表(任意阶段均可 SPI_TRANS_MULTILINE_CMD - SPICOMMON_BUSFLAG_OCTAL +.. only:: SOC_SPI_SUPPORT_DDR_CLOCK + + DDR 时钟 + ^^^^^^^^ + + 在 :cpp:member:`spi_transaction_t::flags` 中设置 :c:macro:`SPI_TRANS_DDRCLK`,可使当前传输事务使用时钟双边沿数据模式,该模式下, cmd/addr/data 段都将在时钟的上升沿和下降沿都进行传输,没有设置该标志时,传输将回到传统单边沿数据模式。DDRCLK 模式在 2/4/8 线模式下同样支持。 + 命令阶段和地址阶段 ^^^^^^^^^^^^^^^^^^^^^^^^^^