mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
Merge branch 'contrib/github_pr_17642_v6.0' into 'release/v6.0'
feat(sdmmc): support multi-block read/writes (GitHub PR) (v6.0) See merge request espressif/esp-idf!46776
This commit is contained in:
@@ -48,6 +48,7 @@ extern "C" {
|
||||
.input_delay_phase = SDMMC_DELAY_PHASE_0, \
|
||||
.set_input_delay = &sdmmc_host_set_input_delay, \
|
||||
.set_input_delayline = &sdmmc_host_set_input_delayline, \
|
||||
.unaligned_multi_block_rw_max_chunk_size = 16, \
|
||||
.dma_aligned_buffer = NULL, \
|
||||
.pwr_ctrl_handle = NULL, \
|
||||
.check_buffer_alignment = &sdmmc_host_check_buffer_alignment, \
|
||||
|
||||
+18
-1
@@ -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
|
||||
*/
|
||||
@@ -56,6 +56,23 @@ void sdmmc_test_rw_with_offset(sdmmc_card_t* card);
|
||||
*/
|
||||
void sdmmc_test_rw_highprio_task(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* @brief Test multi-block read/write with unaligned buffers
|
||||
*
|
||||
* This function verifies that multi-block chunked transfers work correctly
|
||||
* when the source/destination buffers are not DMA-aligned. It exercises:
|
||||
* - Multi-block unaligned writes and reads with more blocks than the configured chunk size
|
||||
* - The dma_aligned_buffer reuse path (when card->host.dma_aligned_buffer is pre-allocated)
|
||||
*
|
||||
* This test function works both with SDMMC and SDSPI hosts.
|
||||
*
|
||||
* @param card Pointer to the card object, must be initialized before calling this function.
|
||||
* @param chunk_size Maximum number of blocks to transfer at once when using an
|
||||
* unaligned bounce buffer. This value is written to
|
||||
* card->host.unaligned_multi_block_rw_max_chunk_size before the test runs.
|
||||
*/
|
||||
void sdmmc_test_rw_unaligned_buffer_multiblock(sdmmc_card_t* card, size_t chunk_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
+75
-1
@@ -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
|
||||
*/
|
||||
@@ -221,3 +221,77 @@ void sdmmc_test_rw_highprio_task(sdmmc_card_t* card)
|
||||
vSemaphoreDelete(args.stop);
|
||||
vSemaphoreDelete(args.done);
|
||||
}
|
||||
|
||||
void sdmmc_test_rw_unaligned_buffer_multiblock(sdmmc_card_t* card, size_t chunk_size)
|
||||
{
|
||||
const size_t block_size = card->csd.sector_size;
|
||||
/* Use 10 blocks so that with chunk_size=4,
|
||||
* the transfer is split into chunks: 4 + 4 + 2 blocks */
|
||||
const size_t block_count = 10;
|
||||
const size_t buffer_size = block_size * block_count;
|
||||
const size_t extra = 4;
|
||||
const size_t total_alloc = buffer_size + extra;
|
||||
|
||||
/* Apply the chunk_size to the card's host config */
|
||||
card->host.unaligned_multi_block_rw_max_chunk_size = chunk_size;
|
||||
|
||||
uint8_t *buffer = heap_caps_malloc(total_alloc, MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(buffer);
|
||||
|
||||
printf("Testing multi-block unaligned R/W: %d blocks, chunk_size=%d\n",
|
||||
(int)block_count, (int)chunk_size);
|
||||
|
||||
/* Test A: Multi-block unaligned write, then unaligned read.
|
||||
* The +1 offset makes the buffer unaligned, forcing the bounce-buffer
|
||||
* chunking path in sdmmc_write_sectors / sdmmc_read_sectors. */
|
||||
const uint32_t seed_a = 0x12345678;
|
||||
fill_buffer(seed_a, buffer + 1, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, buffer + 1, 0, block_count));
|
||||
memset(buffer, 0xcc, total_alloc);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, buffer + 1, 0, block_count));
|
||||
check_buffer(seed_a, buffer + 1, buffer_size / sizeof(uint32_t));
|
||||
|
||||
/* Test B: Aligned write, then unaligned read — verifies read chunking path. */
|
||||
const uint32_t seed_b = 0xdeadbeef;
|
||||
fill_buffer(seed_b, buffer, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, buffer, 0, block_count));
|
||||
memset(buffer, 0xcc, total_alloc);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, buffer + 1, 0, block_count));
|
||||
check_buffer(seed_b, buffer + 1, buffer_size / sizeof(uint32_t));
|
||||
|
||||
/* Test C: Unaligned write, then aligned read — verifies write chunking path. */
|
||||
const uint32_t seed_c = 0xcafebabe;
|
||||
fill_buffer(seed_c, buffer + 1, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, buffer + 1, 8, block_count));
|
||||
memset(buffer, 0xcc, total_alloc);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 8, block_count));
|
||||
check_buffer(seed_c, buffer, buffer_size / sizeof(uint32_t));
|
||||
|
||||
/* Test D: dma_aligned_buffer reuse path.
|
||||
* Pre-set card->host.dma_aligned_buffer so sdmmc_write/read_sectors
|
||||
* uses it instead of allocating a temporary buffer. */
|
||||
void *orig_dma_buf = card->host.dma_aligned_buffer;
|
||||
size_t chunk_blocks = chunk_size;
|
||||
if (chunk_blocks < block_count) {
|
||||
/* Allocate a DMA-capable buffer large enough for chunk_blocks */
|
||||
void *dma_buf = heap_caps_malloc(block_size * chunk_blocks, MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(dma_buf);
|
||||
card->host.dma_aligned_buffer = dma_buf;
|
||||
|
||||
printf("Testing dma_aligned_buffer reuse path (%d block buffer)\n", (int)chunk_blocks);
|
||||
|
||||
const uint32_t seed_d = 0xfeedface;
|
||||
fill_buffer(seed_d, buffer + 1, buffer_size / sizeof(uint32_t));
|
||||
TEST_ESP_OK(sdmmc_write_sectors(card, buffer + 1, 0, block_count));
|
||||
memset(buffer, 0xcc, total_alloc);
|
||||
TEST_ESP_OK(sdmmc_read_sectors(card, buffer + 1, 0, block_count));
|
||||
check_buffer(seed_d, buffer + 1, buffer_size / sizeof(uint32_t));
|
||||
|
||||
card->host.dma_aligned_buffer = orig_dma_buf;
|
||||
free(dma_buf);
|
||||
} else {
|
||||
printf("Skipping dma_aligned_buffer reuse test (chunk_size >= block_count)\n");
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ typedef int sdspi_dev_handle_t;
|
||||
.input_delay_phase = SDMMC_DELAY_PHASE_0, \
|
||||
.set_input_delay = NULL, \
|
||||
.set_input_delayline = NULL, \
|
||||
.unaligned_multi_block_rw_max_chunk_size = 16, \
|
||||
.dma_aligned_buffer = NULL, \
|
||||
.pwr_ctrl_handle = NULL, \
|
||||
.check_buffer_alignment = sdspi_host_check_buffer_alignment, \
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2016-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
|
||||
@@ -225,7 +225,27 @@ typedef struct {
|
||||
sdmmc_delay_phase_t input_delay_phase; /*!< input delay phase, this will only take into effect when the host works in SDMMC_FREQ_HIGHSPEED or SDMMC_FREQ_52M. Driver will print out how long the delay is*/
|
||||
esp_err_t (*set_input_delay)(int slot, sdmmc_delay_phase_t delay_phase); /*!< set input delay phase */
|
||||
esp_err_t (*set_input_delayline)(int slot, sdmmc_delay_line_t delay_line); /*!< set input delay line */
|
||||
void* dma_aligned_buffer; /*!< Leave it NULL. Reserved for cache aligned buffers for SDIO mode */
|
||||
/**
|
||||
* @brief Maximum number of blocks to read/write at once when using an unaligned buffer.
|
||||
*
|
||||
* When a multi-block read/write is requested with an unaligned buffer, the driver
|
||||
* splits the transfer into chunks of this many blocks. Set to 0 to use the default
|
||||
* value of 1 (single-block transfers). Higher values improve throughput but require
|
||||
* a larger DMA-capable temporary buffer.
|
||||
*/
|
||||
size_t unaligned_multi_block_rw_max_chunk_size;
|
||||
/**
|
||||
* @brief Cache aligned buffer for multi-block RW and IO commands
|
||||
*
|
||||
* Use cases:
|
||||
* - Temporary buffer for multi-block read/write transactions to/from unaligned buffers.
|
||||
* Allocate with DMA capable memory, size should be an integer multiple of your card's sector size.
|
||||
* The number of blocks transferred per chunk is controlled by
|
||||
* `unaligned_multi_block_rw_max_chunk_size`.
|
||||
* - Cache aligned buffer for IO commands in SDIO mode.
|
||||
* If you allocate manually, make sure it is at least SDMMC_IO_BLOCK_SIZE bytes large.
|
||||
*/
|
||||
void* dma_aligned_buffer;
|
||||
sd_pwr_ctrl_handle_t pwr_ctrl_handle; /*!< Power control handle */
|
||||
bool (*check_buffer_alignment)(int slot, const void *buf, size_t size); /*!< Check if buffer meets alignment requirements */
|
||||
esp_err_t (*is_slot_set_to_uhs1)(int slot, bool *is_uhs1); /*!< host slot is set to uhs1 or not*/
|
||||
|
||||
+114
-34
@@ -1,15 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include <sys/param.h> // for MIN/MAX
|
||||
#include "esp_private/sdmmc_common.h"
|
||||
|
||||
static const char* TAG = "sdmmc_cmd";
|
||||
|
||||
/**
|
||||
* @brief Get the effective chunk size for unaligned multi-block transfers.
|
||||
*
|
||||
* Returns the configured value, or 1 (single-block) when the field is left
|
||||
* at its zero-initialised default.
|
||||
*/
|
||||
static inline size_t get_chunk_size(const sdmmc_card_t *card)
|
||||
{
|
||||
size_t chunk_size = card->host.unaligned_multi_block_rw_max_chunk_size;
|
||||
return (chunk_size != 0) ? chunk_size : 1;
|
||||
}
|
||||
|
||||
static esp_err_t allocate_dma_buf(size_t* actual_size, size_t block_size, void **buf)
|
||||
{
|
||||
if (actual_size == NULL || buf == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
size_t size = *actual_size;
|
||||
do {
|
||||
if (*actual_size < block_size) {
|
||||
ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
*buf = heap_caps_malloc(*actual_size, MALLOC_CAP_DMA);
|
||||
if (!*buf) {
|
||||
*actual_size /= 2;
|
||||
ESP_LOGD(TAG, "%s: required space for buffer of size %d not available, trying again with size %zu", __func__, size, *actual_size);
|
||||
}
|
||||
} while (!*buf);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
|
||||
{
|
||||
@@ -463,31 +496,54 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
|
||||
err = sdmmc_write_sectors_dma(card, src, start_block, block_count, block_size * block_count);
|
||||
} else {
|
||||
// SDMMC peripheral needs DMA-capable buffers. Split the write into
|
||||
// separate single block writes, if needed, and allocate a temporary
|
||||
// separate (multi) block writes, if needed, and allocate a temporary
|
||||
// DMA-capable buffer.
|
||||
void *tmp_buf = NULL;
|
||||
size_t actual_size = 0;
|
||||
// We don't want to force the allocation into SPIRAM, the allocator
|
||||
// will decide based on the buffer size and memory availability.
|
||||
tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
|
||||
if (!tmp_buf) {
|
||||
ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM);
|
||||
return ESP_ERR_NO_MEM;
|
||||
size_t chunk_size = get_chunk_size(card);
|
||||
size_t blocks_per_write = MIN(chunk_size, block_count);
|
||||
|
||||
// prefer using DMA aligned buffer if available over allocating local temporary buffer
|
||||
bool use_dma_aligned_buffer = (card->host.dma_aligned_buffer != NULL);
|
||||
void* buf = card->host.dma_aligned_buffer;
|
||||
|
||||
size_t actual_size = block_size * blocks_per_write;
|
||||
if (!use_dma_aligned_buffer) {
|
||||
// Allocate a temporary DMA-capable buffer.
|
||||
// We don't want to force the allocation into SPIRAM, the allocator
|
||||
// will decide based on the buffer size and memory availability.
|
||||
// We start with the largest buffer possible to minimize the number of read iterations, but if that fails, we try smaller sizes down to a single block.
|
||||
err = allocate_dma_buf(&actual_size, block_size, &buf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
blocks_per_write = actual_size / block_size;
|
||||
} else {
|
||||
// Check that the provided dma_aligned_buffer is large enough
|
||||
actual_size = heap_caps_get_allocated_size(buf);
|
||||
blocks_per_write = actual_size / card->csd.sector_size;
|
||||
size_t chunk_size = get_chunk_size(card);
|
||||
blocks_per_write = MIN(chunk_size, blocks_per_write);
|
||||
if (blocks_per_write == 0) {
|
||||
ESP_LOGE(TAG, "%s: buffer smaller than sector size: buf=%d, sector=%d", __func__, actual_size, card->csd.sector_size);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
}
|
||||
actual_size = heap_caps_get_allocated_size(tmp_buf);
|
||||
|
||||
const uint8_t* cur_src = (const uint8_t*) src;
|
||||
for (size_t i = 0; i < block_count; ++i) {
|
||||
memcpy(tmp_buf, cur_src, block_size);
|
||||
cur_src += block_size;
|
||||
err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1, actual_size);
|
||||
for (size_t i = 0; i < block_count; i += blocks_per_write) {
|
||||
// make sure not to write more than the remaining blocks, i.e. block_count - i
|
||||
blocks_per_write = MIN(blocks_per_write, (block_count - i));
|
||||
memcpy(buf, cur_src, block_size * blocks_per_write);
|
||||
cur_src += block_size * blocks_per_write;
|
||||
err = sdmmc_write_sectors_dma(card, buf, start_block + i, blocks_per_write, actual_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
|
||||
__func__, err, start_block, i);
|
||||
ESP_LOGD(TAG, "%s: error 0x%x writing blocks %d+[%d..%d]",
|
||||
__func__, err, start_block, i, i + blocks_per_write - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(tmp_buf);
|
||||
if (!use_dma_aligned_buffer) {
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -601,33 +657,57 @@ esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||
err = sdmmc_read_sectors_dma(card, dst, start_block, block_count, block_size * block_count);
|
||||
} else {
|
||||
// SDMMC peripheral needs DMA-capable buffers. Split the read into
|
||||
// separate single block reads, if needed, and allocate a temporary
|
||||
// separate (multi) block reads, if needed, and allocate a temporary
|
||||
// DMA-capable buffer.
|
||||
void *tmp_buf = NULL;
|
||||
size_t actual_size = 0;
|
||||
tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
|
||||
if (!tmp_buf) {
|
||||
ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM);
|
||||
return ESP_ERR_NO_MEM;
|
||||
size_t chunk_size = get_chunk_size(card);
|
||||
size_t blocks_per_read = MIN(chunk_size, block_count);
|
||||
|
||||
// prefer using DMA aligned buffer if available over allocating local temporary buffer
|
||||
bool use_dma_aligned_buffer = (card->host.dma_aligned_buffer != NULL);
|
||||
void* buf = card->host.dma_aligned_buffer;
|
||||
|
||||
size_t actual_size = block_size * blocks_per_read;
|
||||
if (!use_dma_aligned_buffer) {
|
||||
// Allocate a temporary DMA-capable buffer.
|
||||
// We don't want to force the allocation into SPIRAM, the allocator
|
||||
// will decide based on the buffer size and memory availability.
|
||||
// We start with the largest buffer possible to minimize the number of read iterations, but if that fails, we try smaller sizes down to a single block.
|
||||
err = allocate_dma_buf(&actual_size, block_size, &buf);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
blocks_per_read = actual_size / block_size;
|
||||
} else {
|
||||
// Check that the provided dma_aligned_buffer is large enough
|
||||
actual_size = heap_caps_get_allocated_size(buf);
|
||||
blocks_per_read = actual_size / card->csd.sector_size;
|
||||
size_t chunk_size = get_chunk_size(card);
|
||||
blocks_per_read = MIN(chunk_size, blocks_per_read);
|
||||
if (blocks_per_read == 0) {
|
||||
ESP_LOGE(TAG, "%s: buffer smaller than sector size: buf=%d, sector=%d", __func__, actual_size, card->csd.sector_size);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
}
|
||||
actual_size = heap_caps_get_allocated_size(tmp_buf);
|
||||
|
||||
uint8_t* cur_dst = (uint8_t*) dst;
|
||||
for (size_t i = 0; i < block_count; ++i) {
|
||||
err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1, actual_size);
|
||||
for (size_t i = 0; i < block_count; i += blocks_per_read) {
|
||||
// make sure not to read more than the remaining blocks, i.e. block_count - i
|
||||
blocks_per_read = MIN(blocks_per_read, (block_count - i));
|
||||
err = sdmmc_read_sectors_dma(card, buf, start_block + i, blocks_per_read, actual_size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
|
||||
__func__, err, start_block, i);
|
||||
ESP_LOGE(TAG, "%s: error 0x%x reading blocks %d+[%d..%d]",
|
||||
__func__, err, start_block, i, i + blocks_per_read - 1);
|
||||
break;
|
||||
}
|
||||
memcpy(cur_dst, tmp_buf, block_size);
|
||||
cur_dst += block_size;
|
||||
memcpy(cur_dst, buf, block_size * blocks_per_read);
|
||||
cur_dst += block_size * blocks_per_read;
|
||||
}
|
||||
if (!use_dma_aligned_buffer) {
|
||||
free(buf);
|
||||
}
|
||||
free(tmp_buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
||||
size_t start_block, size_t block_count, size_t buffer_len)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
idf_component_register(SRCS "test_sdmmc_app.c"
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_blockdev unity sdmmc esp_driver_sdmmc sdmmc_tests
|
||||
PRIV_REQUIRES esp_blockdev unity sdmmc esp_driver_sdmmc sdmmc_tests common_test_flows
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "sd_protocol_defs.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "sdmmc_test_begin_end_sd.h"
|
||||
#include "sdmmc_test_rw_common.h"
|
||||
#include "esp_blockdev.h"
|
||||
|
||||
TEST_GROUP(sdmmc);
|
||||
@@ -69,9 +70,22 @@ TEST(sdmmc, test_bdl_interface)
|
||||
sdmmc_test_sd_end(&card);
|
||||
}
|
||||
|
||||
TEST(sdmmc, test_multiblock_unaligned_rw)
|
||||
{
|
||||
sdmmc_card_t card;
|
||||
int slot = 1;
|
||||
int width = 1;
|
||||
int freq_khz = SDMMC_FREQ_DEFAULT;
|
||||
sdmmc_test_sd_skip_if_board_incompatible(slot, width, freq_khz, 0, 0);
|
||||
sdmmc_test_sd_begin(slot, width, freq_khz, 0, &card);
|
||||
sdmmc_test_rw_unaligned_buffer_multiblock(&card, 4);
|
||||
sdmmc_test_sd_end(&card);
|
||||
}
|
||||
|
||||
TEST_GROUP_RUNNER(sdmmc)
|
||||
{
|
||||
RUN_TEST_CASE(sdmmc, test_bdl_interface)
|
||||
RUN_TEST_CASE(sdmmc, test_multiblock_unaligned_rw)
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
@@ -6,6 +6,7 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.sdcard
|
||||
@idf_parametrize('config', ['default'], indirect=['config'])
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_sdmmc_extra(dut: Dut) -> None:
|
||||
dut.expect_unity_test_output()
|
||||
|
||||
Reference in New Issue
Block a user