找回密码
 立即注册
首页 业界区 安全 ESP32-QSPI-外部flash驱动

ESP32-QSPI-外部flash驱动

屠焘 2025-6-9 14:03:54
在某些情况下,有些资源过于大,所以需要使用外部flash保存数据
这篇文章使用ESP32S3作为主控,驱动读写外部flash
1.硬件连接

flash通常为8pin,最主要的线为这六根:

  • MOSI GPIO11
  • MISO GPIO13
  • CLK 12
  • HD 9
  • WP 14
  • CS 10
需要注意的是,flash的HD和WP引脚在不同的读写模式下,具有不同的功能

  • HD引脚的默认功能用于暂停SPI通信
  • WP用于保护Flash存储器的某些部分不被写入或擦除
通信接口:目前市场是常见的flash支持五种通信方式:

  • SPI:最常用的通信方式,使用两根数据线进行双向传输
  • DOUT:数据读取使用两根数据线
  • DIO:数据和地址传输都是用两根数据线

    • IO0(通常标记为 MOSI 或 D0): 数据线0
    • IO1(通常标记为 MISO 或 D1): 数据线1

  • QOUT:数据读取使用四根数据线
  • QIO:数据和地址传输都是用四根数据线

    • IO0(通常标记为 MOSI 或 D0): 数据线0
    • IO1(通常标记为 MISO 或 D1): 数据线1
    • IO2(通常标记为 WP 或 D2): 数据线2
    • IO3(通常标记为 HOLD 或 D3): 数据线3

2.软件驱动

