首页 > 其他分享 >ESP32-QSPI-外部flash驱动

ESP32-QSPI-外部flash驱动

时间:2024-12-24 16:19:58浏览次数:8  
标签:PIN esp ESP32 flash NUM time include QSPI

在某些情况下,有些资源过于大,所以需要使用外部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

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>

#include <unity.h>
#include "esp_flash.h"
#include "esp_private/spi_common_internal.h"
#include "esp_flash_spi_init.h"
#include "memspi_host_driver.h"
#include "spi_flash_mmap.h"
#include <esp_attr.h>
#include "esp_log.h"

#include "unity.h"
#include "driver/gpio.h"
#include "soc/io_mux_reg.h"
#include "sdkconfig.h"

#include "esp_partition.h"
#include "esp_rom_gpio.h"
#include "esp_rom_sys.h"
#include "esp_timer.h"
#include "spi_flash_mmap.h"
#include "esp_private/spi_flash_os.h"
#include "esp_timer.h"
#define TAG "SPI_FLASH"

typedef esp_flash_spi_device_config_t flashtest_config_t;

#define FSPI_PIN_NUM_MOSI 11
#define FSPI_PIN_NUM_MISO 13
#define FSPI_PIN_NUM_CLK 12
#define FSPI_PIN_NUM_HD 9
#define FSPI_PIN_NUM_WP 14
#define FSPI_PIN_NUM_CS 10

// Just use the same pins for HSPI
#define HSPI_PIN_NUM_MOSI FSPI_PIN_NUM_MOSI
#define HSPI_PIN_NUM_MISO FSPI_PIN_NUM_MISO
#define HSPI_PIN_NUM_CLK FSPI_PIN_NUM_CLK
#define HSPI_PIN_NUM_HD FSPI_PIN_NUM_HD
#define HSPI_PIN_NUM_WP FSPI_PIN_NUM_WP
#define HSPI_PIN_NUM_CS FSPI_PIN_NUM_CS

#define MAX_ADDR_24BIT 0x1000000
#define TEST_SPI_SPEED 80
#define TEST_SPI_READ_MODE SPI_FLASH_QIO

flashtest_config_t ext_flash_config = {
    .io_mode = TEST_SPI_READ_MODE,
    .freq_mhz = TEST_SPI_SPEED,
    .host_id = SPI2_HOST,
    .cs_id = 0,
    .cs_io_num = FSPI_PIN_NUM_CS,
    .input_delay_ns = 0,
};

static void setup_bus(spi_host_device_t host_id)
{
    if (host_id == SPI2_HOST)
    {
        ESP_LOGI(TAG, "setup flash on SPI%u (FSPI) CS0...\n", host_id + 1);
        spi_bus_config_t fspi_bus_cfg = {
            .mosi_io_num = FSPI_PIN_NUM_MOSI,
            .miso_io_num = FSPI_PIN_NUM_MISO,
            .sclk_io_num = FSPI_PIN_NUM_CLK,
            .quadhd_io_num = FSPI_PIN_NUM_HD,
            .quadwp_io_num = FSPI_PIN_NUM_WP,
            .max_transfer_sz = 16 * 1024,
        };
        esp_err_t ret = spi_bus_initialize(host_id, &fspi_bus_cfg, 0);
        TEST_ESP_OK(ret);
    }
#if SOC_SPI_PERIPH_NUM > 2
    else if (host_id == SPI3_HOST)
    {
        ESP_LOGI(TAG, "setup flash on SPI%u (HSPI) CS0...\n", host_id + 1);
        spi_bus_config_t hspi_bus_cfg = {
            .mosi_io_num = HSPI_PIN_NUM_MOSI,
            .miso_io_num = HSPI_PIN_NUM_MISO,
            .sclk_io_num = HSPI_PIN_NUM_CLK,
            .quadhd_io_num = HSPI_PIN_NUM_HD,
            .quadwp_io_num = HSPI_PIN_NUM_WP,
            .max_transfer_sz = 16 * 1024,
        };
        esp_err_t ret = spi_bus_initialize(host_id, &hspi_bus_cfg, 0);
        TEST_ESP_OK(ret);

        // HSPI have no multiline mode, use GPIO to pull those pins up
        gpio_set_direction(HSPI_PIN_NUM_HD, GPIO_MODE_OUTPUT);
        gpio_set_level(HSPI_PIN_NUM_HD, 1);

        gpio_set_direction(HSPI_PIN_NUM_WP, GPIO_MODE_OUTPUT);
        gpio_set_level(HSPI_PIN_NUM_WP, 1);
    }
#endif
    else
    {
        ESP_LOGE(TAG, "invalid bus");
    }
}

