espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.39k stars 7.24k forks source link

When using the spi method to drive the SD card, the file cannot be created in the specified directory. (IDFGH-11028) #12210

Open BurnerK opened 1 year ago

BurnerK commented 1 year ago

Answers checklist.

IDF version.

v5.0.2

Operating System used.

Windows

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

CMD

Development Kit.

ESP32-C2

Power Supply used.

External 5V

What is the expected behavior?

Using fopen("/sdcard/path/123.txt"), a folder named path is normally created on the sdcard and a file named 123.txt is created in it.

What is the actual behavior?

It actually crashes and reboots right when it gets to that point.

Steps to reproduce.

    esp_err_t ret;

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };

    // sdmmc_card_t *card;
    // const char mount_point[] = MOUNT_POINT;
    log_i("Initializing SD card");

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.
    log_i("Using SPI peripheral");

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    spi_bus_config_t bus_cfg = {
        .mosi_io_num = PIN_NUM_MOSI,
        .miso_io_num = PIN_NUM_MISO,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4000,
    };
    ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
    if (ret != ESP_OK) {
        log_e("Failed to initialize bus.");
        return;
    }    

    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = host.slot;

    log_i("Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(tfcard.mount_point, &host, &slot_config, &mount_config, &tfcard.card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            log_e("Failed to mount filesystem. "
                     "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            log_e("Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        esp_restart();  //未检测到SD卡重启
        return;
    }
    log_i("Filesystem mounted");

    FILE *f = fopen("/sdcard/path/123.txt", "w");
    if (f == NULL)
    {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }
    fprintf(f, "Hello \n");
    fclose(f);
    ESP_LOGI(TAG, "File written");

Debug Logs.

No response

More Information.

No response

igrr commented 1 year ago

Does the directory path already exist on the SD card when you are calling fopen("/sdcard/path/123.txt", "w")?

BurnerK commented 1 year ago

Does the directory path already exist on the SD card when you are calling fopen("/sdcard/path/123.txt", "w")?

no exist

igrr commented 1 year ago

In that case, the behavior makes sense — it works the same way if you use fopen on Linux or Windows with usual filesystems (FAT32, exFAT, ext4,...). fopen doesn't automatically create subdirectories. You need to call mkdir yourself to create it.


Note that the behavior is different with SPIFFS filesystem. SPIFFS doesn't have a concept of directories. When you are opening the file /spiffs/path/123.txt, then it simply creates a file with the name /path/123.txt — forward slashes are part of the filename. So if you were to do the same thing with SPIFFS, it would work without creating a subdirectory first. In fact, mkdir with SPIFFS always returns -1 and sets errno = ENOTSUP.

BurnerK commented 1 year ago

In that case, the behavior makes sense — it works the same way if you use fopen on Linux or Windows with usual filesystems (FAT32, exFAT, ext4,...). fopen doesn't automatically create subdirectories. You need to call mkdir yourself to create it.

Note that the behavior is different with SPIFFS filesystem. SPIFFS doesn't have a concept of directories. When you are opening the file /spiffs/path/123.txt, then it simply creates a file with the name /path/123.txt — forward slashes are part of the filename. So if you were to do the same thing with SPIFFS, it would work without creating a subdirectory first. In fact, mkdir with SPIFFS always returns -1 and sets errno = ENOTSUP.

Does this mean I can't create a directory in the SD card? In any case?

adokitkat commented 1 year ago

If you're using FATFS then you can use mkdir function from C stdlib:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

...

struct stat st = {0};

if (stat("/some/directory", &st) == -1) {
    mkdir("/some/directory", S_IRWXU); // S_IRWXU == 0700 permission
}
BurnerK commented 1 year ago

If you're using FATFS then you can use mkdir function from C stdlib:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

...

struct stat st = {0};

if (stat("/some/directory", &st) == -1) {
    mkdir("/some/directory", S_IRWXU); // S_IRWXU == 0700 permission
}

How do I use FATFS, is it by enabling SDMMC to drive the SD card?

adokitkat commented 1 year ago

It seems like you're already using it. You are using esp_vfs_fat_sdspi_mount function, which mounts your SD connected through SPI as a FAT file system. It doesn't matter if you're going to connect to the SD card via SPI or SDMMC. You just need to have your SD card formatted as FAT12/FAT16/FAT32 - if not, you can do it on PC or you can leave format_if_mount_failed set to true in this portion of your code (this probably means your SD card is already properly formatted though):

esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };
BurnerK commented 1 year ago

It seems like you're already using it. You are using esp_vfs_fat_sdspi_mount function, which mounts your SD connected through SPI as a FAT file system. It doesn't matter if you're going to connect to the SD card via SPI or SDMMC. You just need to have your SD card formatted as FAT12/FAT16/FAT32 - if not, you can do it on PC or you can leave format_if_mount_failed set to true in this portion of your code (this probably means your SD card is already properly formatted though):

esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };
struct stat st = { 0 };
    if (stat("sdcard/path", &st) == -1)
    {
        int ret = mkdir("sdcard/path", S_IRWXU);
        printf("mkdir ret: %d!!!!\n", ret);

    } else
    {
        printf("mkdir fail!!!!\n");
    }

我使用这种方式创建文件夹,但是mkdir()函数返回值为-1。 I create the folder this way, but the mkdir() function returns -1.