硬汉嵌入式论坛

 找回密码
 立即注册
查看: 9249|回复: 17
收起左侧

[LittleFS] 坛子里有用LittleFS的吗?

  [复制链接]

65

主题

432

回帖

632

积分

金牌会员

积分
632
发表于 2018-11-10 17:09:41 | 显示全部楼层 |阅读模式
前段儿有个朋友给我推荐了一个嵌入式的小FS,还是ARM出品的,带掉电保护和擦写均衡,默认的BLOCK是4K大小,这明摆着是冲着SPI FLASH去的,应该是非常适合W25Q系列的FLASH的文件系统,只不过不兼容FAT格式,不知道论坛里有没有同学在使用这个小FS,另外安富莱在整个论坛里好像都没提到过这个小型文件系统,针对SPI FLASH,为何安富莱不主推这个小系统呢?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106678
QQ
发表于 2018-11-11 02:18:31 | 显示全部楼层
LittleFS SPI FLASH 例程基于W25Q64 STM32F103
http://www.armbbs.cn/forum.ph ... id=89337&fromuid=58
(出处: 安富莱电子论坛)


littlefs移植使用
http://www.armbbs.cn/forum.php?m ... 6250&fromuid=58
(出处: 安富莱电子论坛)


回复

使用道具 举报

65

主题

432

回帖

632

积分

金牌会员

积分
632
 楼主| 发表于 2018-11-15 17:36:50 | 显示全部楼层
硬汉能不能基于咱家的开发板写一个LittleFS的DEMO,推广下这个小FS
回复

使用道具 举报

65

主题

432

回帖

632

积分

金牌会员

积分
632
 楼主| 发表于 2018-11-15 17:41:10 | 显示全部楼层
本帖最后由 taobaofarmer 于 2018-11-15 17:44 编辑

/*
********************************************************************************
* .忙状态寄存器的BUSY位(D0位,只读)在写动作(页写、扇区擦除、块擦除、芯片擦除、写
* .状态寄存器)期间有效(指示忙),其余状态非忙,此函数执行后一直等到非忙状态才退出
********************************************************************************
*/
void W25Q_CheckBusy(void)
{
    vu8  i;
   
    W25Q_Select();
    {
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0x05;
            
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        do
        {
            while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
            SPI2->DR = 0xff;
               
            while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        }
        while(SPI2->DR & 0x01);
    }
    W25Q_DeSelect();
}

void W25Q_WriteEnable(void)
{
    vu8  i;
   
    W25Q_Select();
    {
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0x06;
            
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
    }   
    W25Q_DeSelect();
}

void W25Q_WriteDisable(void)
{
    vu8  i;
      
    W25Q_Select();
    {
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0x04;
            
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
    }   
    W25Q_DeSelect();
}

//address - 24位地址
//pHead   - 存储读出数据的头指针
//len     - 读出数据长度
void W25Q_ReadData(u32 address, u8 *pHead, u16 len)
{
    u16  i;
    u8   add2, add1, add0;
        
    add2 = (u8)(address>>16);
    add1 = (u8)(address>>8);
    add0 = (u8)(address);
   
    W25Q_CheckBusy();
   
    W25Q_Select();
    {
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);       //1-送读命令03H
        SPI2->DR = 0x03;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
        
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);       //2-送高字节地址
        SPI2->DR = add2;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
            i = SPI2->DR;
        
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);       //3-送中字节地址
        SPI2->DR = add1;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
            i = SPI2->DR;
        
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);       //4-送低字节地址
            SPI2->DR = add0;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
            i = SPI2->DR;
        
            for(i=0; i<len; i++)
        {
            while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
            SPI2->DR = 0xff;
            while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
                pHead = SPI2->DR;
            }
    }   
    W25Q_DeSelect();
}

//扇区(4K Bytes)擦除,该参数是扇区的绝对起始地址
void W25Q_SectorErase(u32 add)
{
    vu8 i;
    u8  add2,add1,add0;
   
    add2    = (u8)(add>>16);
    add1    = (u8)(add>>8);
    add0    = (u8)(add);
   
    W25Q_CheckBusy();
    W25Q_WriteEnable();
   
    W25Q_Select();
    {
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0x20;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = add2;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = add1;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = add0;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
    }   
    W25Q_DeSelect();
   
    W25Q_WriteDisable();
}

