RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

LittleFS是否可以應(yīng)用于SD卡中呢?

恩智浦MCU加油站 ? 來(lái)源:恩智浦MCU加油站 ? 2023-12-28 16:41 ? 次閱讀

背景介紹

LittleFS是一個(gè)應(yīng)用于單片機(jī)內(nèi)部flash和外掛NOR flash的文件系統(tǒng)。由于它相比傳統(tǒng)的FAT文件系統(tǒng)更適合于小型嵌入式系統(tǒng),所以越來(lái)越多人把它應(yīng)用于自己的項(xiàng)目中。那么除了NOR/NANDflash類(lèi)型的存儲(chǔ)設(shè)備外,LittleFS是否可以應(yīng)用于SD卡中呢?其實(shí)也是可以的。本文將使用i.mxRT1050 SDK中的littlefs_shell項(xiàng)目和sdcard_fatfs項(xiàng)目,改造出一個(gè)讀寫(xiě)SD卡的littefs_shell。

操作步驟

本次實(shí)驗(yàn)采用的是MCUXpresso IDE v11.7,SDK使用2.13版本。littleFS文件系統(tǒng)一共只有4個(gè)文件,其中l(wèi)fs.h中顯示了當(dāng)前的版本是littleFS 2.5。

1. 首先當(dāng)然是把SD相關(guān)的代碼加入littlefs_shell工程。最簡(jiǎn)單的方法莫過(guò)于再導(dǎo)入一個(gè)sdcard_fatfs項(xiàng)目,隨后將其中的sdmmc目錄全部復(fù)制到我們的工程下面。隨后還要復(fù)制board目錄下的sdmmc_config.c和sdmmc_config.h,drivers目錄下的fsl_usdhc.c和fsl_usdhc.h。

2. 修改程序,包括SD卡檢測(cè)和初始化,增加一個(gè)從LittleFS到SD驅(qū)動(dòng)程序的橋梁。在littlefs_shell.c中增加以下代碼。

