[esp8266]RAM不足替代解决办法

问题描述

​ 因为项目是一个小电视项目需要播放动态图,但是由于内部编程地址只有<1M,想要额能够存放更多的动态图。了解到esp-12s不止1M的flash,所以想要利用起来其余的空间。

本方法适用于: 低频率写数据,高频率读取数据,想以此完全替代RAM是不可能的

解决办法原理

​ 项目的是基于arduino库来开发的,而icache自动映射在前面1M(0x1010-0x100000)左右flash中,所以这部分地址可以直接使用,而不需要spi来读取。那么可以将后面非映射的区域(3M左右)拷贝到该区域,代码中直接使用该拷贝的位置即可。

过程

​ 前章知道arduino可将flash的非映射区做成文件系统。在里面由两种FS:SpiFsLittleFs。不过SpiFs已经不再推荐了。这里使用LittleFs来构建。

LittleFsCpp文件,且它引用的都是Cpp文件,我这里使用了C文件来include导致报错,大家需要注意,尽量使用Cpp文件。

Arduino IDE下构建文件系统,在网上偶许多帖子说明了。我这里使用的是vscode + platformio开发模式。

​ 文件夹目录是data该文件需要建立在项目根目录下,不然无法识别,然后在platform.ini中加入:

board_build.ldscript = eagle.flash.4m3m.ld
board_build.filesystem = littlefs

​ 这两句分别是定义链接脚本和文件系统类型,链接脚本可将flash分配成3M文件系统,1M的程序系统

接着可以在data目录下放入正常的文件:

avatar

build Filesystem image表示构建文件系统,会生成bin文件在.pio/build/esp8266/littlsfs.bin

Upload Filesystem Image表示从接口(一般串口)烧录文件系统,不会清除程序。

代码实现过程

​ 将某个Buffer区域定义为指定section,然后在linker脚本的irom0区域加入指定链接位置,指定的链接位置请与扇区size对齐,因为写入数据前往往需要先清空扇区,buffer大小页尽量是sector的整数倍,这样不会错误写入到程序区域,导致不可意料的错误。

注:attribute((at( 某个地址)))也可以实现,但在arduino中似乎被放弃,故使用指定section方式。

​ 关键代码如下:

1.代码申请一块区域

#define ROM_BUFFER __attribute__((section( "\".rom_buffer." __FILE__ "." __STRINGIZE(__LINE__) "."  __STRINGIZE(__COUNTER__) "\"")))
const uint8_t Ani_A_cache_buffer[100][2*1024] ROM_BUFFER = {0x0ff,0x02,0x03,0xff};    //图片缓冲区域。//请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处

​ 赋予初值只是为了方便调试。

2.修改ld文件,ld文件在.pio\build\esp12e\ld\local.eagle.app.v6.common.ld但是该文件不可直接改,因为它是被生成的,修改文件C:\Users\用户名\.platformio\packages\framework-arduinoespressif8266\tools\sdk\ld\eagle.app.v6.common.ld.h

  /* IRAM is split into .text and .text1 to allow for moving specific */
  /* functions into IRAM that would be matched by the irom0.text matcher */
  .text : ALIGN(4)
  {
 	......................
    . = ALIGN(4);
    __eh_frame = ABSOLUTE(.);
    KEEP(*(.eh_frame))
    . = ALIGN(4096);	//添加。//请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处
    *(.rom_buffer.*)	//添加。//请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处
    . = (. + 7) & ~ 3;  /* Add a 0 entry to terminate the list */
	//请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处
    _irom0_text_end = ABSOLUTE(.);
    _flash_code_end = ABSOLUTE(.);
  } >irom0_0_seg :irom0_0_phdr

. = ALIGN(4096);*(.rom_buffer.*)为添加的语句。意为将linker点移到4096对齐位置,然后下面的含有rom_buffer的段符号都被写入irom0_0_seg位置

3.使用改区域

//写
//擦除缓冲区
//擦除从起始位置(flash位置或者0x40200000)开始的sec_no个扇区
#include <LittleFS.h>
#include "spi_flash.h"

#define ROM_MAP_START 0x40200000

SpiFlashOpResult spi_erase_anim_cache_buffer( uint32_t sec_no)
{
    SpiFlashOpResult res = SPI_FLASH_RESULT_OK;
    //注意要减1,扇区从0开始。//请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处
    uint32_t sec_strat = ((uint32_t)Ani_A_cache_buffer-ROM_MAP_START)/SPI_FLASH_SEC_SIZE - 1;
    uint32_t sec_end = sec_strat + sec_no;
    if( sec_end > (sec_strat+(Animate_Max_Fps/2)))
        sec_end = sec_strat+(Animate_Max_Fps/2);
    Serial.printf("Erase file system: %d - %d,sec_no:%d,MAX:%d\r\n", sec_strat, sec_end, sec_no, Animate_Max_Fps/2);
    for( uint32_t i = sec_strat; i < sec_end; i++)
    {
        ESP.wdtFeed();	//喂狗,注意时间过程会导致狗复位。//请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处
        res = spi_flash_erase_sector( i);
        //Serial.printf("%d[%x-%x] ", i, i*SPI_FLASH_SEC_SIZE, (i+1)*SPI_FLASH_SEC_SIZE-1);
        if( res)
            return res;
    }
    //AniAcSysPrintf("\r\n");
    return res;
}

void setup()
{
    int sec_no = sizeof(Ani_A_cache_buffer)/4096;
    int addr = (unsigned int)Ani_A_cache_buffer;
    unsigned char src_ptr[100] = "Hello, this is my test Code";
    spi_erase_anim_cache_buffer( sec_no);
	res = spi_flash_write( addr-ROM_MAP_START, (unsigned int)src_ptr, 40);//写入size需要4字节对齐。//请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处
}

//读
unsigned char c1 = Ani_A_cache_buffer[0][0];
Serial.prinf( "c1=%d\r\n", c1);

​ 写入请不要直接使用改变量名,因为这个本就是const变量。

请勿cpoy我的邪恶法师文章,这个为原创,转载声明出处

调试过程

​ 在platform.ini加入

build_flags = 
				...............
				-Wl,-Map=filemap.map

​ 可在项目根目录下有filemap.map搜索rom_buffer可看到:

 *fill*         0x00000000402b60a4      0xf5c 
 *(.rom_buffer.*)
 .rom_buffer.AnimateAccSys.cpp.65.0
                0x00000000402b7000    0x32000 .pio\build\esp12e\src\AnimateAccSys\AnimateAccSys.cpp.o
                0x00000000402e9004                . = ((. + 0x7) & 0xfffffffffffffffc)
 *fill*         0x00000000402e9000        0x4 
                0x00000000402e9004                _irom0_text_end = ABSOLUTE (.)
                0x00000000402e9004                _flash_code_end = ABSOLUTE (.)

.rom_buffer.AnimateAccSys.cpp.65.00x00000000402b7000刚好对齐4096,并且大小为0x32000恰是申请的Ani_A_cache_buffer的大小。

​ 也可在代码中打印该地址:

Serial.printf("Ani_A_cache_buffer address:%p\r\n" , Ani_A_cache_buffer);

测试

​ 相对来说速度正常,理论比spi读取更快。

参考链接

arduino LittleFs 文档

posted @ 2022-04-15 10:18  邪恶法师  阅读(1587)  评论(0编辑  收藏  举报