ESP32 存储系统及Flash,进而考虑HTTP升级文件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhejfl/article/details/87936604

1、背景

由于做的下载文件速度过慢,而OTA Demo的下载速度很快,应该有必要了解两者的差距。

1、OTA只有一个Get请求,而我的1K接1K的请求,速度就慢了

2、两片Flash的问题。

1.1 资料

ESP32技术参考手册

ESP32系列芯片技术规格书

2、ESP32 存储系统

2.1 存储系统简介

地址0x4000_0000以下的部分属于数据总线的地址范围;

地址0x4000_0000~0x4FFF_FFFF部分属于指令总线的地址范围;

地址0x5000_0000及以上的部分是数据总线和指令总线共用的地址范围。

存储系统分为片上存储和片外存储。

其中片上存储包括:

扫描二维码关注公众号,回复: 5454852 查看本文章

• 448 KB 的 ROM,用于程序启动和内核功能调用
• 520 KB 片上 SRAM,用于数据和指令存储
• RTC 快速存储器,为 8 KB 的 SRAM,可以在 Deep-sleep 模式下 RTC 启动时用于数据存储以及被主 CPU
访问

• RTC 慢速存储器,为 8 KB 的 SRAM,可以在 Deep-sleep 模式下被协处理器访问
• 1 Kbit 的 eFuse,其中 256 bit 为系统专用(MAC 地址和芯片设置) ; 其余 768 bit 保留给用户程序, 这些程
序包括 flash 加密和芯片 ID
• 嵌入式 flash  ESP32-D2WD带有16Mbit,40MHz的嵌入式flash,与GPIO16,GPIO17,SD_CMD,SD_CLK,SD_DATA_0和SD_DATA_1连接。

ESP32支持多个外部QSPI flash和静态随机存储器SRAM。

外部 flash 可以同时映射到 CPU 指令和只读数据空间。外部 flash 最大可支持 16 MB。

外部 SRAM 可映射到 CPU 数据空间。外部 SRAM 最大可支持 8 MB。一次最多可映射 4 MB。虽然ESP32能够支持多种类型的RAM芯片,但ESP32_SDK目前支持ESP_PSRAM32、ESP_PSRAM64  这是1.8V器件。
在芯片启动后,用户程序可以MAP外部SRAM或flash到CPU地址空间。

下面QSPI接口下的对Flash和SRAM的并行支持。 

外部SRAM包含在存储器映射中,并且在某些限定内,可以用与内部数据RAM相同的方式来使用。乐鑫的WROVER系列模组就包括了ESP 1.8v Flash和集成在模块内的ESP-PSRAM。

2.2 软件支持的外部RAM(External RAM)

ESP-IDF完全支持在应用程序中使用外部RAM,在启动时初始化外部RAM,提供了多种方式来配置处理外部RAM。

Initialize SPI RAM when booting the ESP32,即在ESP32boot时初始化SPI RAM。 

方式1、整合RAM到ESP32内存映射。这是一个外部RAM的基本选项。外部RAM指向地址空间0x3F800000(字节访问)。外部RAM的区域大小是SPI RAM大小(最大4MB)。通过指针指向外部RAM来放置数据。

方式2、初始化RAM并将其添加到功能分配器。这就允许程序使用heap_caps_malloc(size,MALLOC_CAP_SPIRAM)专门分配一块外部RAM。可以使用该内存,然后使用正常的free()来释放。映射到0x3F800000.

方式3、初始化 RAM,将其添加到功能分配器,并将内存添加到可由 malloc() 返回的 RAM 池中。 这允许任何应用程序使用外部 RAM 而无需重写代码以使用 heap_caps_malloc。这是默认。

方式4、允许在外部RAM放置BSS段,这段地址空间起始于0x3F800000,用于lwip、net80211、libpp和bluedroid ESP-IDF库存储初始化为零的数据(bss段)。通过在静态声明中应用EXT_RAM_ATTR 宏(未初始化为0值)从内部BSS段移到外部RAM。这有效减少BSS段使用的内部静态内存。

 2.3 外部Flash

与SPI flash配合使用的底层ROM功能没有与附加到SPI外围设备(SPI0除外)的Flash芯片一起工作的规定。

ESP-IDF提供了使用Flash的功能函数,但是一般来说,尽量避免使用原始的SPI flash功能,推荐使用特定分区的功能。因此需要配置分区表。

2.3.1 分区表

2.3.1.1、概念和组成