驱动使用IDF中的驱动库:esp_flash,简单的配置相关接口,就可以进行读写操作了,ESP32中,模组内部的flash也使用的该组件
代码中,可以很方便的配置相关信息:连接引脚,时钟速率,读取模式,使用的SPI
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <inttypes.h>
  4. #include <freertos/FreeRTOS.h>
  5. #include <freertos/task.h>
  6. #include <freertos/semphr.h>
  7. #include <unity.h>
  8. #include "esp_flash.h"
  9. #include "esp_private/spi_common_internal.h"
  10. #include "esp_flash_spi_init.h"
  11. #include "memspi_host_driver.h"
  12. #include "spi_flash_mmap.h"
  13. #include <esp_attr.h>
  14. #include "esp_log.h"
  15. #include "unity.h"
  16. #include "driver/gpio.h"
  17. #include "soc/io_mux_reg.h"
  18. #include "sdkconfig.h"
  19. #include "esp_partition.h"
  20. #include "esp_rom_gpio.h"
  21. #include "esp_rom_sys.h"
  22. #include "esp_timer.h"
  23. #include "spi_flash_mmap.h"
  24. #include "esp_private/spi_flash_os.h"
  25. #include "esp_timer.h"
  26. #define TAG "SPI_FLASH"
  27. typedef esp_flash_spi_device_config_t flashtest_config_t;
  28. #define FSPI_PIN_NUM_MOSI 11
  29. #define FSPI_PIN_NUM_MISO 13
  30. #define FSPI_PIN_NUM_CLK 12
  31. #define FSPI_PIN_NUM_HD 9
  32. #define FSPI_PIN_NUM_WP 14
  33. #define FSPI_PIN_NUM_CS 10
  34. // Just use the same pins for HSPI
  35. #define HSPI_PIN_NUM_MOSI FSPI_PIN_NUM_MOSI
  36. #define HSPI_PIN_NUM_MISO FSPI_PIN_NUM_MISO
  37. #define HSPI_PIN_NUM_CLK FSPI_PIN_NUM_CLK
  38. #define HSPI_PIN_NUM_HD FSPI_PIN_NUM_HD
  39. #define HSPI_PIN_NUM_WP FSPI_PIN_NUM_WP
  40. #define HSPI_PIN_NUM_CS FSPI_PIN_NUM_CS
  41. #define MAX_ADDR_24BIT 0x1000000
  42. #define TEST_SPI_SPEED 80
  43. #define TEST_SPI_READ_MODE SPI_FLASH_QIO
  44. flashtest_config_t ext_flash_config = {
  45.     .io_mode = TEST_SPI_READ_MODE,
  46.     .freq_mhz = TEST_SPI_SPEED,
  47.     .host_id = SPI2_HOST,
  48.     .cs_id = 0,
  49.     .cs_io_num = FSPI_PIN_NUM_CS,
  50.     .input_delay_ns = 0,
  51. };
  52. static void setup_bus(spi_host_device_t host_id)
  53. {
  54.     if (host_id == SPI2_HOST)
  55.     {
  56.         ESP_LOGI(TAG, "setup flash on SPI%u (FSPI) CS0...\n", host_id + 1);
  57.         spi_bus_config_t fspi_bus_cfg = {
  58.             .mosi_io_num = FSPI_PIN_NUM_MOSI,
  59.             .miso_io_num = FSPI_PIN_NUM_MISO,
  60.             .sclk_io_num = FSPI_PIN_NUM_CLK,
  61.             .quadhd_io_num = FSPI_PIN_NUM_HD,
  62.             .quadwp_io_num = FSPI_PIN_NUM_WP,
  63.             .max_transfer_sz = 16 * 1024,
  64.         };
  65.         esp_err_t ret = spi_bus_initialize(host_id, &fspi_bus_cfg, 0);
  66.         TEST_ESP_OK(ret);
  67.     }
  68. #if SOC_SPI_PERIPH_NUM > 2
  69.     else if (host_id == SPI3_HOST)
  70.     {
  71.         ESP_LOGI(TAG, "setup flash on SPI%u (HSPI) CS0...\n", host_id + 1);
  72.         spi_bus_config_t hspi_bus_cfg = {
  73.             .mosi_io_num = HSPI_PIN_NUM_MOSI,
  74.             .miso_io_num = HSPI_PIN_NUM_MISO,
  75.             .sclk_io_num = HSPI_PIN_NUM_CLK,
  76.             .quadhd_io_num = HSPI_PIN_NUM_HD,
  77.             .quadwp_io_num = HSPI_PIN_NUM_WP,
  78.             .max_transfer_sz = 16 * 1024,
  79.         };
  80.         esp_err_t ret = spi_bus_initialize(host_id, &hspi_bus_cfg, 0);
  81.         TEST_ESP_OK(ret);
  82.         // HSPI have no multiline mode, use GPIO to pull those pins up
  83.         gpio_set_direction(HSPI_PIN_NUM_HD, GPIO_MODE_OUTPUT);
  84.         gpio_set_level(HSPI_PIN_NUM_HD, 1);
  85.         gpio_set_direction(HSPI_PIN_NUM_WP, GPIO_MODE_OUTPUT);
  86.         gpio_set_level(HSPI_PIN_NUM_WP, 1);
  87.     }
  88. #endif
  89.     else
  90.     {
  91.         ESP_LOGE(TAG, "invalid bus");
  92.     }
  93. }
  94. static void setup_new_chip(const flashtest_config_t *test_cfg, esp_flash_t **out_chip)
  95. {
  96.     setup_bus(test_cfg->host_id);
  97.     esp_flash_spi_device_config_t dev_cfg = {
  98.         .host_id = test_cfg->host_id,
  99.         .io_mode = test_cfg->io_mode,
  100.         .freq_mhz = test_cfg->freq_mhz,
  101.         .cs_id = test_cfg->cs_id,
  102.         .cs_io_num = test_cfg->cs_io_num,
  103.         .input_delay_ns = test_cfg->input_delay_ns,
  104.     };
  105.     esp_flash_t *init_chip;
  106.     esp_err_t err = spi_bus_add_flash_device(&init_chip, &dev_cfg);
  107.     if (err != ESP_OK)
  108.     {
  109.         printf("error in spi bus init:%d\n", err);
  110.         return;
  111.     }
  112.     err = esp_flash_init(init_chip);
  113.     if (err != ESP_OK)
  114.     {
  115.         printf("error in esp flash init:%d\n", err);
  116.         return;
  117.     }
  118.     *out_chip = init_chip;
  119. }
  120. void write_erase_read_time_test(esp_flash_t *chip, uint32_t size)
  121. {
  122.     printf("\ntest start...\n");
  123.     printf("test size:%ld\n", size);
  124.     long long start_time, end_time;
  125.     uint8_t *write_buffer = malloc(size);
  126.     uint8_t *read_buffer = malloc(size);
  127.     // 写入部分测试值
  128.     for (int i = 0; i < size; i++)
  129.     {
  130.         write_buffer[i] = i;
  131.     }
  132.     start_time = esp_timer_get_time();
  133.     esp_flash_erase_region(chip, 0, size);
  134.     end_time = esp_timer_get_time();
  135.     printf("erase time:%lluus\n", end_time - start_time);
  136.     start_time = esp_timer_get_time();
  137.     esp_flash_write(chip, write_buffer, 0, size);
  138.     end_time = esp_timer_get_time();
  139.     printf("write time:%lluus\n", end_time - start_time);
  140.     start_time = esp_timer_get_time();
  141.     esp_flash_read(chip, read_buffer, 0, size);
  142.     end_time = esp_timer_get_time();
  143.     printf("read time:%lluus\n", end_time - start_time);
  144.     printf("test end...\n\n");
  145.     free(write_buffer);
  146.     free(read_buffer);
  147. }
  148. #define TEST_DATA_SIZE 1024
  149. void app_main(void)
  150. {
  151.     esp_flash_t *chip;
  152.     setup_new_chip(&ext_flash_config, &chip);
  153.     uint32_t size;
  154.     esp_err_t err = esp_flash_get_size(chip, &size);
  155.     if (err != ESP_OK)
  156.     {
  157.         printf("error in esp flash get size :%d\n", err);
  158.         return;
  159.     }
  160.     printf("get size:%ld\n", size);
  161.     write_erase_read_time_test(chip, 1 * TEST_DATA_SIZE);
  162.     write_erase_read_time_test(chip, 2 * TEST_DATA_SIZE);
  163.     write_erase_read_time_test(chip, 4 * TEST_DATA_SIZE);
  164.     write_erase_read_time_test(chip, 8 * TEST_DATA_SIZE);
  165.     write_erase_read_time_test(chip, 16 * TEST_DATA_SIZE);
  166. }
复制代码
3.性能实际测试

时钟速率为20、40、80Mhz,使用QIO模式,分别对1、2、4、8、16KB文件大小进行读写测试,结果如下
写/读测试(us)20Mhz40Mhz80Mhz1KB3711/2133453/1543069/1242KB3634/4083120/2892431/2284KB16787/80915806/56016778/4408KB196421/158417545/110516426/86416KB42919/315539205/219236918/1713出于好奇,我也测试了一下SPI与QSPI的速度差距,但是只测试了80Mhz时钟速率下的:
SPI 写/读测试(us)80Mhz1KB3056/2062KB2852/3924KB13440/7688KB16411/152116KB35974/3024可以看到,虽然QSPI的数据理论上是SPI的四倍,但是实际测试下来,速度提升还不到两倍,没有想象中那么高
实际的QSPI的速率也太低了,按照16KB的速度折合下来也才10MB每秒,只有理论速度的25%,某些地方应该还存在优化空间

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册