extern sd_card_t m_sdCard;  
status_t sdcardWaitCardInsert(void)  
{  
    BOARD_SD_Config(&m_sdCard, NULL, BOARD_SDMMC_SD_HOST_IRQ_PRIORITY, NULL);  
  
    /* SD host init function */  
    if (SD_HostInit(&m_sdCard) != kStatus_Success)  
    {  
        PRINTF("
SD host init fail
");  
        return kStatus_Fail;  
    }  
  
    /* wait card insert */  
    if (SD_PollingCardInsert(&m_sdCard, kSD_Inserted) == kStatus_Success)  
    {  
        PRINTF("
Card inserted.
");  
        /* power off card */  
        SD_SetCardPower(&m_sdCard, false);  
        /* power on the card */  
        SD_SetCardPower(&m_sdCard, true);  
//        SdMmc_Init();  
    }  
    else  
    {  
        PRINTF("
Card detect fail.
");  
        return kStatus_Fail;  
    }  
  
    return kStatus_Success;  
}  
status_t sd_disk_initialize()  
{  
    static bool isCardInitialized = false;  
  
    /* demostrate the normal flow of card re-initialization. If re-initialization is not neccessary, return RES_OK directly will be fine */  
    if(isCardInitialized)  
    {  
        SD_Deinit(&m_sdCard);  
    }  
  
    if (kStatus_Success != SD_Init(&m_sdCard))  
    {  
        SD_Deinit(&m_sdCard);  
        memset(&m_sdCard, 0U, sizeof(m_sdCard));  
        return kStatus_Fail;  
    }  
  
    isCardInitialized = true;  
  
    return kStatus_Success;  
}
在main()里添加:

 if (sdcardWaitCardInsert() != kStatus_Success)  
 {  
     return -1;  
 }  
  
status=sd_disk_initialize();

3.新建一個(gè)c文件,lfs_sdmmc.c。調(diào)用順序是littlefs->lfs_sdmmc.c->lfs_sdmmc_bridge.c->fsl_sd.c。

lfs_sdmmc.c和lfs_sdmmc_bridge.c作為中間層,可以連接littlefs和sd上層驅(qū)動(dòng)。其中必須要注意的是地址的映射關(guān)系。littleFS給出的地址是塊地址 + 偏移地址。見(jiàn)下圖。這是一次mount命令所發(fā)出的讀指令。其中的塊地址指的是擦除塊(sector)的地址。而讀寫(xiě)操作使用的是最小的讀寫(xiě)塊地址(BLOCK),具體在下文中說(shuō)明。

因此在lfs_sdmmc.c中先把littleFS給的地址轉(zhuǎn)換成byte地址。再在lfs_sdmmc_bridge.c中把SD卡讀寫(xiě)地址改為BLOCK地址。由于目前大多數(shù)SD卡都超過(guò)了4GB,byte地址需用64位變量。

下圖是littleFS在mount的時(shí)候讀BLOCK的情況:

98b7b718-a559-11ee-8b88-92fbcf53809c.png

下面是lfs_sdmmc.c中read和erase的函數(shù):

int lfs_sdmmc_read(const struct lfs_config *lfsc, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)  
{  
    struct lfs_sdmmc_ctx *ctx;  
    uint64_t flash_addr;  
  
    assert(lfsc);  
    flash_addr =  block * lfsc->block_size + off;  
    if (lfssd_Read (flash_addr, size, buffer ) != kStatus_Success)  
        return LFS_ERR_IO;  
  
    return LFS_ERR_OK;  
}  
int lfs_sdmmc_erase(const struct lfs_config *lfsc, lfs_block_t block)  
{  
    status_t status = kStatus_Success;  
    struct lfs_sdmmc_ctx *ctx;  
    uint64_t sdmmc_addr;  
  
    assert(lfsc);  
    sdmmc_addr =  block * lfsc->block_size;  
    for (uint32_t sector_ofs = 0; sector_ofs < lfsc->block_size; sector_ofs +=lfsc->block_size)  
    {  
        status = lfssd_EraseBlocks (sdmmc_addr + sector_ofs, 512);  
        if (status != kStatus_Success)  
            break;  
    }  
  
    if (status != kStatus_Success)  
        return LFS_ERR_IO;  
  
    return LFS_ERR_OK;  
}

這是lfs_sdmmc_bridge.c中read和erase函數(shù)。可以分辨其中的地址映射關(guān)系:

bool lfssd_EraseBlocks (uint64_t address, uint32_t len)  
{  
    if (address % BLOCK_SIZE > 0)    return kStatus_Fail;  
  
    uint32_t startDataBlockIndex = address / BLOCK_SIZE;  
  
    if(SD_EraseBlocks (&m_sdCard, startDataBlockIndex, len/BLOCK_SIZE) == kStatus_Success)  
        return kStatus_Success;  
    else  
        return kStatus_Fail;  
}  
  
bool lfssd_Read (uint64_t address, uint32_t dataLen, void* buff)  
{  
    if (dataLen == 0)  
        return true;  
  
    if (kStatus_Success != SD_ReadBlocks (&m_sdCard, buff, address/BLOCK_SIZE, SD_CARD_DATA_BLOCK_COUNT))  
    {  
        return kStatus_Fail;  
    }  
  
    return kStatus_Success;  
}
4. 最重要的一步是littleFS參數(shù)配置。在peripherals.c中有一個(gè)結(jié)構(gòu)體LittlsFS_config,這個(gè)結(jié)構(gòu)體中不但包含了SD卡的操作函數(shù),還包括讀寫(xiě)扇區(qū)和緩存大小。這個(gè)結(jié)構(gòu)體的設(shè)置非常關(guān)鍵。如果設(shè)的不好,不但影響性能,更可能會(huì)運(yùn)行出錯(cuò)。在設(shè)置之前,讓我們先來(lái)介紹一下SD卡和littleFS的大致原理。

SD卡的存儲(chǔ)單元是BLOCK,讀寫(xiě)都可以按照BLOCK進(jìn)行。不同的卡每個(gè)BLOCK的大小是可以不同的。對(duì)于標(biāo)準(zhǔn)SD卡,可以用CMD16設(shè)置塊命令的長(zhǎng)度,對(duì)于SDHC卡塊命令長(zhǎng)度固定為512字節(jié)。SD卡的擦除是按照扇區(qū)或者說(shuō)SECTOR進(jìn)行的。每個(gè)扇區(qū)的大小需要查SD卡的CSD寄存器。

如果CSD寄存器ERASE_BLK_EN= 0時(shí),Sector是最小的擦除單元,它的單位是“塊”。Sector的值等于CSD寄存器中的SECTOR_SIZE的值+1。比如SECTOR_SIZE是127,那么最小擦除單元是512*(127+1)=65536字節(jié)。另外有時(shí)候會(huì)有疑問(wèn),現(xiàn)在的SD卡其實(shí)很多都有磨損功能以降低頻繁擦寫(xiě)帶來(lái)的損耗,延長(zhǎng)使用壽命。所以其實(shí)刪除操作或者是讀寫(xiě)操作并不一定是真正的物理地址。而是經(jīng)過(guò)SD控制器映射的。但是對(duì)用戶(hù)來(lái)說(shuō),這種映射是透明的。所以不用擔(dān)心這會(huì)對(duì)正常操作產(chǎn)生影響。

LittleFS是一個(gè)輕量級(jí)的文件系統(tǒng),相比FAT系統(tǒng),它有掉電恢復(fù)能力和動(dòng)態(tài)磨損均衡功能。掛載后,littlefs提供了一整套類(lèi)似POSIX的文件和目錄功能,所以可以象操作一般常見(jiàn)文件系統(tǒng)一樣的進(jìn)行操作。LittleFS一共只有4個(gè)文件,使用時(shí)基本不需要修改。由于LittleFS要操作的NOR/NAND flash本質(zhì)是一種塊設(shè)備,所以為了使用方便,LittleFS是以塊為單位進(jìn)行讀寫(xiě)的,對(duì)底層NOR/NAND Flash接口驅(qū)動(dòng)都是以block為單位進(jìn)行的。

下面來(lái)看一下LittleFS配置參數(shù)的具體內(nèi)容:

const struct lfs_config LittleFS_config = {  
  .context = (void*)0,  
  .read = lfs_sdmmc_read,  
  .prog = lfs_sdmmc_prog,  
  .erase = lfs_sdmmc_erase,  
  .sync = lfs_sdmmc_sync,  
  .read_size = 512,  
  .prog_size = 512,  
  .block_size = 65536,  
  .block_count = 128,  
  .block_cycles = 100,  
  .cache_size = 512,  
  .lookahead_size = LITTLEFS_LOOKAHEAD_SIZE  
};

其中,第一項(xiàng)在本項(xiàng)目沒(méi)有什么用,在SDK中用來(lái)保存文件系統(tǒng)在Flash中存放的偏移量;

第二項(xiàng)(.read)到第五項(xiàng)(.sync)指向各項(xiàng)操作的處理函數(shù);

第六項(xiàng).read_size是讀操作的最小單位。這個(gè)值大致等于SD卡的BLOCK大小。在SD卡驅(qū)動(dòng)程序中,這個(gè)大小已經(jīng)固定設(shè)為512。所以為了方便這里也一樣設(shè)為512。

第七項(xiàng).prog_size就是每次寫(xiě)入的字節(jié)數(shù),這里和.read_size一樣都是512字節(jié)。

第八項(xiàng)是.block_size。這一項(xiàng)可以認(rèn)為就是進(jìn)行擦除操作時(shí)SD卡支持的最小擦除塊。這里默認(rèn)值不重要,需要在SD卡初始化后根據(jù)實(shí)際情況在程序中設(shè)置。

第九項(xiàng)(.block_count)是用來(lái)表示一共有多少可擦除塊的。和.block_size相乘就可以得到卡的大小。本次實(shí)驗(yàn)中使用的卡就是64k字節(jié)為一個(gè)擦除塊,所以這里直接使用65536。如果卡是可換的則需要在SD卡初始化后再根據(jù)參數(shù)確定。

第十項(xiàng)(.block_cycles)是每個(gè)block的擦寫(xiě)循環(huán)次數(shù)。

第十一項(xiàng)(.cache_size)緩存大小。給人的感覺(jué)應(yīng)該是越大越好,但實(shí)際上修改這個(gè)值后會(huì)無(wú)法工作。所以還是512。

第十二項(xiàng)(lookahead_size)littlefs中使用一個(gè)lookahead buffer來(lái)管理和分配塊。lookahead buffer是一個(gè)固定大小的bitmap,記錄一片區(qū)域內(nèi)塊分配的信息。lookaheadbuffer只記錄了一片區(qū)域內(nèi)塊分配的信息,當(dāng)需要知道其他區(qū)域塊分配的情況時(shí),就需要進(jìn)行掃描文件系統(tǒng)來(lái)查找已分配的塊。如lookahead buffer中已經(jīng)沒(méi)有空閑塊、需要推移lookaheadbuffer來(lái)查找文件系統(tǒng)中的其他空閑塊。每次lookahead buffer位置推移一個(gè)lookahead_size。這里使用原來(lái)的值即可。

好了,到此為止基本上都改好了。插上卡試一試。

98d939ec-a559-11ee-8b88-92fbcf53809c.png

果然,移植非常成功,format以后,可以寫(xiě)可以讀可以建目錄。還可以在已有的文件后面添加。

可我們還是在多次測(cè)試后發(fā)現(xiàn)一個(gè)問(wèn)題,如果對(duì)一個(gè)文件進(jìn)行反復(fù)的添加->關(guān)閉->添加->關(guān)閉操作后,這個(gè)文件的打開(kāi)會(huì)越來(lái)越慢,甚至需要幾秒鐘。這是應(yīng)為添加的內(nèi)容并不是直接寫(xiě)在文件最后一個(gè)BLOCK里,而是會(huì)新申請(qǐng)一個(gè)BLOCK,不管之前的BLOCK是否寫(xiě)滿(mǎn)。如圖:

98ef557e-a559-11ee-8b88-92fbcf53809c.png

上圖是把每次write命令中用到的所有讀、寫(xiě)、擦除操作的次數(shù)打印出來(lái)??梢钥吹矫看卧趌fs_file_open中都要比上次寫(xiě)操作多一次讀。這樣在經(jīng)過(guò)幾十上百次循環(huán)后一個(gè)文件會(huì)涉及很多個(gè)BLOCK。這些BLOCK依次讀下來(lái)非常耗費(fèi)時(shí)間。測(cè)試中發(fā)現(xiàn)超過(guò)100次寫(xiě)操作后所用的時(shí)間超過(guò)秒級(jí)。為了加快速度,建議在一個(gè)文件添加幾十次后,把內(nèi)容復(fù)制到另一個(gè)文件中去。這樣分散的內(nèi)容會(huì)整合起來(lái)寫(xiě)入少量的BLOCK。這可以大大加快讀寫(xiě)的速度。

總結(jié)

LittleFS作為一個(gè)輕量級(jí)的文件系統(tǒng),具有比FAT小的多的footprint。同時(shí),它又比FAT更加可靠,更適合嵌入式環(huán)境下使用。而SD卡不但容量遠(yuǎn)遠(yuǎn)超過(guò)NOR flash,同時(shí)又能支持SPI接口,并且可以隨意插拔,具有極大的靈活性。將兩者結(jié)合可以使單片機(jī)系統(tǒng)具有很強(qiáng)的數(shù)據(jù)記錄能力。







審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 控制器
    +關(guān)注

    關(guān)注

    112

    文章

    16332

    瀏覽量

    177806
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5336

    瀏覽量

    120230
  • SD卡
    +關(guān)注

    關(guān)注

    2

    文章

    564

    瀏覽量

    63889
  • CSD
    CSD
    +關(guān)注

    關(guān)注

    0

    文章

    56

    瀏覽量

    12680
  • NOR flash
    +關(guān)注

    關(guān)注

    2

    文章

    90

    瀏覽量

    23003

原文標(biāo)題:LittleFS是否可以應(yīng)用于SD卡中呢?不妨這樣試試

文章出處:【微信號(hào):NXP_SMART_HARDWARE,微信公眾號(hào):恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    請(qǐng)問(wèn)QUAD SPI是否支持SD?

    如題,單線(xiàn)SPI讀取SD肯定沒(méi)有4線(xiàn)讀取的速率快,問(wèn)題是STM32某些芯片沒(méi)有SDIO引腳,但是有QUAD SPI,不知道是否能4線(xiàn)驅(qū)動(dòng)SD
    發(fā)表于 03-25 06:34

    SD的識(shí)別、掛載、讀寫(xiě)性能是如何檢測(cè)的?

    工業(yè)網(wǎng)關(guān)和工業(yè)路由器,經(jīng)常用到SD,SD的識(shí)別、掛載、讀寫(xiě)性能是如何檢測(cè)的?有哪些檢測(cè)命
    發(fā)表于 07-24 08:18

    SD是什么?SD命令有哪些基本特性

    SD是什么?分為哪幾類(lèi)?SD的物理結(jié)構(gòu)是由哪些部分組成的?SD命令有哪些基本特性
    發(fā)表于 12-10 07:06

    Rockchip怎樣將SD啟動(dòng)或升級(jí)固件到本地存儲(chǔ)

    分區(qū)上,主控從 SD 啟動(dòng)時(shí),SD 升級(jí)代碼將固件升級(jí)到本地主存儲(chǔ)。主要用于設(shè)備固件損壞,
    發(fā)表于 04-07 15:33

    如何正確卸載SD處理程序?

    我正在努力完成以下任務(wù):無(wú)論是否插入 SD 或未開(kāi)機(jī),我的系統(tǒng)都可以完美地使用 SDCard 運(yùn)行。因此,如果卡在開(kāi)機(jī)時(shí)存在,文件系統(tǒng)就會(huì)工作。它還會(huì)在系統(tǒng)運(yùn)行時(shí)檢測(cè)
    發(fā)表于 12-15 07:00

    請(qǐng)問(wèn)一下SD能掛載littleFS文件系統(tǒng)嗎?

    請(qǐng)問(wèn)一下SD能掛載littleFS文件系統(tǒng)嗎?謝謝!
    發(fā)表于 01-10 16:41

    sdio接口除了可以用于SD的讀寫(xiě),還有什么應(yīng)用嗎?

    sdio接口除了可以用于SD的讀寫(xiě),還有什么應(yīng)用嗎?
    發(fā)表于 10-24 06:42

    sd量產(chǎn)工具,下載

    金士頓sd量產(chǎn)工具:是松下出品的一款比較好用的SD格式化工具(可以處理SD
    發(fā)表于 04-21 00:37 ?777次下載
    <b class='flag-5'>sd</b><b class='flag-5'>卡</b>量產(chǎn)工具,下載

    SD-IDE是什么?

    SD-IDE是什么?  問(wèn):最近在逛電子市場(chǎng)時(shí),發(fā)現(xiàn)一種SD-IDE,商家說(shuō)這種設(shè)備
    發(fā)表于 02-05 09:29 ?1414次閱讀

    如何實(shí)現(xiàn)單片機(jī)讀寫(xiě)SD的底層驅(qū)動(dòng)程序

    SD“交流”的方法,之后再介紹單片機(jī)讀取SD整個(gè)過(guò)程)在官方英文資料《SD協(xié)議》
    發(fā)表于 07-15 17:39 ?2次下載
    如何實(shí)現(xiàn)單片機(jī)讀寫(xiě)<b class='flag-5'>SD</b><b class='flag-5'>卡</b>的底層驅(qū)動(dòng)程序

    如何使用單片機(jī)讀寫(xiě)SD

    SD(Secure Digital Memory Card): 又叫安全數(shù)碼,是一種基于半導(dǎo)體閃存工藝的存儲(chǔ),被廣泛應(yīng)用于便攜式電子
    發(fā)表于 07-10 17:40 ?2次下載
    如何使用單片機(jī)讀寫(xiě)<b class='flag-5'>SD</b><b class='flag-5'>卡</b>

    MicroSD如何轉(zhuǎn)接成SD?

    SD套主要用于將TF(MicroSD)轉(zhuǎn)接成SD
    的頭像 發(fā)表于 01-11 14:00 ?1430次閱讀
    MicroSD<b class='flag-5'>卡</b>如何轉(zhuǎn)接成<b class='flag-5'>SD</b><b class='flag-5'>卡</b>?

    sd是什么有什么用途 TFSD有什么區(qū)別

    和用途 SD,全稱(chēng)Secure Digital Card,是一種以閃存為介質(zhì)的可插拔式存儲(chǔ)。它是由SanDisk、Panasonic和Toshiba聯(lián)合研發(fā)的,于1999年首次發(fā)布,被廣泛
    的頭像 發(fā)表于 01-25 10:44 ?4982次閱讀

    sd是什么有什么用途 TFSD有什么區(qū)別

    SD(Secure Digital Card)是一種便攜式存儲(chǔ)設(shè)備,一般用于存儲(chǔ)照片、視頻、音頻和其他文件。SD是由
    的頭像 發(fā)表于 01-31 13:53 ?5182次閱讀

    SD連接器是什么

    SD連接器是數(shù)字設(shè)備中用于連接SD的一種硬件接口。隨著數(shù)字設(shè)備的普及,SD
    的頭像 發(fā)表于 03-04 10:48 ?6843次閱讀
    RM新时代网站-首页