ESP32的flash可以包含多个应用程序以及多种不同类型的数据(例如校正数据、文件系统数据、参数存储器数据),因此,需要引入分区表的概念。

分区表帮助用户在实际产品开发过程中对Flash分区定制才能更好地满足产品需求。

分区表一般在flash中的默认偏移地址为0x8000处烧写,大小为C00(最多可以保存95条分区表条目)。分区表数据后还保存着该表的MD5校验和,校验分区表的完整性。使能安全启动,还存有签名信息。

分区表组成

Name标签 不超过16个字符  
Type类型 类型有两种app(0)或者data(1),也可以使用其他数据(0x40~0xFE)作为自定义分区类型 1
SubType子类型 8bit,具体和Type有关;Type==app时,SubType可以指定为factory(0)、ota_0(0x10)....ota_15(0x1F)或者test(0x20)--预留app子类型,用于工厂测试过程 ESP-IDF目前不支持;
Type==data,SubType字段可以指定为0ta(0)-OTA数据分区、phy(1)—PHY初始化信息、nvs(2)-非易失性存储或nvs_keys(4)-NVS秘钥分区,至少4096字节。
 
Offset偏移量 指定偏移地址,一般紧跟前一个分区开始,app分区必须0x10000(64K)对齐,用","时gen_esp32part.py工具会自动计算一个满足对齐要求的偏移地址,没有对齐,工具会报错  
Size 支持K和M的倍数单位,默认字节  
flag 应支持encrypted标志,在启动Flash加密后,该分区会被加密。app分区始终被加密。  

分区表例子

自定义分区表CSV

NVS和PHY分区是必不可少的。

2.3.1.2、分区表类型----使用make partition_table命令来打印分区表摘要

内置分区表:

                   Single factory app,no OTA

# Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs,data,nvs,0x9000,24K,
phy_init,data,phy,0xf000,4K,
factory,app,factory,0x10000,2M,

              Factory app, two OTA definitions

             比自定义分区表CSV图的结构多factory分区。factory分区可有也可无。

自定义分区表

就该CSV文件可以根据需求,描述任意数量的分区信息。分区表的offset可以为空,gen_esp32part.py工具会从分区表位置的后面自动计算并填充该分区的偏移地址,并确保每个分区的偏移地址正确对齐。

2.3.1.3、生成二进制分区表和MD5校验和

CSV->bin工具: partition_table/gen_esp32part.py工具   

方法:在make menuconfig指定分区表CSV文件的名称,执行make partition_table.

3、思考HTTP流下载文件

当应用程序需要打开连接和以active方式控制数据的读取。用了数据流的API,就不能使用esp_http_client_perform接口,但在使用之前先用esp_http_client_init()接口来获得句柄。

esp_http_client_init()-----创建和生成句柄;

esp_http_client_open()-------用write_len参数打开http连接,需要读时write_len=0;写头字符串;

esp_http_client_fetch_headers()-----读HTTP Server 应答头,返回服务器的content-length和调用esp_http_client_get_status_code() 获得连接HTTP状态

esp_http_client_read()------从HTTP数据流中读取数据

esp_http_client_close-------关闭连接

esp_http_client_cleanup()-----释放资源

例程:protocols/esp_http_client


static void http_perform_as_stream_reader()
{
    char *buffer = malloc(MAX_HTTP_RECV_BUFFER + 1);
    if (buffer == NULL) {
        ESP_LOGE(TAG, "Cannot malloc http receive buffer");
        return;
    }
    esp_http_client_config_t config = {
        .url = "http://httpbin.org/get",
        .event_handler = _http_event_handler,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_err_t err;
    if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
        ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
        free(buffer);
        return;
    }
    int content_length =  esp_http_client_fetch_headers(client);
    int total_read_len = 0, read_len;
    if (total_read_len < content_length && content_length <= MAX_HTTP_RECV_BUFFER) {
        read_len = esp_http_client_read(client, buffer, content_length);
        if (read_len <= 0) {
            ESP_LOGE(TAG, "Error read data");
        }
        buffer[read_len] = 0;
        ESP_LOGD(TAG, "read_len = %d", read_len);
    }
    ESP_LOGI(TAG, "HTTP Stream reader Status = %d, content_length = %d",
                    esp_http_client_get_status_code(client),
                    esp_http_client_get_content_length(client));
    esp_http_client_close(client);
    esp_http_client_cleanup(client);
    free(buffer);
}

猜你喜欢

转载自blog.csdn.net/zhejfl/article/details/87936604