//整片擦除
void W25Q_ChipErase(void)
{
    vu8 id;
   
    W25Q_CheckBusy();
    W25Q_WriteEnable();

    W25Q_Select();
    {        
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0xc7;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
            id = SPI2->DR;
        
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0xff;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        id = SPI2->DR;
    }
    W25Q_DeSelect();
   
    W25Q_CheckBusy();
   
    W25Q_WriteDisable();
}

//address - 24位地址
//pHead   - 存储读出数据的头指针
//len     - 读出数据长度
void W25Q_PageProgram(u32 address, u8 *pHead, u16 len)
{
    u16  i;
    vu8  j;
    u8   add2, add1, add0;
        
    add2 = (u8)(address>>16);
    add1 = (u8)(address>>8);
    add0 = (u8)(address);
   
    W25Q_CheckBusy();
    W25Q_WriteEnable();
   
    W25Q_Select();
    {
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0x02;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = add2;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = add1;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = add0;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        i = SPI2->DR;
            
        for(i=0; i<len; i++)
        {
            while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
            SPI2->DR = pHead;
                    
            while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
            j = SPI2->DR;
        }
    }
    W25Q_DeSelect();
   
    W25Q_WriteDisable();
}

//Winbond Serial Flash Manufacturer ID:EFH
u8 W25Q_ReadManufacturer(void)
{
    vu8 id;

    W25Q_CheckBusy();
   
    W25Q_Select();
    {        
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0x9f;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
            id = SPI2->DR;
        
        while((SPI2->SR & SPI_I2S_FLAG_TXE) == RESET);
        SPI2->DR = 0xff;
        while((SPI2->SR & SPI_I2S_FLAG_RXNE) == RESET);
        id = SPI2->DR;
    }
    W25Q_DeSelect();

    return id;
}

//下面是移植代码,只有4个函数,我用的是W25Q128
void lfs_MyConfig(u32 sectors)
{
    //cfg.context = NULL;

    //block device operations
    cfg.read  = lfs_block_device_read;
    cfg.prog  = lfs_block_device_prog;
    cfg.erase = lfs_block_device_erase;
    cfg.sync  = lfs_block_device_sync;

    //block device configuration
    cfg.read_size   = 256;
    cfg.prog_size   = 256;
    cfg.block_size  = 4096;
    cfg.block_count = sectors;
    cfg.lookahead   = 256;

    cfg.read_buffer      = lfs_read_buf;
    cfg.prog_buffer      = lfs_prog_buf;
    cfg.lookahead_buffer = lfs_lookahead_buf;
    cfg.file_buffer      = lfs_file_buf;
}

int lfs_block_device_read(const struct lfs_config *lfsc,
                          lfs_block_t block,
                          lfs_off_t off,
                          void *buffer,
                          lfs_size_t size)
{
    W25Q_ReadData((block*lfsc->block_size)+off, (uint8_t *)buffer, size);
    return LFS_ERR_OK;
}

int lfs_block_device_prog(const struct lfs_config *lfsc,
                          lfs_block_t block,
                          lfs_off_t off,
                          const void *buffer,
                          lfs_size_t size)
{
    W25Q_PageProgram((block*lfsc->block_size)+off, (uint8_t*)buffer, size);
    return LFS_ERR_OK;
}

int lfs_block_device_erase(const struct lfs_config *lfsc, lfs_block_t block)
{
    W25Q_SectorErase(block*lfsc->block_size);
    return LFS_ERR_OK;
}

int lfs_block_device_sync(const struct lfs_config *lfsc)
{
    return LFS_ERR_OK;
}


uTemp[0] = W25Q_ReadManufacturer();
W25Q_ChipErase();
lfs_MyConfig(4096);
   
    err = lfs_mount(&lfs, &cfg);

    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
    if(err)
    {
        lfs_format(&lfs, &cfg);
        lfs_mount(&lfs, &cfg);
    }
   
    lfs_file_open(&lfs, &file_SysParams, "/boot", LFS_O_RDWR | LFS_O_CREAT);
   
    memset(uTemp, 0x77, 32);
    lfs_file_write(&lfs, &file_SysParams, uTemp, 15);
    memset(uTemp, 0x88, 32);
    lfs_file_read(&lfs, &file_SysParams, uTemp, 16);

回复

使用道具 举报

65

主题

432

回帖

632

积分

金牌会员

积分
632
 楼主| 发表于 2018-11-15 17:53:51 | 显示全部楼层
