1. 程式人生 > 其它 >[esp8266]RAM不足替代解決辦法

[esp8266]RAM不足替代解決辦法

問題描述

​ 因為專案是一個小電視專案需要播放動態圖,但是由於內部程式設計地址只有<1M,想要額能夠存放更多的動態圖。瞭解到esp-12s不止1M的flash,所以想要利用起來其餘的空間。

本方法適用於: 低頻率寫資料,高頻率讀取資料,想以此完全替代RAM是不可能的

解決辦法原理

​ 專案的是基於arduino庫來開發的,而icache自動對映在前面1M(0x1010-0x100000)左右flash中,所以這部分地址可以直接使用,而不需要spi來讀取。那麼可以將後面非對映的區域(3M左右)拷貝到該區域,程式碼中直接使用該拷貝的位置即可。

過程

​ 前章知道arduino可將flash的非對映區做成檔案系統。在裡面由兩種FS:SpiFs

LittleFs。不過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目錄下放入正常的檔案:

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\" , Ani_A_cache_buffer);

測試

​ 相對來說速度正常,理論比spi讀取更快。

參考連結

arduino LittleFs 文件