static void setup_new_chip(const flashtest_config_t *test_cfg, esp_flash_t **out_chip)
{
    setup_bus(test_cfg->host_id);
    esp_flash_spi_device_config_t dev_cfg = {
        .host_id = test_cfg->host_id,
        .io_mode = test_cfg->io_mode,
        .freq_mhz = test_cfg->freq_mhz,
        .cs_id = test_cfg->cs_id,
        .cs_io_num = test_cfg->cs_io_num,
        .input_delay_ns = test_cfg->input_delay_ns,
    };
    esp_flash_t *init_chip;
    esp_err_t err = spi_bus_add_flash_device(&init_chip, &dev_cfg);
    if (err != ESP_OK)
    {
        printf("error in spi bus init:%d\n", err);
        return;
    }

    err = esp_flash_init(init_chip);

    if (err != ESP_OK)
    {
        printf("error in esp flash init:%d\n", err);
        return;
    }
    *out_chip = init_chip;
}

void write_erase_read_time_test(esp_flash_t *chip, uint32_t size)
{
    printf("\ntest start...\n");
    printf("test size:%ld\n", size);

    long long start_time, end_time;
    uint8_t *write_buffer = malloc(size);
    uint8_t *read_buffer = malloc(size);

    // 写入部分测试值
    for (int i = 0; i < size; i++)
    {
        write_buffer[i] = i;
    }
    start_time = esp_timer_get_time();
    esp_flash_erase_region(chip, 0, size);
    end_time = esp_timer_get_time();
    printf("erase time:%lluus\n", end_time - start_time);

    start_time = esp_timer_get_time();
    esp_flash_write(chip, write_buffer, 0, size);
    end_time = esp_timer_get_time();
    printf("write time:%lluus\n", end_time - start_time);

    start_time = esp_timer_get_time();
    esp_flash_read(chip, read_buffer, 0, size);
    end_time = esp_timer_get_time();
    printf("read time:%lluus\n", end_time - start_time);

    printf("test end...\n\n");
}

#define TEST_DATA_SIZE 1024
void app_main(void)
{
    esp_flash_t *chip;
    setup_new_chip(&ext_flash_config, &chip);

    uint32_t size;
    esp_err_t err = esp_flash_get_size(chip, &size);
    if (err != ESP_OK)
    {
        printf("error in esp flash get size :%d\n", err);
        return;
    }
    printf("get size:%ld\n", size);

    write_erase_read_time_test(chip, 1 * TEST_DATA_SIZE);
    write_erase_read_time_test(chip, 2 * TEST_DATA_SIZE);
    write_erase_read_time_test(chip, 4 * TEST_DATA_SIZE);
    write_erase_read_time_test(chip, 8 * TEST_DATA_SIZE);
    write_erase_read_time_test(chip, 16 * TEST_DATA_SIZE);
}

3.性能实际测试

时钟速率为20、40、80Mhz,使用QIO模式,分别对1、2、4、8、16KB文件大小进行读写测试,结果如下

写/读测试(us) 20Mhz 40Mhz 80Mhz
1KB 3711/213 3453/154 3069/124
2KB 3634/408 3120/289 2431/228
4KB 16787/809 15806/560 16778/440
8KB 196421/1584 17545/1105 16426/864
16KB 42919/3155 39205/2192 36918/1713

出于好奇,我也测试了一下SPI与QSPI的速度差距,但是只测试了80Mhz时钟速率下的:

SPI 写/读测试(us) 80Mhz
1KB 3056/206
2KB 2852/392
4KB 13440/768
8KB 16411/1521
16KB 35974/3024

可以看到,虽然QSPI的数据理论上是SPI的四倍,但是实际测试下来,速度提升还不到两倍,没有想象中那么高