上面的代码分3不分,第1不分是我写的25Q128驱动;第2部分是针对LittleFS的移植,移植比较简单,填好cfg结构的各个参数和4个移植函数;第3部分是我的应用,挂载FS,读写文件,但出现了问题。
首先uTemp[0] = W25Q_ReadManufacturer();这句代码执行是正确的,读出的是厂家的代码0xef
W25Q_ChipErase();执行这句的时候除了问题,这句是整片擦除,擦除指令后我进行了等待,应该是等待挺久之后才会退出这个函数,可是执行的时候,刷一下就执行完了,而且芯片确实没有擦除掉,因为我执行读函数之后,发现读出来的数据根本不是整片擦除之后的效果
Q128的驱动函数,以前在没用FS的时候,保存参数和字库都是没问题的
最后一个,在挂载FS的时候,返回的还是正确的,然后打开文件,写文件,读文件出来的数据根本不对
本来裸奔用的好好的,现在整的都胡思乱想怀疑人生了,哪位用过LittleFS的师兄帮忙看看,谢谢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106678
QQ
发表于 2018-11-17 01:22:22 | 显示全部楼层
taobaofarmer 发表于 2018-11-15 17:36
硬汉能不能基于咱家的开发板写一个LittleFS的DEMO,推广下这个小FS

好的,后面我也研究研究。
回复

使用道具 举报

2

主题

569

回帖

575

积分

金牌会员

积分
575
发表于 2018-11-17 07:37:59 | 显示全部楼层
楼主这个搞法估计不对,打开 写 关闭, 然后再打开 读 再关闭,写完立马读,读不到,fatfs也不行
回复

使用道具 举报

65

主题

432

回帖

632

积分

金牌会员

积分
632
 楼主| 发表于 2018-11-17 09:16:58 | 显示全部楼层
W25Q的驱动问题不大,读、写、扇区擦除用了很多年了,一直没问题
回复

使用道具 举报

18

主题

285

回帖

339

积分

高级会员

积分
339
发表于 2018-11-26 09:52:46 | 显示全部楼层
好东西,有机会比较下在spi lash下littlefs、spiffs和RL-Flashfs的区别与性能
回复

使用道具 举报

65

主题

432

回帖

632

积分

金牌会员

积分
632
 楼主| 发表于 2018-11-26 10:05:13 | 显示全部楼层
你准备什么时候干这事儿?
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2020-4-28 15:50:17 | 显示全部楼层
挂载文件的时候,提示Invalid superblock at,是什么原因啊?
回复

使用道具 举报

8

主题

28

回帖

52

积分

初级会员

积分
52
发表于 2020-7-14 13:35:55 | 显示全部楼层
表示最近也在研究这个文件系统,没搞成功,希望硬汉哥有空也给咱搞个demo参考一下。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106678
QQ
发表于 2020-7-14 15:32:53 | 显示全部楼层
jiulinzeng 发表于 2020-7-14 13:35
表示最近也在研究这个文件系统,没搞成功,希望硬汉哥有空也给咱搞个demo参考一下。

二楼的两个就行。
回复

使用道具 举报

8

主题

25

回帖

49

积分

初级会员

积分
49
发表于 2020-10-31 22:50:10 | 显示全部楼层
有没有人在sd卡上用这个成功的,目前在nor flash上移植没问题,sd上移植各种问题,sd卡底层直接读写都没问题
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106678
QQ
发表于 2020-11-1 10:05:32 | 显示全部楼层
老鸟kkk 发表于 2020-10-31 22:50
有没有人在sd卡上用这个成功的,目前在nor flash上移植没问题,sd上移植各种问题,sd卡底层直接读写都没问 ...

SD卡不太适合用嵌入式文件系统littlefs,还是用FAT的靠谱。
回复

使用道具 举报

65

主题

432

回帖

632

积分

金牌会员

积分
632
 楼主| 发表于 2020-11-1 12:58:14 | 显示全部楼层
看来这个小FS还真是有不少关注的
回复

使用道具 举报

8

主题

25

回帖

49

积分

初级会员

积分
49
发表于 2020-11-13 13:58:24 | 显示全部楼层
请问sd卡用fat的,一般怎么保证断电数据异常的问题,特别是异常断电导致的文件系统损坏?加大电容?
回复

使用道具 举报

1

主题

109

回帖

112

积分

初级会员

固件開發工程師

积分
112
QQ
发表于 2023-8-20 17:50:56 | 显示全部楼层
有人用LittleFS推SPI NAND吗?

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-29 10:13 , Processed in 0.248243 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表