|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Unlicense OR CC0-1.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <stdlib.h> |
| 8 | +#include <stdio.h> |
| 9 | +#include <string.h> |
| 10 | + |
| 11 | +#include "esp_system.h" |
| 12 | +#include "soc/spi_pins.h" |
| 13 | +#include "esp_vfs_fat_nand.h" |
| 14 | +#include "esp_nand_blockdev.h" |
| 15 | + |
| 16 | +#define EXAMPLE_FLASH_FREQ_KHZ 40000 |
| 17 | + |
| 18 | +static const char *TAG = "example_bdl"; |
| 19 | + |
| 20 | +// Pin mapping |
| 21 | +// ESP32 (VSPI) |
| 22 | +#ifdef CONFIG_IDF_TARGET_ESP32 |
| 23 | +#define HOST_ID SPI3_HOST |
| 24 | +#define PIN_MOSI SPI3_IOMUX_PIN_NUM_MOSI |
| 25 | +#define PIN_MISO SPI3_IOMUX_PIN_NUM_MISO |
| 26 | +#define PIN_CLK SPI3_IOMUX_PIN_NUM_CLK |
| 27 | +#define PIN_CS SPI3_IOMUX_PIN_NUM_CS |
| 28 | +#define PIN_WP SPI3_IOMUX_PIN_NUM_WP |
| 29 | +#define PIN_HD SPI3_IOMUX_PIN_NUM_HD |
| 30 | +#define SPI_DMA_CHAN SPI_DMA_CH_AUTO |
| 31 | +#else // Other chips (SPI2/HSPI) |
| 32 | +#define HOST_ID SPI2_HOST |
| 33 | +#define PIN_MOSI SPI2_IOMUX_PIN_NUM_MOSI |
| 34 | +#define PIN_MISO SPI2_IOMUX_PIN_NUM_MISO |
| 35 | +#define PIN_CLK SPI2_IOMUX_PIN_NUM_CLK |
| 36 | +#define PIN_CS SPI2_IOMUX_PIN_NUM_CS |
| 37 | +#define PIN_WP SPI2_IOMUX_PIN_NUM_WP |
| 38 | +#define PIN_HD SPI2_IOMUX_PIN_NUM_HD |
| 39 | +#define SPI_DMA_CHAN SPI_DMA_CH_AUTO |
| 40 | +#endif |
| 41 | + |
| 42 | +// Mount path for the partition |
| 43 | +const char *base_path = "/nandflash_bdl_example"; |
| 44 | + |
| 45 | +void app_main(void) |
| 46 | +{ |
| 47 | + esp_err_t ret; |
| 48 | + spi_device_handle_t spi = NULL; |
| 49 | + esp_blockdev_handle_t wl_bdl = NULL; |
| 50 | + |
| 51 | + // Initialize SPI bus |
| 52 | + const spi_bus_config_t bus_config = { |
| 53 | + .mosi_io_num = PIN_MOSI, |
| 54 | + .miso_io_num = PIN_MISO, |
| 55 | + .sclk_io_num = PIN_CLK, |
| 56 | + .quadhd_io_num = PIN_HD, |
| 57 | + .quadwp_io_num = PIN_WP, |
| 58 | + .max_transfer_sz = 4096 * 2, |
| 59 | + }; |
| 60 | + |
| 61 | + ESP_LOGI(TAG, "DMA CHANNEL: %d", SPI_DMA_CHAN); |
| 62 | + ESP_ERROR_CHECK(spi_bus_initialize(HOST_ID, &bus_config, SPI_DMA_CHAN)); |
| 63 | + |
| 64 | + const uint32_t spi_flags = SPI_DEVICE_HALFDUPLEX; |
| 65 | + spi_device_interface_config_t devcfg = { |
| 66 | + .clock_speed_hz = EXAMPLE_FLASH_FREQ_KHZ * 1000, |
| 67 | + .mode = 0, |
| 68 | + .spics_io_num = PIN_CS, |
| 69 | + .queue_size = 10, |
| 70 | + .flags = spi_flags, |
| 71 | + }; |
| 72 | + |
| 73 | + ESP_ERROR_CHECK(spi_bus_add_device(HOST_ID, &devcfg, &spi)); |
| 74 | + |
| 75 | + // Create Flash Block Device Layer (this will initialize the device) |
| 76 | + spi_nand_flash_config_t config = { |
| 77 | + .device_handle = spi, |
| 78 | + .io_mode = SPI_NAND_IO_MODE_SIO, |
| 79 | + .flags = spi_flags, |
| 80 | + .gc_factor = 4, // Wear leveling GC factor |
| 81 | + }; |
| 82 | + |
| 83 | + ret = spi_nand_flash_init_with_layers(&config, &wl_bdl); |
| 84 | + if (ret != ESP_OK) { |
| 85 | + ESP_LOGE(TAG, "Failed to create BDL: %s", esp_err_to_name(ret)); |
| 86 | + goto cleanup; |
| 87 | + } |
| 88 | + |
| 89 | + // Mount FATFS using BDL |
| 90 | + esp_vfs_fat_mount_config_t mount_config = { |
| 91 | + .max_files = 4, |
| 92 | +#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED |
| 93 | + .format_if_mount_failed = true, |
| 94 | +#else |
| 95 | + .format_if_mount_failed = false, |
| 96 | +#endif |
| 97 | + .allocation_unit_size = 16 * 1024 |
| 98 | + }; |
| 99 | + |
| 100 | + ret = esp_vfs_fat_nand_mount_bdl(base_path, wl_bdl, &mount_config); |
| 101 | + if (ret != ESP_OK) { |
| 102 | + if (ret == ESP_FAIL) { |
| 103 | + ESP_LOGE(TAG, "Failed to mount filesystem. " |
| 104 | + "If you want the flash memory to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); |
| 105 | + } |
| 106 | + goto cleanup; |
| 107 | + } |
| 108 | + |
| 109 | + // Print FAT FS size information |
| 110 | + uint64_t bytes_total, bytes_free; |
| 111 | + esp_vfs_fat_info(base_path, &bytes_total, &bytes_free); |
| 112 | + ESP_LOGI(TAG, "FAT FS: %" PRIu64 " kB total, %" PRIu64 " kB free", bytes_total / 1024, bytes_free / 1024); |
| 113 | + |
| 114 | + // Create a file in FAT FS |
| 115 | + ESP_LOGI(TAG, "Opening file"); |
| 116 | + FILE *f = fopen("/nandflash/hello_bdl.txt", "wb"); |
| 117 | + if (f == NULL) { |
| 118 | + ESP_LOGE(TAG, "Failed to open file for writing"); |
| 119 | + goto cleanup_fs; |
| 120 | + } |
| 121 | + fprintf(f, "Written using ESP-IDF %s with BDL API\n", esp_get_idf_version()); |
| 122 | + fclose(f); |
| 123 | + ESP_LOGI(TAG, "File written"); |
| 124 | + |
| 125 | + // Open file for reading |
| 126 | + ESP_LOGI(TAG, "Reading file"); |
| 127 | + f = fopen("/nandflash/hello_bdl.txt", "rb"); |
| 128 | + if (f == NULL) { |
| 129 | + ESP_LOGE(TAG, "Failed to open file for reading"); |
| 130 | + goto cleanup_fs; |
| 131 | + } |
| 132 | + char line[128]; |
| 133 | + fgets(line, sizeof(line), f); |
| 134 | + fclose(f); |
| 135 | + // strip newline |
| 136 | + char *pos = strchr(line, '\n'); |
| 137 | + if (pos) { |
| 138 | + *pos = '\0'; |
| 139 | + } |
| 140 | + ESP_LOGI(TAG, "Read from file: '%s'", line); |
| 141 | + |
| 142 | + esp_vfs_fat_info(base_path, &bytes_total, &bytes_free); |
| 143 | + ESP_LOGI(TAG, "FAT FS: %" PRIu64 " kB total, %" PRIu64 " kB free", bytes_total / 1024, bytes_free / 1024); |
| 144 | + |
| 145 | +cleanup_fs: |
| 146 | + // Unmount FATFS |
| 147 | + esp_vfs_fat_nand_unmount_bdl(base_path, wl_bdl); |
| 148 | + |
| 149 | +cleanup: |
| 150 | + // Release block device handles |
| 151 | + if (wl_bdl) { |
| 152 | + wl_bdl->ops->release(wl_bdl); |
| 153 | + } |
| 154 | + // Cleanup SPI bus |
| 155 | + if (spi) { |
| 156 | + ESP_ERROR_CHECK(spi_bus_remove_device(spi)); |
| 157 | + } |
| 158 | + ESP_ERROR_CHECK(spi_bus_free(HOST_ID)); |
| 159 | +} |
| 160 | + |
0 commit comments