实际的QSPI的速率也太低了,按照16KB的速度折合下来也才10MB每秒,只有理论速度的25%,某些地方应该还存在优化空间

标签:PIN,esp,ESP32,flash,NUM,time,include,QSPI
From: https://www.cnblogs.com/shumei52/p/18628009

相关文章

  • ESP32 HTTP Client 报错'Error parse url'
    ESP32HTTPClient报错今天在使用ESP32中的esp_http_client_init如下//结构体typedefstruct{charurl[256];}config_t;//主要代码strncpy(config.url,"http://xxx/xxx",sizeof(config.url)-1);config.url[sizeof(config.url)-1]='\0';//确保字符串以空......
  • OpenAI 推出嵌入式硬件 SDK,支持 ESP32 语音开发;INFP:音频驱动的双人对话头像生成,自动区
      开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(Real-TimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编辑的......
  • qemu模拟仿真esp32开发板并启动esp_lcd_qemu_rgb图形支持
    QEMU是一个强大的开源模拟器,可以模拟各种硬件环境。现在就让我们使用QEMU来模拟ESP32开发板,并启用esp_lcd_qemu_rgb图形支持。本次实践需要用到图形仿真套件:espressif/esp_lcd_qemu_rgb:espressif/esp_lcd_qemu_rgb•v1.0.2•ESPComponentRegistry前置准备安装qemu仿......
  • 【EPS32硬件】ESP32最小系统绘制
    以ESP32-WROOM-32E原理图为例模组原理图外围设计原理图●EPAD管脚39可以不焊接到底板。如果您想将EPAD焊接到底板,请确保使用适量焊膏,避免过量焊膏造成模组与底板距离过大,影响管脚与底板之间的贴合。●为确保ESP32芯片上电时的供电正常,EN管脚处需要增加RC延迟......
  • Flash动画综合设计
    Flash动画综合设计并发布、嵌入到网页 【作业要求】自己选定主题,创意制作Flash动画,并与网页进行集成【实验环境】l 所需硬件环境为微机;l 所需软件环境为Flash8.0【创意内容】 一、国旗飘荡效果 设计思路:我选择了中国国旗作为主题,想通过动态效果让国旗看起来像在风......
  • 基于ESP32的可穿戴智能手表
    DRMWatch3-打造适合日常使用的智能手表展示视频:基于ESP32的可穿戴智能手表源码模型等下载链接点我进行资料下载引言在这篇文章中,我们将一起探索如何制作一款基于ESP32-S2的DIY智能手表——DRMWatch3。这款手表不仅具备传统电子手表的功能,还增添了许多额外的......
  • esp32s3 simd 指令集简单介绍与使用
    什么是SIMDSIMD(SingleInstruction,MultipleData,单指令多数据)是一种并行计算的架构和技术,用于在计算机处理器中同时对多个数据点执行相同的操作单指令多数据在SIMD架构中,一条指令可以处理多个数据并行处理SIMD通过并行处理多个数据元素来提高计算效率。这种并行性特别......
  • QSpinBox & DoubleQSPinBox
    两个控件QSpinBox&DoubleQSPinBox都是QAbstractSpinBox的子类。其中我们不再举例DoubleQSPinBox ,因为其主要区别在于精度上,也就是说尤如其名DoubleQSPinBox 是double类型的我们来看看QSpinBox的几个主要功能。QSpinBox::value()//读取数据QSpinBox......
  • stm32读写外部flash失败怎么回事
    STM32读写外部Flash失败可能由多种原因引起,以下是一些常见的问题及其解决方案:一、硬件连接问题SPI接口连接错误:确保STM32的SPI接口与外部Flash的连接正确,包括时钟线(SCK)、数据线(MOSI、MISO)以及片选线(CS)。检查连接是否松动或存在断路。电源和地连接:确保外部Flash......
  • esp32-将flash分区进行内存映射
    1.前言为什么需要将flash分区映射到内存区域在某些情况下,我们需要在单片机中保存一个较大的数组,比如字库、图片等,使用const将其编译到代码中,可以指针访问他们。但是,在线刷或者OTA更新时,也不可避免的需要更新他们,造成了时间或者空间上的浪费。但是如果将其按照静态数据存放在fl......